diff --git a/package.json b/package.json index 0982dc3b..3ceb4009 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ } }, "devDependencies": { + "@types/ini": "^4.1.1", "@types/jszip": "^3.4.0", "@types/opentype.js": "^1.3.9", "electron": "^40.6.0", @@ -71,6 +72,7 @@ "bson": "^7.2.0", "celaria-formats": "^1.0.2", "imagetracer": "^0.2.2", + "ini": "^6.0.0", "js-synthesizer": "^1.11.0", "json5": "^2.2.3", "jszip": "^3.10.1", diff --git a/src/handlers/config.ts b/src/handlers/config.ts new file mode 100644 index 00000000..52534b45 --- /dev/null +++ b/src/handlers/config.ts @@ -0,0 +1,150 @@ +import type { FileData, FileFormat, FormatHandler } from "../FormatHandler.ts"; +import CommonFormats from "src/CommonFormats.ts"; +import * as ini from 'ini'; +import * as yaml from 'yaml'; + +class configHandler implements FormatHandler { + public name: string = "cfg"; + public supportedFormats?: FileFormat[]; + public ready: boolean = false; + + private readonly cfgExt = ['ini', 'cfg', 'cnf', 'conf', 'cf']; + private readonly yamlExt = ['yaml', 'yml']; + + async init() { + this.supportedFormats = [ + CommonFormats.JSON.builder("json").markLossless().allowFrom(true).allowTo(true), + CommonFormats.YML.builder("yaml").markLossless().allowFrom(true).allowTo(true), + { + name: "INI Configuration File", + format: "ini", + extension: "ini", + mime: "text/plain", + from: true, + to: true, + internal: "ini", + lossless: true, + }, + { + name: "Configuration File", + format: "cfg", + extension: "cfg", + mime: "text/plain", + from: true, + to: true, + internal: "ini", + lossless: true, + }, + { + name: "Configuration File", + format: "cnf", + extension: "cnf", + mime: "text/plain", + from: true, + to: true, + internal: "ini", + lossless: true, + }, + { + name: "Configuration File", + format: "conf", + extension: "conf", + mime: "text/plain", + from: true, + to: true, + internal: "ini", + lossless: true, + }, + { + name: "Configuration File", + format: "cf", + extension: "cf", + mime: "text/plain", + from: true, + to: true, + internal: "ini", + lossless: true, + }, + ]; + this.ready = true; + } + + async doConvert( + inputFiles: FileData[], + inputFormat: FileFormat, + outputFormat: FileFormat, + ): Promise { + const outputFiles: FileData[] = []; + const decoder = new TextDecoder(); + const encoder = new TextEncoder(); + + const cfgRegex = new RegExp(`\\.(${this.cfgExt.join('|')})$`, 'i'); + const yamlRegex = new RegExp(`\\.(${this.yamlExt.join('|')})$`, 'i'); + + // config -> json + if (inputFormat.internal === "ini" && outputFormat.internal === "json") { + for (const file of inputFiles) { + const decode = decoder.decode(file.bytes); + const parsed = ini.parse(decode); + const stringified = JSON.stringify(parsed, null, 2); + + outputFiles.push({ + name: file.name.replace(cfgRegex, ".json"), + bytes: encoder.encode(stringified), + }); + } + } + + // json -> config + if (inputFormat.internal === "json" && outputFormat.internal === "ini") { + for (const file of inputFiles) { + const decode = decoder.decode(file.bytes); + const parsed = JSON.parse(decode); + const stringified = ini.stringify(parsed); + + outputFiles.push({ + name: file.name.replace(/\.json$/i, `.${outputFormat.extension}`), + bytes: encoder.encode(stringified), + }); + } + } + + // yaml -> config + if (inputFormat.internal === "yaml" && outputFormat.internal === "ini") { + for (const file of inputFiles) { + const decode = decoder.decode(file.bytes); + const parsed = yaml.parse(decode); + const stringified = ini.stringify(parsed); + + outputFiles.push({ + name: file.name.replace(yamlRegex, `.${outputFormat.extension}`), + bytes: encoder.encode(stringified), + }); + } + } + + // config -> yaml + if (inputFormat.internal === "ini" && outputFormat.internal === "yaml") { + for (const file of inputFiles) { + const decode = decoder.decode(file.bytes); + const parsed = ini.parse(decode); + const stringified = yaml.stringify(parsed); + + outputFiles.push({ + name: file.name.replace(cfgRegex, ".yaml"), + bytes: encoder.encode(stringified), + }); + } + } + + if (outputFiles.length === 0) { + throw new Error( + `configHandler does not support route: ${inputFormat.internal} -> ${outputFormat.internal}`, + ); + } + + return outputFiles; + } +} + +export default configHandler; diff --git a/src/handlers/index.ts b/src/handlers/index.ts index f49b4f5d..a8bd9691 100644 --- a/src/handlers/index.ts +++ b/src/handlers/index.ts @@ -66,6 +66,7 @@ import mclangHandler from "./minecraftLangfileHandler.ts"; import celariaMapHandler from "./celariaMap.ts"; import cybergrindHandler from "./cybergrindHandler.ts"; import textToSourceHandler from "./textToSource.ts"; +import configHandler from "./config.ts"; import xcursorHandler from "./xcursor.ts"; const handlers: FormatHandler[] = []; @@ -141,6 +142,7 @@ try { handlers.push(new mclangHandler()) } catch (_) { }; try { handlers.push(new celariaMapHandler()) } catch (_) { }; try { handlers.push(new cybergrindHandler()) } catch (_) { }; try { handlers.push(new textToSourceHandler()) } catch (_) { }; +try { handlers.push(new configHandler()) } catch (_) { }; try { handlers.push(new xcursorHandler()) } catch (_) { }; export default handlers;