-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from GZH2K19/main
Main
- Loading branch information
Showing
2 changed files
with
95 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,113 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
from sys import argv | ||
from pathlib import Path | ||
import os | ||
import json | ||
|
||
def transEncKey(key:str): | ||
import hashlib | ||
md5 = hashlib.md5(key.encode()).hexdigest() | ||
lis = [int(md5[i:i+2], 16) for i in range(0, len(md5), 2)] | ||
lis += lis[::-1] | ||
return lis | ||
|
||
def decrypt(data:bytes, tEncKey:list[int]): | ||
try: | ||
header = ",".join("{:02x}".format(i) for i in data[:32]) | ||
if header != "41,52,54,00,45,4e,43,52,59,50,54,45,52,31,30,30,46,52,45,45,00,56,45,52,53,49,4f,4e,00,00,00,00": | ||
raise Exception | ||
num = min(len(data), len(tEncKey)) | ||
data = bytearray(data[num:]) | ||
for i in range(num): | ||
data[i] ^= tEncKey[i] | ||
return bytes(data) | ||
except: | ||
return | ||
class E3000D: | ||
"""3000 Decrypter""" | ||
HEADER_STR = "41,52,54,00,45,4e,43,52,59,50,54,45,52,31,30,30,46,52,45,45,00,56,45,52,53,49,4f,4e,00,00,00,00" | ||
def __init__(self, key:str): | ||
self.key = self.trans_key(key) | ||
self.key_len = len(self.key) | ||
|
||
@staticmethod | ||
def trans_key(key:str): | ||
import hashlib | ||
md5 = hashlib.md5(key.encode()).hexdigest() | ||
lis = [int(md5[i:i+2], 16) for i in range(0, len(md5), 2)] | ||
return lis + lis[::-1] | ||
|
||
def decrypt(self, data:bytes): | ||
try: | ||
header = ','.join("{:02x}".format(i) for i in data[:32]) | ||
if header != E3000D.HEADER_STR: | ||
raise Exception("Invalid E3000 header") | ||
leng = min(len(data), self.key_len) | ||
data = bytearray(data[leng:]) | ||
for i in range(leng): | ||
data[i] ^= self.key[i] | ||
return bytes(data) | ||
except: | ||
return None | ||
|
||
class NKD: | ||
"""No-Key Decrypter""" | ||
HEADER_STR = "89,50,4E,47,0D,0A,1A,0A,00,00,00,0D,49,48,44,52" | ||
def __init__(self): | ||
self.HEADER_LEN = 16 | ||
self.HEADER = self.get_header(self.HEADER_LEN) | ||
|
||
@staticmethod | ||
def get_header(header_len:int=16): | ||
header_array = NKD.HEADER_STR.split(',') | ||
png_header = bytearray(header_len) | ||
for r in range(header_len): | ||
png_header[r] = int(header_array[r], 16) | ||
return png_header | ||
|
||
def decrypt(self, data:bytes): | ||
data = data[self.HEADER_LEN*2:] | ||
arr = bytearray(self.HEADER_LEN + len(data)) | ||
arr[:self.HEADER_LEN] = self.HEADER | ||
arr[self.HEADER_LEN:] = data | ||
return arr | ||
|
||
def process_game(game_path:str): | ||
game = Path(game_path) | ||
print("Processing:", game) | ||
def process_project(project_path:Path): | ||
print("Processing:", project_path) | ||
|
||
json_system = game/"data"/"System.json" | ||
if not json_system.exists(): | ||
sys_json = project_path / "data/System.json" | ||
if not sys_json.exists(): | ||
print("Error: System.json not found.\n") | ||
return | ||
with open(json_system, "r", encoding="utf-8-sig") as f: | ||
data = json.load(f) | ||
encKey = data.get("encryptionKey") | ||
if not encKey: | ||
print("Error: Encryption key not found.\n") | ||
return | ||
print("Encryption key:", encKey) | ||
tEncKey = transEncKey(encKey) | ||
|
||
with open(sys_json, "r", encoding="utf-8-sig") as f: | ||
sys_info = json.load(f) | ||
folderList = [] | ||
if data.get("hasEncryptedImages"): | ||
if sys_info.get("hasEncryptedImages"): | ||
folderList.append("img") | ||
if data.get("hasEncryptedAudio"): | ||
if sys_info.get("hasEncryptedAudio"): | ||
folderList.append("audio") | ||
if not folderList: | ||
print("Error: Encrypted files not found.\n") | ||
print("Warning: Encrypted files not found.\n") | ||
return | ||
|
||
if (project_path / "js/plugins/Art_Decrypterator3000.js").exists(): | ||
dec_type = "E3000" | ||
encKey = sys_info.get("encryptionKey") | ||
if not encKey: | ||
print("Error: Encryption key not found.\n") | ||
return | ||
print("Encryption key:", encKey) | ||
decrypter = E3000D(encKey) | ||
else: | ||
dec_type = "default" | ||
decrypter = NKD() | ||
print("Decryption type:", dec_type) | ||
|
||
for folder in folderList: | ||
print("Decrypting", folder, "files...") | ||
ori = game/folder | ||
new = game/(folder+"_decrypted") | ||
for r in ori.rglob("*.*_"): | ||
with open(r, "rb") as f: | ||
byt = decrypt(f.read(), tEncKey) | ||
if not byt: | ||
print("Failed to decrypt:", r.relative_to(game)) | ||
print("Decrypting folder:", folder) | ||
ori = project_path / folder | ||
new = project_path / (folder+"_dec") | ||
for file in ori.rglob("*.*_"): | ||
with open(file, "rb") as f: | ||
data = decrypter.decrypt(f.read()) | ||
if not data: | ||
print("Failed to decrypt:", file.relative_to(project_path)) | ||
continue | ||
file = str(new/r.relative_to(ori))[:-1] | ||
os.makedirs(os.path.dirname(file), exist_ok=True) | ||
with open(file, "wb") as f: | ||
f.write(byt) | ||
new_file = new / file.relative_to(ori).parent / file.name[:-1] | ||
new_file.parent.mkdir(parents=True, exist_ok=True) | ||
with open(new_file, "wb") as f: | ||
f.write(data) | ||
print("Done.\n") | ||
|
||
|
||
if __name__ == "__main__": | ||
if not argv[1:]: | ||
print("No arguments provided. Please drag the game folder onto this script.") | ||
print("No arguments provided. Please drag the project folder onto this script.\n") | ||
else: | ||
try: | ||
for game_path in argv[1:]: | ||
process_game(game_path) | ||
except: | ||
from traceback import format_exc | ||
print(format_exc()) | ||
os.system("pause") | ||
for path in argv[1:]: | ||
try: | ||
process_project(Path(path)) | ||
except: | ||
from traceback import format_exc | ||
print(format_exc(), '\n') | ||
input("Press ENTER to exit...") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
## Instruction | ||
Just drag the game folder onto this script (on Windows). The decrypted resource folder will be created in the same directory as the source folder. | ||
Just drag the project folder onto this script. The decrypted resource folder will be created in the same directory as the source folder. | ||
|
||
Supported encryption types: | ||
- Default | ||
- Encrypterator 3000 | ||
|
||
## Disclaimer | ||
All code is for research and educational purposes only. Do not use for illegal activities. The author is also not responsible for any consequences arising from the use of this code. | ||
All code is for research and educational purposes only. Do not use for illegal activities. The author is also not responsible for any consequences arising from the use of the code. |