data: switch to recovered game DB and refresh cards and charts
Publish GitHub Pages output / publish (push) Successful in 44s
Publish GitHub Pages output / publish (push) Successful in 44s
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { gunzipSync } from 'node:zlib';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export const gameDbRoot = fileURLToPath(new URL('../data/game-db/', import.meta.url));
|
||||
export const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
|
||||
|
||||
export function openGameDb(pointer, root = gameDbRoot) {
|
||||
if (!/^snapshots\/[a-zA-Z0-9]{64}$/.test(pointer.snapshot ?? '')) throw new Error('Invalid game DB snapshot path');
|
||||
const directory = path.join(root, pointer.snapshot);
|
||||
const bytes = readFileSync(path.join(directory, 'manifest.json'));
|
||||
if (sha256(bytes) !== pointer.manifestSha256) throw new Error('Game DB manifest hash mismatch');
|
||||
const manifest = JSON.parse(bytes);
|
||||
if (manifest.schemaVersion !== 1 || manifest.kind !== 'android-master-db' || manifest.locale !== 'kor' ||
|
||||
pointer.snapshot !== `snapshots/${manifest.masterDataVersion}` || !Object.keys(manifest.tables ?? {}).length) {
|
||||
throw new Error('Invalid game DB manifest');
|
||||
}
|
||||
return { ...manifest, ...pointer, directory };
|
||||
}
|
||||
|
||||
export function sourceMetadata(source) {
|
||||
return { kind: source.kind, masterDataVersion: source.masterDataVersion, locale: source.locale,
|
||||
capturedAt: source.capturedAt, manifestSha256: source.manifestSha256 };
|
||||
}
|
||||
|
||||
export function prepareGameDbSource(locale = 'kor', { review = false } = {}) {
|
||||
if (locale !== 'kor') throw new Error(`Current game DB contains Korean only; no external fallback for ${locale}`);
|
||||
const pointer = JSON.parse(readFileSync(path.join(gameDbRoot, 'current.json'), 'utf8'));
|
||||
const source = openGameDb(pointer);
|
||||
if (!review) {
|
||||
let report;
|
||||
try { report = JSON.parse(readFileSync(path.join(gameDbRoot, 'review/latest.json'), 'utf8')); }
|
||||
catch { throw new Error('Run npm run data:sync and report its changes before data:apply.'); }
|
||||
if (report.target.manifestSha256 !== source.manifestSha256) throw new Error('Game DB changed after review; run data:sync again.');
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
export function readGameDbText(source, filename) {
|
||||
const name = filename.replace(/\.json$/, '');
|
||||
const table = source.tables[name];
|
||||
if (!table || table.file !== `${name}.json.gz` || !/^[A-Za-z0-9_]+$/.test(name)) throw new Error(`Missing game DB table: ${filename}`);
|
||||
const packed = readFileSync(path.join(source.directory, table.file));
|
||||
if (sha256(packed) !== table.gzipSha256) throw new Error(`Game DB archive hash mismatch: ${name}`);
|
||||
const raw = gunzipSync(packed);
|
||||
if (sha256(raw) !== table.sha256) throw new Error(`Game DB table hash mismatch: ${name}`);
|
||||
const rows = JSON.parse(raw);
|
||||
if (!Array.isArray(rows) || rows.length !== table.rows) throw new Error(`Game DB row count mismatch: ${name}`);
|
||||
return raw.toString('utf8');
|
||||
}
|
||||
|
||||
export function readGameDbTable(locale, filename) {
|
||||
return JSON.parse(readGameDbText(prepareGameDbSource(locale), filename));
|
||||
}
|
||||
Reference in New Issue
Block a user