diff --git a/src/Main.ts b/src/Main.ts index b1b51e5..60be08c 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -2,13 +2,17 @@ import 'reflect-metadata'; import { container } from 'tsyringe'; import { Configuration } from './common/config/Configuration'; import { IConverter } from './common/converters/IConverter'; -import { EffectConverter } from './converters/effect/EffectConverter'; -import { ExternalTextsConverter } from './converters/externaltexts/ExternalTextsConverter'; -import { FigureConverter } from './converters/figure/FigureConverter'; -import { FigureDataConverter } from './converters/figuredata/FigureDataConverter'; -import { FurnitureConverter } from './converters/furniture/FurnitureConverter'; -import { PetConverter } from './converters/pet/PetConverter'; -import { ProductDataConverter } from './converters/productdata/ProductDataConverter'; +import { EffectConverter } from './converters/EffectConverter'; +import { EffectMapConverter } from './converters/EffectMapConverter'; +import { ExternalTextsConverter } from './converters/ExternalTextsConverter'; +import { FigureConverter } from './converters/FigureConverter'; +import { FigureDataConverter } from './converters/FigureDataConverter'; +import { FigureMapConverter } from './converters/FigureMapConverter'; +import { FurnitureConverter } from './converters/FurnitureConverter'; +import { FurnitureDataConverter } from './converters/FurnitureDataConverter'; +import { OldAssetConverter } from './converters/OldAssetConverter'; +import { PetConverter } from './converters/PetConverter'; +import { ProductDataConverter } from './converters/ProductDataConverter'; (async () => { @@ -18,13 +22,17 @@ import { ProductDataConverter } from './converters/productdata/ProductDataConver await config.init(); const converters = [ + FurnitureDataConverter, + FigureDataConverter, ProductDataConverter, ExternalTextsConverter, + EffectMapConverter, + FigureMapConverter, + FurnitureConverter, FigureConverter, EffectConverter, - FurnitureConverter, PetConverter, - FigureDataConverter + OldAssetConverter ]; const [ arg1, arg2, ...rest ] = process.argv; diff --git a/src/common/SWFDownloader.ts b/src/common/SWFDownloader.ts new file mode 100644 index 0000000..5d9e345 --- /dev/null +++ b/src/common/SWFDownloader.ts @@ -0,0 +1,59 @@ +import { HabboAssetSWF } from '../swf/HabboAssetSWF'; +import { FileUtilities } from '../utils/FileUtilities'; + +export class SWFDownloader +{ + public static USES_REVISION: boolean = true; + public static LOG_DOWNLOADS: boolean = true; + + public static async download(baseUrl: string, className: string, revision: number): Promise + { + let url = baseUrl; + + if(!url || !url.length) return; + + if(SWFDownloader.USES_REVISION && (revision > -1)) url = url.replace('%revision%', revision.toString()); + + url = url.replace('%className%', className); + + if(SWFDownloader.LOG_DOWNLOADS) + { + console.log(); + console.log(` Downloading ${ className } from ${url}`); + } + + const habboAssetSWF = await this.extractSWF(url); + + if(!habboAssetSWF) return null; + + return habboAssetSWF; + } + + public static async downloadFromUrl(url: string, className: string, revision: number): Promise + { + if(SWFDownloader.LOG_DOWNLOADS) + { + console.log(); + console.log(` Downloading ${ className } from ${url}`); + } + + const habboAssetSWF = await this.extractSWF(url); + + if(!habboAssetSWF) return null; + + return habboAssetSWF; + } + + public static async extractSWF(url: string): Promise + { + const buffer = await FileUtilities.readFileAsBuffer(url); + + if(!buffer) return null; + + const habboAssetSWF = new HabboAssetSWF(buffer); + + await habboAssetSWF.setupAsync(); + + return habboAssetSWF; + } +} diff --git a/src/common/bundle/BundleProvider.ts b/src/common/bundle/BundleProvider.ts index 2fcb9f6..8f57cbb 100644 --- a/src/common/bundle/BundleProvider.ts +++ b/src/common/bundle/BundleProvider.ts @@ -1,20 +1,22 @@ import { packAsync } from 'free-tex-packer-core'; -import { singleton } from 'tsyringe'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { ImageBundle } from './ImageBundle'; import { SpriteBundle } from './SpriteBundle'; -@singleton() export class BundleProvider { public static imageSource: Map = new Map(); - public async generateSpriteSheet(habboAssetSWF: HabboAssetSWF): Promise + public static async generateSpriteSheet(habboAssetSWF: HabboAssetSWF, convertCase: boolean = false): Promise { const tagList = habboAssetSWF.symbolTags(); const names: string[] = []; const tags: number[] = []; + let documentClass = habboAssetSWF.getDocumentClass(); + + if(convertCase) documentClass = (documentClass.replace(/(?:^|\.?)([A-Z])/g, (x,y) => ('_' + y.toLowerCase().replace(/^_/, '')))); + for(const tag of tagList) { names.push(...tag.names); @@ -39,7 +41,7 @@ export class BundleProvider if(imageTag.className.indexOf('_32_') >= 0) continue; - BundleProvider.imageSource.set(names[i].substring(habboAssetSWF.getDocumentClass().length + 1), imageTag.className.substring(habboAssetSWF.getDocumentClass().length + 1)); + BundleProvider.imageSource.set(names[i].substring(documentClass.length + 1), imageTag.className.substring(documentClass.length + 1)); } } @@ -47,20 +49,24 @@ export class BundleProvider if(imageTag.className.indexOf('_32_') >= 0) continue; - imageBundle.addImage(imageTag.className, imageTag.imgData); + let className = imageTag.className; + + if(convertCase) className = ((className.replace(/(?:^|\.?)([A-Z])/g, (x,y) => ('_' + y.toLowerCase().replace(/^_/, '')))).substring(1)); + + imageBundle.addImage(className, imageTag.imgData); } if(!imageBundle.images.length) return null; - return await this.packImages(habboAssetSWF.getDocumentClass(), imageBundle); + return await this.packImages(documentClass, imageBundle, convertCase); } - async packImages(documentClass: string, imageBundle: ImageBundle): Promise + private static async packImages(documentClass: string, imageBundle: ImageBundle, convertCase: boolean = false): Promise { const files = await packAsync(imageBundle.images, { - textureName: documentClass, - width: 3072, - height: 2048, + textureName: (convertCase ? documentClass.substring(1) : documentClass), + width: 10240, + height: 4320, fixedSize: false, allowRotation: false, detectIdentical: true, @@ -86,6 +92,8 @@ export class BundleProvider name: item.name, buffer: item.buffer }; + + if(convertCase) bundle.imageData.name = (documentClass.replace(/(?:^|\.?)([A-Z])/g, (x,y) => ('_' + y.toLowerCase().replace(/^_/, '')))).substring(1); } } diff --git a/src/common/converters/SWFConverter.ts b/src/common/converters/SWFConverter.ts index 836997e..e3d20aa 100644 --- a/src/common/converters/SWFConverter.ts +++ b/src/common/converters/SWFConverter.ts @@ -1,7 +1,7 @@ import { wrap } from 'bytebuffer'; -import { writeFile } from 'fs/promises'; import { parseStringPromise } from 'xml2js'; import { IAssetData } from '../../mapping/json'; +import { AnimationMapper, AssetMapper, IndexMapper, LogicMapper, ManifestMapper, VisualizationMapper } from '../../mapping/mappers'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { DefineBinaryDataTag } from '../../swf/tags/DefineBinaryDataTag'; import { NitroBundle } from '../../utils/NitroBundle'; @@ -10,24 +10,27 @@ import { Converter } from './Converter'; export class SWFConverter extends Converter { - protected async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, assetData: IAssetData, spriteBundle: SpriteBundle): Promise + private static removeComments(data: string): string + { + return data.replace(//sg, ''); + } + + public static createNitroBundle(className: string, assetData: IAssetData, spriteBundle: SpriteBundle): NitroBundle { if(spriteBundle && (spriteBundle.spritesheet !== undefined)) assetData.spritesheet = spriteBundle.spritesheet; - const name = habboAssetSWF.getDocumentClass(); - const path = outputFolder + '/' + name + '.nitro'; const nitroBundle = new NitroBundle(); - nitroBundle.addFile((name + '.json'), Buffer.from(JSON.stringify(assetData))); + nitroBundle.addFile((className + '.json'), Buffer.from(JSON.stringify(assetData))); if(spriteBundle && (spriteBundle.imageData !== undefined)) nitroBundle.addFile(spriteBundle.imageData.name, spriteBundle.imageData.buffer); - await writeFile(path, await nitroBundle.toBufferAsync()); + return nitroBundle; } - private static getBinaryData(habboAssetSWF: HabboAssetSWF, type: string, documentNameTwice: boolean): DefineBinaryDataTag + public static getBinaryData(habboAssetSWF: HabboAssetSWF, type: string, documentNameTwice: boolean, snakeCase: boolean = false): DefineBinaryDataTag { - let binaryName = habboAssetSWF.getFullClassName(type, documentNameTwice); + let binaryName = habboAssetSWF.getFullClassName(type, documentNameTwice, snakeCase); let tag = habboAssetSWF.getBinaryTagByName(binaryName); if(!tag) @@ -39,64 +42,61 @@ export class SWFConverter extends Converter return tag; } - protected static async getManifestXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getManifestXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'manifest', false); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'manifest', false, snakeCase); if(!binaryData) return null; - return await parseStringPromise(binaryData.binaryData); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static async getIndexXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getIndexXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'index', false); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'index', false, snakeCase); if(!binaryData) return null; - //Will fix some malformed comments exception. - const removedComments: string = binaryData.binaryData.replace(//sg, ''); - - return await parseStringPromise(removedComments); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static async getAssetsXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getAssetsXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'assets', true); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'assets', true, snakeCase); if(!binaryData) return null; - return await parseStringPromise(binaryData.binaryData); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static async getLogicXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getLogicXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'logic', true); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'logic', true, snakeCase); if(!binaryData) return null; - return await parseStringPromise(binaryData.binaryData); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static async getVisualizationXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getVisualizationXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'visualization', true); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'visualization', true, snakeCase); if(!binaryData) return null; - return await parseStringPromise(binaryData.binaryData); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static async getAnimationXML(habboAssetSWF: HabboAssetSWF): Promise + public static async getAnimationXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise { - const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'animation', false); + const binaryData = SWFConverter.getBinaryData(habboAssetSWF, 'animation', false, snakeCase); if(!binaryData) return null; - return await parseStringPromise(binaryData.binaryData); + return await parseStringPromise(this.removeComments(binaryData.binaryData)); } - protected static getPalette(habboAssetSWF: HabboAssetSWF, paletteName: string): [ number, number, number ][] + public static getPalette(habboAssetSWF: HabboAssetSWF, paletteName: string): [ number, number, number ][] { const binaryData = SWFConverter.getBinaryData(habboAssetSWF, paletteName, false); @@ -131,4 +131,65 @@ export class SWFConverter extends Converter return paletteColors; } + + public static async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string, snakeCase: boolean = false): Promise + { + if(!habboAssetSWF) return null; + + const output: IAssetData = {}; + + output.type = assetType; + + const indexXML = await this.getIndexXML(habboAssetSWF, snakeCase); + + if(indexXML) IndexMapper.mapXML(indexXML, output); + + const manifestXML = await this.getManifestXML(habboAssetSWF, snakeCase); + + if(manifestXML) ManifestMapper.mapXML(manifestXML, output); + + const assetXML = await this.getAssetsXML(habboAssetSWF, snakeCase); + + if(assetXML) + { + AssetMapper.mapXML(assetXML, output); + + if(output.palettes !== undefined) + { + for(const paletteId in output.palettes) + { + const palette = output.palettes[paletteId]; + + const paletteColors = this.getPalette(habboAssetSWF, palette.source); + + if(!paletteColors) + { + delete output.palettes[paletteId]; + + continue; + } + + const rgbs: [ number, number, number ][] = []; + + for(const rgb of paletteColors) rgbs.push([ rgb[0], rgb[1], rgb[2] ]); + + palette.rgb = rgbs; + } + } + } + + const animationXML = await this.getAnimationXML(habboAssetSWF, snakeCase); + + if(animationXML) AnimationMapper.mapXML(animationXML, output); + + const logicXML = await this.getLogicXML(habboAssetSWF, snakeCase); + + if(logicXML) LogicMapper.mapXML(logicXML, output); + + const visualizationXML = await this.getVisualizationXML(habboAssetSWF, snakeCase); + + if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, output); + + return output; + } } diff --git a/src/configuration.json.example b/src/configuration.json.example index 6916571..27521c3 100644 --- a/src/configuration.json.example +++ b/src/configuration.json.example @@ -12,13 +12,10 @@ "flash.dynamic.download.url": "", "dynamic.download.furniture.url": "${flash.dynamic.download.url}%revision%/%className%.swf", "external.variables.url": "https://www.habbo.com/gamedata/external_variables/1", - "external.texts.url": "${external.texts.txt}", - "convert.productdata": "1", - "convert.externaltexts": "1", - "convert.figure": "1", - "convert.figuredata": "1", - "convert.effect": "1", - "convert.furniture": "1", - "convert.pet": "1", + "external.texts.url": "", + "convert.figure": "0", + "convert.effect": "0", + "convert.furniture": "0", + "convert.pet": "0", "misc.log_download_urls": "0" } diff --git a/src/converters/EffectConverter.ts b/src/converters/EffectConverter.ts new file mode 100644 index 0000000..c495b4d --- /dev/null +++ b/src/converters/EffectConverter.ts @@ -0,0 +1,107 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { BundleProvider } from '../common/bundle/BundleProvider'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { SWFConverter } from '../common/converters/SWFConverter'; +import { SWFDownloader } from '../common/SWFDownloader'; +import { File } from '../utils/File'; +import { FileUtilities } from '../utils/FileUtilities'; +import { EffectMapConverter } from './EffectMapConverter'; + +@singleton() +export class EffectConverter extends Converter +{ + public effectTypes: Map = new Map(); + + constructor( + private readonly _effectMapConverter: EffectMapConverter, + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + if(!this._configuration.getBoolean('convert.effect')) return; + + const now = Date.now(); + const spinner = ora('Preparing Effects').start(); + const baseUrl = this._configuration.getValue('dynamic.download.effect.url'); + const effectMap = this._effectMapConverter.effectMap; + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'effect'); + + const classNames: string[] = []; + + if(effectMap.effects !== undefined) + { + for(const library of effectMap.effects) + { + const className = library.lib; + + if(classNames.indexOf(className) >= 0) continue; + + classNames.push(className); + + this.effectTypes.set(className, library.type); + } + } + + for(const className of classNames) + { + try + { + const path = new File(directory.path + '/' + className + '.nitro'); + + if(path.exists()) continue; + + const habboAssetSWF = await SWFDownloader.download(baseUrl, className, -1); + + if(!habboAssetSWF) + { + spinner.text = 'Couldnt convert effect: ' + className; + + spinner.render(); + + continue; + } + else + { + spinner.text = 'Converting: ' + className; + + spinner.render(); + } + + const spriteBundle = await BundleProvider.generateSpriteSheet(habboAssetSWF); + const assetData = await SWFConverter.mapXML2JSON(habboAssetSWF, className); + + if(assetData) + { + assetData.name = className; + assetData.type = this.effectTypes.get(className); + } + + const nitroBundle = SWFConverter.createNitroBundle(habboAssetSWF.getDocumentClass(), assetData, spriteBundle); + + await writeFile(path.path, await nitroBundle.toBufferAsync()); + + spinner.text = 'Finished: ' + className; + + spinner.render(); + } + + catch (error) + { + spinner.text = `Error converting ${ className }: ${ error.message }`; + + spinner.render(); + + continue; + } + } + + console.log(); + spinner.succeed(`Effects finished in ${ Date.now() - now }ms`); + } +} diff --git a/src/converters/EffectMapConverter.ts b/src/converters/EffectMapConverter.ts new file mode 100644 index 0000000..35210fb --- /dev/null +++ b/src/converters/EffectMapConverter.ts @@ -0,0 +1,58 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { parseStringPromise } from 'xml2js'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IEffectMap } from '../mapping/json'; +import { EffectMapMapper } from '../mapping/mappers'; +import { FileUtilities } from '../utils/FileUtilities'; + +@singleton() +export class EffectMapConverter extends Converter +{ + public effectMap: IEffectMap = null; + + constructor( + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing EffectMap').start(); + const url = this._configuration.getValue('effectmap.load.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + const xml = await parseStringPromise(content.replace(/&/g,'&')); + + this.effectMap = await this.mapXML2JSON(xml); + } + else + { + this.effectMap = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/EffectMap.json'; + + await writeFile(path, JSON.stringify(this.effectMap), 'utf8'); + + spinner.succeed(`EffectMap finished in ${ Date.now() - now }ms`); + } + + private async mapXML2JSON(xml: any): Promise + { + if(!xml) return null; + + const output: IEffectMap = {}; + + EffectMapMapper.mapXML(xml, output); + + return output; + } +} diff --git a/src/converters/ExternalTextsConverter.ts b/src/converters/ExternalTextsConverter.ts new file mode 100644 index 0000000..7115d22 --- /dev/null +++ b/src/converters/ExternalTextsConverter.ts @@ -0,0 +1,61 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IExternalTexts } from '../mapping/json'; +import { FileUtilities } from '../utils/FileUtilities'; + +@singleton() +export class ExternalTextsConverter extends Converter +{ + public externalTexts: IExternalTexts = null; + + constructor( + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing ExternalTexts').start(); + const url = this._configuration.getValue('external.texts.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + this.externalTexts = await this.mapText2JSON(content); + } + else + { + this.externalTexts = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/ExternalTexts.json'; + + await writeFile(path, JSON.stringify(this.externalTexts), 'utf8'); + + spinner.succeed(`ExternalTexts finished in ${ Date.now() - now }ms`); + } + + private async mapText2JSON(text: string): Promise + { + if(!text) return null; + + const output: IExternalTexts = {}; + + const parts = text.split(/\n\r{1,}|\n{1,}|\r{1,}/mg); + + for(const part of parts) + { + const [ key, value ] = part.split('='); + + output[key] = value; + } + + return output; + } +} diff --git a/src/converters/FigureConverter.ts b/src/converters/FigureConverter.ts new file mode 100644 index 0000000..ae412f7 --- /dev/null +++ b/src/converters/FigureConverter.ts @@ -0,0 +1,105 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { BundleProvider } from '../common/bundle/BundleProvider'; +import { Configuration } from '../common/config/Configuration'; +import { SWFConverter } from '../common/converters/SWFConverter'; +import { SWFDownloader } from '../common/SWFDownloader'; +import { File } from '../utils/File'; +import { FileUtilities } from '../utils/FileUtilities'; +import { FigureMapConverter } from './FigureMapConverter'; + +@singleton() +export class FigureConverter +{ + public figureTypes: Map = new Map(); + + constructor( + private readonly _figureMapConverter: FigureMapConverter, + private readonly _configuration: Configuration) + {} + + public async convertAsync(args: string[] = []): Promise + { + if(!this._configuration.getBoolean('convert.figure')) return; + + const now = Date.now(); + const spinner = ora('Preparing Figure').start(); + const baseUrl = this._configuration.getValue('dynamic.download.figure.url'); + const figureMap = this._figureMapConverter.figureMap; + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'figure'); + const classNames: string[] = []; + + if(figureMap.libraries !== undefined) + { + for(const library of figureMap.libraries) + { + const className = library.id.split('*')[0]; + + if(className === 'hh_human_fx' || className === 'hh_pets') continue; + + if(classNames.indexOf(className) >= 0) continue; + + classNames.push(className); + + this.figureTypes.set(className, library.parts[0].type); + } + } + + for(const className of classNames) + { + try + { + const path = new File(directory.path + '/' + className + '.nitro'); + + if(path.exists()) continue; + + const habboAssetSWF = await SWFDownloader.download(baseUrl, className, -1); + + if(!habboAssetSWF) + { + spinner.text = 'Couldnt convert figure: ' + className; + + spinner.render(); + + continue; + } + else + { + spinner.text = 'Converting: ' + className; + + spinner.render(); + } + + const spriteBundle = await BundleProvider.generateSpriteSheet(habboAssetSWF); + const assetData = await SWFConverter.mapXML2JSON(habboAssetSWF, className); + + if(assetData) + { + assetData.name = className; + assetData.type = this.figureTypes.get(className); + } + + const nitroBundle = SWFConverter.createNitroBundle(habboAssetSWF.getDocumentClass(), assetData, spriteBundle); + + await writeFile(path.path, await nitroBundle.toBufferAsync()); + + spinner.text = 'Finished: ' + className; + + spinner.render(); + } + + catch (error) + { + spinner.text = `Error converting ${ className }: ${ error.message }`; + + spinner.render(); + + continue; + } + } + + console.log(); + spinner.succeed(`Figures finished in ${ Date.now() - now }ms`); + } +} diff --git a/src/converters/FigureDataConverter.ts b/src/converters/FigureDataConverter.ts new file mode 100644 index 0000000..76b5e07 --- /dev/null +++ b/src/converters/FigureDataConverter.ts @@ -0,0 +1,58 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { parseStringPromise } from 'xml2js'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IFigureData } from '../mapping/json/figuredata/IFigureData'; +import { FigureDataMapper } from '../mapping/mappers/FigureDataMapper'; +import { FileUtilities } from '../utils/FileUtilities'; + +@singleton() +export class FigureDataConverter extends Converter +{ + public figureData: IFigureData = null; + + constructor( + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing FigureData').start(); + const url = this._configuration.getValue('figuredata.load.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + const xml = await parseStringPromise(content.replace(/&/g,'&')); + + this.figureData = await this.mapXML2JSON(xml); + } + else + { + this.figureData = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/FigureData.json'; + + await writeFile(path, JSON.stringify(this.figureData), 'utf8'); + + spinner.succeed(`FigureData finished in ${ Date.now() - now }ms`); + } + + private async mapXML2JSON(xml: any): Promise + { + if(!xml) return null; + + const output: IFigureData = {}; + + FigureDataMapper.mapXML(xml, output); + + return output; + } +} diff --git a/src/converters/FigureMapConverter.ts b/src/converters/FigureMapConverter.ts new file mode 100644 index 0000000..f087c79 --- /dev/null +++ b/src/converters/FigureMapConverter.ts @@ -0,0 +1,60 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { parseStringPromise } from 'xml2js'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IFigureMap } from '../mapping/json'; +import { FigureMapMapper } from '../mapping/mappers'; +import { FileUtilities } from '../utils/FileUtilities'; +import { Logger } from '../utils/Logger'; + +@singleton() +export class FigureMapConverter extends Converter +{ + public figureMap: IFigureMap = null; + + constructor( + private readonly _configuration: Configuration, + private readonly _logger: Logger) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing FigureMap').start(); + const url = this._configuration.getValue('figuremap.load.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + const xml = await parseStringPromise(content.replace(/&/g,'&')); + + this.figureMap = await this.mapXML2JSON(xml); + } + else + { + this.figureMap = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/FigureMap.json'; + + await writeFile(path, JSON.stringify(this.figureMap), 'utf8'); + + spinner.succeed(`FigureMap finished in ${ Date.now() - now }ms`); + } + + private async mapXML2JSON(xml: any): Promise + { + if(!xml) return null; + + const output: IFigureMap = {}; + + FigureMapMapper.mapXML(xml, output); + + return output; + } +} diff --git a/src/converters/FurnitureConverter.ts b/src/converters/FurnitureConverter.ts new file mode 100644 index 0000000..d0972e0 --- /dev/null +++ b/src/converters/FurnitureConverter.ts @@ -0,0 +1,121 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { BundleProvider } from '../common/bundle/BundleProvider'; +import { Configuration } from '../common/config/Configuration'; +import { SWFConverter } from '../common/converters/SWFConverter'; +import { SWFDownloader } from '../common/SWFDownloader'; +import { IAssetData } from '../mapping/json'; +import { File } from '../utils/File'; +import { FileUtilities } from '../utils/FileUtilities'; +import { FurnitureDataConverter } from './FurnitureDataConverter'; + +@singleton() +export class FurnitureConverter +{ + public assets: Map = new Map(); + + constructor( + private readonly _furniDataConverter: FurnitureDataConverter, + private readonly _configuration: Configuration) + {} + + public async convertAsync(args: string[] = []): Promise + { + if(!this._configuration.getBoolean('convert.furniture')) return; + + const now = Date.now(); + const spinner = ora('Preparing Furniture').start(); + const baseUrl = this._configuration.getValue('dynamic.download.furniture.url'); + const furniData = this._furniDataConverter.furnitureData; + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'furniture'); + const classNames: string[] = []; + const revisions: number[] = []; + + if(furniData.roomitemtypes) + { + if(furniData.roomitemtypes.furnitype) + { + for(const furniType of furniData.roomitemtypes.furnitype) + { + const className = furniType.classname.split('*')[0]; + const revision = furniType.revision; + + if(classNames.indexOf(className) >= 0) continue; + + classNames.push(className); + revisions.push(revision); + } + } + } + + if(furniData.wallitemtypes) + { + if(furniData.wallitemtypes.furnitype) + { + for(const furniType of furniData.wallitemtypes.furnitype) + { + const className = furniType.classname.split('*')[0]; + const revision = furniType.revision; + + if(classNames.indexOf(className) >= 0) continue; + + classNames.push(className); + revisions.push(revision); + } + } + } + + for(let i = 0; i < classNames.length; i++) + { + const className = classNames[i]; + const revision = revisions[i]; + + try + { + const path = new File(directory.path + '/' + className + '.nitro'); + + if(path.exists()) continue; + + const habboAssetSWF = await SWFDownloader.download(baseUrl, className, revision); + + if(!habboAssetSWF) + { + spinner.text = 'Couldnt convert furni: ' + className; + + spinner.render(); + + continue; + } + else + { + spinner.text = 'Couldnt convert furni: ' + className; + + spinner.render(); + } + + const spriteBundle = await BundleProvider.generateSpriteSheet(habboAssetSWF); + const assetData = await SWFConverter.mapXML2JSON(habboAssetSWF, 'furniture'); + const nitroBundle = SWFConverter.createNitroBundle(habboAssetSWF.getDocumentClass(), assetData, spriteBundle); + + await writeFile(path.path, await nitroBundle.toBufferAsync()); + + spinner.text = 'Finished: ' + className; + + spinner.render(); + } + + catch (error) + { + spinner.text = `Error converting ${ className }: ${ error.message }`; + + spinner.render(); + + continue; + } + } + + console.log(); + spinner.succeed(`Furniture finished in ${ Date.now() - now }ms`); + } +} diff --git a/src/converters/FurnitureDataConverter.ts b/src/converters/FurnitureDataConverter.ts new file mode 100644 index 0000000..9038240 --- /dev/null +++ b/src/converters/FurnitureDataConverter.ts @@ -0,0 +1,59 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { parseStringPromise } from 'xml2js'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IFurnitureData } from '../mapping/json'; +import { FurnitureDataMapper } from '../mapping/mappers'; +import { FileUtilities } from '../utils/FileUtilities'; + +@singleton() +export class FurnitureDataConverter extends Converter +{ + public furnitureData: IFurnitureData = null; + + constructor( + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing FurnitureData').start(); + const url = this._configuration.getValue('furnidata.load.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + const xml = await parseStringPromise(content.replace(/&/g,'&')); + const furnitureData = await this.mapXML2JSON(xml); + + this.furnitureData = furnitureData; + } + else + { + this.furnitureData = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/FurnitureData.json'; + + await writeFile(path, JSON.stringify(this.furnitureData), 'utf8'); + + spinner.succeed(`FurnitureData finished in ${ Date.now() - now }ms`); + } + + private async mapXML2JSON(xml: any): Promise + { + if(!xml) return null; + + const output: IFurnitureData = {}; + + FurnitureDataMapper.mapXML(xml, output); + + return output; + } +} diff --git a/src/converters/OldAssetConverter.ts b/src/converters/OldAssetConverter.ts new file mode 100644 index 0000000..90c25bf --- /dev/null +++ b/src/converters/OldAssetConverter.ts @@ -0,0 +1,50 @@ +import { writeFile } from 'fs/promises'; +import { singleton } from 'tsyringe'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { File } from '../utils/File'; +import { FileUtilities } from '../utils/FileUtilities'; +import { Logger } from '../utils/Logger'; +import { NitroBundle } from '../utils/NitroBundle'; + +@singleton() +export class OldAssetConverter extends Converter +{ + constructor( + private readonly _configuration: Configuration, + private readonly _logger: Logger) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + if(args.shift() !== 'old-asset') return; + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'generic'); + const baseDirectory = FileUtilities.getDirectory(args.shift()); + + for(const className of args) + { + try + { + const path = new File(directory.path + '/' + className + '.nitro'); + const jsonBuffer = await FileUtilities.readFileAsBuffer(baseDirectory.path + '/' + className + '/' + className + '.json'); + const imageBuffer = await FileUtilities.readFileAsBuffer(baseDirectory.path + '/' + className + '/' + className + '.png'); + const nitroBundle = new NitroBundle(); + + nitroBundle.addFile((className + '.json'), jsonBuffer); + nitroBundle.addFile((className + '.png'), imageBuffer); + + await writeFile(path.path, await nitroBundle.toBufferAsync()); + + console.log('Finished converting: ' + className); + } + + catch (error) + { + console.log('Error converting: ' + className); + } + } + } +} diff --git a/src/converters/PetConverter.ts b/src/converters/PetConverter.ts new file mode 100644 index 0000000..b2be9da --- /dev/null +++ b/src/converters/PetConverter.ts @@ -0,0 +1,98 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { BundleProvider } from '../common/bundle/BundleProvider'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { SWFConverter } from '../common/converters/SWFConverter'; +import { SWFDownloader } from '../common/SWFDownloader'; +import { File } from '../utils/File'; +import { FileUtilities } from '../utils/FileUtilities'; +import { Logger } from '../utils/Logger'; + +@singleton() +export class PetConverter extends Converter +{ + constructor( + private readonly _configuration: Configuration, + private readonly _logger: Logger) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + if(!this._configuration.getBoolean('convert.pet')) return; + + const now = Date.now(); + const spinner = ora('Preparing Pets').start(); + const baseUrl = this._configuration.getValue('dynamic.download.pet.url'); + const classNames = this.getPetTypes(); + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'pet'); + + for(const className of classNames) + { + try + { + const path = new File(directory.path + '/' + className + '.nitro'); + + if(path.exists()) continue; + + const habboAssetSWF = await SWFDownloader.download(baseUrl, className, -1); + + if(!habboAssetSWF) + { + spinner.text = 'Couldnt convert pet: ' + className; + + spinner.render(); + + continue; + } + else + { + spinner.text = 'Converting: ' + className; + + spinner.render(); + } + + const spriteBundle = await BundleProvider.generateSpriteSheet(habboAssetSWF); + const assetData = await SWFConverter.mapXML2JSON(habboAssetSWF, 'pet'); + const nitroBundle = SWFConverter.createNitroBundle(habboAssetSWF.getDocumentClass(), assetData, spriteBundle); + + await writeFile(path.path, await nitroBundle.toBufferAsync()); + + spinner.text = 'Finished: ' + className; + + spinner.render(); + } + + catch (error) + { + spinner.text = `Error converting ${ className }: ${ error.message }`; + + spinner.render(); + + continue; + } + } + + console.log(); + spinner.succeed(`Pets finished in ${ Date.now() - now }ms`); + } + + private getPetTypes(): string[] + { + const petTypes: string[] = []; + + const pets = this._configuration.getValue('pet.configuration'); + + if(pets) + { + const types = pets.split(','); + + for(const type of types) petTypes.push(type); + } + + return petTypes; + } +} diff --git a/src/converters/ProductDataConverter.ts b/src/converters/ProductDataConverter.ts new file mode 100644 index 0000000..acfb0a6 --- /dev/null +++ b/src/converters/ProductDataConverter.ts @@ -0,0 +1,86 @@ +import { writeFile } from 'fs/promises'; +import * as ora from 'ora'; +import { singleton } from 'tsyringe'; +import { Configuration } from '../common/config/Configuration'; +import { Converter } from '../common/converters/Converter'; +import { IProductData } from '../mapping/json'; +import { FileUtilities } from '../utils/FileUtilities'; + +@singleton() +export class ProductDataConverter extends Converter +{ + public productData: IProductData = null; + + constructor( + private readonly _configuration: Configuration) + { + super(); + } + + public async convertAsync(args: string[] = []): Promise + { + const now = Date.now(); + const spinner = ora('Preparing ProductData').start(); + const url = this._configuration.getValue('productdata.load.url'); + const content = await FileUtilities.readFileAsString(url); + + if(!content.startsWith('{')) + { + const productData = await this.mapText2JSON(content); + + this.productData = productData; + } + else + { + this.productData = JSON.parse(content); + } + + const directory = FileUtilities.getDirectory(this._configuration.getValue('output.folder'), 'gamedata'); + const path = directory.path + '/ProductData.json'; + + await writeFile(path, JSON.stringify(this.productData), 'utf8'); + + spinner.succeed(`ProductData finished in ${ Date.now() - now }ms`); + } + + private async mapText2JSON(text: string): Promise + { + if(!text) return null; + + const output: IProductData = { + productdata: { + product: [] + } + }; + + text = text.replace(/"{1,}/g, ''); + + const parts = text.split(/\n\r{1,}|\n{1,}|\r{1,}/mg); + + for(const part of parts) + { + const set = part.match(/\[+?((.)*?)\]/g); + + if(set) + { + for(const entry of set) + { + let value = entry.replace(/\[{1,}/mg, ''); + value = entry.replace(/\]{1,}/mg, ''); + + value = value.replace('[[', ''); + value = value.replace('[', ''); + + const pieces = value.split(','); + const code = pieces.shift(); + const name = pieces.shift(); + const description = pieces.join(','); + + output.productdata.product.push({ code, name, description }); + } + } + } + + return output; + } +} diff --git a/src/converters/catalog/CatalogConverter.ts b/src/converters/catalog/CatalogConverter.ts deleted file mode 100644 index ae11b3f..0000000 --- a/src/converters/catalog/CatalogConverter.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { createWriteStream } from 'fs'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { SWFConverter } from '../../common/converters/SWFConverter'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { FurnitureConverter } from '../furniture/FurnitureConverter'; -import { FurnitureDataConverter } from '../furnituredata/FurnitureDataConverter'; - -@singleton() -export class CatalogConverter extends SWFConverter -{ - constructor( - private readonly _furniDataConverter: FurnitureDataConverter, - private readonly _furniConverter: FurnitureConverter, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.catalog')) return; - - const now = Date.now(); - - const spinner = ora('Building catalog').start(); - - const directory = this.getDirectory(); - - //await unlink(directory.path + '/catalog.txt'); - - const file = createWriteStream(directory.path + '/catalog.txt', { - flags: 'a' - }); - - file.write('INSERT INTO `furniture_definitions` (`id`, `sprite_id`, `public_name`, `product_name`, `type`, `logic`, `total_states`, `x`, `y`, `z`, `can_stack`, `can_walk`, `can_sit`, `can_lay`, `can_recycle`, `can_trade`, `can_group`, `can_sell`, `extra_data`, `date_created`, `date_updated`) VALUES'); - - try - { - const furnitureData = this._furniDataConverter.furnitureData; - - const floorItems = furnitureData.roomitemtypes; - - if(floorItems && floorItems.furnitype) - { - for(const furniture of floorItems.furnitype) - { - const assetData = this._furniConverter.assets.get(furniture.classname.split('*')[0]); - - if(!assetData) continue; - - let totalStates = 0; - - if(assetData.visualizations) - { - for(const visualization of assetData.visualizations) - { - if(visualization.size !== 64) continue; - - const animations = visualization.animations; - - for(const animationKey in animations) - { - const animation = animations[animationKey]; - - if((animation.transitionTo !== undefined) || (animation.transitionFrom !== undefined)) continue; - - totalStates++; - } - } - - file.write(`(NULL, ${ furniture.id }, "${ furniture.name }", "${ furniture.classname }", 's', 'default', ${ totalStates }, ${ isNaN(assetData.dimensions.x) ? 0 : assetData.dimensions.x }, ${ isNaN(assetData.dimensions.y) ? 0 : assetData.dimensions.y }, ${ isNaN(assetData.dimensions.z) ? 0 : assetData.dimensions.z }, 0, ${ furniture.canstandon ? 1 : 0 }, ${ furniture.cansiton ? 1 : 0 }, ${ furniture.canlayon ? 1 : 0 }, 1, 1, 1, 1, NULL, '2021-03-21 22:58:36.000000', '2021-03-21 22:58:45.000000'), `); - } - } - } - - const wallItems = furnitureData.wallitemtypes; - - if(wallItems && wallItems.furnitype) - { - for(const furniture of wallItems.furnitype) - { - const assetData = this._furniConverter.assets.get(furniture.classname.split('*')[0]); - - if(!assetData) continue; - - let totalStates = 0; - - for(const visualization of assetData.visualizations) - { - if(visualization.size !== 64) continue; - - const animations = visualization.animations; - - for(const animationKey in animations) - { - const animation = animations[animationKey]; - - if((animation.transitionTo !== undefined) || (animation.transitionFrom !== undefined)) continue; - - totalStates++; - } - } - - file.write(`(NULL, ${ furniture.id }, "${ furniture.name }", "${ furniture.classname }", 'i', 'default', ${ totalStates }, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, NULL, '2021-03-21 22:58:36.000000', '2021-03-21 22:58:45.000000'), `); - } - } - - file.end(); - - spinner.succeed(`Catalog finished in ${ Date.now() - now }ms`); - } - - catch (error) - { - spinner.fail('Catalog failed: ' + error.message); - } - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + 'catalog'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - return gameDataFolder; - } -} diff --git a/src/converters/effect/EffectConverter.ts b/src/converters/effect/EffectConverter.ts deleted file mode 100644 index 68cd92f..0000000 --- a/src/converters/effect/EffectConverter.ts +++ /dev/null @@ -1,99 +0,0 @@ -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { BundleProvider } from '../../common/bundle/BundleProvider'; -import { Configuration } from '../../common/config/Configuration'; -import { SWFConverter } from '../../common/converters/SWFConverter'; -import { IAssetData } from '../../mapping/json'; -import { AnimationMapper, ManifestMapper } from '../../mapping/mappers'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { EffectDownloader } from './EffectDownloader'; - -@singleton() -export class EffectConverter extends SWFConverter -{ - constructor( - private readonly _effectDownloader: EffectDownloader, - private readonly _configuration: Configuration, - private readonly _bundleProvider: BundleProvider, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.effect')) return; - - const now = Date.now(); - - const spinner = ora('Preparing Effects').start(); - - const directory = this.getDirectory(); - - try - { - await this._effectDownloader.download(directory, async (habboAssetSwf: HabboAssetSWF, className: string) => - { - if(!habboAssetSwf) - { - spinner.text = 'Couldnt convert effect: ' + className; - } - else - { - spinner.text = 'Parsing Effect: ' + habboAssetSwf.getDocumentClass(); - } - - spinner.render(); - - if(!habboAssetSwf) return; - - const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); - const assetData = await this.mapXML2JSON(habboAssetSwf, className); - - await this.fromHabboAsset(habboAssetSwf, directory.path, assetData.type, assetData, spriteBundle); - }); - - spinner.succeed(`Effects finished in ${ Date.now() - now }ms`); - } - - catch (error) - { - spinner.fail('Effects failed: ' + error.message); - } - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + 'effect'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - return gameDataFolder; - } - - private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise - { - if(!habboAssetSWF) return null; - - const output: IAssetData = {}; - - output.name = assetType; - output.type = EffectDownloader.EFFECT_TYPES.get(assetType); - - const manifestXML = await EffectConverter.getManifestXML(habboAssetSWF); - - if(manifestXML) ManifestMapper.mapXML(manifestXML, output); - - const animationXML = await EffectConverter.getAnimationXML(habboAssetSWF); - - if(animationXML) AnimationMapper.mapXML(animationXML, output); - - return output; - } -} diff --git a/src/converters/effect/EffectDownloader.ts b/src/converters/effect/EffectDownloader.ts deleted file mode 100644 index 0fa1f79..0000000 --- a/src/converters/effect/EffectDownloader.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { IEffectMap } from '../../mapping/json'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { FileUtilities } from '../../utils/FileUtilities'; -import { Logger } from '../../utils/Logger'; -import { EffectMapConverter } from '../effectmap/EffectMapConverter'; - -@singleton() -export class EffectDownloader -{ - public static EFFECT_TYPES: Map = new Map(); - - constructor( - private readonly _effectMapConverter: EffectMapConverter, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - {} - - public async download(directory: File, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - await this._effectMapConverter.convertAsync(); - - const effectMap = await this.parseEffectMap(); - const classNames: string[] = []; - - if(effectMap.effects !== undefined) - { - for(const library of effectMap.effects) - { - const className = library.lib; - - const existingFile = new File(directory.path + '/' + className + '.nitro'); - - if(existingFile.exists()) continue; - - if(classNames.indexOf(className) >= 0) continue; - - classNames.push(className); - - try - { - EffectDownloader.EFFECT_TYPES.set(className, library.type); - - await this.extractEffect(className, callback); - } - - catch (error) - { - console.log(); - console.error(`Error parsing ${ className }: ` + error.message); - - this._logger.logError(`Error parsing ${ className }: ` + error.message); - } - } - } - } - - public async parseEffectMap(): Promise - { - const url = this._configuration.getValue('effectmap.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading effect map from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return (JSON.parse(content) as IEffectMap); - } - - public async extractEffect(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - let url = this._configuration.getValue('dynamic.download.effect.url'); - - if(!url || !url.length) return; - - url = url.replace('%className%', className); - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading effect from ${url}`); - - const buffer = await FileUtilities.readFileAsBuffer(url); - - if(!buffer) return; - - const newHabboAssetSWF = new HabboAssetSWF(buffer); - - await newHabboAssetSWF.setupAsync(); - - await callback(newHabboAssetSWF, className); - } -} diff --git a/src/converters/effectmap/EffectMapConverter.ts b/src/converters/effectmap/EffectMapConverter.ts deleted file mode 100644 index c28bc60..0000000 --- a/src/converters/effectmap/EffectMapConverter.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { parseStringPromise } from 'xml2js'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { IEffectMap } from '../../mapping/json'; -import { EffectMapMapper } from '../../mapping/mappers'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { EffectMapDownloader } from './EffectMapDownloader'; - -@singleton() -export class EffectMapConverter extends Converter -{ - constructor( - private readonly _effectMapDownloader: EffectMapDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing EffectMap').start(); - - const directory = this.getDirectory(); - - try - { - this._effectMapDownloader.download(async (content: string) => - { - spinner.text = 'Parsing EffectMap'; - - spinner.render(); - - let effectMapString = content; - - if(!effectMapString.startsWith('{')) - { - const xml = await parseStringPromise(effectMapString); - - const effectMap = await this.mapXML2JSON(xml); - - effectMapString = JSON.stringify(effectMap); - } - - const path = directory.path + '/EffectMap.json'; - - await writeFile(path, effectMapString, 'utf8'); - - this._configuration.setValue('effectmap.load.url', path); - - spinner.succeed(`EffectMap finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('EffectMap failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapXML2JSON(xml: any): Promise - { - if(!xml) return null; - - const output: IEffectMap = {}; - - EffectMapMapper.mapXML(xml, output); - - return output; - } -} diff --git a/src/converters/effectmap/EffectMapDownloader.ts b/src/converters/effectmap/EffectMapDownloader.ts deleted file mode 100644 index 8be56d2..0000000 --- a/src/converters/effectmap/EffectMapDownloader.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class EffectMapDownloader -{ - constructor(private readonly _configuration: Configuration) - {} - - public async download(callback: (content: string) => Promise): Promise - { - const effectMap = await this.parseEffectMap(); - - if(!effectMap) throw new Error('invalid_effect_map'); - - callback(effectMap); - } - - public async parseEffectMap(): Promise - { - const url = this._configuration.getValue('effectmap.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading effect map from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/converters/externaltexts/ExternalTextsConverter.ts b/src/converters/externaltexts/ExternalTextsConverter.ts deleted file mode 100644 index f119084..0000000 --- a/src/converters/externaltexts/ExternalTextsConverter.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { IExternalTexts } from '../../mapping/json'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { ExternalTextsDownloader } from './ExternalTextsDownloader'; - -@singleton() -export class ExternalTextsConverter extends Converter -{ - constructor( - private readonly _externalTextsDownloader: ExternalTextsDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.externaltexts')) return; - - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing ExternalTexts').start(); - - const directory = this.getDirectory(); - - try - { - this._externalTextsDownloader.download(async (content: string) => - { - spinner.text = 'Parsing ExternalTexts'; - - spinner.render(); - - let externalTextsString = content; - - if(!externalTextsString.startsWith('{')) - { - const externalTexts = await this.mapText2JSON(externalTextsString); - - externalTextsString = JSON.stringify(externalTexts); - } - - const path = directory.path + '/ExternalTexts.json'; - - await writeFile(path, externalTextsString, 'utf8'); - - spinner.succeed(`ExternalTexts finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('ExternalTexts failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapText2JSON(text: string): Promise - { - if(!text) return null; - - const output: IExternalTexts = {}; - - const parts = text.split(/\n\r{1,}|\n{1,}|\r{1,}/mg); - - for(const part of parts) - { - const [ key, value ] = part.split('='); - - output[key] = value; - } - - return output; - } -} diff --git a/src/converters/externaltexts/ExternalTextsDownloader.ts b/src/converters/externaltexts/ExternalTextsDownloader.ts deleted file mode 100644 index 0edbc8b..0000000 --- a/src/converters/externaltexts/ExternalTextsDownloader.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class ExternalTextsDownloader -{ - constructor(private readonly _configuration: Configuration) - {} - - public async download(callback: (content: string) => Promise): Promise - { - const productData = await this.parseExternalTexts(); - - if(!productData) throw new Error('invalid_external_texts'); - - callback(productData); - } - - public async parseExternalTexts(): Promise - { - const url = this._configuration.getValue('external.texts.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading external texts from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/converters/figure/FigureConverter.ts b/src/converters/figure/FigureConverter.ts deleted file mode 100644 index 4e02bbb..0000000 --- a/src/converters/figure/FigureConverter.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { BundleProvider } from '../../common/bundle/BundleProvider'; -import { Configuration } from '../../common/config/Configuration'; -import { SWFConverter } from '../../common/converters/SWFConverter'; -import { IAssetData } from '../../mapping/json'; -import { ManifestMapper } from '../../mapping/mappers'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { FigureDownloader } from './FigureDownloader'; - -@singleton() -export class FigureConverter extends SWFConverter -{ - constructor( - private readonly _figureDownloader: FigureDownloader, - private readonly _configuration: Configuration, - private readonly _bundleProvider: BundleProvider, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.figure')) return; - - const now = Date.now(); - - const spinner = ora('Preparing Figure').start(); - - const directory = this.getDirectory(); - - try - { - await this._figureDownloader.download(directory, async (habboAssetSwf: HabboAssetSWF, className: string) => - { - if(!habboAssetSwf) - { - spinner.text = 'Couldnt convert figure: ' + className; - } - else - { - spinner.text = 'Parsing Figure: ' + habboAssetSwf.getDocumentClass(); - } - - spinner.render(); - - if(!habboAssetSwf) return; - - const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); - const assetData = await this.mapXML2JSON(habboAssetSwf, className); - - await this.fromHabboAsset(habboAssetSwf, directory.path, assetData.type, assetData, spriteBundle); - }); - - spinner.succeed(`Figures finished in ${ Date.now() - now }ms`); - } - - catch (error) - { - spinner.fail('Figures failed: ' + error.message); - } - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + 'figure'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - return gameDataFolder; - } - - private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise - { - if(!habboAssetSWF) return null; - - const output: IAssetData = {}; - - output.name = assetType; - output.type = FigureDownloader.FIGURE_TYPES.get(assetType); - - const manifestXML = await FigureConverter.getManifestXML(habboAssetSWF); - - if(manifestXML) ManifestMapper.mapXML(manifestXML, output); - - return output; - } -} diff --git a/src/converters/figure/FigureDownloader.ts b/src/converters/figure/FigureDownloader.ts deleted file mode 100644 index 3ff7e4d..0000000 --- a/src/converters/figure/FigureDownloader.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { IFigureMap } from '../../mapping/json'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { FileUtilities } from '../../utils/FileUtilities'; -import { Logger } from '../../utils/Logger'; -import { FigureMapConverter } from '../figuremap/FigureMapConverter'; - -@singleton() -export class FigureDownloader -{ - public static FIGURE_TYPES: Map = new Map(); - - constructor( - private readonly _figureMapConverter: FigureMapConverter, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - {} - - public async download(directory: File, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - await this._figureMapConverter.convertAsync(); - - const figureMap = await this.parseFigureMap(); - const classNames: string[] = []; - - if(figureMap.libraries !== undefined) - { - for(const library of figureMap.libraries) - { - const className = library.id.split('*')[0]; - - const existingFile = new File(directory.path + '/' + className + '.nitro'); - - if(existingFile.exists()) continue; - - if(className === 'hh_human_fx' || className === 'hh_pets') continue; - - if(classNames.indexOf(className) >= 0) continue; - - classNames.push(className); - - try - { - FigureDownloader.FIGURE_TYPES.set(className, library.parts[0].type); - - await this.extractFigure(className, callback); - } - - catch (error) - { - console.log(); - console.error(`Error parsing ${ className }: ` + error.message); - - this._logger.logError(`Error parsing ${ className }: ` + error.message); - } - } - } - } - - public async parseFigureMap(): Promise - { - const url = this._configuration.getValue('figuremap.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading figure data from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return (JSON.parse(content) as IFigureMap); - } - - public async extractFigure(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - let url = this._configuration.getValue('dynamic.download.figure.url'); - - if(!url || !url.length) return; - - url = url.replace('%className%', className); - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading figure from ${url}`); - - const buffer = await FileUtilities.readFileAsBuffer(url); - - if(!buffer) return; - - const newHabboAssetSWF = new HabboAssetSWF(buffer); - - await newHabboAssetSWF.setupAsync(); - - await callback(newHabboAssetSWF, className); - } -} diff --git a/src/converters/figuredata/FigureDataConverter.ts b/src/converters/figuredata/FigureDataConverter.ts deleted file mode 100644 index e396206..0000000 --- a/src/converters/figuredata/FigureDataConverter.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { parseStringPromise } from 'xml2js'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { FigureDataMapper } from '../../mapping/mappers/FigureDataMapper'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { IFigureData } from './../../mapping/json/figuredata/IFigureData'; -import { FigureDataDownloader } from './FigureDataDownloader'; - -@singleton() -export class FigureDataConverter extends Converter -{ - constructor( - private readonly _figureDataDownloader: FigureDataDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.figuredata')) return; - - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing FigureData').start(); - - const directory = this.getDirectory(); - - try - { - this._figureDataDownloader.download(async (content: string) => - { - spinner.text = 'Parsing FigureData'; - - spinner.render(); - - let figureDataString = content; - - if(!figureDataString.startsWith('{')) - { - const xml = await parseStringPromise(figureDataString); - - const figureData = await this.mapXML2JSON(xml); - - figureDataString = JSON.stringify(figureData); - } - - const path = directory.path + '/FigureData.json'; - - await writeFile(path, figureDataString, 'utf8'); - - this._configuration.setValue('figuredata.load.url', path); - - spinner.succeed(`FigureData finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('FigureData failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapXML2JSON(xml: any): Promise - { - if(!xml) return null; - - const output: IFigureData = {}; - - FigureDataMapper.mapXML(xml, output); - - return output; - } -} diff --git a/src/converters/figuredata/FigureDataDownloader.ts b/src/converters/figuredata/FigureDataDownloader.ts deleted file mode 100644 index 7b49005..0000000 --- a/src/converters/figuredata/FigureDataDownloader.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class FigureDataDownloader -{ - constructor(private readonly _configuration: Configuration) - {} - - public async download(callback: (content: string) => Promise): Promise - { - const figureData = await this.parseFigureData(); - - if(!figureData) throw new Error('invalid_figure_data'); - - callback(figureData); - } - - public async parseFigureData(): Promise - { - const url = this._configuration.getValue('figuredata.load.url'); - - if(!url || !url.length) return null; - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/converters/figuremap/FigureMapConverter.ts b/src/converters/figuremap/FigureMapConverter.ts deleted file mode 100644 index dc17567..0000000 --- a/src/converters/figuremap/FigureMapConverter.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { parseStringPromise } from 'xml2js'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { IFigureMap } from '../../mapping/json'; -import { FigureMapMapper } from '../../mapping/mappers'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { FigureMapDownloader } from './FigureMapDownloader'; - -@singleton() -export class FigureMapConverter extends Converter -{ - constructor( - private readonly _figureMapDownloader: FigureMapDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing FigureMap').start(); - - const directory = this.getDirectory(); - - try - { - this._figureMapDownloader.download(async (content: string) => - { - spinner.text = 'Parsing FigureMap'; - - spinner.render(); - - let figureMapString = content; - - if(!figureMapString.startsWith('{')) - { - const xml = await parseStringPromise(figureMapString); - - const figureMap = await this.mapXML2JSON(xml); - - figureMapString = JSON.stringify(figureMap); - } - - const path = directory.path + '/FigureMap.json'; - - await writeFile(path, figureMapString, 'utf8'); - - this._configuration.setValue('figuremap.load.url', path); - - spinner.succeed(`FigureMap finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('FigureMap failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapXML2JSON(xml: any): Promise - { - if(!xml) return null; - - const output: IFigureMap = {}; - - FigureMapMapper.mapXML(xml, output); - - return output; - } -} diff --git a/src/converters/figuremap/FigureMapDownloader.ts b/src/converters/figuremap/FigureMapDownloader.ts deleted file mode 100644 index 92e3adf..0000000 --- a/src/converters/figuremap/FigureMapDownloader.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class FigureMapDownloader -{ - constructor(private readonly _configuration: Configuration) - {} - - public async download(callback: (content: string) => Promise): Promise - { - const figureMap = await this.parseFigureMap(); - - if(!figureMap) throw new Error('invalid_figure_map'); - - callback(figureMap); - } - - public async parseFigureMap(): Promise - { - const url = this._configuration.getValue('figuremap.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading figure map from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/converters/furniture/FurnitureConverter.ts b/src/converters/furniture/FurnitureConverter.ts deleted file mode 100644 index d933051..0000000 --- a/src/converters/furniture/FurnitureConverter.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { BundleProvider } from '../../common/bundle/BundleProvider'; -import { Configuration } from '../../common/config/Configuration'; -import { SWFConverter } from '../../common/converters/SWFConverter'; -import { IAssetData } from '../../mapping/json'; -import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { FurnitureDownloader } from './FurnitureDownloader'; - -@singleton() -export class FurnitureConverter extends SWFConverter -{ - public assets: Map = new Map(); - - constructor( - private readonly _furniDownloader: FurnitureDownloader, - private readonly _configuration: Configuration, - private readonly _bundleProvider: BundleProvider, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(): Promise - { - if(!this._configuration.getBoolean('convert.furniture')) return; - - const now = Date.now(); - - const spinner = ora('Preparing Furniture').start(); - - const directory = this.getDirectory(); - - try - { - await this._furniDownloader.download(directory, async (habboAssetSwf: HabboAssetSWF, className: string) => - { - if(!habboAssetSwf) - { - spinner.text = 'Couldnt convert furni: ' + className; - } - else - { - spinner.text = (`Parsing Furniture: ${ habboAssetSwf.getDocumentClass() } (${ (this._furniDownloader.totalFinished + 1) } / ${ this._furniDownloader.totalItems })`); - } - - spinner.render(); - - if(!habboAssetSwf) return; - - const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); - const assetData = await this.mapXML2JSON(habboAssetSwf, 'furniture'); - - await this.fromHabboAsset(habboAssetSwf, directory.path, 'furniture', assetData, spriteBundle); - - this.assets.set(assetData.name, assetData); - }); - - spinner.succeed(`Furniture finished in ${ Date.now() - now }ms`); - } - - catch (error) - { - spinner.fail('Furniture failed: ' + error.message); - } - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + 'furniture'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - return gameDataFolder; - } - - private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise - { - if(!habboAssetSWF) return null; - - const output: IAssetData = {}; - - output.type = assetType; - - const indexXML = await FurnitureConverter.getIndexXML(habboAssetSWF); - - if(indexXML) IndexMapper.mapXML(indexXML, output); - - const assetXML = await FurnitureConverter.getAssetsXML(habboAssetSWF); - - if(assetXML) AssetMapper.mapXML(assetXML, output); - - const logicXML = await FurnitureConverter.getLogicXML(habboAssetSWF); - - if(logicXML) LogicMapper.mapXML(logicXML, output); - - const visualizationXML = await FurnitureConverter.getVisualizationXML(habboAssetSWF); - - if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, output); - - return output; - } -} diff --git a/src/converters/furniture/FurnitureDownloader.ts b/src/converters/furniture/FurnitureDownloader.ts deleted file mode 100644 index edecb60..0000000 --- a/src/converters/furniture/FurnitureDownloader.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { IFurnitureData } from '../../mapping/json'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { FileUtilities } from '../../utils/FileUtilities'; -import { Logger } from '../../utils/Logger'; -import { FurnitureDataConverter } from '../furnituredata/FurnitureDataConverter'; - -@singleton() -export class FurnitureDownloader -{ - private _totalItems: number = 0; - private _totalFinished: number = 0; - - constructor( - private readonly _furnitureDataConverter: FurnitureDataConverter, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - {} - - public async download(directory: File, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - await this._furnitureDataConverter.convertAsync(); - - const furniData = await this.parseFurniData(); - - if(!furniData) throw new Error('invalid_furnidata'); - - const classNames: string[] = []; - const revisions: number[] = []; - - if(furniData.roomitemtypes !== undefined) - { - if(furniData.roomitemtypes.furnitype !== undefined) - { - for(const furniType of furniData.roomitemtypes.furnitype) - { - const className = furniType.classname.split('*')[0]; - const revision = furniType.revision; - - const existingFile = new File(directory.path + '/' + className + '.nitro'); - - if(existingFile.exists()) continue; - - if(classNames.indexOf(className) >= 0) continue; - - classNames.push(className); - revisions.push(revision); - } - } - } - - if(furniData.wallitemtypes !== undefined) - { - if(furniData.wallitemtypes.furnitype !== undefined) - { - for(const furniType of furniData.wallitemtypes.furnitype) - { - const className = furniType.classname.split('*')[0]; - const revision = furniType.revision; - - const existingFile = new File(directory.path + '/' + className + '.nitro'); - - if(existingFile.exists()) continue; - - if(classNames.indexOf(className) >= 0) continue; - - classNames.push(className); - revisions.push(revision); - } - } - } - - this._totalItems = classNames.length; - - this._totalFinished = 0; - - while(this._totalFinished < this._totalItems) - { - const className = classNames[this._totalFinished]; - const revision = revisions[this._totalFinished]; - - try - { - await this.extractFurniture(revision, className, callback); - } - - catch (error) - { - console.log(); - console.error(`Error parsing ${ className }: ` + error.message); - - this._logger.logError(`Error parsing ${ className }: ` + error.message); - } - - this._totalFinished++; - } - } - - public async parseFurniData(): Promise - { - const url = this._configuration.getValue('furnidata.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading furniture data from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return (JSON.parse(content) as IFurnitureData); - } - - public async extractFurniture(revision: number, className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - let url = this._configuration.getValue('dynamic.download.furniture.url'); - - if(!url || !url.length) return; - - //url = url.replace('%revision%', revision.toString()); - url = url.replace('%className%', className); - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading furniture from ${url}`); - - const buffer = await FileUtilities.readFileAsBuffer(url); - - if(!buffer) return; - - const newHabboAssetSWF = new HabboAssetSWF(buffer); - - await newHabboAssetSWF.setupAsync(); - - await callback(newHabboAssetSWF, className); - } - - public get totalItems(): number - { - return this._totalItems; - } - - public get totalFinished(): number - { - return this._totalFinished; - } -} diff --git a/src/converters/furnituredata/FurnitureDataConverter.ts b/src/converters/furnituredata/FurnitureDataConverter.ts deleted file mode 100644 index ff9b8b8..0000000 --- a/src/converters/furnituredata/FurnitureDataConverter.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { parseStringPromise } from 'xml2js'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { IFurnitureData } from '../../mapping/json'; -import { FurnitureDataMapper } from '../../mapping/mappers'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { FurnitureDataDownloader } from './FurnitureDataDownloader'; - -@singleton() -export class FurnitureDataConverter extends Converter -{ - public furnitureData: IFurnitureData = null; - - constructor( - private readonly _furnitureDataDownloader: FurnitureDataDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing FurnitureData').start(); - - const directory = this.getDirectory(); - - try - { - this._furnitureDataDownloader.download(async (content: string) => - { - spinner.text = 'Parsing FurnitureData'; - - spinner.render(); - - let furnitureDataString = content; - - if(!furnitureDataString.startsWith('{')) - { - const xml = await parseStringPromise(furnitureDataString - .replace(/&/g,'&')); - - const furnitureData = await this.mapXML2JSON(xml); - - this.furnitureData = furnitureData; - - furnitureDataString = JSON.stringify(furnitureData); - } - else - { - this.furnitureData = JSON.parse(furnitureDataString); - } - - const path = directory.path + '/FurnitureData.json'; - - await writeFile(path, furnitureDataString, 'utf8'); - - this._configuration.setValue('furnidata.load.url', path); - - spinner.succeed(`FurnitureData finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('FurnitureData failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapXML2JSON(xml: any): Promise - { - if(!xml) return null; - - const output: IFurnitureData = {}; - - FurnitureDataMapper.mapXML(xml, output); - - return output; - } -} diff --git a/src/converters/furnituredata/FurnitureDataDownloader.ts b/src/converters/furnituredata/FurnitureDataDownloader.ts deleted file mode 100644 index d3abee8..0000000 --- a/src/converters/furnituredata/FurnitureDataDownloader.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class FurnitureDataDownloader -{ - constructor(private readonly _configuration: Configuration) - { - } - - public async download(callback: (content: string) => Promise): Promise - { - const furnitureData = await this.parseFurnitureData(); - - if(!furnitureData) throw new Error('invalid_furniture_data'); - - callback(furnitureData); - } - - public async parseFurnitureData(): Promise - { - const url = this._configuration.getValue('furnidata.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading furniture data from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/converters/pet/PetConverter.ts b/src/converters/pet/PetConverter.ts deleted file mode 100644 index 7a9398d..0000000 --- a/src/converters/pet/PetConverter.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { BundleProvider } from '../../common/bundle/BundleProvider'; -import { Configuration } from '../../common/config/Configuration'; -import { SWFConverter } from '../../common/converters/SWFConverter'; -import { IAssetData } from '../../mapping/json'; -import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { PetDownloader } from './PetDownloader'; - -@singleton() -export class PetConverter extends SWFConverter -{ - constructor( - private readonly _petDownloader: PetDownloader, - private readonly _configuration: Configuration, - private readonly _bundleProvider: BundleProvider, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.pet')) return; - - const now = Date.now(); - - const spinner = ora('Preparing Pets').start(); - - const directory = this.getDirectory(); - - try - { - await this._petDownloader.download(directory, async (habboAssetSwf: HabboAssetSWF, className: string) => - { - if(!habboAssetSwf) - { - spinner.text = 'Couldnt convert pet: ' + className; - } - else - { - spinner.text = 'Parsing Pet: ' + habboAssetSwf.getDocumentClass(); - } - - spinner.render(); - - if(!habboAssetSwf) return; - - const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); - const assetData = await this.mapXML2JSON(habboAssetSwf, 'pet'); - - await this.fromHabboAsset(habboAssetSwf, directory.path, 'pet', assetData, spriteBundle); - }); - - spinner.succeed(`Pets finished in ${ Date.now() - now }ms`); - } - - catch (error) - { - spinner.fail('Pet failed: ' + error.message); - } - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + 'pet'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - return gameDataFolder; - } - - private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise - { - if(!habboAssetSWF) return null; - - const output: IAssetData = {}; - - output.type = assetType; - - const indexXML = await PetConverter.getIndexXML(habboAssetSWF); - - if(indexXML) IndexMapper.mapXML(indexXML, output); - - const assetXML = await PetConverter.getAssetsXML(habboAssetSWF); - - if(assetXML) - { - AssetMapper.mapXML(assetXML, output); - - if(output.palettes !== undefined) - { - for(const paletteId in output.palettes) - { - const palette = output.palettes[paletteId]; - - const paletteColors = PetConverter.getPalette(habboAssetSWF, palette.source); - - if(!paletteColors) - { - delete output.palettes[paletteId]; - - continue; - } - - const rgbs: [ number, number, number ][] = []; - - for(const rgb of paletteColors) rgbs.push([ rgb[0], rgb[1], rgb[2] ]); - - palette.rgb = rgbs; - } - } - } - - const logicXML = await PetConverter.getLogicXML(habboAssetSWF); - - if(logicXML) LogicMapper.mapXML(logicXML, output); - - const visualizationXML = await PetConverter.getVisualizationXML(habboAssetSWF); - - if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, output); - - return output; - } -} diff --git a/src/converters/pet/PetDownloader.ts b/src/converters/pet/PetDownloader.ts deleted file mode 100644 index 2ce9e5f..0000000 --- a/src/converters/pet/PetDownloader.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; -import File from '../../utils/File'; -import { FileUtilities } from '../../utils/FileUtilities'; -import { Logger } from '../../utils/Logger'; - -@singleton() -export class PetDownloader -{ - constructor( - private readonly _configuration: Configuration, - private readonly _logger: Logger) - {} - - public async download(directory: File, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - const petTypes = await this.parsePetTypes(); - - if(!petTypes) throw new Error('invalid_pets'); - - const classNames: string[] = []; - - for(const petType of petTypes) - { - const existingFile = new File(directory.path + '/' + petType + '.nitro'); - - if(existingFile.exists()) continue; - - if(classNames.indexOf(petType) >= 0) continue; - - classNames.push(petType); - - try - { - await this.extractPet(petType, callback); - } - - catch (error) - { - console.log(); - console.error(`Error parsing ${ petType }: ` + error.message); - - this._logger.logError(`Error parsing ${ petType }: ` + error.message); - } - } - } - - public async parsePetTypes(): Promise - { - const petTypes: string[] = []; - - const pets = this._configuration.getValue('pet.configuration'); - - if(pets) - { - const types = pets.split(','); - - for(const type of types) petTypes.push(type); - } - - return petTypes; - } - - public async extractPet(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise): Promise - { - let url = this._configuration.getValue('dynamic.download.pet.url'); - - if(!url || !url.length) return; - - url = url.replace('%className%', className); - - const buffer = await FileUtilities.readFileAsBuffer(url); - - if(!buffer) return; - - const newHabboAssetSWF = new HabboAssetSWF(buffer); - - await newHabboAssetSWF.setupAsync(); - - await callback(newHabboAssetSWF, className); - } -} diff --git a/src/converters/productdata/ProductDataConverter.ts b/src/converters/productdata/ProductDataConverter.ts deleted file mode 100644 index b23ce15..0000000 --- a/src/converters/productdata/ProductDataConverter.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { writeFile } from 'fs/promises'; -import * as ora from 'ora'; -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { Converter } from '../../common/converters/Converter'; -import { IProductData } from '../../mapping/json'; -import File from '../../utils/File'; -import { Logger } from '../../utils/Logger'; -import { ProductDataDownloader } from './ProductDataDownloader'; - -@singleton() -export class ProductDataConverter extends Converter -{ - constructor( - private readonly _productDataDownloader: ProductDataDownloader, - private readonly _configuration: Configuration, - private readonly _logger: Logger) - { - super(); - } - - public async convertAsync(args: string[] = []): Promise - { - if(!this._configuration.getBoolean('convert.productdata')) return; - - return new Promise((resolve, reject) => - { - const now = Date.now(); - - const spinner = ora('Preparing ProductData').start(); - - const directory = this.getDirectory(); - - try - { - this._productDataDownloader.download(async (content: string) => - { - spinner.text = 'Parsing FurnitureData'; - - spinner.render(); - - let productDataString = content; - - if(!productDataString.startsWith('{')) - { - const productData = await this.mapText2JSON(productDataString); - - productDataString = JSON.stringify(productData); - } - - const path = directory.path + '/ProductData.json'; - - await writeFile(path, productDataString, 'utf8'); - - spinner.succeed(`ProductData finished in ${ Date.now() - now }ms`); - - resolve(); - }); - } - - catch (error) - { - spinner.fail('ProductData failed: ' + error.message); - - reject(error); - } - }); - } - - private getDirectory(): File - { - const baseFolder = new File(this._configuration.getValue('output.folder')); - - if(!baseFolder.isDirectory()) baseFolder.mkdirs(); - - const gameDataFolder = new File(baseFolder.path + '/gamedata'); - - if(!gameDataFolder.isDirectory()) gameDataFolder.mkdirs(); - - const jsonFolder = new File(gameDataFolder.path + '/json'); - - if(!jsonFolder.isDirectory()) jsonFolder.mkdirs(); - - return jsonFolder; - } - - private async mapText2JSON(text: string): Promise - { - if(!text) return null; - - const output: IProductData = { - productdata: { - product: [] - } - }; - - text = text.replace(/"{1,}/g, ''); - - const parts = text.split(/\n\r{1,}|\n{1,}|\r{1,}/mg); - - for(const part of parts) - { - const set = part.match(/\[+?((.)*?)\]/g); - - if(set) - { - for(const entry of set) - { - let value = entry.replace(/\[{1,}/mg, ''); - value = entry.replace(/\]{1,}/mg, ''); - - value = value.replace('[[', ''); - value = value.replace('[', ''); - - const pieces = value.split(','); - const code = pieces.shift(); - const name = pieces.shift(); - const description = pieces.join(','); - - output.productdata.product.push({ code, name, description }); - } - } - } - - return output; - } -} diff --git a/src/converters/productdata/ProductDataDownloader.ts b/src/converters/productdata/ProductDataDownloader.ts deleted file mode 100644 index 1d7dced..0000000 --- a/src/converters/productdata/ProductDataDownloader.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Configuration } from '../../common/config/Configuration'; -import { FileUtilities } from '../../utils/FileUtilities'; - -@singleton() -export class ProductDataDownloader -{ - constructor(private readonly _configuration: Configuration) - {} - - public async download(callback: (content: string) => Promise): Promise - { - const productData = await this.parseProductData(); - - if(!productData) throw new Error('invalid_product_data'); - - callback(productData); - } - - public async parseProductData(): Promise - { - const url = this._configuration.getValue('productdata.load.url'); - - if(!url || !url.length) return null; - - const logDownloads = this._configuration.getBoolean('misc.log_download_urls'); - - if(logDownloads) console.log(` Downloading product data from ${url}`); - - const content = await FileUtilities.readFileAsString(url); - - if(!content || !content.length) return null; - - return content; - } -} diff --git a/src/mapping/json/asset/IAssetData.ts b/src/mapping/json/asset/IAssetData.ts index 2bccf86..884ee67 100644 --- a/src/mapping/json/asset/IAssetData.ts +++ b/src/mapping/json/asset/IAssetData.ts @@ -1,11 +1,8 @@ import { IAssetAnimation } from './animation'; import { IAsset } from './IAsset'; import { IAssetAlias } from './IAssetAlias'; -import { IAssetDimension } from './IAssetDimension'; import { IAssetPalette } from './IAssetPalette'; -import { IPlanetSystem } from './IPlanetSystem'; -import { ISoundSample } from './ISoundSample'; -import { IParticleSystem } from './particlesystem'; +import { IAssetLogicData } from './logic/IAssetLogicData'; import { ISpritesheetData } from './spritesheet'; import { IAssetVisualizationData } from './visualization'; @@ -14,15 +11,8 @@ export interface IAssetData { name?: string; visualizationType?: string; logicType?: string; - maskType?: string; - credits?: string; - soundSample?: ISoundSample; - action?: { link?: string, startState?: number }; - planetSystems?: IPlanetSystem[]; - particleSystems?: IParticleSystem[]; spritesheet?: ISpritesheetData; - dimensions?: IAssetDimension; - directions?: number[]; + logic?: IAssetLogicData; assets?: { [index: string]: IAsset }; aliases?: { [index: string]: IAssetAlias }; animations?: { [index: string]: IAssetAnimation }; diff --git a/src/mapping/json/asset/index.ts b/src/mapping/json/asset/index.ts index 88559df..3d5fabb 100644 --- a/src/mapping/json/asset/index.ts +++ b/src/mapping/json/asset/index.ts @@ -2,8 +2,7 @@ export * from './animation'; export * from './IAsset'; export * from './IAssetAlias'; export * from './IAssetData'; -export * from './IAssetDimension'; export * from './IAssetPalette'; -export * from './IPlanetSystem'; +export * from './logic'; export * from './spritesheet'; export * from './visualization'; diff --git a/src/mapping/json/asset/logic/IAssetLogicCustomVars.ts b/src/mapping/json/asset/logic/IAssetLogicCustomVars.ts new file mode 100644 index 0000000..1ebd4a8 --- /dev/null +++ b/src/mapping/json/asset/logic/IAssetLogicCustomVars.ts @@ -0,0 +1,4 @@ +export interface ICustomVars +{ + variables?: string[]; +} diff --git a/src/mapping/json/asset/logic/IAssetLogicData.ts b/src/mapping/json/asset/logic/IAssetLogicData.ts new file mode 100644 index 0000000..acf484c --- /dev/null +++ b/src/mapping/json/asset/logic/IAssetLogicData.ts @@ -0,0 +1,17 @@ +import { ICustomVars } from './IAssetLogicCustomVars'; +import { IAssetLogicPlanetSystem } from './IAssetLogicPlanetSystem'; +import { ISoundSample } from './ISoundSample'; +import { IAssetLogicModel } from './model/IAssetLogicModel'; +import { IParticleSystem } from './particlesystem'; + +export interface IAssetLogicData +{ + model?: IAssetLogicModel; + maskType?: string; + credits?: string; + soundSample?: ISoundSample; + action?: { link?: string, startState?: number }; + planetSystems?: IAssetLogicPlanetSystem[]; + particleSystems?: IParticleSystem[]; + customVars?: ICustomVars; +} diff --git a/src/mapping/json/asset/IPlanetSystem.ts b/src/mapping/json/asset/logic/IAssetLogicPlanetSystem.ts similarity index 80% rename from src/mapping/json/asset/IPlanetSystem.ts rename to src/mapping/json/asset/logic/IAssetLogicPlanetSystem.ts index 16acf87..47521bc 100644 --- a/src/mapping/json/asset/IPlanetSystem.ts +++ b/src/mapping/json/asset/logic/IAssetLogicPlanetSystem.ts @@ -1,4 +1,4 @@ -export interface IPlanetSystem +export interface IAssetLogicPlanetSystem { id?: number; name?: string; diff --git a/src/mapping/json/asset/ISoundSample.ts b/src/mapping/json/asset/logic/ISoundSample.ts similarity index 100% rename from src/mapping/json/asset/ISoundSample.ts rename to src/mapping/json/asset/logic/ISoundSample.ts diff --git a/src/mapping/json/asset/logic/index.ts b/src/mapping/json/asset/logic/index.ts new file mode 100644 index 0000000..569907b --- /dev/null +++ b/src/mapping/json/asset/logic/index.ts @@ -0,0 +1,6 @@ +export * from './IAssetLogicCustomVars'; +export * from './IAssetLogicData'; +export * from './IAssetLogicPlanetSystem'; +export * from './ISoundSample'; +export * from './model'; +export * from './particlesystem'; diff --git a/src/mapping/json/asset/IAssetDimension.ts b/src/mapping/json/asset/logic/model/IAssetDimension.ts similarity index 100% rename from src/mapping/json/asset/IAssetDimension.ts rename to src/mapping/json/asset/logic/model/IAssetDimension.ts diff --git a/src/mapping/json/asset/logic/model/IAssetLogicModel.ts b/src/mapping/json/asset/logic/model/IAssetLogicModel.ts new file mode 100644 index 0000000..eef3245 --- /dev/null +++ b/src/mapping/json/asset/logic/model/IAssetLogicModel.ts @@ -0,0 +1,7 @@ +import { IAssetDimension } from './IAssetDimension'; + +export interface IAssetLogicModel +{ + dimensions?: IAssetDimension; + directions?: number[]; +} diff --git a/src/mapping/json/asset/logic/model/index.ts b/src/mapping/json/asset/logic/model/index.ts new file mode 100644 index 0000000..4d08a72 --- /dev/null +++ b/src/mapping/json/asset/logic/model/index.ts @@ -0,0 +1,2 @@ +export * from './IAssetDimension'; +export * from './IAssetLogicModel'; diff --git a/src/mapping/json/asset/particlesystem/IParticleSystem.ts b/src/mapping/json/asset/logic/particlesystem/IParticleSystem.ts similarity index 100% rename from src/mapping/json/asset/particlesystem/IParticleSystem.ts rename to src/mapping/json/asset/logic/particlesystem/IParticleSystem.ts diff --git a/src/mapping/json/asset/particlesystem/IParticleSystemEmitter.ts b/src/mapping/json/asset/logic/particlesystem/IParticleSystemEmitter.ts similarity index 100% rename from src/mapping/json/asset/particlesystem/IParticleSystemEmitter.ts rename to src/mapping/json/asset/logic/particlesystem/IParticleSystemEmitter.ts diff --git a/src/mapping/json/asset/particlesystem/IParticleSystemParticle.ts b/src/mapping/json/asset/logic/particlesystem/IParticleSystemParticle.ts similarity index 100% rename from src/mapping/json/asset/particlesystem/IParticleSystemParticle.ts rename to src/mapping/json/asset/logic/particlesystem/IParticleSystemParticle.ts diff --git a/src/mapping/json/asset/particlesystem/IParticleSystemSimulation.ts b/src/mapping/json/asset/logic/particlesystem/IParticleSystemSimulation.ts similarity index 100% rename from src/mapping/json/asset/particlesystem/IParticleSystemSimulation.ts rename to src/mapping/json/asset/logic/particlesystem/IParticleSystemSimulation.ts diff --git a/src/mapping/json/asset/particlesystem/index.ts b/src/mapping/json/asset/logic/particlesystem/index.ts similarity index 100% rename from src/mapping/json/asset/particlesystem/index.ts rename to src/mapping/json/asset/logic/particlesystem/index.ts diff --git a/src/mapping/json/asset/visualization/IAssetVisualizationData.ts b/src/mapping/json/asset/visualization/IAssetVisualizationData.ts index e0a2d1d..e17ecdc 100644 --- a/src/mapping/json/asset/visualization/IAssetVisualizationData.ts +++ b/src/mapping/json/asset/visualization/IAssetVisualizationData.ts @@ -14,6 +14,7 @@ export interface IAssetVisualizationData colors?: { [index: string]: IAssetColor }; directions?: { [index: string]: IAssetVisualizationDirection }; animations?: { [index: string]: IAssetVisualAnimation }; - postures?: { [index: string]: IAssetPosture }; - gestures?: { [index: string]: IAssetGesture }; + defaultPosture?: string; + postures?: { defaultPosture?: string, postures?: IAssetPosture[] }; + gestures?: IAssetGesture[]; } diff --git a/src/mapping/mappers/FurnitureDataMapper.ts b/src/mapping/mappers/FurnitureDataMapper.ts index 725d140..2a45c40 100644 --- a/src/mapping/mappers/FurnitureDataMapper.ts +++ b/src/mapping/mappers/FurnitureDataMapper.ts @@ -64,7 +64,7 @@ export class FurnitureDataMapper extends Mapper furnitureType.name = typeXML.name; furnitureType.description = typeXML.description; furnitureType.adurl = typeXML.adurl; - furnitureType.offerid = typeXML.offerid; + furnitureType.offerid = typeXML.id; furnitureType.buyout = typeXML.buyout; furnitureType.rentofferid = typeXML.rentofferid; furnitureType.rentbuyout = typeXML.rentbuyout; diff --git a/src/mapping/mappers/asset/LogicMapper.ts b/src/mapping/mappers/asset/LogicMapper.ts index 5b39f9d..8d29529 100644 --- a/src/mapping/mappers/asset/LogicMapper.ts +++ b/src/mapping/mappers/asset/LogicMapper.ts @@ -1,5 +1,4 @@ -import { IAssetData, IPlanetSystem } from '../../json'; -import { IParticleSystem, IParticleSystemEmitter, IParticleSystemParticle, IParticleSystemSimulation } from '../../json/asset/particlesystem'; +import { IAssetData, IAssetLogicData, IAssetLogicPlanetSystem, IParticleSystem, IParticleSystemEmitter, IParticleSystemParticle, IParticleSystemSimulation } from '../../json'; import { LogicXML, ParticleSystemEmitterXML, ParticleSystemObjectXML, ParticleSystemParticleXML, ParticleSystemSimulationXML, PlanetSystemObjectXML } from '../../xml'; import { Mapper } from './Mapper'; @@ -9,18 +8,22 @@ export class LogicMapper extends Mapper { if(!logic || !output) return; - LogicMapper.mapLogicXML(new LogicXML(logic.objectData), output); + output.logic = {}; + + LogicMapper.mapLogicXML(new LogicXML(logic.objectData), output.logic); } - private static mapLogicXML(xml: LogicXML, output: IAssetData): void + private static mapLogicXML(xml: LogicXML, output: IAssetLogicData): void { if(!xml || !output) return; if(xml.model !== undefined) { + output.model = {}; + if(xml.model.dimensions !== undefined) { - output.dimensions = { + output.model.dimensions = { x: xml.model.dimensions.x, y: xml.model.dimensions.y, z: xml.model.dimensions.z @@ -40,23 +43,21 @@ export class LogicMapper extends Mapper for(const direction of xml.model.directions) directions.push(parseInt(direction.id.toString())); } - output.directions = directions; + output.model.directions = directions; } } if(xml.action !== undefined) { + output.action = {}; + if(xml.action.link !== undefined) { - if(!output.action) output.action = {}; - output.action.link = xml.action.link; } if(xml.action.startState !== undefined) { - if(!output.action) output.action = {}; - output.action.startState = xml.action.startState; } } @@ -75,32 +76,38 @@ export class LogicMapper extends Mapper if(xml.planetSystem !== undefined) { - if(!output.planetSystems) - { - output.planetSystems = []; - } + output.planetSystems = []; if(xml.planetSystem.objects !== undefined) LogicMapper.mapPlanetSystemXML(xml.planetSystem.objects, output.planetSystems); } if(xml.particleSystem !== undefined) { - if(!output.particleSystems) - { - output.particleSystems = []; - } + output.particleSystems = []; if(xml.particleSystem.objects !== undefined) LogicMapper.mapParticleSystemXML(xml.particleSystem.objects, output.particleSystems); } + + if(xml.customVars !== undefined) + { + output.customVars = {}; + + if(xml.customVars.variables !== undefined) + { + output.customVars.variables = []; + + for(const customVar of xml.customVars.variables) output.customVars.variables.push(customVar); + } + } } - private static mapPlanetSystemXML(xml: PlanetSystemObjectXML[], output: IPlanetSystem[]): void + private static mapPlanetSystemXML(xml: PlanetSystemObjectXML[], output: IAssetLogicPlanetSystem[]): void { if(!xml || !xml.length || !output) return; for(const planetSystemObjectXML of xml) { - const planetObject: IPlanetSystem = {}; + const planetObject: IAssetLogicPlanetSystem = {}; if(planetSystemObjectXML.id !== undefined) planetObject.id = planetSystemObjectXML.id; if(planetSystemObjectXML.name !== undefined) planetObject.name = planetSystemObjectXML.name; @@ -121,6 +128,11 @@ export class LogicMapper extends Mapper for(const particleSystemXML of xml) { + if(particleSystemXML.size !== undefined) + { + if([ 32 ].indexOf(particleSystemXML.size) >= 0) continue; + } + const particleObject: IParticleSystem = {}; if(particleSystemXML.size !== undefined) particleObject.size = particleSystemXML.size; diff --git a/src/mapping/mappers/asset/VisualizationMapper.ts b/src/mapping/mappers/asset/VisualizationMapper.ts index 7c580f6..5e87fb0 100644 --- a/src/mapping/mappers/asset/VisualizationMapper.ts +++ b/src/mapping/mappers/asset/VisualizationMapper.ts @@ -85,11 +85,15 @@ export class VisualizationMapper extends Mapper if(visualizationDataXML.postures !== undefined) { + visualizationData.postures = {}; + + if(visualizationDataXML.defaultPosture !== undefined) visualizationData.postures.defaultPosture = visualizationDataXML.defaultPosture; + if(visualizationDataXML.postures.length) { - visualizationData.postures = {}; + visualizationData.postures.postures = []; - VisualizationMapper.mapVisualizationPostureXML(visualizationDataXML.postures, visualizationData.postures); + VisualizationMapper.mapVisualizationPostureXML(visualizationDataXML.postures, visualizationData.postures.postures); } } @@ -97,7 +101,7 @@ export class VisualizationMapper extends Mapper { if(visualizationDataXML.gestures.length) { - visualizationData.gestures = {}; + visualizationData.gestures = []; VisualizationMapper.mapVisualizationGestureXML(visualizationDataXML.gestures, visualizationData.gestures); } @@ -185,6 +189,26 @@ export class VisualizationMapper extends Mapper } } + private static requestNextInsertId(requestId: number, output: { [index: string]: IAssetVisualAnimation }): string + { + let id = requestId.toString(); + + if(!output[id]) return id; + + let i = 1; + + while(i < 6) + { + id += '_' + i; + + if(!output[id]) return id; + + i++; + } + + return null; + } + private static mapVisualizationAnimationXML(xml: AnimationXML[], output: { [index: string]: IAssetVisualAnimation }): void { if(!xml || !xml.length || !output) return; @@ -208,7 +232,11 @@ export class VisualizationMapper extends Mapper } } - output[animationXML.id.toString()] = animation; + const id = this.requestNextInsertId(animationXML.id, output); + + if(!id) continue; + + output[id] = animation; } } @@ -321,7 +349,7 @@ export class VisualizationMapper extends Mapper } } - private static mapVisualizationPostureXML(xml: PostureXML[], output: { [index: string]: IAssetPosture }): void + private static mapVisualizationPostureXML(xml: PostureXML[], output: IAssetPosture[]): void { if(!xml || !xml.length || !output) return; @@ -332,11 +360,11 @@ export class VisualizationMapper extends Mapper if(postureXML.id !== undefined) posture.id = postureXML.id; if(postureXML.animationId !== undefined) posture.animationId = postureXML.animationId; - output[postureXML.id] = posture; + output.push(posture); } } - private static mapVisualizationGestureXML(xml: GestureXML[], output: { [index: string]: IAssetGesture }): void + private static mapVisualizationGestureXML(xml: GestureXML[], output: IAssetGesture[]): void { if(!xml || !xml.length || !output) return; @@ -347,7 +375,7 @@ export class VisualizationMapper extends Mapper if(gestureXML.id !== undefined) gesture.id = gestureXML.id; if(gestureXML.animationId !== undefined) gesture.animationId = gestureXML.animationId; - output[gestureXML.id] = gesture; + output.push(gesture); } } } diff --git a/src/mapping/mappers/asset/index.ts b/src/mapping/mappers/asset/index.ts index c2d78ad..8303c33 100644 --- a/src/mapping/mappers/asset/index.ts +++ b/src/mapping/mappers/asset/index.ts @@ -3,4 +3,5 @@ export * from './AssetMapper'; export * from './IndexMapper'; export * from './LogicMapper'; export * from './ManifestMapper'; +export * from './Mapper'; export * from './VisualizationMapper'; diff --git a/src/mapping/xml/asset/logic/CustomVarsXML.ts b/src/mapping/xml/asset/logic/CustomVarsXML.ts new file mode 100644 index 0000000..a0bf354 --- /dev/null +++ b/src/mapping/xml/asset/logic/CustomVarsXML.ts @@ -0,0 +1,29 @@ +export class CustomVarsXML +{ + private readonly _variables: string[]; + + constructor(xml: any) + { + const attributes = xml.$; + + if((xml.variable !== undefined) && Array.isArray(xml.variable)) + { + this._variables = []; + + for(const variable of xml.variable) + { + const attributes = variable.$; + + if(attributes !== undefined) + { + if(attributes.name !== undefined) this._variables.push(attributes.name); + } + } + } + } + + public get variables(): string[] + { + return this._variables; + } +} diff --git a/src/mapping/xml/asset/logic/LogicXML.ts b/src/mapping/xml/asset/logic/LogicXML.ts index 6a64ad1..0dd3e0a 100644 --- a/src/mapping/xml/asset/logic/LogicXML.ts +++ b/src/mapping/xml/asset/logic/LogicXML.ts @@ -1,8 +1,9 @@ import { ActionXML } from './ActionXML'; import { CreditsXML } from './CreditsXML'; +import { CustomVarsXML } from './CustomVarsXML'; import { MaskXML } from './MaskXML'; -import { ModelXML } from './model/ModelXML'; -import { ParticleSystemXML } from './ParticleSystemXML'; +import { ModelXML } from './model'; +import { ParticleSystemXML } from './particlesystem'; import { PlanetSystemXML } from './PlanetSystemXML'; import { SoundSampleXML } from './SoundSampleXML'; @@ -16,6 +17,7 @@ export class LogicXML private readonly _soundSample: SoundSampleXML; private readonly _planetSystem: PlanetSystemXML; private readonly _particleSystem: ParticleSystemXML; + private readonly _customVars: CustomVarsXML; constructor(xml: any) { @@ -48,7 +50,7 @@ export class LogicXML if(xml.sound !== undefined) { - if(xml.sound[0] !== undefined) this._soundSample = new SoundSampleXML(xml.sound[0].sample); + if(xml.sound[0] !== undefined) this._soundSample = new SoundSampleXML(xml.sound[0].sample[0]); } if(xml.planetsystem !== undefined) @@ -60,6 +62,11 @@ export class LogicXML { if(xml.particlesystems[0] !== undefined) this._particleSystem = new ParticleSystemXML(xml.particlesystems[0]); } + + if(xml.customvars !== undefined) + { + if(xml.customvars[0] !== undefined) this._customVars = new CustomVarsXML(xml.customvars[0]); + } } public get type(): string @@ -101,4 +108,9 @@ export class LogicXML { return this._particleSystem; } + + public get customVars(): CustomVarsXML + { + return this._customVars; + } } diff --git a/src/mapping/xml/asset/logic/PlanetSystemXML.ts b/src/mapping/xml/asset/logic/PlanetSystemXML.ts index 4374064..0ea89b6 100644 --- a/src/mapping/xml/asset/logic/PlanetSystemXML.ts +++ b/src/mapping/xml/asset/logic/PlanetSystemXML.ts @@ -1,4 +1,4 @@ -import { PlanetSystemObjectXML } from './PlanetSystemObjectXML'; +import { PlanetSystemObjectXML } from './particlesystem/PlanetSystemObjectXML'; export class PlanetSystemXML { @@ -6,14 +6,11 @@ export class PlanetSystemXML constructor(xml: any) { - if(xml.object !== undefined) + if((xml.object !== undefined) && Array.isArray(xml.object)) { - if(Array.isArray(xml.object)) - { - this._objects = []; + this._objects = []; - for(const object of xml.object) this._objects.push(new PlanetSystemObjectXML(object)); - } + for(const object of xml.object) this._objects.push(new PlanetSystemObjectXML(object)); } } diff --git a/src/mapping/xml/asset/logic/SoundSampleXML.ts b/src/mapping/xml/asset/logic/SoundSampleXML.ts index 2ce1453..cb8778a 100644 --- a/src/mapping/xml/asset/logic/SoundSampleXML.ts +++ b/src/mapping/xml/asset/logic/SoundSampleXML.ts @@ -5,7 +5,7 @@ export class SoundSampleXML constructor(xml: any) { - const attributes = xml[0].$; + const attributes = xml.$; if(attributes !== undefined) { diff --git a/src/mapping/xml/asset/logic/index.ts b/src/mapping/xml/asset/logic/index.ts index 8876f19..298dee7 100644 --- a/src/mapping/xml/asset/logic/index.ts +++ b/src/mapping/xml/asset/logic/index.ts @@ -1,13 +1,9 @@ export * from './ActionXML'; export * from './CreditsXML'; +export * from './CustomVarsXML'; export * from './LogicXML'; export * from './MaskXML'; export * from './model'; -export * from './ParticleSystemEmitterXML'; -export * from './ParticleSystemObjectXML'; -export * from './ParticleSystemParticleXML'; -export * from './ParticleSystemSimulationXML'; -export * from './ParticleSystemXML'; -export * from './PlanetSystemObjectXML'; +export * from './particlesystem'; export * from './PlanetSystemXML'; export * from './SoundSampleXML'; diff --git a/src/mapping/xml/asset/logic/model/DimensionsXML.ts b/src/mapping/xml/asset/logic/model/ModelDimensionsXML.ts similarity index 95% rename from src/mapping/xml/asset/logic/model/DimensionsXML.ts rename to src/mapping/xml/asset/logic/model/ModelDimensionsXML.ts index 800c8c3..2507c6a 100644 --- a/src/mapping/xml/asset/logic/model/DimensionsXML.ts +++ b/src/mapping/xml/asset/logic/model/ModelDimensionsXML.ts @@ -1,4 +1,4 @@ -export class DimensionsXML +export class ModelDimensionsXML { private readonly _x: number; private readonly _y: number; diff --git a/src/mapping/xml/asset/logic/model/ModelXML.ts b/src/mapping/xml/asset/logic/model/ModelXML.ts index 467e9d2..cb66f0e 100644 --- a/src/mapping/xml/asset/logic/model/ModelXML.ts +++ b/src/mapping/xml/asset/logic/model/ModelXML.ts @@ -1,33 +1,30 @@ -import { DimensionsXML } from './DimensionsXML'; +import { ModelDimensionsXML } from './ModelDimensionsXML'; import { ModelDirectionXML } from './ModelDirectionXML'; export class ModelXML { - private readonly _dimensions: DimensionsXML; + private readonly _dimensions: ModelDimensionsXML; private readonly _directions: ModelDirectionXML[]; constructor(xml: any) { if(xml.dimensions !== undefined) { - if(xml.dimensions[0] !== undefined) this._dimensions = new DimensionsXML(xml.dimensions[0]); + if(xml.dimensions[0] !== undefined) this._dimensions = new ModelDimensionsXML(xml.dimensions[0]); } - if(xml.directions !== undefined) + if((xml.directions !== undefined) && Array.isArray(xml.directions)) { - if(Array.isArray(xml.directions)) - { - this._directions = []; + this._directions = []; - for(const directionParent of xml.directions) - { - if(Array.isArray(directionParent.direction)) for(const direction of directionParent.direction) this._directions.push(new ModelDirectionXML(direction.$)); - } + for(const directionParent of xml.directions) + { + if(Array.isArray(directionParent.direction)) for(const direction of directionParent.direction) this._directions.push(new ModelDirectionXML(direction.$)); } } } - public get dimensions(): DimensionsXML + public get dimensions(): ModelDimensionsXML { return this._dimensions; } diff --git a/src/mapping/xml/asset/logic/model/index.ts b/src/mapping/xml/asset/logic/model/index.ts index 7de88b2..3987aeb 100644 --- a/src/mapping/xml/asset/logic/model/index.ts +++ b/src/mapping/xml/asset/logic/model/index.ts @@ -1,2 +1,3 @@ -export * from './DimensionsXML'; +export * from './ModelDimensionsXML'; export * from './ModelDirectionXML'; +export * from './ModelXML'; diff --git a/src/mapping/xml/asset/logic/ParticleSystemEmitterXML.ts b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemEmitterXML.ts similarity index 90% rename from src/mapping/xml/asset/logic/ParticleSystemEmitterXML.ts rename to src/mapping/xml/asset/logic/particlesystem/ParticleSystemEmitterXML.ts index 9a4706a..de5cf55 100644 --- a/src/mapping/xml/asset/logic/ParticleSystemEmitterXML.ts +++ b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemEmitterXML.ts @@ -33,14 +33,11 @@ export class ParticleSystemEmitterXML if(xml.simulation[0] !== undefined) this._simulation = new ParticleSystemSimulationXML(xml.simulation[0]); } - if((xml.particles !== undefined) && (xml.particles[0] !== undefined)) + if((xml.particles !== undefined) && (xml.particles[0] !== undefined) && Array.isArray(xml.particles[0].particle)) { - if(Array.isArray(xml.particles[0].particle)) - { - this._particles = []; + this._particles = []; - for(const particle of xml.particles[0].particle) this._particles.push(new ParticleSystemParticleXML(particle)); - } + for(const particle of xml.particles[0].particle) this._particles.push(new ParticleSystemParticleXML(particle)); } } diff --git a/src/mapping/xml/asset/logic/ParticleSystemObjectXML.ts b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemObjectXML.ts similarity index 84% rename from src/mapping/xml/asset/logic/ParticleSystemObjectXML.ts rename to src/mapping/xml/asset/logic/particlesystem/ParticleSystemObjectXML.ts index 47f6802..d9a626e 100644 --- a/src/mapping/xml/asset/logic/ParticleSystemObjectXML.ts +++ b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemObjectXML.ts @@ -22,14 +22,11 @@ export class ParticleSystemObjectXML if(attributes.bgcolor !== undefined) this._bgColor = attributes.bgcolor; } - if(xml.emitter !== undefined) + if((xml.emitter !== undefined) && Array.isArray(xml.emitter)) { - if(Array.isArray(xml.emitter)) - { - this._emitters = []; + this._emitters = []; - for(const emitter of xml.emitter) this._emitters.push(new ParticleSystemEmitterXML(emitter)); - } + for(const emitter of xml.emitter) this._emitters.push(new ParticleSystemEmitterXML(emitter)); } } diff --git a/src/mapping/xml/asset/logic/ParticleSystemParticleXML.ts b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemParticleXML.ts similarity index 81% rename from src/mapping/xml/asset/logic/ParticleSystemParticleXML.ts rename to src/mapping/xml/asset/logic/particlesystem/ParticleSystemParticleXML.ts index b31c37e..1a8829d 100644 --- a/src/mapping/xml/asset/logic/ParticleSystemParticleXML.ts +++ b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemParticleXML.ts @@ -17,14 +17,11 @@ export class ParticleSystemParticleXML if(attributes.fade !== undefined) this._fade = (attributes.fade === 'true'); } - if(xml.frame !== undefined) + if((xml.frame !== undefined) && Array.isArray(xml.frame)) { - if(Array.isArray(xml.frame)) - { - this._frames = []; + this._frames = []; - for(const frame of xml.frame) this._frames.push(frame.$.name); - } + for(const frame of xml.frame) this._frames.push(frame.$.name); } } diff --git a/src/mapping/xml/asset/logic/ParticleSystemSimulationXML.ts b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemSimulationXML.ts similarity index 100% rename from src/mapping/xml/asset/logic/ParticleSystemSimulationXML.ts rename to src/mapping/xml/asset/logic/particlesystem/ParticleSystemSimulationXML.ts diff --git a/src/mapping/xml/asset/logic/ParticleSystemXML.ts b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemXML.ts similarity index 53% rename from src/mapping/xml/asset/logic/ParticleSystemXML.ts rename to src/mapping/xml/asset/logic/particlesystem/ParticleSystemXML.ts index a8a3c93..faabf4f 100644 --- a/src/mapping/xml/asset/logic/ParticleSystemXML.ts +++ b/src/mapping/xml/asset/logic/particlesystem/ParticleSystemXML.ts @@ -6,14 +6,11 @@ export class ParticleSystemXML constructor(xml: any) { - if(xml.particlesystem !== undefined) + if((xml.particlesystem !== undefined) && Array.isArray(xml.particlesystem)) { - if(Array.isArray(xml.particlesystem)) - { - this._objects = []; + this._objects = []; - for(const object of xml.particlesystem) this._objects.push(new ParticleSystemObjectXML(object)); - } + for(const object of xml.particlesystem) this._objects.push(new ParticleSystemObjectXML(object)); } } diff --git a/src/mapping/xml/asset/logic/PlanetSystemObjectXML.ts b/src/mapping/xml/asset/logic/particlesystem/PlanetSystemObjectXML.ts similarity index 100% rename from src/mapping/xml/asset/logic/PlanetSystemObjectXML.ts rename to src/mapping/xml/asset/logic/particlesystem/PlanetSystemObjectXML.ts diff --git a/src/mapping/xml/asset/logic/particlesystem/index.ts b/src/mapping/xml/asset/logic/particlesystem/index.ts new file mode 100644 index 0000000..e8180ff --- /dev/null +++ b/src/mapping/xml/asset/logic/particlesystem/index.ts @@ -0,0 +1,6 @@ +export * from './ParticleSystemEmitterXML'; +export * from './ParticleSystemObjectXML'; +export * from './ParticleSystemParticleXML'; +export * from './ParticleSystemSimulationXML'; +export * from './ParticleSystemXML'; +export * from './PlanetSystemObjectXML'; diff --git a/src/mapping/xml/asset/manifest/ManifestLibraryXML.ts b/src/mapping/xml/asset/manifest/ManifestLibraryXML.ts index b27516d..39951b4 100644 --- a/src/mapping/xml/asset/manifest/ManifestLibraryXML.ts +++ b/src/mapping/xml/asset/manifest/ManifestLibraryXML.ts @@ -18,35 +18,23 @@ export class ManifestLibraryXML if(attributes.version !== undefined) this._version = attributes.version; } - if(xml.assets !== undefined) + if((xml.assets !== undefined) && Array.isArray(xml.assets)) { - if(Array.isArray(xml.assets)) - { - this._assets = []; + this._assets = []; - for(const assetParent of xml.assets) - { - if(Array.isArray(assetParent.asset)) - { - for(const asset of assetParent.asset) this._assets.push(new ManifestLibraryAssetXML(asset)); - } - } + for(const assetParent of xml.assets) + { + if(Array.isArray(assetParent.asset)) for(const asset of assetParent.asset) this._assets.push(new ManifestLibraryAssetXML(asset)); } } - if(xml.aliases !== undefined) + if((xml.aliases !== undefined) && Array.isArray(xml.aliases)) { - if(Array.isArray(xml.aliases)) - { - this._aliases = []; + this._aliases = []; - for(const aliasParent of xml.aliases) - { - if(Array.isArray(aliasParent.alias)) - { - for(const alias of aliasParent.alias) this._aliases.push(new ManifestLibraryAliasXML(alias)); - } - } + for(const aliasParent of xml.aliases) + { + if(Array.isArray(aliasParent.alias)) for(const alias of aliasParent.alias) this._aliases.push(new ManifestLibraryAliasXML(alias)); } } } diff --git a/src/mapping/xml/asset/visualization/VisualDirectionXML.ts b/src/mapping/xml/asset/visualization/VisualDirectionXML.ts index 634c662..6014286 100644 --- a/src/mapping/xml/asset/visualization/VisualDirectionXML.ts +++ b/src/mapping/xml/asset/visualization/VisualDirectionXML.ts @@ -14,14 +14,11 @@ export class VisualDirectionXML if(attributes.id !== undefined) this._id = parseInt(attributes.id); } - if(xml.layer !== undefined) + if((xml.layer !== undefined) && Array.isArray(xml.layer)) { - if(Array.isArray(xml.layer)) - { - this._layers = []; + this._layers = []; - for(const layer of xml.layer) this._layers.push(new LayerXML(layer)); - } + for(const layer of xml.layer) this._layers.push(new LayerXML(layer)); } } diff --git a/src/mapping/xml/asset/visualization/VisualizationDataXML.ts b/src/mapping/xml/asset/visualization/VisualizationDataXML.ts index 4d6cc36..b151ea9 100644 --- a/src/mapping/xml/asset/visualization/VisualizationDataXML.ts +++ b/src/mapping/xml/asset/visualization/VisualizationDataXML.ts @@ -15,6 +15,7 @@ export class VisualizationDataXML private readonly _directions: VisualDirectionXML[]; private readonly _colors: ColorXML[]; private readonly _animations: AnimationXML[]; + private readonly _defaultPosture: string; private readonly _postures: PostureXML[]; private readonly _gestures: GestureXML[]; @@ -29,99 +30,72 @@ export class VisualizationDataXML if(attributes.angle !== undefined) this._angle = parseInt(attributes.angle); } - if(xml.layers !== undefined) + if((xml.layers !== undefined) && Array.isArray(xml.layers)) { - if(Array.isArray(xml.layers)) - { - this._layers = []; + this._layers = []; - for(const layerParent of xml.layers) - { - if(Array.isArray(layerParent.layer)) - { - for(const layer of layerParent.layer) this._layers.push(new LayerXML(layer)); - } - } + for(const layerParent of xml.layers) + { + if(Array.isArray(layerParent.layer)) for(const layer of layerParent.layer) this._layers.push(new LayerXML(layer)); } } - if(xml.directions !== undefined) + if((xml.directions !== undefined) && Array.isArray(xml.directions)) { - if(Array.isArray(xml.directions)) - { - this._directions = []; + this._directions = []; - for(const directionParent of xml.directions) - { - if(Array.isArray(directionParent.direction)) - { - for(const direction of directionParent.direction) this._directions.push(new VisualDirectionXML(direction)); - } - } + for(const directionParent of xml.directions) + { + if(Array.isArray(directionParent.direction)) for(const direction of directionParent.direction) this._directions.push(new VisualDirectionXML(direction)); } } - if(xml.colors !== undefined) + if((xml.colors !== undefined) && Array.isArray(xml.colors)) { - if(Array.isArray(xml.colors)) - { - this._colors = []; + this._colors = []; - for(const colorParent of xml.colors) - { - if(Array.isArray(colorParent.color)) - { - for(const color of colorParent.color) this._colors.push(new ColorXML(color)); - } - } + for(const colorParent of xml.colors) + { + if(Array.isArray(colorParent.color)) for(const color of colorParent.color) this._colors.push(new ColorXML(color)); } } - if(xml.animations !== undefined) + if((xml.animations !== undefined) && Array.isArray(xml.animations)) { - if(Array.isArray(xml.animations)) - { - this._animations = []; + this._animations = []; - for(const animationParent of xml.animations) - { - if(Array.isArray(animationParent.animation)) - { - for(const animation of animationParent.animation) this._animations.push(new AnimationXML(animation)); - } - } + for(const animationParent of xml.animations) + { + if(Array.isArray(animationParent.animation)) for(const animation of animationParent.animation) this._animations.push(new AnimationXML(animation)); } } - if(xml.postures !== undefined) + if((xml.postures !== undefined) && Array.isArray(xml.postures)) { - if(Array.isArray(xml.postures)) + const parent = xml.postures[0]; + + const attributes = parent.$; + + if(attributes !== undefined) + { + if(attributes.defaultPosture !== undefined) this._defaultPosture = attributes.defaultPosture; + } + + if(Array.isArray(parent.posture)) { this._postures = []; - for(const postureParent of xml.postures) - { - if(Array.isArray(postureParent.posture)) - { - for(const posture of postureParent.posture) this._postures.push(new PostureXML(posture)); - } - } + for(const posture of parent.posture) this._postures.push(new PostureXML(posture)); } } - if(xml.gestures !== undefined) + if((xml.gestures !== undefined) && Array.isArray(xml.gestures)) { - if(Array.isArray(xml.gestures)) - { - this._gestures = []; + this._gestures = []; - for(const gestureParent of xml.gestures) - { - if(Array.isArray(gestureParent.gesture)) - { - for(const gesture of gestureParent.gesture) this._gestures.push(new GestureXML(gesture)); - } - } + for(const gestureParent of xml.gestures) + { + if(Array.isArray(gestureParent.gesture)) for(const gesture of gestureParent.gesture) this._gestures.push(new GestureXML(gesture)); } } } @@ -161,6 +135,11 @@ export class VisualizationDataXML return this._animations; } + public get defaultPosture(): string + { + return this._defaultPosture; + } + public get postures(): PostureXML[] { return this._postures; diff --git a/src/mapping/xml/asset/visualization/VisualizationXML.ts b/src/mapping/xml/asset/visualization/VisualizationXML.ts index d4a4120..73fc147 100644 --- a/src/mapping/xml/asset/visualization/VisualizationXML.ts +++ b/src/mapping/xml/asset/visualization/VisualizationXML.ts @@ -14,20 +14,13 @@ export class VisualizationXML if(attributes.type !== undefined) this._type = attributes.type; } - if(xml.graphics !== undefined) + if((xml.graphics !== undefined) && Array.isArray(xml.graphics)) { - if(Array.isArray(xml.graphics)) + this._visualizations = []; + + for(const graphic of xml.graphics) { - this._visualizations = []; - - for(const graphic of xml.graphics) - { - if(Array.isArray(graphic.visualization)) - { - for(const visualization of graphic.visualization) this._visualizations.push(new VisualizationDataXML(visualization)); - - } - } + if(Array.isArray(graphic.visualization)) for(const visualization of graphic.visualization) this._visualizations.push(new VisualizationDataXML(visualization)); } } } diff --git a/src/mapping/xml/asset/visualization/animation/AnimationLayerXML.ts b/src/mapping/xml/asset/visualization/animation/AnimationLayerXML.ts index 2870832..8765db4 100644 --- a/src/mapping/xml/asset/visualization/animation/AnimationLayerXML.ts +++ b/src/mapping/xml/asset/visualization/animation/AnimationLayerXML.ts @@ -23,14 +23,11 @@ export class AnimationLayerXML if(attributes.randomStart !== undefined) this._randomStart = parseInt(attributes.randomStart); } - if(xml.frameSequence !== undefined) + if((xml.frameSequence !== undefined) && Array.isArray(xml.frameSequence)) { - if(Array.isArray(xml.frameSequence)) - { - this._frameSequences = []; + this._frameSequences = []; - for(const frameSequence of xml.frameSequence) this._frameSequences.push(new FrameSequenceXML(frameSequence)); - } + for(const frameSequence of xml.frameSequence) this._frameSequences.push(new FrameSequenceXML(frameSequence)); } } diff --git a/src/mapping/xml/asset/visualization/animation/AnimationXML.ts b/src/mapping/xml/asset/visualization/animation/AnimationXML.ts index a20d0d0..2c185f3 100644 --- a/src/mapping/xml/asset/visualization/animation/AnimationXML.ts +++ b/src/mapping/xml/asset/visualization/animation/AnimationXML.ts @@ -22,14 +22,11 @@ export class AnimationXML if(attributes.randomStart !== undefined) this._randomStart = (attributes.randomStart === '1'); } - if(xml.animationLayer !== undefined) + if((xml.animationLayer !== undefined) && Array.isArray(xml.animationLayer)) { - if(Array.isArray(xml.animationLayer)) - { - this._layers = []; + this._layers = []; - for(const animationLayer of xml.animationLayer) this._layers.push(new AnimationLayerXML(animationLayer)); - } + for(const animationLayer of xml.animationLayer) this._layers.push(new AnimationLayerXML(animationLayer)); } } diff --git a/src/mapping/xml/asset/visualization/animation/FrameSequenceXML.ts b/src/mapping/xml/asset/visualization/animation/FrameSequenceXML.ts index 3c4b9d6..8f7f75f 100644 --- a/src/mapping/xml/asset/visualization/animation/FrameSequenceXML.ts +++ b/src/mapping/xml/asset/visualization/animation/FrameSequenceXML.ts @@ -17,14 +17,11 @@ export class FrameSequenceXML if(attributes.random !== undefined) this._random = parseInt(attributes.random); } - if(xml.frame !== undefined) + if((xml.frame !== undefined) && Array.isArray(xml.frame)) { - if(Array.isArray(xml.frame)) - { - this._frames = []; + this._frames = []; - for(const frame of xml.frame) this._frames.push(new FrameXML(frame)); - } + for(const frame of xml.frame) this._frames.push(new FrameXML(frame)); } } diff --git a/src/mapping/xml/asset/visualization/animation/FrameXML.ts b/src/mapping/xml/asset/visualization/animation/FrameXML.ts index 962c5f3..7cf87d0 100644 --- a/src/mapping/xml/asset/visualization/animation/FrameXML.ts +++ b/src/mapping/xml/asset/visualization/animation/FrameXML.ts @@ -23,19 +23,13 @@ export class FrameXML if(attributes.randomY !== undefined) this._randomY = parseInt(attributes.randomY); } - if(xml.offsets !== undefined) + if((xml.offsets !== undefined) && Array.isArray(xml.offsets)) { - if(Array.isArray(xml.offsets)) - { - this._offsets = []; + this._offsets = []; - for(const offsetParent of xml.offsets) - { - if(Array.isArray(offsetParent.offset)) - { - for(const offset of offsetParent.offset) this._offsets.push(new FrameOffsetXML(offset)); - } - } + for(const offsetParent of xml.offsets) + { + if(Array.isArray(offsetParent.offset)) for(const offset of offsetParent.offset) this._offsets.push(new FrameOffsetXML(offset)); } } } diff --git a/src/mapping/xml/asset/visualization/color/ColorXML.ts b/src/mapping/xml/asset/visualization/color/ColorXML.ts index 2c683b7..f17c7e8 100644 --- a/src/mapping/xml/asset/visualization/color/ColorXML.ts +++ b/src/mapping/xml/asset/visualization/color/ColorXML.ts @@ -14,7 +14,7 @@ export class ColorXML if(attributes.id !== undefined) this._id = parseInt(attributes.id); } - if(xml.colorLayer !== undefined) + if((xml.colorLayer !== undefined) && Array.isArray(xml.colorLayer)) { this._layers = []; diff --git a/src/swf/HabboAssetSWF.ts b/src/swf/HabboAssetSWF.ts index 8232af6..732e3ae 100644 --- a/src/swf/HabboAssetSWF.ts +++ b/src/swf/HabboAssetSWF.ts @@ -180,25 +180,30 @@ export class HabboAssetSWF return streamTag; } - public getFullClassName(type: string, documentNameTwice: boolean): string + public getFullClassName(type: string, documentNameTwice: boolean, snakeCase: boolean = false): string { - return this.getFullClassNameSnake(type, documentNameTwice, false); + return this.getFullClassNameSnake(type, documentNameTwice, snakeCase); } - public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string + public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean = false): string { - let result: string = this.getDocumentClass() + '_'; + let result: string = this.getDocumentClass(); + if(documentNameTwice) { if(snakeCase) { - //result += CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, this.swf.getDocumentClass()) + "_"; + result = (result + (result.replace(/(?:^|\.?)([A-Z])/g, (x,y) => ('_' + y.toLowerCase().replace(/^_/, '')))) + '_'); } else { - result += this.getDocumentClass() + '_'; + result += '_' + this.getDocumentClass() + '_'; } } + else + { + result += '_'; + } return result + type; } diff --git a/src/utils/File.ts b/src/utils/File.ts index fa02ea1..5ff7c3a 100644 --- a/src/utils/File.ts +++ b/src/utils/File.ts @@ -1,8 +1,7 @@ import { existsSync, lstatSync, mkdirSync, readdirSync, RmOptions, rmSync } from 'fs'; -export default class File +export class File { - private readonly _path: string; constructor(path: string) @@ -32,13 +31,13 @@ export default class File return this.exists() && lstatSync(this._path).isDirectory(); } - get path(): string - { - return this._path; - } - public rmdir(options: RmOptions): void { return rmSync(this._path, options); } + + public get path(): string + { + return this._path; + } } diff --git a/src/utils/FileUtilities.ts b/src/utils/FileUtilities.ts index e121948..4917252 100644 --- a/src/utils/FileUtilities.ts +++ b/src/utils/FileUtilities.ts @@ -1,11 +1,28 @@ import { readFile } from 'fs'; import * as fetch from 'node-fetch'; import { promisify } from 'util'; +import { File } from './File'; const readFileAsync = promisify(readFile); export class FileUtilities { + public static getDirectory(baseFolderPath: string, childFolderName: string = null): File + { + let folder = new File(baseFolderPath); + + if(!folder.isDirectory()) folder.mkdirs(); + + if(childFolderName) + { + folder = new File(folder.path + childFolderName); + + if(!folder.isDirectory()) folder.mkdirs(); + } + + return folder; + } + public static async readFileAsBuffer(url: string): Promise { if(!url) return null; @@ -42,7 +59,7 @@ export class FileUtilities if(url.startsWith('http')) { const data = await fetch.default(url); - if (data.status === 404) return null; + if(data.status === 404) return null; if(data) content = await data.text(); }