import { readFileSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { readGameDbTable } from './game-db-source.mjs'; import { susScoreWeight } from './sus-score-weight.mjs'; import { scoreRatioFromFormula } from '../src/lib/engine/score.ts'; const music = new Map(readGameDbTable('kor', 'Music.json').map(({ data }) => [data.id, data])); const levels = new Map(readGameDbTable('kor', 'MusicDifficulty.json').map(({ data }) => [data.musicId + '.' + data.difficultyType.split('_').at(-1).toLowerCase(), data.difficultyLevel])); export function formulaScoreMetadata(songId, difficulty, difficultyLevel, totalNotes, coefficient, expectedHash) { const master = music.get(songId), key = songId + '.' + difficulty; if (!master || levels.get(key) !== difficultyLevel) throw new Error(key + ': DB metadata mismatch'); const bytes = readFileSync(new URL('../data/raw/sus/chart_' + songId + '_' + difficulty + '.sus', import.meta.url)); const hash = createHash('sha256').update(bytes).digest('hex'); if (expectedHash && hash !== expectedHash) throw new Error(key + ': SUS hash mismatch'); const weight = susScoreWeight(bytes.toString('utf8')); if (weight.songId !== songId || weight.fullComboNoteCount !== totalNotes || (coefficient !== undefined && coefficient !== master.liveScoreCoefficientPermil)) { throw new Error(key + ': SUS / DB score inputs mismatch'); } const scoreFormula = { perfectNoteWeight: weight.perfectNoteWeight, liveScoreCoefficientPermil: master.liveScoreCoefficientPermil, difficultyLevel }; return { scoreRatio: scoreRatioFromFormula(scoreFormula), scoreCalibrationSource: 'formula-v1', scoreFormula }; }