const express = require('express'); const bodyParser = require('body-parser'); const axios = require('axios'); const { execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); const archiver = require('archiver'); const app = express(); app.use(bodyParser.json({ limit: '100mb' })); const DUMMY_ZIP = 'dummy_lite.zepeto.zip'; if (!fs.existsSync('uploads')) fs.mkdirSync('uploads', { recursive: true }); function getTS() { return new Date().toLocaleTimeString(); } function findPath(base, targetName, isFolder = false) { if (!fs.existsSync(base)) return null; const items = fs.readdirSync(base); for (const item of items) { const fullPath = path.join(base, item); const stat = fs.lstatSync(fullPath); if (isFolder && stat.isDirectory() && item.toLowerCase() === targetName.toLowerCase()) return fullPath; if (!isFolder && stat.isFile() && item.toLowerCase().endsWith(targetName.toLowerCase())) return fullPath; if (stat.isDirectory()) { const found = findPath(fullPath, targetName, isFolder); if (found) return found; } } return null; } function createZepetoFile(sourceDir, outPath) { return new Promise((resolve, reject) => { const output = fs.createWriteStream(outPath); const archive = archiver('zip', { zlib: { level: 6 } }); output.on('close', resolve); archive.on('error', reject); archive.pipe(output); archive.directory(sourceDir, false); archive.finalize(); }); } app.post('/convert', async (req, res) => { const logs = []; const logMsg = (msg) => { const line = `[${getTS()}] ${msg}`; console.log(line); logs.push(line); }; const targetFilename = req.body.filename || 'result.zepeto'; logMsg(`Mulai Konversi (Ultimate Sync Mode): ${targetFilename}`); const timeId = Date.now(); const tempHP = path.join(__dirname, 'uploads', `hp_${timeId}.zepeto`); const extDummy = path.join(__dirname, 'uploads', `ext_dummy_${timeId}`); const extHP = path.join(__dirname, 'uploads', `ext_hp_${timeId}`); const stagePath = path.join(__dirname, 'uploads', `stage_${timeId}`); const finalOut = path.join(__dirname, 'uploads', `final_${timeId}.zepeto`); try { logMsg(`[STEP 1] Mendownload file High Poly...`); const response = await axios({ method: 'GET', url: req.body.file_url, responseType: 'stream' }); const writer = fs.createWriteStream(tempHP); response.data.pipe(writer); writer.on('finish', async () => { try { logMsg(`[STEP 2] Ekstraksi Dummy dan High Poly...`); fs.mkdirSync(extDummy, { recursive: true }); fs.mkdirSync(extHP, { recursive: true }); fs.mkdirSync(stagePath, { recursive: true }); execSync(`unzip -q -o "${DUMMY_ZIP}" -d "${extDummy}"`); execSync(`unzip -q -o "${tempHP}" -d "${extHP}"`); logMsg(`[STEP 3] Analisis Struktur & Penyelarasan GUID Mutlak...`); // 1. Ekstrak GUID dari Meta Dummy (KTP Target) const dummyMetaPath = findPath(extDummy, '.prefab.meta', false); if (!dummyMetaPath) throw new Error("File .meta Dummy tidak ditemukan!"); const dummyPrefabName = path.basename(dummyMetaPath, '.meta'); const dummyMetaContent = fs.readFileSync(dummyMetaPath, 'utf8'); const dummyGuidMatch = dummyMetaContent.match(/guid:\s*([a-f0-9]+)/); if (!dummyGuidMatch) throw new Error("Gagal membaca GUID dari Dummy!"); const dummyGuid = dummyGuidMatch[1]; // 2. Ekstrak GUID dari Meta Raksasa (KTP Asli Raksasa) const hpMetaPath = findPath(extHP, '.prefab.meta', false); if (!hpMetaPath) throw new Error("File .meta Raksasa tidak ditemukan!"); const hpMetaContent = fs.readFileSync(hpMetaPath, 'utf8'); const hpGuidMatch = hpMetaContent.match(/guid:\s*([a-f0-9]+)/); if (!hpGuidMatch) throw new Error("Gagal membaca GUID dari Raksasa!"); const hpGuid = hpGuidMatch[1]; const stageBundles = path.join(stagePath, 'assetbundles'); fs.mkdirSync(stageBundles, { recursive: true }); // 3. Pasang KTP Dummy (.meta) ke Stage Root fs.copyFileSync(dummyMetaPath, path.join(stagePath, dummyPrefabName + '.meta')); logMsg(`KTP Dummy diamankan. GUID: ${dummyGuid}`); // 4. Eksekusi Pencucian Otak: Timpa GUID di dalam Kertas Blueprint Raksasa const hpPrefabPath = findPath(extHP, '.prefab', false); let prefabContent = fs.readFileSync(hpPrefabPath, 'utf8'); // Operasi Mutlak: Ganti GUID Raksasa di dalam m_RenderDataKey menjadi GUID Dummy prefabContent = prefabContent.split(hpGuid).join(dummyGuid); fs.writeFileSync(path.join(stagePath, dummyPrefabName), prefabContent); logMsg(`Pencucian otak Blueprint sukses! Mengganti ${hpGuid} -> ${dummyGuid}`); // 5. Eksekusi Python untuk mencuci identitas CAB Biner const hpBundleDir = findPath(extHP, 'assetbundles', true); const dummyBundleDir = findPath(extDummy, 'assetbundles', true); if (!hpBundleDir || !dummyBundleDir) throw new Error("Folder assetbundles tidak ditemukan!"); logMsg(`[STEP 4] Memanggil Python untuk Surgical Patching CAB...`); try { const pyLog = execSync(`python3 uabe_packer.py "${dummyBundleDir}" "${hpBundleDir}" "${stageBundles}"`).toString(); pyLog.split('\n').forEach(line => { if(line.trim()) logMsg(`[PY] ${line}`); }); if (!pyLog.includes('Injeksi selesai 100%')) throw new Error("Python gagal melakukan patching."); logMsg(`[STEP 5] Repacking ZIP (Flat Root Strategy)...`); const output = fs.createWriteStream(finalOut); const archive = archiver('zip', { zlib: { level: 6 } }); output.on('close', () => { logMsg(`[FINISH] Bypass Sempurna. Ukuran: ${fs.statSync(finalOut).size} bytes.`); res.download(finalOut, targetFilename, () => { try { fs.rmSync(extDummy, { recursive: true, force: true }); fs.rmSync(extHP, { recursive: true, force: true }); fs.rmSync(stagePath, { recursive: true, force: true }); fs.unlinkSync(tempHP); fs.unlinkSync(finalOut); } catch (e) {} }); }); archive.pipe(output); archive.directory(stagePath, false); archive.finalize(); } catch (pyErr) { logMsg(`[CRASH] Kesalahan Python: ${pyErr.message}`); res.status(500).json({ error: pyErr.message, logs }); } } catch (err) { logMsg(`[ERROR] ${err.message}`); res.status(500).json({ error: err.message, logs }); } }); } catch (err) { logMsg(`[ERROR] Download gagal: ${err.message}`); res.status(500).json({ error: err.message, logs }); } }); const PORT = 7860; app.listen(PORT, () => console.log(`[${getTS()}] 🚀 Server Ultimate Sync Bypass Aktif di Port ${PORT}`));