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,41 @@
|
||||
// Extract display geometry only. Never change score notes or collection baselines.
|
||||
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { prepareGameDbSource } from './game-db-source.mjs';
|
||||
import { parseSusTimeline } from './sus-timeline.mjs';
|
||||
|
||||
prepareGameDbSource('kor');
|
||||
const catalog = JSON.parse(await readFile('src/lib/data/exact-chart-catalog.json', 'utf8'));
|
||||
const outputs = [];
|
||||
for (const entry of catalog.charts) {
|
||||
const key = `${entry.songId}.${entry.difficulty}`;
|
||||
const score = JSON.parse(await readFile(`static${entry.path}`, 'utf8'));
|
||||
const raw = await readFile(`data/raw/sus/chart_${entry.songId}_${entry.difficulty}.sus`);
|
||||
const sha256 = createHash('sha256').update(raw).digest('hex');
|
||||
let visual;
|
||||
let source;
|
||||
if (sha256 === score.source.susSha256) {
|
||||
const parsed = parseSusTimeline(raw.toString('utf8'));
|
||||
if (parsed.songId !== entry.songId || parsed.notes.length !== score.notes.length ||
|
||||
parsed.notes.some((note, i) => Math.abs(note.timeSeconds - score.notes[i].timeSeconds) > 0.00001)) {
|
||||
throw new Error(`${key}: visual and score timing differ`);
|
||||
}
|
||||
visual = parsed.visual;
|
||||
source = { kind: 'sus', susSha256: sha256 };
|
||||
} else {
|
||||
throw new Error(`${key}: regenerate score and visual from the same game SUS first`);
|
||||
}
|
||||
for (const point of [...visual.notes, ...visual.holds.flat()]) {
|
||||
if (![point.timeSeconds, point.lane, point.width].every(Number.isFinite) || point.width <= 0 || point.lane < -0.001 || point.lane + point.width > 12.001) {
|
||||
throw new Error(`${key}: invalid chart geometry ${JSON.stringify(point)}`);
|
||||
}
|
||||
// Match the six-decimal timestamps of the existing score source at note boundaries.
|
||||
const near = score.notes.find(n => Math.abs(n.timeSeconds - point.timeSeconds) < 0.000001);
|
||||
point.timeSeconds = near?.timeSeconds ?? point.timeSeconds;
|
||||
}
|
||||
outputs.push([key, { schemaVersion: 1, songId: entry.songId, difficulty: entry.difficulty, durationSeconds: score.durationSeconds, ...visual, source }]);
|
||||
}
|
||||
// Validate the entire catalog before writing any output.
|
||||
await mkdir('static/data/chart-visuals', { recursive: true });
|
||||
for (const [key, data] of outputs) await writeFile(`static/data/chart-visuals/${key}.json`, JSON.stringify(data));
|
||||
console.log(`채보 시각화 ${outputs.length}개 생성 (동일 게임 SUS 검증 완료)`);
|
||||
Reference in New Issue
Block a user