Files
Tiago Valente bf1ca4b61a CheatSpecter 0.3.0
Browser-based real-time modding tool for offline single-player games on a
jailbroken PS5, served by a payload on the console.

- Value search, memory and code views, find what writes to a value
- Cheats as patches, held values, injected code, calls and scripts
- .cspector cheat files, saved on the PS5 or on your device
- Community tables in JSON, SHN and MC4 from three repositories
- Game library with names, logos and sizes; start a game from the browser
- MCP server for assistants
2026-10-10 10:16:57 -07:00

65 lines
2.4 KiB
JavaScript

// Turns the built web app (dist/) into a C file the payload compiles in.
// Every file is stored gzip-compressed and served that way.
//
// node scripts/embed-assets.mjs <dist dir> <output .c>
import fs from 'node:fs';
import path from 'node:path';
import zlib from 'node:zlib';
import { fileURLToPath } from 'node:url';
const [distDir, outFile] = process.argv.slice(2);
if (!distDir || !outFile) {
console.error('usage: embed-assets.mjs <dist dir> <output .c>');
process.exit(1);
}
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
const MIME = {
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.svg': 'image/svg+xml',
'.json': 'application/json; charset=utf-8',
'.png': 'image/png',
'.ico': 'image/x-icon',
'.wasm': 'application/wasm',
};
function walk(dir, prefix = '') {
const out = [];
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const rel = `${prefix}/${entry.name}`;
if (entry.isDirectory()) out.push(...walk(path.join(dir, entry.name), rel));
else out.push(rel);
}
return out.sort();
}
const files = walk(distDir);
if (!files.includes('/index.html')) {
console.error(`${distDir} has no index.html; run "npm run build" first`);
process.exit(1);
}
let c = '/* Generated by scripts/embed-assets.mjs. Do not edit. */\n#include "assets.h"\n\n';
c += `const char CS_VERSION[] = ${JSON.stringify(version)};\n\n`;
const rows = [];
let total = 0;
files.forEach((rel, i) => {
const gz = zlib.gzipSync(fs.readFileSync(path.join(distDir, rel)), { level: 9 });
total += gz.length;
const bytes = [];
for (let o = 0; o < gz.length; o += 20) bytes.push(' ' + [...gz.subarray(o, o + 20)].join(','));
c += `static const unsigned char asset_${i}[] = {\n${bytes.join(',\n')}\n};\n\n`;
const mime = MIME[path.extname(rel).toLowerCase()] ?? 'application/octet-stream';
rows.push(` {${JSON.stringify(rel)}, ${JSON.stringify(mime)}, asset_${i}, sizeof asset_${i}, ${rel.startsWith('/assets/') ? 1 : 0}}`);
});
c += `const struct asset CS_ASSETS[] = {\n${rows.join(',\n')}\n};\n\nconst int CS_ASSET_COUNT = ${files.length};\n`;
fs.mkdirSync(path.dirname(outFile), { recursive: true });
fs.writeFileSync(outFile, c);
console.log(`Embedded ${files.length} files, ${(total / 1024).toFixed(0)} KB compressed -> ${outFile}`);