Files
HolodoriCalc/scripts/import-legacy-song-data.mjs

72 lines
2.5 KiB
JavaScript

import { readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
const sourcePath = process.argv[2];
if (!sourcePath) {
throw new Error('사용법: npm run data:import-songs -- <기존 계산기 HTML 경로>');
}
const source = await readFile(sourcePath, 'utf8');
function parseConstant(name) {
const prefix = `const ${name} = `;
const start = source.indexOf(prefix);
if (start < 0) throw new Error(`${name} 데이터를 찾을 수 없습니다.`);
const valueStart = start + prefix.length;
const valueEnd = source.indexOf(';\n', valueStart);
if (valueEnd < 0) throw new Error(`${name} 데이터의 끝을 찾을 수 없습니다.`);
return JSON.parse(source.slice(valueStart, valueEnd));
}
const songs = parseConstant('SONGS');
const specials = parseConstant('SONG_SPECIAL');
const noteHistograms = parseConstant('SONG_NOTE_HIST');
const difficultyNames = ['hard', 'expert'];
function parseNotes(value) {
if (typeof value !== 'string' || value.length === 0) return [];
return value.split(',').map((noteCount) => Number(noteCount));
}
function specialStarts(songId, difficultyIndex) {
const starts = Array(5).fill(null);
for (const entry of specials[songId]?.[difficultyIndex] ?? []) {
const [slot, startSeconds] = entry;
if (Number.isInteger(slot) && slot >= 0 && slot < starts.length && Number.isFinite(startSeconds)) {
starts[slot] = startSeconds;
}
}
return starts;
}
const normalizedSongs = songs.flatMap(([id, title, durationSeconds]) => {
const histograms = noteHistograms[id];
if (!histograms) return [];
const difficulties = Object.fromEntries(
difficultyNames.flatMap((difficulty, difficultyIndex) => {
const notesPerSecond = parseNotes(histograms[difficultyIndex]);
if (notesPerSecond.length === 0) return [];
return [[difficulty, {
notesPerSecond,
specialStartSeconds: specialStarts(id, difficultyIndex),
totalNotes: notesPerSecond.reduce((sum, noteCount) => sum + noteCount, 0)
}]];
})
);
if (Object.keys(difficulties).length === 0) return [];
return [{ id, title, durationSeconds, difficulties }];
});
const output = {
schemaVersion: 1,
source: path.basename(sourcePath),
songs: normalizedSongs
};
const outputPath = path.resolve('src/lib/data/song-charts.json');
await writeFile(outputPath, `${JSON.stringify(output, null, 2)}\n`, 'utf8');
console.log(`${normalizedSongs.length}곡의 채보 밀도 데이터를 ${outputPath}에 저장했습니다.`);