Team Ai
Apppublic

Cnass/UnityPy

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
app.py282 linesDownload Raw Back to root
1import os2import zipfile3import tempfile4import json5import re6import subprocess7import time8import uuid9import UnityPy10from UnityPy.enums import CompressionFlags11from fastapi import FastAPI, UploadFile, File, BackgroundTasks12from fastapi.responses import FileResponse, JSONResponse13from fastapi.middleware.cors import CORSMiddleware14from typing import Optional15 16app = FastAPI()17 18app.add_middleware(19    CORSMiddleware,20    allow_origins=["*"], 21    allow_credentials=True,22    allow_methods=["*"],23    allow_headers=["*"],24)25 26jobs = {}27 28def process_repack(job_id: str, asset_path: str, zip_path: str, original_filename: str):29    try:30        jobs[job_id] = {"status": "processing", "progress": 5, "message": "Oyun dosyaları çözümleniyor..."}31        env = UnityPy.load(asset_path)32        33        filename_to_translations = {}34        with zipfile.ZipFile(zip_path, 'r') as zipf:35            for file_info in zipf.infolist():36                filename = file_info.filename37                if "__MACOSX" in filename or filename.split("/")[-1].startswith("._"): continue38                if filename.endswith(".json"):39                    try:40                        content = zipf.read(filename)41                        filename_to_translations[filename.split("/")[-1]] = json.loads(content.decode('utf-8'))42                    except json.JSONDecodeError: pass43 44        total_objects = len(env.objects)45        processed = 046        start_time = time.time()47 48        for obj in env.objects:49            processed += 150            if processed % 50 == 0:51                elapsed = time.time() - start_time52                rate = processed / elapsed if elapsed > 0 else 153                remaining_seconds = int((total_objects - processed) / rate)54                prog = 5 + int((processed / total_objects) * 45)55                56                jobs[job_id].update({57                    "progress": prog,58                    "message": f"Diyaloglar yamalanıyor... (Kalan Süre: ~{remaining_seconds} sn)"59                })60 61            if obj.type.name in ["TextAsset", "MonoBehaviour"]:62                asset_name = getattr(obj.assets_file, 'name', 'assets')63                asset_name = "".join([c if c.isalnum() else "_" for c in asset_name])64                obj_name = f"{asset_name}_{obj.type.name}_{obj.path_id}"65                target_filename = f"{obj_name}_raw.json"66                67                if target_filename in filename_to_translations:68                    translations = filename_to_translations[target_filename]69                    if obj.type.name == "MonoBehaviour":70                        raw_data = obj.get_raw_data()71                        patched_data = bytearray(raw_data)72                        # ... (process_repack içindeki for döngüsünde) ...73                    for orig_str, trans_str in translations.items():74                        if orig_str == trans_str: continue75                        76                        orig_bytes = orig_str.encode('utf-8')77                        trans_bytes = trans_str.encode('utf-8')78                        79                        # KRİTİK DEĞİŞİKLİK: 4 baytlık hizalamayı (alignment) zorla yapıyoruz80                        # Unity, dosyaları 4'ün katı olan bloklarda okur. 81                        # Metni değiştirdiğimizde bu bloğu bozarız. 82                        # Aşağıdaki kod, metni 4 bayta tamamlaya(padding) zorlar.83                        84                        orig_pad = (4 - (len(orig_bytes) % 4)) % 485                        orig_block = len(orig_bytes).to_bytes(4, 'little') + orig_bytes + (b'\x00' * orig_pad)86                        87                        trans_pad = (4 - (len(trans_bytes) % 4)) % 488                        trans_block = len(trans_bytes).to_bytes(4, 'little') + trans_bytes + (b'\x00' * trans_pad)89                        90                        # Bloğu sadece ve sadece %100 eşleşen bayt dizisinde değiştir91                        patched_data = patched_data.replace(orig_block, trans_block)92                        obj.set_raw_data(bytes(patched_data))93 94        jobs[job_id].update({95            "progress": 60, 96            "message": "Sıkıştırma algoritmaları deneniyor... (Sunucu hızına göre 1-3 dakika sürebilir)"97        })98        99        output_path = os.path.join(tempfile.gettempdir(), f"repacked_{job_id}_{original_filename}")100        101        compression_error = ""102        try:103            # 1. Deneme: Modern UnityPy String Formatı (LZMA)104            packed_data = env.file.save(packer="lzma")105        except Exception as e1:106            try:107                # 2. Deneme: Daha hızlı olan LZ4 Formatı108                packed_data = env.file.save(packer="lz4")109            except Exception as e2:110                try:111                    # 3. Deneme: Eski Sürüm UnityPy Enum Formatı112                    packed_data = env.file.save(packer=CompressionFlags.LZMA)113                except Exception as e3:114                    # HİÇBİRİ İŞE YARAMAZSA HATAYI EKRANA YANSIT115                    compression_error = f"LZMA_Str: {str(e1)} | LZ4_Str: {str(e2)} | Enum: {str(e3)}"116                    packed_data = env.file.save()117                    118        with open(output_path, "wb") as f:119            f.write(packed_data)120 121        if compression_error:122            final_msg = f"Dosya hazırlandı AMA Sıkıştırılamadı! Hata Logu: {compression_error}"123        else:124            final_msg = "İşlem %100 Tamamlandı ve Orijinal Boyuta Sıkıştırıldı! Dosya İndiriliyor..."125 126        jobs[job_id].update({127            "status": "completed",128            "progress": 100,129            "message": final_msg,130            "file_path": output_path,131            "filename": original_filename132        })133 134    except Exception as e:135        jobs[job_id].update({"status": "error", "message": f"Kritik Hata: {str(e)}"})136 137 138# --------- API UÇ NOKTALARI (ENDPOINTS) ---------139 140@app.post("/generate_dll")141async def generate_dll(binary_file: UploadFile = File(...), metadata_file: UploadFile = File(...)):142    temp_dir = tempfile.mkdtemp()143    binary_path = os.path.join(temp_dir, binary_file.filename)144    metadata_path = os.path.join(temp_dir, metadata_file.filename)145    with open(binary_path, "wb") as f: f.write(await binary_file.read())146    with open(metadata_path, "wb") as f: f.write(await metadata_file.read())147    output_dir = os.path.join(temp_dir, "output")148    os.makedirs(output_dir, exist_ok=True)149    subprocess.run(["/app/dumper/Il2CppDumper", binary_path, metadata_path, output_dir], capture_output=True)150    zip_path = os.path.join(temp_dir, "DummyDlls.zip")151    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:152        for root, _, files in os.walk(output_dir):153            for file in files:154                if file.endswith(".dll"): zipf.write(os.path.join(root, file), arcname=file)155    return FileResponse(zip_path, media_type="application/zip", filename="DummyDlls.zip")156 157@app.post("/extract")158async def extract_dialogues(file: UploadFile = File(...), assembly_file: Optional[UploadFile] = File(None)):159    temp_dir = tempfile.mkdtemp()160    asset_path = os.path.join(temp_dir, file.filename)161    with open(asset_path, "wb") as f: f.write(await file.read())162    env = UnityPy.load(asset_path)163    zip_filename = os.path.join(temp_dir, f"{file.filename}_dialogues.zip")164    with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:165        for obj in env.objects:166            if obj.type.name in ["TextAsset", "MonoBehaviour"]:167                asset_name = getattr(obj.assets_file, 'name', 'assets')168                asset_name = "".join([c if c.isalnum() else "_" for c in asset_name])169                obj_name = f"{asset_name}_{obj.type.name}_{obj.path_id}"170                if obj.type.name == "MonoBehaviour":171                    raw_data = obj.get_raw_data()172                    found_strings = {}173                    i = 28174                    while i <= len(raw_data) - 4:175                        length = int.from_bytes(raw_data[i:i+4], 'little')176                        if 2 <= length <= 5000 and (i + 4 + length) <= len(raw_data):177                            str_bytes = raw_data[i+4 : i+4+length]178                            pad_len = (4 - (length % 4)) % 4179                            if (i + 4 + length + pad_len) <= len(raw_data):180                                padding = raw_data[i+4+length : i+4+length+pad_len]181                                if all(b == 0 for b in padding):182                                    try:183                                        text = str_bytes.decode('utf-8')184                                        if len(text.strip()) > 1 and any(c.isalpha() for c in text): found_strings[text] = text185                                        i += 4 + length + pad_len186                                        continue187                                    except UnicodeDecodeError: pass188                        i += 4189                    if found_strings: zipf.writestr(f"{obj_name}_raw.json", json.dumps(found_strings, indent=4, ensure_ascii=False).encode('utf-8'))190                elif obj.type.name == "TextAsset":191                    try:192                        content = obj.read().text.encode('utf-8')193                        zipf.writestr(f"{obj_name}.txt", content)194                    except: pass195    return FileResponse(zip_filename, media_type="application/zip", filename=f"{file.filename}_dialogues.zip")196 197@app.post("/repack_async")198async def repack_async(background_tasks: BackgroundTasks, asset_file: UploadFile = File(...), mod_zip: UploadFile = File(...)):199    job_id = str(uuid.uuid4())200    temp_dir = tempfile.mkdtemp()201    asset_path = os.path.join(temp_dir, asset_file.filename)202    zip_path = os.path.join(temp_dir, mod_zip.filename)203    204    with open(asset_path, "wb") as f: f.write(await asset_file.read())205    with open(zip_path, "wb") as f: f.write(await mod_zip.read())206    207    background_tasks.add_task(process_repack, job_id, asset_path, zip_path, asset_file.filename)208    jobs[job_id] = {"status": "queued", "progress": 0, "message": "İşlem sıraya alındı, başlıyor..."}209    return {"job_id": job_id}210 211@app.get("/status/{job_id}")212async def get_status(job_id: str):213    if job_id not in jobs: return JSONResponse(status_code=404, content={"error": "Görev bulunamadı"})214    return jobs[job_id]215 216@app.get("/download/{job_id}")217async def download_file(job_id: str):218    job = jobs.get(job_id)219    if not job or job.get("status") != "completed": return JSONResponse(status_code=400, content={"error": "Dosya hazır değil"})220    return FileResponse(job["file_path"], media_type="application/octet-stream", filename=job["filename"])221 222@app.post("/check_errors")223async def check_errors(mod_zip: UploadFile = File(...)):224    temp_dir = tempfile.mkdtemp()225    zip_path = os.path.join(temp_dir, mod_zip.filename)226    with open(zip_path, "wb") as f: f.write(await mod_zip.read())227 228    report_lines = ["=== SİSTEM KODU ÇEVİRİSİ HATA TARAMA RAPORU ===\n"]229    report_lines.append("Aşağıdaki listede bulunan çeviriler, oyunun çökmesine sebep olan 'Sistem Kodları' olabilir.")230    report_lines.append("Lütfen bu dosyalara girip, bu kelimeleri tekrar orijinal (İngilizce) haline geri döndürün.\n")231    232    error_count = 0233 234    with zipfile.ZipFile(zip_path, 'r') as zipf:235        for file_info in zipf.infolist():236            filename = file_info.filename237            if "__MACOSX" in filename or filename.split("/")[-1].startswith("._"): continue238            239            if filename.endswith(".json"):240                try:241                    content = zipf.read(filename)242                    translations = json.loads(content.decode('utf-8'))243                    file_suspects = []244 245                    for orig, trans in translations.items():246                        if orig != trans: # Sadece çevrilmiş olanları kontrol et247                            is_system = False248                            249                            # 1. Kural: Cümlede hiç boşluk yoksa büyük ihtimalle koddur (Örn: PlayerSpawnPoint)250                            if " " not in orig and len(orig) > 2: is_system = True251                            252                            # 2. Kural: Alt tire içeriyorsa koddur (Örn: bg_sky_01)253                            if "_" in orig: is_system = True254                            255                            # 3. Kural: Dosya yolu içeriyorsa koddur (Örn: Assets/Sprites/hero)256                            if "/" in orig or "\\" in orig: is_system = True257                            258                            if is_system:259                                file_suspects.append(f"  [RİSKLİ] Orijinal: '{orig}'\n           Çevirin: '{trans}'\n")260 261                    if file_suspects:262                        report_lines.append(f"--------------------------------------------------")263                        report_lines.append(f"DOSYA: {filename}")264                        report_lines.append(f"--------------------------------------------------")265                        report_lines.extend(file_suspects)266                        error_count += len(file_suspects)267                except:268                    pass269 270    if error_count == 0:271        report_lines.append("\nHarika! Görünürde hiçbir sistem kodunu çevirmemişsin. Çökme sebebi başka olabilir.")272    else:273        report_lines.append(f"\n==================================================")274        report_lines.append(f"TOPLAM BULUNAN RİSKLİ ÇEVİRİ: {error_count} ADET")275        report_lines.append(f"==================================================")276 277    report_path = os.path.join(temp_dir, "hata_raporu.txt")278    with open(report_path, "w", encoding="utf-8") as f:279        f.write("\n".join(report_lines))280 281    return FileResponse(report_path, media_type="text/plain", filename="hata_raporu.txt")282