File size: 7,831 Bytes
1e3f1ee
e7ba391
 
ecc3587
e7ba391
1e3f1ee
e7ba391
1e3f1ee
 
e7ba391
171abdd
e7ba391
171abdd
e7ba391
53da9ca
989adf8
 
bb8b08c
989adf8
bb8b08c
 
 
 
 
 
 
 
f8b7647
 
 
 
7bc4d06
 
db1caa1
 
 
 
 
 
 
 
 
 
 
 
e7ba391
 
989adf8
 
 
 
 
e7ba391
766be0f
db1caa1
e7ba391
 
48f38e0
1ce2ae6
 
f8b7647
48f38e0
e7ba391
 
edd6767
e7ba391
48f38e0
e7ba391
 
7bc4d06
e7ba391
989adf8
1ce2ae6
 
f8b7647
 
1ce2ae6
 
f8b7647
db1caa1
edd6767
db1caa1
bb8b08c
db1caa1
 
 
 
 
 
 
 
 
 
 
 
 
 
bb8b08c
f8b7647
 
 
db1caa1
f8b7647
db1caa1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a16ed6
989adf8
db1caa1
 
 
0a16ed6
db1caa1
 
 
 
e7ba391
db1caa1
766be0f
e7ba391
 
 
989adf8
766be0f
e7ba391
 
1e3f1ee
989adf8
db1caa1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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}`));