import os import zipfile import tempfile import json import re import subprocess import UnityPy from fastapi import FastAPI, UploadFile, File from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware from typing import Optional app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.post("/generate_dll") async def generate_dll(binary_file: UploadFile = File(...), metadata_file: UploadFile = File(...)): temp_dir = tempfile.mkdtemp() binary_path = os.path.join(temp_dir, binary_file.filename) metadata_path = os.path.join(temp_dir, metadata_file.filename) with open(binary_path, "wb") as f: f.write(await binary_file.read()) with open(metadata_path, "wb") as f: f.write(await metadata_file.read()) output_dir = os.path.join(temp_dir, "output") os.makedirs(output_dir, exist_ok=True) subprocess.run(["/app/dumper/Il2CppDumper", binary_path, metadata_path, output_dir], capture_output=True) zip_path = os.path.join(temp_dir, "DummyDlls.zip") with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for root, _, files in os.walk(output_dir): for file in files: if file.endswith(".dll"): zipf.write(os.path.join(root, file), arcname=file) return FileResponse(zip_path, media_type="application/zip", filename="DummyDlls.zip") @app.post("/extract") async def extract_dialogues(file: UploadFile = File(...), assembly_file: Optional[UploadFile] = File(None)): temp_dir = tempfile.mkdtemp() asset_path = os.path.join(temp_dir, file.filename) with open(asset_path, "wb") as f: f.write(await file.read()) env = UnityPy.load(asset_path) zip_filename = os.path.join(temp_dir, f"{file.filename}_dialogues.zip") extracted_count = 0 with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: for obj in env.objects: if obj.type.name in ["TextAsset", "MonoBehaviour"]: # Dosya ismini geri paketlerken tanıyabilmemiz için ID'yi garanti altına alıyoruz obj_name = f"{obj.type.name}_{obj.path_id}" if obj.type.name == "MonoBehaviour": raw_data = obj.get_raw_data() found_strings = {} i = 28 while i <= len(raw_data) - 4: length = int.from_bytes(raw_data[i:i+4], 'little') if 2 <= length <= 5000 and (i + 4 + length) <= len(raw_data): str_bytes = raw_data[i+4 : i+4+length] pad_len = (4 - (length % 4)) % 4 if (i + 4 + length + pad_len) <= len(raw_data): padding = raw_data[i+4+length : i+4+length+pad_len] if all(b == 0 for b in padding): try: text = str_bytes.decode('utf-8') if len(text.strip()) > 1 and any(c.isalpha() for c in text): found_strings[text] = text i += 4 + length + pad_len continue except UnicodeDecodeError: pass i += 4 if found_strings: zipf.writestr(f"{obj_name}_raw.json", json.dumps(found_strings, indent=4, ensure_ascii=False).encode('utf-8')) extracted_count += 1 elif obj.type.name == "TextAsset": try: content = obj.read().text.encode('utf-8') zipf.writestr(f"{obj_name}.txt", content) extracted_count += 1 except: pass return FileResponse(zip_filename, media_type="application/zip", filename=f"{file.filename}_dialogues.zip") @app.post("/repack") async def repack_dialogues(asset_file: UploadFile = File(...), mod_zip: UploadFile = File(...)): temp_dir = tempfile.mkdtemp() asset_path = os.path.join(temp_dir, asset_file.filename) zip_path = os.path.join(temp_dir, mod_zip.filename) with open(asset_path, "wb") as f: f.write(await asset_file.read()) with open(zip_path, "wb") as f: f.write(await mod_zip.read()) # DİNAMİK PAKETLEME VE IŞIK HIZINDA O(1) ALGORİTMASI env = UnityPy.load(asset_path) # 1. Zip'in içindeki çevirileri ID'lerine göre hafızaya al (Hız Aşırtma) path_id_to_translations = {} with zipfile.ZipFile(zip_path, 'r') as zipf: for file_info in zipf.infolist(): filename = file_info.filename if filename.endswith(".json"): # Regex ile dosya isminden ID'yi güvenle kopar match = re.search(r'_(\d+)(_raw)?\.json$', filename) if match: path_id = int(match.group(1)) content = zipf.read(filename) path_id_to_translations[path_id] = json.loads(content.decode('utf-8')) # 2. Oyun dosyasını sadece BİR KERE tara (Zaman Aşımını Kırar) for obj in env.objects: if obj.path_id in path_id_to_translations: translations = path_id_to_translations[obj.path_id] if obj.type.name == "MonoBehaviour": raw_data = obj.get_raw_data() patched_data = bytearray(raw_data) for orig_str, trans_str in translations.items(): if orig_str == trans_str: continue # Orijinal metnin tam Binary (İkili) haritasını çıkar orig_bytes = orig_str.encode('utf-8') orig_pad = (4 - (len(orig_bytes) % 4)) % 4 orig_block = len(orig_bytes).to_bytes(4, 'little') + orig_bytes + (b'\x00' * orig_pad) # Çevrilmiş YENİ metnin (uzunluğu fark etmeksizin) Binary haritasını çıkar trans_bytes = trans_str.encode('utf-8') trans_pad = (4 - (len(trans_bytes) % 4)) % 4 trans_block = len(trans_bytes).to_bytes(4, 'little') + trans_bytes + (b'\x00' * trans_pad) # Bloğu değiştir (Eski sınırlar kalktı!) patched_data = patched_data.replace(orig_block, trans_block) # Değişmiş veriyi kaydet (UnityPy burada POINTER REBUILD yapar!) obj.set_raw_data(bytes(patched_data)) # 3. Dosyayı Kaydet output_path = os.path.join(temp_dir, f"repacked_{asset_file.filename}") with open(output_path, "wb") as f: f.write(env.file.save()) return FileResponse(output_path, media_type="application/octet-stream", filename=asset_file.filename)