map viewer
This commit is contained in:
parent
93bc6e7a07
commit
2b1f1a0191
15 changed files with 2355 additions and 40 deletions
170
psx-map-exporter/scripts/export-all.mjs
Normal file
170
psx-map-exporter/scripts/export-all.mjs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env node
|
||||
// Batch-export every LSET*/L*.WDL found under the PSX disc root into a
|
||||
// permanent .output-render/<mapStem>/<variant>/ directory tree. Each export
|
||||
// runs as a separate child process so an OOM or crash on one map cannot kill
|
||||
// the batch.
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const PROJECT_ROOT = path.resolve(SCRIPT_DIR, '..');
|
||||
const CLI_PATH = path.join(PROJECT_ROOT, 'src', 'cli.js');
|
||||
|
||||
const VARIANTS = [
|
||||
{ label: 'auto', mapSource: 'auto' },
|
||||
{ label: 'region01', mapSource: 'region01' },
|
||||
];
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = {
|
||||
discRoot: 'E:/emu/psx/Crusader - No Remorse',
|
||||
outputRoot: path.join(PROJECT_ROOT, '.output-render'),
|
||||
debugLabels: true,
|
||||
only: null,
|
||||
timeoutMs: 300000,
|
||||
};
|
||||
for (let index = 2; index < argv.length; index += 1) {
|
||||
const arg = argv[index];
|
||||
const next = argv[index + 1];
|
||||
if (arg === '--disc-root') { options.discRoot = path.resolve(next); index += 1; }
|
||||
else if (arg === '--output-root') { options.outputRoot = path.resolve(next); index += 1; }
|
||||
else if (arg === '--no-debug-labels') { options.debugLabels = false; }
|
||||
else if (arg === '--only') {
|
||||
options.only = String(next).split(',').map((v) => v.trim()).filter(Boolean);
|
||||
index += 1;
|
||||
} else if (arg === '--timeout-ms') {
|
||||
options.timeoutMs = Number(next); index += 1;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
async function discoverMaps(discRoot) {
|
||||
const entries = await fs.readdir(discRoot, { withFileTypes: true });
|
||||
const maps = [];
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (!/^LSET\d+$/i.test(entry.name)) continue;
|
||||
const dir = path.join(discRoot, entry.name);
|
||||
const files = await fs.readdir(dir);
|
||||
for (const file of files) {
|
||||
if (!/^L\d+\.WDL$/i.test(file)) continue;
|
||||
maps.push({
|
||||
set: entry.name,
|
||||
name: path.parse(file).name,
|
||||
wdlPath: path.join(dir, file),
|
||||
sourceRel: `${entry.name}/${file}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
maps.sort((a, b) => {
|
||||
const an = Number.parseInt(a.name.replace(/^L/i, ''), 10);
|
||||
const bn = Number.parseInt(b.name.replace(/^L/i, ''), 10);
|
||||
return an - bn;
|
||||
});
|
||||
return maps;
|
||||
}
|
||||
|
||||
function runExport(mapEntry, variant, options) {
|
||||
return new Promise((resolve) => {
|
||||
const outDir = path.join(options.outputRoot, mapEntry.name, variant.label);
|
||||
const args = [
|
||||
'--max-old-space-size=4096',
|
||||
CLI_PATH,
|
||||
'--disc-root', options.discRoot,
|
||||
'--source', mapEntry.sourceRel,
|
||||
'--map-source', variant.mapSource,
|
||||
'--output-root', outDir,
|
||||
];
|
||||
if (options.debugLabels) args.push('--debug-labels');
|
||||
|
||||
const started = Date.now();
|
||||
const child = spawn(process.execPath, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
let killed = false;
|
||||
const timer = setTimeout(() => {
|
||||
killed = true;
|
||||
child.kill('SIGKILL');
|
||||
}, options.timeoutMs);
|
||||
|
||||
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
||||
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
||||
child.on('close', (code, signal) => {
|
||||
clearTimeout(timer);
|
||||
const ms = Date.now() - started;
|
||||
resolve({ outDir, code, signal, killed, ms, stdout, stderr });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv);
|
||||
const maps = await discoverMaps(options.discRoot);
|
||||
const filtered = options.only ? maps.filter((m) => options.only.includes(m.name)) : maps;
|
||||
|
||||
console.log(`Found ${filtered.length} maps under ${options.discRoot}`);
|
||||
await fs.mkdir(options.outputRoot, { recursive: true });
|
||||
|
||||
const summary = [];
|
||||
let okCount = 0;
|
||||
let failCount = 0;
|
||||
for (const mapEntry of filtered) {
|
||||
for (const variant of VARIANTS) {
|
||||
const tag = `[${mapEntry.set}/${mapEntry.name}] variant=${variant.label}`;
|
||||
process.stdout.write(`${tag} ... `);
|
||||
const result = await runExport(mapEntry, variant, options);
|
||||
if (result.code === 0) {
|
||||
okCount += 1;
|
||||
process.stdout.write(`OK (${result.ms}ms)\n`);
|
||||
summary.push({
|
||||
set: mapEntry.set,
|
||||
map: mapEntry.name,
|
||||
variant: variant.label,
|
||||
ms: result.ms,
|
||||
ok: true,
|
||||
outDir: path.relative(options.outputRoot, result.outDir),
|
||||
});
|
||||
} else {
|
||||
failCount += 1;
|
||||
const reason = result.killed
|
||||
? `TIMEOUT after ${result.ms}ms`
|
||||
: `exit ${result.code}${result.signal ? ' signal ' + result.signal : ''}`;
|
||||
process.stdout.write(`FAIL (${reason})\n`);
|
||||
if (result.stderr) {
|
||||
process.stdout.write(` stderr: ${result.stderr.trim().split(/\r?\n/).slice(-5).join('\n stderr: ')}\n`);
|
||||
}
|
||||
summary.push({
|
||||
set: mapEntry.set,
|
||||
map: mapEntry.name,
|
||||
variant: variant.label,
|
||||
ms: result.ms,
|
||||
ok: false,
|
||||
reason,
|
||||
stderrTail: result.stderr.trim().split(/\r?\n/).slice(-10).join('\n'),
|
||||
outDir: path.relative(options.outputRoot, result.outDir),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const indexPath = path.join(options.outputRoot, 'index.json');
|
||||
await fs.writeFile(indexPath, JSON.stringify({
|
||||
discRoot: options.discRoot,
|
||||
generatedAt: new Date().toISOString(),
|
||||
variants: VARIANTS.map((v) => v.label),
|
||||
okCount,
|
||||
failCount,
|
||||
maps: summary,
|
||||
}, null, 2));
|
||||
console.log(`\nWrote index ${indexPath} (ok=${okCount} fail=${failCount})`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue