Spaces:
Sleeping
Sleeping
| 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}`)); |