mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-30 19:07:58 +03:00
211 lines
7.4 KiB
TypeScript
211 lines
7.4 KiB
TypeScript
import { Command } from "commander";
|
|
import sqlite3 from "better-sqlite3";
|
|
import { parse } from "csv-parse/sync";
|
|
import fs from "fs";
|
|
const { CreateChartID, ReadCollection, WriteCollection } = require("../util");
|
|
|
|
// The tables are at https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pubhtml.
|
|
// Download as csv using these links:
|
|
// us1-14: https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pub?output=csv&gid=1782673801
|
|
// us15-18+: https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pub?output=csv&gid=1134382369
|
|
// os: https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pub?output=csv&gid=144278610
|
|
// us0*: https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pub?output=csv&gid=1456079294
|
|
// You must also download the "packages" sheet, which maps song pack names to directories. Pass this using the '-p' option:
|
|
// https://docs.google.com/spreadsheets/u/1/d/e/2PACX-1vRH1beDh3I76qS-UUppW4_GibEj9bBDgcpO1XM4SrMylnSZYjyPjPYcMuQHaMrB-JGpIdsZtb-0wzmp/pub?output=csv&gid=899058659
|
|
|
|
const DIFF_MAP = {
|
|
"Light": "NOV",
|
|
"Challenge": "ADV",
|
|
"Extended": "EXH",
|
|
"Infinite": "INF"
|
|
}
|
|
|
|
// Imagine if sdvx/ksm players could just copy and paste properly...
|
|
function fixBrokenData(tier, title, diff, level, packDir) {
|
|
const tierDiffLevel = `${tier} ${diff} ${level}`;
|
|
if (title === "FALSE" && [
|
|
"us3 NOV 3",
|
|
"us9 ADV 9",
|
|
"us13 EXH 13",
|
|
].includes(tierDiffLevel)){
|
|
return ["False", diff, level, `\\${packDir}\\`];
|
|
}
|
|
if (title === "Agitatus" && [
|
|
"us4 NOV 4",
|
|
"us11 ADV 11"
|
|
].includes(tierDiffLevel)) {
|
|
// This is actually a typo in the chart, only for NOV and ADV.
|
|
return [" Agitatus", diff, level, `\\${packDir}\\`];
|
|
}
|
|
if (title === "Amnèhilesie" && [
|
|
"us6 NOV 6",
|
|
"us12 ADV 12"
|
|
].includes(tierDiffLevel)) {
|
|
return ["Amnéhilesie", diff, level, `\\${packDir}\\`];
|
|
}
|
|
if (title === "White Wail -the \"X-carols\"-" && [
|
|
"us9 NOV 9",
|
|
"us16 ADV 16",
|
|
"os3 EXH 19",
|
|
"os10 INF 20",
|
|
].includes(tierDiffLevel)) {
|
|
return ["White Wail -the \"X Carols\"-", diff, level, `\\${packDir}\\`];
|
|
}
|
|
if (title === "Re:building ZERO" && tierDiffLevel === "us16 EXH 16") {
|
|
return [title, diff, "17", `\\${packDir}\\`];
|
|
}
|
|
if (title === "Our School Festival!" && tierDiffLevel === "us18 INF 18") {
|
|
// The INF has a weird title, the other diffs are all fine.
|
|
return ["Our School festival", diff, level, `\\${packDir}\\`];
|
|
}
|
|
// We must distinguish between the finale and the non-finale version manually.
|
|
// Why the fuck they set it up like this I will never know.
|
|
if (title === "#curtaincall(finale)" && tierDiffLevel === "os5 INF 20") {
|
|
return ["#curtaincall", diff, level, "B4UT&KUOC K-shoot mania Package 2016\\03_BOSS_B4UT\\04_#curtaincall_finale\\"];
|
|
}
|
|
if (title === "#curtaincall" && tierDiffLevel === "os9* INF 20") {
|
|
return ["#curtaincall", diff, level, "B4UT&KUOC K-shoot mania Package 2016\\03_BOSS_B4UT\\03_#curtaincall\\"];
|
|
}
|
|
if (title === "双極のバラキエル" && tierDiffLevel === "os6 INF 20") {
|
|
return [title, diff, "19", `\\${packDir}\\`];
|
|
}
|
|
if (title === "Modern Times Paradox" && tierDiffLevel === "us6 NOV 6" ||
|
|
title === "Modern Times Paradox[B.B.K. Collabo]" && tierDiffLevel === "os8* INF 20") {
|
|
// These charts are corrupted in USC.
|
|
return null;
|
|
}
|
|
|
|
return [title, diff, level, `\\${packDir}\\`];
|
|
}
|
|
|
|
|
|
function tableFolder(tierString: string): { table: string; level: string } {
|
|
const match = tierString.match(/^(us|os)(\d{1,2}\+?\*?)$/)
|
|
return {
|
|
table: match[1],
|
|
level: match[2],
|
|
}
|
|
}
|
|
|
|
const program = new Command();
|
|
program.requiredOption("-d, --db <maps.db>");
|
|
program.requiredOption("-p, --packages <パッケージ集.csv>");
|
|
program.requiredOption("-c, --charts [chartlists.csv...]");
|
|
|
|
program.parse(process.argv);
|
|
const options = program.opts();
|
|
|
|
const directories: Record<string, string[]> = {};
|
|
const packageData = parse(fs.readFileSync(options.packages), {from: 1});
|
|
|
|
let currentPack = "";
|
|
for (let i = 0; i < packageData.length; i++) {
|
|
const row = packageData[i];
|
|
// first column is this weird star thing we ignore
|
|
if (row[1] !== "") {
|
|
currentPack = row[1];
|
|
directories[currentPack] = [];
|
|
}
|
|
const directory = row[2];
|
|
|
|
directories[currentPack].push(directory);
|
|
}
|
|
|
|
const db = sqlite3(options.db);
|
|
|
|
// We could use proper queries to be more efficient,
|
|
// but the db isn't really large enough for it to matter...
|
|
const dbRows = db.prepare("SELECT * FROM Charts").all();
|
|
|
|
const songs = ReadCollection("songs-usc.json");
|
|
const charts = ReadCollection("charts-usc.json");
|
|
let maxSongId = Math.max(...songs.map(s => s.id));
|
|
const folderIdToSongId = {};
|
|
|
|
for (const tableFile of options.charts) {
|
|
const tableData = parse(fs.readFileSync(tableFile), {from: 2});
|
|
for (const chartData of tableData) {
|
|
const [tier, origTitle, ksmDiff, origLevel, pack, _notes] = chartData;
|
|
const origDiff = DIFF_MAP[ksmDiff];
|
|
const packDir = directories[pack];
|
|
|
|
const fixedData = fixBrokenData(tier, origTitle, origDiff, origLevel, packDir);
|
|
if (fixedData === null) {
|
|
continue;
|
|
}
|
|
const [title, difficulty, level, pathMatch] = fixedData;
|
|
|
|
let dbRowMatches = dbRows.filter(row =>
|
|
row.title === title &&
|
|
row.diff_shortname === difficulty &&
|
|
row.level === Number(level) &&
|
|
row.path.includes(pathMatch)
|
|
);
|
|
|
|
if (dbRowMatches.length === 0) {
|
|
console.log(`Couldn't find matching row for ${title} (${difficulty} ${level}) in ${tier}. (From package ${pack})`);
|
|
continue;
|
|
} else if (dbRowMatches.length !== 1) {
|
|
if (dbRowMatches.length === 2 && dbRowMatches.filter(row => row.path.endsWith("data.ksh")).length === 1) {
|
|
// These files are an old version of the chart I believe.
|
|
dbRowMatches = dbRowMatches.filter(row => !row.path.endsWith("data.ksh"))
|
|
} else {
|
|
console.log(dbRowMatches);
|
|
//throw new Error(`Wrong number of matching rows for ${title} (${difficulty} ${level}) in ${tier}`);
|
|
}
|
|
}
|
|
const dbRow = dbRowMatches[0];
|
|
|
|
const matchingCharts = charts.filter(chart => chart.hashSHA1 === dbRow.hash);
|
|
if (matchingCharts.length === 2) {
|
|
for (const chart of matchingCharts) {
|
|
chart.data.tableFolders = [tableFolder(tier)];
|
|
}
|
|
} else if (matchingCharts.length === 0) {
|
|
if (!folderIdToSongId[dbRow.folderid]) {
|
|
const songID = maxSongId + 1;
|
|
maxSongId = songID;
|
|
// May need to change to forward slash depending on OS.
|
|
const splitPath = dbRow.path.split("\\");
|
|
const folderName = splitPath[splitPath.length - 2];
|
|
songs.push({
|
|
id: songID,
|
|
title,
|
|
altTitles: [],
|
|
artist: dbRow.artist,
|
|
data: {},
|
|
searchTerms: [folderName]
|
|
});
|
|
folderIdToSongId[dbRow.folderid] = songID;
|
|
}
|
|
|
|
const songID = folderIdToSongId[dbRow.folderid]
|
|
|
|
for (const playtype of ["Controller", "Keyboard"]) {
|
|
charts.push({
|
|
chartID: CreateChartID(),
|
|
data: {
|
|
effector: dbRow.effector,
|
|
hashSHA1: dbRow.hash,
|
|
isOfficial: false,
|
|
tableFolders: [tableFolder(tier)],
|
|
},
|
|
difficulty,
|
|
isPrimary: true,
|
|
level,
|
|
// So it doesn't give VF. (Consider changing this.)
|
|
levelNum: 0,
|
|
playtype,
|
|
rgcID: null,
|
|
songID,
|
|
tierlistInfo: {},
|
|
versions: [],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
WriteCollection("songs-usc.json", songs);
|
|
WriteCollection("charts-usc.json", charts);
|