UNPKG

patchwork-mapconverter

Version:

Executable wrapper for https://github.com/ChiefOfGxBxL/WC3MapTranslator

219 lines (189 loc) 9.14 kB
import path from 'path' import { LoggerFactory } from '../logging/LoggerFactory' import directoryTree, { type DirectoryTree } from 'directory-tree' import { translatorRecord } from './TranslatorRecord' import { copyFileWithDirCreation } from './FileCopier' import { readFile, writeFile } from 'fs/promises' import EnhancementManager from '../enhancements/EnhancementManager' import ImportComposer from '../enhancements/ImportComposer' import { type TriggerTranslatorOutput, TriggersTranslator } from '../translator/TriggerTranslator' import { CustomScriptsTranslator } from '../translator/CustomScriptsTranslator' import { type TriggerContainer } from '../translator/data/TriggerContainer' import { ContentType, type TriggerContent } from '../translator/data/content/TriggerContent' import { type ScriptContent } from '../translator/data/properties/ScriptContent' import { type MapHeader } from '../translator/data/MapHeader' import { WriteAndCreatePath } from '../util/WriteAndCreatePath' import { FileBlacklist } from '../enhancements/FileBlacklist' import { TriggerComposer } from '../enhancements/TriggerComposer' import { type Import } from '../wc3maptranslator/data' import { type Translator, ImportsTranslator } from '../wc3maptranslator/translators' import { FormatConverters } from './formats/FormatConverters' const log = LoggerFactory.createLogger('Json2War') let translatorCount = 0 async function processFile<T> (input: string, translator: Translator<T>, output: string): Promise<void> { const asyncLog = log.getSubLogger({ name: `${translator.constructor.name}-${translatorCount++}` }) asyncLog.info('Processing', input) const buffer = FormatConverters[EnhancementManager.mapDataExtension].parse(await readFile(input, { encoding: 'utf8' })) as T const result = translator.jsonToWar(buffer) if (result.errors != null && result.errors.length > 0) { for (const error of result.errors) { asyncLog.error(error) } } else { await WriteAndCreatePath(output, result.buffer) asyncLog.info('Finished processing', output) } } async function exportImportsFile (data: Import[], output: string): Promise<void> { const translator = ImportsTranslator.getInstance() const asyncLog = log.getSubLogger({ name: `${translator.constructor.name}-${translatorCount++}` }) asyncLog.info('Exporting generated war3map.imp file.') const result = translator.jsonToWar(data) if (result.errors != null && result.errors.length > 0) { for (const error of result.errors) { asyncLog.error(error) } } else { await WriteAndCreatePath(output, result.buffer) asyncLog.info('Finished exporting', output) } } function getAllContentForScriptFile (root: TriggerContainer): TriggerContent[] { const triggerStack: TriggerContent[] = [root] const result: TriggerContent[] = [] while (triggerStack.length > 0) { const currentTrigger = triggerStack.pop() if (currentTrigger == null) continue switch (currentTrigger.contentType) { case ContentType.HEADER: result.push(currentTrigger) // eslint-disable-next-line no-fallthrough case ContentType.LIBRARY: case ContentType.CATEGORY: triggerStack.push(...(currentTrigger as TriggerContainer).children.reverse()) break case ContentType.CUSTOM_SCRIPT: case ContentType.TRIGGER: case ContentType.TRIGGER_SCRIPTED: result.push(currentTrigger) break } } return result } async function exportTriggers (triggersJson: TriggerContainer, output: string): Promise<void> { const tasks: Array<Promise<unknown>> = [] const triggerTranslator = TriggersTranslator.getInstance() const triggerLog = log.getSubLogger({ name: `${triggerTranslator.constructor.name}-${translatorCount++}` }) const triggerAndScript: TriggerTranslatorOutput = { roots: [triggersJson], scriptReferences: getAllContentForScriptFile(triggersJson) as ScriptContent[] } const triggerResult = triggerTranslator.jsonToWar(triggerAndScript) tasks.push(writeFile(path.join(output, 'war3map.wtg'), triggerResult.buffer) .then(() => triggerLog.info('Finished exporting triggers.'))) const scriptTranslator = CustomScriptsTranslator.getInstance() const scriptLog = log.getSubLogger({ name: `${scriptTranslator.constructor.name}-${translatorCount++}` }) const scriptArg: { headerComments: string[], scripts: string[] } = { headerComments: [], scripts: [] } for (const trigger of triggerAndScript.scriptReferences) { if (trigger != null) { if ((trigger as MapHeader).children != null) { // Found header scriptArg.headerComments.push(trigger.description) } scriptArg.scripts.push(trigger.script) } else { scriptArg.scripts.push('') } } const scriptResult = scriptTranslator.jsonToWar(scriptArg) tasks.push(writeFile(path.join(output, 'war3map.wct'), scriptResult.buffer) .then(() => scriptLog.info('Finished exporting custom scripts.'))) await Promise.all(tasks) } async function processTriggers (input: string, output: string): Promise<void> { log.info('Reading triggers file') const buffer = FormatConverters[EnhancementManager.mapDataExtension].parse(await readFile(input, { encoding: 'utf8' })) as TriggerContainer[] await exportTriggers(buffer[0], output) } const Json2WarService = { convert: async function (inputPath: string, outputPath: string): Promise<void> { log.info(`Converting Warcraft III json data in '${inputPath}' and outputting to '${outputPath}'`) const promises: Array<Promise<void>> = [] const fileStack: Array<DirectoryTree<Record<string, unknown>>> = [directoryTree(inputPath, { attributes: ['type', 'extension'] })] let importDirectoryTree: DirectoryTree<Record<string, unknown>> | null = null while (fileStack.length > 0) { const file = fileStack.pop() if (file == null) break if (FileBlacklist.isDirectoryTreeBlacklisted(file)) continue if (file.type === 'directory') { if (EnhancementManager.smartImport && file.path.endsWith(EnhancementManager.importFolder)) { importDirectoryTree = file continue // skip imports } if (EnhancementManager.composeTriggers && file.path.endsWith(EnhancementManager.sourceFolder)) { log.debug('ComposeTriggers requested') promises.push((async (): Promise<void> => { const triggerJson = await TriggerComposer.composeTriggerJson(file) await exportTriggers(triggerJson, outputPath) })()) continue // skip triggers } const children = file.children if (children != null) { for (const child of children) { fileStack.push(child) } } } else { let translator: Translator<unknown> | null = null if (!EnhancementManager.composeTriggers && file.name.endsWith(`triggers${EnhancementManager.mapDataExtension}`)) { promises.push(processTriggers(file.path, outputPath)) continue } for (const [extension, thisTranslator] of Object.entries(translatorRecord)) { if (file.name.endsWith(extension)) { translator = thisTranslator break } } let outputFile = path.join(outputPath, path.relative(inputPath, file.path)) if (translator != null) { outputFile = outputFile.substring(0, outputFile.lastIndexOf('.')) // remove final extension if (!EnhancementManager.smartImport || !(translator instanceof ImportsTranslator)) { promises.push(processFile(file.path, translator, outputFile)) } } else { promises.push(copyFileWithDirCreation(file.path, outputFile)) } } } if (EnhancementManager.smartImport) { log.debug('SmartImports requested') const importFileOutputPath = path.join(outputPath, 'war3map.imp') if (importDirectoryTree != null) { const importedFiles = ImportComposer.composeImportRegistry(importDirectoryTree) promises.push(exportImportsFile(importedFiles, importFileOutputPath)) fileStack.push(importDirectoryTree) while (fileStack.length > 0) { const file = fileStack.pop() if (file == null) break if (file.type === 'directory') { const children = file.children if (children != null) { for (const child of children) { fileStack.push(child) } } } else { const outputFile = path.join(outputPath, path.relative(importDirectoryTree.path, file.path)) promises.push(copyFileWithDirCreation(file.path, outputFile)) } } } else { promises.push(exportImportsFile([], importFileOutputPath)) } } await Promise.all(promises) } } export default Json2WarService