import os import zipfile import tempfile import json import subprocess import time import uuid import glob import struct from fastapi import FastAPI, UploadFile, File, BackgroundTasks from fastapi.responses import FileResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware # ========================================== # C# SCRIPT FABRİKASI (KUSURSUZ X-RAY GÖRSEL ÇIKARICI) # ========================================== def generate_csx_scripts(): extract_code = """using System.IO; using System.Text.Json; using System.Collections.Generic; using System.Linq; using System.Reflection; using UndertaleModLib; // YENİLENMİŞ X-RAY: Hem obje içindeki hem de doğrudan çıplak (byte[]) olan resmi bulur. byte[] GetImg(UndertaleEmbeddedTexture emb) { if (emb == null || emb.TextureData == null) return null; object td = emb.TextureData; if (td is byte[] b) return b; // Eğer resim doğrudan çıplak haldeyse direkt al! var t = td.GetType(); var p = t.GetProperties().FirstOrDefault(x => x.PropertyType == typeof(byte[])); if (p != null) return p.GetValue(td) as byte[]; var f = t.GetFields().FirstOrDefault(x => x.FieldType == typeof(byte[])); if (f != null) return f.GetValue(td) as byte[]; return null; } var dict = new Dictionary(); foreach (var str in Data.Strings) { if (!string.IsNullOrWhiteSpace(str.Content)) { if(!dict.ContainsKey(str.Content)) dict.Add(str.Content, str.Content); } } var opt = new JsonSerializerOptions { WriteIndented = true, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; File.WriteAllText("strings.json", JsonSerializer.Serialize(dict, opt)); // FONT VE RESİMLERİ (ATLAS) ÇIKAR Directory.CreateDirectory("Fonts"); var fontData = new Dictionary(); foreach (var font in Data.Fonts) { if (font.Texture != null && font.Texture.TexturePage != null) { var emb = font.Texture.TexturePage; // İsmi yoksa bile özel isim uydurarak o PNG'yi zorla kaydeder string texName = emb.Name?.Content; if (string.IsNullOrEmpty(texName)) texName = "Texture_" + font.Name.Content; string texPath = "Fonts/" + texName + ".png"; if (!File.Exists(texPath)) { byte[] img = GetImg(emb); if (img != null) { File.WriteAllBytes(texPath, img); } } var glyphs = new Dictionary(); foreach (var g in font.Glyphs) { glyphs[g.Character.ToString()] = new { x = g.SourceX, y = g.SourceY, w = g.SourceWidth, h = g.SourceHeight, shift = g.Shift, offset = g.Offset }; } fontData[font.Name.Content] = new { Texture = texName, Glyphs = glyphs }; } } File.WriteAllText("Fonts/font_data.json", JsonSerializer.Serialize(fontData, opt)); """ repack_code = """using System; using System.IO; using System.Text.Json; using System.Collections.Generic; using System.Linq; using System.Reflection; using UndertaleModLib; // YENİLENMİŞ X-RAY: Değiştirilen PNG resmini oyun dosyasına kusursuzca gömer void SetImg(UndertaleEmbeddedTexture emb, byte[] d) { if (emb == null || emb.TextureData == null) return; object td = emb.TextureData; if (td is byte[]) { // Bazı UMT sürümlerinde TextureData sadece bir byte dizisidir var prop = emb.GetType().GetProperty("TextureData"); if (prop != null && prop.CanWrite) prop.SetValue(emb, d); return; } var t = td.GetType(); var p = t.GetProperties().FirstOrDefault(x => x.PropertyType == typeof(byte[]) && x.CanWrite); if (p != null) { p.SetValue(td, d); return; } var f = t.GetFields().FirstOrDefault(x => x.FieldType == typeof(byte[])); if (f != null) { f.SetValue(td, d); return; } } string json = File.ReadAllText("strings.json"); var dict = JsonSerializer.Deserialize>(json); foreach (var str in Data.Strings) { if (dict.TryGetValue(str.Content, out string newStr)) { str.Content = newStr; } } // FONT RESİMLERİNİ VE KOORDİNAT HARİTASINI GERİ YÜKLE if (Directory.Exists("Fonts")) { string fontJsonPath = "Fonts/font_data.json"; if (File.Exists(fontJsonPath)) { string fJson = File.ReadAllText(fontJsonPath); var fDict = JsonSerializer.Deserialize>(fJson); foreach (var font in Data.Fonts) { if (fDict.TryGetValue(font.Name.Content, out JsonElement fData)) { // Görseli Güncelle if (fData.TryGetProperty("Texture", out JsonElement texElement)) { string texName = texElement.GetString(); string texPath = "Fonts/" + texName + ".png"; if (File.Exists(texPath) && font.Texture != null && font.Texture.TexturePage != null) { SetImg(font.Texture.TexturePage, File.ReadAllBytes(texPath)); } } // Koordinatları Güncelle if (fData.TryGetProperty("Glyphs", out JsonElement glyphsJson)) { foreach (var glyphProp in glyphsJson.EnumerateObject()) { ushort charCode; if (!ushort.TryParse(glyphProp.Name, out charCode)) continue; var gData = glyphProp.Value; var existingGlyph = font.Glyphs.FirstOrDefault(g => g.Character == charCode); if (existingGlyph == null) { existingGlyph = new UndertaleFont.Glyph { Character = charCode }; font.Glyphs.Add(existingGlyph); } existingGlyph.SourceX = gData.GetProperty("x").GetUInt16(); existingGlyph.SourceY = gData.GetProperty("y").GetUInt16(); existingGlyph.SourceWidth = gData.GetProperty("w").GetUInt16(); existingGlyph.SourceHeight = gData.GetProperty("h").GetUInt16(); existingGlyph.Shift = gData.GetProperty("shift").GetInt16(); existingGlyph.Offset = gData.GetProperty("offset").GetInt16(); } } } } } } // Reflection ile Dinamik Kaydetme using (var stream = new FileStream("repacked_game.droid", FileMode.Create, FileAccess.Write)) { var m2 = typeof(UndertaleIO).GetMethod("Write", new[] { typeof(Stream), typeof(UndertaleData) }); if (m2 != null) m2.Invoke(null, new object[] { stream, Data }); else { var m3 = typeof(UndertaleIO).GetMethods().FirstOrDefault(m => m.Name == "Write" && m.GetParameters().Length == 3); if (m3 != null) m3.Invoke(null, new object[] { stream, Data, null }); else { var w3 = typeof(UndertaleWriter).GetMethods().FirstOrDefault(m => m.Name == "Write" && m.GetParameters().Length == 3); w3?.Invoke(null, new object[] { stream, Data, null }); } } } """ with open("/app/extract.csx", "w", encoding="utf-8") as f: f.write(extract_code) with open("/app/repack.csx", "w", encoding="utf-8") as f: f.write(repack_code) generate_csx_scripts() app = FastAPI() app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) jobs = {} def get_cli_command(): paths = glob.glob("/app/umt/**/UndertaleModCli*", recursive=True) + glob.glob("/app/umt/**/UTMT*", recursive=True) for p in paths: if os.path.isfile(p): if p.endswith(".dll"): return ["dotnet", p] elif not p.endswith(".exe"): return [p] return ["/app/umt/UTMT_CLI"] def deep_heal_droid(filepath): try: with open(filepath, "rb") as f: data = bytearray(f.read()) original_header = data[0:4] if original_header != b'FORM': data[0:4] = b'FORM' offset = 8 file_len = len(data) log = [] while offset < file_len: if offset + 8 > file_len: data = data[:offset] break chunk_name = data[offset:offset+4] chunk_size = struct.unpack(" file_len: log.append(f"TUZAK KESİLDİ: Bozuk Blok '{chunk_name}' | Konum: {offset}") data = data[:offset] break offset += 8 + chunk_size data[4:8] = struct.pack("