multi / server.js
webzepetoku's picture
Update server.js
db1caa1 verified
Raw
History Blame Contribute Delete
7.83 kB
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}`));