From 89be828ef84bcc7b5e5e3c4301f0cfdf922a05be Mon Sep 17 00:00:00 2001 From: Thome Valentin Date: Sun, 1 May 2022 12:24:17 +0200 Subject: [PATCH] Rewrite MDB loading code to support loading .xml, .json or .b64 files. This applies to the default MDB (determined by the version of the game requesting it), or custom MDB if that setting is enabled. To use a custom MDB, enable it in Web UI, then place a custom.json, custom.xml or custom.b64 file in the data/mdb subfolder. --- gitadora@asphyxia/data/mdb/.gitignore | 3 +- gitadora@asphyxia/data/mdb/index.ts | 103 +++++++++++++++++------- gitadora@asphyxia/handlers/MusicList.ts | 9 ++- 3 files changed, 80 insertions(+), 35 deletions(-) diff --git a/gitadora@asphyxia/data/mdb/.gitignore b/gitadora@asphyxia/data/mdb/.gitignore index 33a0b3f..7a7be89 100644 --- a/gitadora@asphyxia/data/mdb/.gitignore +++ b/gitadora@asphyxia/data/mdb/.gitignore @@ -6,4 +6,5 @@ ex.json mt.json nt.json hv.json -custom.xml \ No newline at end of file +custom.xml +custom.json \ No newline at end of file diff --git a/gitadora@asphyxia/data/mdb/index.ts b/gitadora@asphyxia/data/mdb/index.ts index d78e877..5b4d1f9 100644 --- a/gitadora@asphyxia/data/mdb/index.ts +++ b/gitadora@asphyxia/data/mdb/index.ts @@ -21,6 +21,9 @@ export enum DATAVersion { MATTIX = "mt" } +const allowedFormats = ['.json', '.xml', '.b64'] +const mdbFolder = "data/mdb/" + type processRawDataHandler = (path: string) => Promise const logger = new Logger("mdb") @@ -32,30 +35,41 @@ export async function readXML(path: string) { return json } -export async function readJSON(path: string) { - logger.debugInfo(`Loading MDB data from ${path}.`) - const str = await IO.ReadFile(path, 'utf-8'); - const json = JSON.parse(str) - return json -} +export async function readMDBFile(path: string, processHandler?: processRawDataHandler): Promise { -export async function readJSONOrXML(jsonPath: string, xmlPath: string, processHandler: processRawDataHandler): Promise { - if (!IO.Exists(jsonPath)) { - logger.debugInfo(`Loading MDB data from ${xmlPath}.`) - const data = await processHandler(xmlPath) - await IO.WriteFile(jsonPath, JSON.stringify(data)) - return data - } else { - logger.debugInfo(`Loading MDB data from ${jsonPath}.`) - const json = JSON.parse(await IO.ReadFile(jsonPath, 'utf-8')) - return json + if (!IO.Exists(path)) { + throw "Unable to find MDB file at " + path } -} -export async function readB64JSON(b64path: string) { - logger.debugInfo(`Loading MDB data from ${b64path}.`) - const buff = await IO.ReadFile(b64path, 'utf-8'); - return JSON.parse(Buffer.from(buff, 'base64').toString('utf-8')); + logger.debugInfo(`Loading MDB data from ${path}.`) + + let result : CommonMusicData; + const fileType = path.substring(path.lastIndexOf('.')).toLowerCase() + + switch (fileType) { + case '.json': + const str = await IO.ReadFile(path, 'utf-8'); + result = JSON.parse(str) + break; + case '.xml': + processHandler ?? defaultProcessRawXmlData + result = await processHandler(path) + // Uncomment to save the loaded XML file as JSON. + // await IO.WriteFile(path.replace(".xml", ".json"), JSON.stringify(data)) + break; + case '.b64': + const buff = await IO.ReadFile(path, 'utf-8'); + const json = Buffer.from(buff, 'base64').toString('utf-8') + // Uncomment to save the decoded base64 file as JSON. + // await IO.WriteFile(path.replace(".b64",".json"), json) + result = JSON.parse(json) + break; + default: + throw `Invalid MDB file type: ${fileType}. Only .json, .xml, .b64 are supported.` + } + + logger.debugInfo(`Loaded ${result.music.length} songs from MDB file.`) + return result } export function gameVerToDataVer(ver: string): DATAVersion { @@ -72,18 +86,47 @@ export function gameVerToDataVer(ver: string): DATAVersion { } } -export async function processDataBuilder(gameVer: string, processHandler?: processRawDataHandler) { - const ver = gameVerToDataVer(gameVer) - const base = `data/mdb/${ver}` - if (IO.Exists(`${base}.b64`)) { - return await readB64JSON(`${base}.b64`); +/** + * Attempts to find a .json, .xml, or .b64 file (in that order) matching the given name in the specified folder. + * @param fileNameWithoutExtension - The name of the file to find (without the extension). + * @param path - The path to the folder to search. If left null, the default MDB folder ('data/mdb' in the plugin folder) will be used. + * @returns - The path of the first matching file found, or null if no file was found. + */ +export function findMDBFile(fileNameWithoutExtension: string, path: string = null): string { + + path = path ?? mdbFolder + if (!IO.Exists(path)) { + throw `Path does not exist: ${path}` } - const { music } = await readJSONOrXML(`${base}.json`, `${base}.xml`, processHandler ?? defaultProcessRawData) - // await IO.WriteFile(`${base}.b64`, Buffer.from(JSON.stringify({music})).toString("base64")) - return { music }; + + if (!path.endsWith("/")) { + path += "/" + } + + for (const ext of allowedFormats) { + const filePath = path + fileNameWithoutExtension + ext + if (IO.Exists(filePath)) { + return filePath + } + } + + return null } -export async function defaultProcessRawData(path: string): Promise { +export async function loadSongsForGameVersion(gameVer: string, processHandler?: processRawDataHandler) { + const ver = gameVerToDataVer(gameVer) + + let mdbFile = findMDBFile(ver, mdbFolder) + + if (mdbFile == null) { + throw `No valid MDB files were found in the data/mdb subfolder. Ensure that this folder contains at least one of the following: ${ver}.json, ${ver}.xml or ${ver}.b64` + } + + const music = await readMDBFile(mdbFile, processHandler ?? defaultProcessRawXmlData) + return music +} + +export async function defaultProcessRawXmlData(path: string): Promise { const data = await readXML(path) const mdb = $(data).elements("mdb.mdb_data"); const music: any[] = []; diff --git a/gitadora@asphyxia/handlers/MusicList.ts b/gitadora@asphyxia/handlers/MusicList.ts index 10841b1..18fafd7 100644 --- a/gitadora@asphyxia/handlers/MusicList.ts +++ b/gitadora@asphyxia/handlers/MusicList.ts @@ -1,6 +1,5 @@ import { getVersion } from "../utils"; -import { defaultProcessRawData, processDataBuilder } from "../data/mdb" -import { CommonMusicDataField, readJSONOrXML, readXML } from "../data/mdb"; +import { CommonMusicDataField, findMDBFile, readMDBFile, loadSongsForGameVersion } from "../data/mdb"; import Logger from "../utils/logger" const logger = new Logger("MusicList") @@ -10,7 +9,9 @@ export const playableMusic: EPR = async (info, data, send) => { let music: CommonMusicDataField[] = []; try { if (U.GetConfig("enable_custom_mdb")) { - music = (await defaultProcessRawData('data/mdb/custom.xml')).music + let customMdb = findMDBFile("custom") + + music = (await readMDBFile(customMdb)).music } } catch (e) { logger.warn("Read Custom MDB failed. Using default MDB as a fallback.") @@ -19,7 +20,7 @@ export const playableMusic: EPR = async (info, data, send) => { } if (music.length == 0) { - music = _.get(await processDataBuilder(version), 'music', []); + music = (await loadSongsForGameVersion(version)).music } await send.object({