Spaces:
Runtime error
Runtime error
Update index.js
Browse files
index.js
CHANGED
|
@@ -9,7 +9,7 @@ const axios = require('axios');
|
|
| 9 |
const crypto = require('crypto');
|
| 10 |
|
| 11 |
const app = express();
|
| 12 |
-
const PORT =
|
| 13 |
|
| 14 |
// ββ DOWNLOAD HASH βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
// A stable hash generated once at startup. Share this URL to download the APK.
|
|
@@ -230,18 +230,42 @@ app.post('/patch', express.json({ limit: '10mb' }), (req, res) => {
|
|
| 230 |
ensureKeystore();
|
| 231 |
const tools = checkTools();
|
| 232 |
|
| 233 |
-
// 1.
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
});
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
|
|
|
| 245 |
|
| 246 |
// 3. zipalign (align before signing for v2/v3; v1 is fine either way)
|
| 247 |
let toSign = patchedApk;
|
|
@@ -299,7 +323,7 @@ app.get('/', (req, res) => {
|
|
| 299 |
});
|
| 300 |
|
| 301 |
// ββ START βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 302 |
-
app.listen(
|
| 303 |
console.log(`\n𦸠Super City Editor backend running at http://localhost:${PORT}`);
|
| 304 |
console.log(`\n⬠Direct APK download: http://localhost:${PORT}/download/${DOWNLOAD_HASH}\n`);
|
| 305 |
try { ensureKeystore(); } catch (e) { console.warn('Keystore not ready yet:', e.message); }
|
|
|
|
| 9 |
const crypto = require('crypto');
|
| 10 |
|
| 11 |
const app = express();
|
| 12 |
+
const PORT = 3000;
|
| 13 |
|
| 14 |
// ββ DOWNLOAD HASH βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
// A stable hash generated once at startup. Share this URL to download the APK.
|
|
|
|
| 230 |
ensureKeystore();
|
| 231 |
const tools = checkTools();
|
| 232 |
|
| 233 |
+
// 1. Repack APK using system unzip/zip to preserve per-entry compression.
|
| 234 |
+
// AdmZip re-deflates ALL entries including files Android requires to be
|
| 235 |
+
// STORED (resources.arsc, .so libs, etc.) β causing "App not installed".
|
| 236 |
+
const workDir = path.join(sessionDir, 'apk_work');
|
| 237 |
+
if (fs.existsSync(workDir)) fs.rmSync(workDir, { recursive: true, force: true });
|
| 238 |
+
fs.mkdirSync(workDir, { recursive: true });
|
| 239 |
+
|
| 240 |
+
// Extract APK
|
| 241 |
+
const unzipResult = spawnSync('unzip', ['-q', '-o', originalApk, '-d', workDir], { encoding: 'utf8' });
|
| 242 |
+
if (unzipResult.status !== 0) throw new Error('unzip failed: ' + unzipResult.stderr);
|
| 243 |
+
|
| 244 |
+
// Remove META-INF so apksigner can add a fresh signature
|
| 245 |
+
const metaInfDir = path.join(workDir, 'META-INF');
|
| 246 |
+
if (fs.existsSync(metaInfDir)) fs.rmSync(metaInfDir, { recursive: true, force: true });
|
| 247 |
+
|
| 248 |
+
// Overwrite characters.txt with edited content
|
| 249 |
+
const charsDest = path.join(workDir, 'assets', 'characters.txt');
|
| 250 |
+
fs.mkdirSync(path.dirname(charsDest), { recursive: true });
|
| 251 |
+
fs.writeFileSync(charsDest, Buffer.from(characters, 'utf8'));
|
| 252 |
+
|
| 253 |
+
// Repack: compress most files, but re-add known STORED types uncompressed.
|
| 254 |
+
// Step A β pack everything with compression
|
| 255 |
+
const zipResult = spawnSync('zip', ['-r', '-9', patchedApk, '.'], { cwd: workDir, encoding: 'utf8' });
|
| 256 |
+
if (zipResult.status !== 0) throw new Error('zip failed: ' + zipResult.stderr);
|
| 257 |
+
|
| 258 |
+
// Step B β find files that must stay STORED and re-add them uncompressed
|
| 259 |
+
const storedPatterns = ['*.so', '*.arsc', '*.png', '*.gif', '*.jpg', '*.jpeg',
|
| 260 |
+
'*.webp', '*.wav', '*.mp3', '*.ogg', '*.aac', '*.ttf', '*.otf'];
|
| 261 |
+
const storedFiles = storedPatterns.flatMap(pat => {
|
| 262 |
+
const r = spawnSync('find', ['.', '-name', pat], { cwd: workDir, encoding: 'utf8' });
|
| 263 |
+
return r.stdout.trim().split('\n').filter(Boolean);
|
| 264 |
});
|
| 265 |
+
if (storedFiles.length > 0) {
|
| 266 |
+
const sr = spawnSync('zip', ['-0', '-u', patchedApk, ...storedFiles], { cwd: workDir, encoding: 'utf8' });
|
| 267 |
+
if (sr.status !== 0 && sr.status !== 12) console.warn('zip -0 warning:', sr.stderr);
|
| 268 |
+
}
|
| 269 |
|
| 270 |
// 3. zipalign (align before signing for v2/v3; v1 is fine either way)
|
| 271 |
let toSign = patchedApk;
|
|
|
|
| 323 |
});
|
| 324 |
|
| 325 |
// ββ START βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 326 |
+
app.listen(7860, () => {
|
| 327 |
console.log(`\n𦸠Super City Editor backend running at http://localhost:${PORT}`);
|
| 328 |
console.log(`\n⬠Direct APK download: http://localhost:${PORT}/download/${DOWNLOAD_HASH}\n`);
|
| 329 |
try { ensureKeystore(); } catch (e) { console.warn('Keystore not ready yet:', e.message); }
|