Add more converters

This commit is contained in:
Bill 2021-02-21 00:27:46 -05:00
parent c3f894b231
commit c54fddd5ea
54 changed files with 1162 additions and 265 deletions

2
.gitignore vendored
View File

@ -41,7 +41,7 @@ yarn-error.log
testem.log testem.log
/typings /typings
.git .git
error.log *.log
# System Files # System Files
.DS_Store .DS_Store

View File

View File

@ -2,21 +2,55 @@ import 'reflect-metadata';
import { container } from 'tsyringe'; import { container } from 'tsyringe';
import { Configuration } from './common/config/Configuration'; import { Configuration } from './common/config/Configuration';
import { EffectConverter } from './converters/effect/EffectConverter'; import { EffectConverter } from './converters/effect/EffectConverter';
import { EffectMapConverter } from './converters/effectmap/EffectMapConverter';
import { FigureConverter } from './converters/figure/FigureConverter'; import { FigureConverter } from './converters/figure/FigureConverter';
import { FigureMapConverter } from './converters/figuremap/FigureMapConverter';
import { FurnitureConverter } from './converters/furniture/FurnitureConverter'; import { FurnitureConverter } from './converters/furniture/FurnitureConverter';
import { FurnitureDataConverter } from './converters/furnituredata/FurnitureDataConverter';
import { PetConverter } from './converters/pet/PetConverter'; import { PetConverter } from './converters/pet/PetConverter';
import { ProductDataConverter } from './converters/productdata/ProductDataConverter';
(async () => (async () =>
{ {
const config = container.resolve(Configuration); const config = container.resolve(Configuration);
await config.init(); await config.init();
if(config.getBoolean('convert.figuremap'))
{
const figureMapConverter = container.resolve(FigureMapConverter);
await figureMapConverter.convertAsync();
}
if(config.getBoolean('convert.effectmap'))
{
const effectMapConverter = container.resolve(EffectMapConverter);
await effectMapConverter.convertAsync();
}
if(config.getBoolean('convert.furnidata'))
{
const furniDataConverter = container.resolve(FurnitureDataConverter);
await furniDataConverter.convertAsync();
}
if(config.getBoolean('convert.productdata'))
{
const productDataConverter = container.resolve(ProductDataConverter);
await productDataConverter.convertAsync();
}
if(config.getBoolean('convert.figure')) if(config.getBoolean('convert.figure'))
{ {
const figureConverter = container.resolve(FigureConverter); const figureConverter = container.resolve(FigureConverter);
await figureConverter.convertAsync(); await figureConverter.convertAsync();
} }
if(config.getBoolean('convert.effect'))
{
const effectConverter = container.resolve(EffectConverter);
await effectConverter.convertAsync();
}
if(config.getBoolean('convert.furniture')) if(config.getBoolean('convert.furniture'))
{ {
const furnitureConverter = container.resolve(FurnitureConverter); const furnitureConverter = container.resolve(FurnitureConverter);
@ -28,10 +62,4 @@ import { PetConverter } from './converters/pet/PetConverter';
const petConverter = container.resolve(PetConverter); const petConverter = container.resolve(PetConverter);
await petConverter.convertAsync(); await petConverter.convertAsync();
} }
if(config.getBoolean('convert.effect'))
{
const effectConverter = container.resolve(EffectConverter);
await effectConverter.convertAsync();
}
})(); })();

View File

@ -14,26 +14,22 @@ export class Configuration
public async init(): Promise<void> public async init(): Promise<void>
{ {
for(const key of Object.keys(configuration)) this._config.set(key, configuration[key]); this.parseConfiguration(configuration);
await this.loadExternalVariables();
this.parseConfiguration(configuration);
} }
public async loadExternalVariables(): Promise<void> public async loadExternalVariables(): Promise<void>
{ {
const url = this.getValue('external_vars.url'); const url = this.getValue('external.variables.url');
try try
{ {
const content = await FileUtilities.readFileAsString(url); const content = await FileUtilities.readFileAsString(url);
const config: string[] = content.split('\n');
for(const configEntry of config) this.parseExternalVariables(content);
{
const configEntrySplit = configEntry.split('=');
const configKey = configEntrySplit[0];
const configValue = configEntrySplit[1];
this._config.set(configKey, configValue);
}
} }
catch (error) catch (error)
@ -43,19 +39,106 @@ export class Configuration
} }
} }
public getBoolean(key: string): boolean private parseConfiguration(content: Object): boolean
{ {
return this._config.get(key) === '1'; if(!content) return false;
try
{
const regex = new RegExp(/\${(.*?)}/g);
for(const key of Object.keys(configuration))
{
if(this._config.get(key))
{
if(!configuration[key].length) continue;
}
this._config.set(key, this.interpolate(configuration[key], regex));
}
return true;
}
catch (e)
{
console.log();
console.error(e);
return false;
}
} }
public getValue(key: string, value: string = ''): string private parseExternalVariables(content: string): boolean
{ {
if(this._config.has(key)) if(!content || (content === '')) return false;
try
{ {
// @ts-ignore const regex = new RegExp(/\${(.*?)}/g);
return this._config.get(key); const lines: string[] = content.split('\n');
for(const line of lines)
{
const [ key, value ] = line.split('=');
if(key.startsWith('landing.view')) continue;
this._config.set(key, this.interpolate((value || ''), regex));
}
return true;
}
catch (e)
{
console.log();
console.error(e);
return false;
}
}
public interpolate(value: string, regex: RegExp = null): string
{
if(!regex) regex = new RegExp(/\${(.*?)}/g);
const pieces = value.match(regex);
if(pieces && pieces.length)
{
for(const piece of pieces)
{
const existing = this._config.get(this.removeInterpolateKey(piece));
if(existing) (value = value.replace(piece, existing));
}
} }
return value; return value;
} }
private removeInterpolateKey(value: string): string
{
return value.replace('${', '').replace('}', '');
}
public getValue(key: string, value: string = ''): string
{
if(this._config.has(key)) return this._config.get(key);
return value;
}
public setValue(key: string, value: string): void
{
this._config.set(key, value);
}
public getBoolean(key: string): boolean
{
const value = this.getValue(key);
return ((value === 'true') || (value === '1'));
}
} }

View File

@ -0,0 +1,4 @@
export class Converter
{
}

View File

@ -6,8 +6,9 @@ import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import { DefineBinaryDataTag } from '../../swf/tags/DefineBinaryDataTag'; import { DefineBinaryDataTag } from '../../swf/tags/DefineBinaryDataTag';
import { NitroBundle } from '../../utils/NitroBundle'; import { NitroBundle } from '../../utils/NitroBundle';
import { SpriteBundle } from '../bundle/SpriteBundle'; import { SpriteBundle } from '../bundle/SpriteBundle';
import { Converter } from './Converter';
export class SWFConverter export class SWFConverter extends Converter
{ {
protected async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, assetData: IAssetData, spriteBundle: SpriteBundle): Promise<void> protected async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, assetData: IAssetData, spriteBundle: SpriteBundle): Promise<void>
{ {

View File

@ -1,18 +0,0 @@
{
"output.folder.furniture": "/converted-assets/furniture/",
"output.folder.figure": "/converted-assets/figure/",
"output.folder.effect": "/converted-assets/effect/",
"output.folder.pet": "/converted-assets/pet/",
"furnidata.url": "/gamedata/json/furnidata",
"figuremap.url": "/gamedata/figuremap.xml",
"effectmap.url": "/gamedata/effectmap.xml",
"external_vars.url": "/gamedata/external_variables.txt",
"dynamic.download.url.furniture": "/dcr/hof_furni/%className%.swf",
"dynamic.download.url.figure": "/gordon/PRODUCTION/%className%.swf",
"dynamic.download.url.effect": "/gordon/PRODUCTION/%className%.swf",
"dynamic.download.url.pet": "/gordon/PRODUCTION/%className%.swf",
"convert.furniture": "1",
"convert.figure": "1",
"convert.effect": "1",
"convert.pet": "1"
}

View File

@ -0,0 +1,22 @@
{
"output.folder": "C:/nitro-converted-assets/",
"flash.client.url": "",
"furnidata.load.url": "",
"productdata.load.url": "",
"figuremap.load.url": "${flash.client.url}figuremap.xml",
"effectmap.load.url": "${flash.client.url}effectmap.xml",
"dynamic.download.pet.url": "${flash.client.url}%className%.swf",
"dynamic.download.figure.url": "${flash.client.url}%className%.swf",
"dynamic.download.effect.url": "${flash.client.url}%className%.swf",
"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",
"convert.furnidata": "1",
"convert.productdata": "1",
"convert.figuremap": "1",
"convert.effectmap": "1",
"convert.figure": "1",
"convert.effect": "1",
"convert.furniture": "1",
"convert.pet": "1"
}

View File

@ -7,7 +7,7 @@ import { IAssetData } from '../../mapping/json';
import { AnimationMapper, ManifestMapper } from '../../mapping/mappers'; import { AnimationMapper, ManifestMapper } from '../../mapping/mappers';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import File from '../../utils/File'; import File from '../../utils/File';
import Logger from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import { EffectDownloader } from './EffectDownloader'; import { EffectDownloader } from './EffectDownloader';
@singleton() @singleton()
@ -28,9 +28,7 @@ export class EffectConverter extends SWFConverter
const spinner = ora('Preparing Effects').start(); const spinner = ora('Preparing Effects').start();
const outputFolder = new File(this._configuration.getValue('output.folder.effect')); const directory = this.getDirectory();
if(!outputFolder.isDirectory()) outputFolder.mkdirs();
try try
{ {
@ -43,7 +41,7 @@ export class EffectConverter extends SWFConverter
const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf);
const assetData = await this.mapXML2JSON(habboAssetSwf, className); const assetData = await this.mapXML2JSON(habboAssetSwf, className);
await this.fromHabboAsset(habboAssetSwf, outputFolder.path, assetData.type, assetData, spriteBundle); await this.fromHabboAsset(habboAssetSwf, directory.path, assetData.type, assetData, spriteBundle);
}); });
spinner.succeed(`Effects finished in ${ Date.now() - now }ms`); spinner.succeed(`Effects finished in ${ Date.now() - now }ms`);
@ -55,23 +53,36 @@ export class EffectConverter extends SWFConverter
} }
} }
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<IAssetData> private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise<IAssetData>
{ {
if(!habboAssetSWF) return null; if(!habboAssetSWF) return null;
const assetData: IAssetData = {}; const output: IAssetData = {};
assetData.name = assetType; output.name = assetType;
assetData.type = EffectDownloader.EFFECT_TYPES.get(assetType); output.type = EffectDownloader.EFFECT_TYPES.get(assetType);
const manifestXML = await EffectConverter.getManifestXML(habboAssetSWF); const manifestXML = await EffectConverter.getManifestXML(habboAssetSWF);
if(manifestXML) ManifestMapper.mapXML(manifestXML, assetData); if(manifestXML) ManifestMapper.mapXML(manifestXML, output);
const animationXML = await EffectConverter.getAnimationXML(habboAssetSWF); const animationXML = await EffectConverter.getAnimationXML(habboAssetSWF);
if(animationXML) AnimationMapper.mapXML(animationXML, assetData); if(animationXML) AnimationMapper.mapXML(animationXML, output);
return assetData; return output;
} }
} }

View File

@ -3,13 +3,16 @@ import { Configuration } from '../../common/config/Configuration';
import { IEffectMap } from '../../mapping/json'; import { IEffectMap } from '../../mapping/json';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import { FileUtilities } from '../../utils/FileUtilities'; import { FileUtilities } from '../../utils/FileUtilities';
import { Logger } from '../../utils/Logger';
@singleton() @singleton()
export class EffectDownloader export class EffectDownloader
{ {
public static EFFECT_TYPES: Map<string, string> = new Map(); public static EFFECT_TYPES: Map<string, string> = new Map();
constructor(private readonly _configuration: Configuration) constructor(
private readonly _configuration: Configuration,
private readonly _logger: Logger)
{} {}
public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
@ -38,6 +41,8 @@ export class EffectDownloader
{ {
console.log(); console.log();
console.error(`Error parsing ${ className }: ` + error.message); console.error(`Error parsing ${ className }: ` + error.message);
this._logger.logError(`Error parsing ${ className }: ` + error.message);
} }
} }
} }
@ -45,7 +50,7 @@ export class EffectDownloader
public async parseEffectMap(): Promise<IEffectMap> public async parseEffectMap(): Promise<IEffectMap>
{ {
const url = this._configuration.getValue('effectmap.url'); const url = this._configuration.getValue('effectmap.load.url');
if(!url || !url.length) return null; if(!url || !url.length) return null;
@ -58,7 +63,7 @@ export class EffectDownloader
public async extractEffect(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async extractEffect(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
{ {
let url = this._configuration.getValue('dynamic.download.url.effect'); let url = this._configuration.getValue('dynamic.download.effect.url');
if(!url || !url.length) return; if(!url || !url.length) return;

View File

@ -0,0 +1,94 @@
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(): Promise<void>
{
const now = Date.now();
const spinner = ora('Preparing EffectMap').start();
const directory = this.getDirectory();
try
{
await 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`);
}
catch (error)
{
spinner.fail('EffectMap 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 + '/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<IEffectMap>
{
if(!xml) return null;
const output: IEffectMap = {};
EffectMapMapper.mapXML(xml, output);
return output;
}
}

View File

@ -0,0 +1,32 @@
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<void>): Promise<void>
{
const effectMap = await this.parseEffectMap();
if(!effectMap) throw new Error('invalid_effect_map');
callback(effectMap);
}
public async parseEffectMap(): Promise<string>
{
const url = this._configuration.getValue('effectmap.load.url');
if(!url || !url.length) return null;
const content = await FileUtilities.readFileAsString(url);
if(!content || !content.length) return null;
return content;
}
}

View File

@ -7,7 +7,7 @@ import { IAssetData } from '../../mapping/json';
import { ManifestMapper } from '../../mapping/mappers'; import { ManifestMapper } from '../../mapping/mappers';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import File from '../../utils/File'; import File from '../../utils/File';
import Logger from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import { FigureDownloader } from './FigureDownloader'; import { FigureDownloader } from './FigureDownloader';
@singleton() @singleton()
@ -28,9 +28,7 @@ export class FigureConverter extends SWFConverter
const spinner = ora('Preparing Figure').start(); const spinner = ora('Preparing Figure').start();
const outputFolder = new File(this._configuration.getValue('output.folder.figure')); const directory = this.getDirectory();
if(!outputFolder.isDirectory()) outputFolder.mkdirs();
try try
{ {
@ -43,7 +41,7 @@ export class FigureConverter extends SWFConverter
const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf);
const assetData = await this.mapXML2JSON(habboAssetSwf, className); const assetData = await this.mapXML2JSON(habboAssetSwf, className);
await this.fromHabboAsset(habboAssetSwf, outputFolder.path, assetData.type, assetData, spriteBundle); await this.fromHabboAsset(habboAssetSwf, directory.path, assetData.type, assetData, spriteBundle);
}); });
spinner.succeed(`Figures finished in ${ Date.now() - now }ms`); spinner.succeed(`Figures finished in ${ Date.now() - now }ms`);
@ -55,19 +53,32 @@ export class FigureConverter extends SWFConverter
} }
} }
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<IAssetData> private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise<IAssetData>
{ {
if(!habboAssetSWF) return null; if(!habboAssetSWF) return null;
const assetData: IAssetData = {}; const output: IAssetData = {};
assetData.name = assetType; output.name = assetType;
assetData.type = FigureDownloader.FIGURE_TYPES.get(assetType); output.type = FigureDownloader.FIGURE_TYPES.get(assetType);
const manifestXML = await FigureConverter.getManifestXML(habboAssetSWF); const manifestXML = await FigureConverter.getManifestXML(habboAssetSWF);
if(manifestXML) ManifestMapper.mapXML(manifestXML, assetData); if(manifestXML) ManifestMapper.mapXML(manifestXML, output);
return assetData; return output;
} }
} }

View File

@ -3,13 +3,16 @@ import { Configuration } from '../../common/config/Configuration';
import { IFigureMap } from '../../mapping/json'; import { IFigureMap } from '../../mapping/json';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import { FileUtilities } from '../../utils/FileUtilities'; import { FileUtilities } from '../../utils/FileUtilities';
import { Logger } from '../../utils/Logger';
@singleton() @singleton()
export class FigureDownloader export class FigureDownloader
{ {
public static FIGURE_TYPES: Map<string, string> = new Map(); public static FIGURE_TYPES: Map<string, string> = new Map();
constructor(private readonly _configuration: Configuration) constructor(
private readonly _configuration: Configuration,
private readonly _logger: Logger)
{} {}
public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
@ -40,6 +43,8 @@ export class FigureDownloader
{ {
console.log(); console.log();
console.error(`Error parsing ${ className }: ` + error.message); console.error(`Error parsing ${ className }: ` + error.message);
this._logger.logError(`Error parsing ${ className }: ` + error.message);
} }
} }
} }
@ -47,7 +52,7 @@ export class FigureDownloader
public async parseFigureMap(): Promise<IFigureMap> public async parseFigureMap(): Promise<IFigureMap>
{ {
const url = this._configuration.getValue('figuremap.url'); const url = this._configuration.getValue('figuremap.load.url');
if(!url || !url.length) return null; if(!url || !url.length) return null;
@ -60,7 +65,7 @@ export class FigureDownloader
public async extractFigure(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async extractFigure(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
{ {
let url = this._configuration.getValue('dynamic.download.url.figure'); let url = this._configuration.getValue('dynamic.download.figure.url');
if(!url || !url.length) return; if(!url || !url.length) return;

View File

@ -0,0 +1,94 @@
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(): Promise<void>
{
const now = Date.now();
const spinner = ora('Preparing FigureMap').start();
const directory = this.getDirectory();
try
{
await 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`);
}
catch (error)
{
spinner.fail('FigureMap 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 + '/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<IFigureMap>
{
if(!xml) return null;
const output: IFigureMap = {};
FigureMapMapper.mapXML(xml, output);
return output;
}
}

View File

@ -0,0 +1,32 @@
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<void>): Promise<void>
{
const figureMap = await this.parseFigureMap();
if(!figureMap) throw new Error('invalid_figure_map');
callback(figureMap);
}
public async parseFigureMap(): Promise<string>
{
const url = this._configuration.getValue('figuremap.load.url');
if(!url || !url.length) return null;
const content = await FileUtilities.readFileAsString(url);
if(!content || !content.length) return null;
return content;
}
}

View File

@ -7,7 +7,7 @@ import { IAssetData } from '../../mapping/json';
import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers'; import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import File from '../../utils/File'; import File from '../../utils/File';
import Logger from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import { FurnitureDownloader } from './FurnitureDownloader'; import { FurnitureDownloader } from './FurnitureDownloader';
@singleton() @singleton()
@ -28,9 +28,7 @@ export class FurnitureConverter extends SWFConverter
const spinner = ora('Preparing Furniture').start(); const spinner = ora('Preparing Furniture').start();
const outputFolder = new File(this._configuration.getValue('output.folder.furniture')); const directory = this.getDirectory();
if(!outputFolder.isDirectory()) outputFolder.mkdirs();
try try
{ {
@ -43,7 +41,7 @@ export class FurnitureConverter extends SWFConverter
const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf);
const assetData = await this.mapXML2JSON(habboAssetSwf, 'furniture'); const assetData = await this.mapXML2JSON(habboAssetSwf, 'furniture');
await this.fromHabboAsset(habboAssetSwf, outputFolder.path, 'furniture', assetData, spriteBundle); await this.fromHabboAsset(habboAssetSwf, directory.path, 'furniture', assetData, spriteBundle);
}); });
spinner.succeed(`Furniture finished in ${ Date.now() - now }ms`); spinner.succeed(`Furniture finished in ${ Date.now() - now }ms`);
@ -55,30 +53,43 @@ export class FurnitureConverter extends SWFConverter
} }
} }
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<IAssetData> private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise<IAssetData>
{ {
if(!habboAssetSWF) return null; if(!habboAssetSWF) return null;
const assetData: IAssetData = {}; const output: IAssetData = {};
assetData.type = assetType; output.type = assetType;
const indexXML = await FurnitureConverter.getIndexXML(habboAssetSWF); const indexXML = await FurnitureConverter.getIndexXML(habboAssetSWF);
if(indexXML) IndexMapper.mapXML(indexXML, assetData); if(indexXML) IndexMapper.mapXML(indexXML, output);
const assetXML = await FurnitureConverter.getAssetsXML(habboAssetSWF); const assetXML = await FurnitureConverter.getAssetsXML(habboAssetSWF);
if(assetXML) AssetMapper.mapXML(assetXML, assetData); if(assetXML) AssetMapper.mapXML(assetXML, output);
const logicXML = await FurnitureConverter.getLogicXML(habboAssetSWF); const logicXML = await FurnitureConverter.getLogicXML(habboAssetSWF);
if(logicXML) LogicMapper.mapXML(logicXML, assetData); if(logicXML) LogicMapper.mapXML(logicXML, output);
const visualizationXML = await FurnitureConverter.getVisualizationXML(habboAssetSWF); const visualizationXML = await FurnitureConverter.getVisualizationXML(habboAssetSWF);
if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, assetData); if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, output);
return assetData; return output;
} }
} }

View File

@ -3,11 +3,14 @@ import { Configuration } from '../../common/config/Configuration';
import { IFurnitureData } from '../../mapping/json'; import { IFurnitureData } from '../../mapping/json';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import { FileUtilities } from '../../utils/FileUtilities'; import { FileUtilities } from '../../utils/FileUtilities';
import { Logger } from '../../utils/Logger';
@singleton() @singleton()
export class FurnitureDownloader export class FurnitureDownloader
{ {
constructor(private readonly _configuration: Configuration) constructor(
private readonly _configuration: Configuration,
private readonly _logger: Logger)
{} {}
public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async download(callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
@ -39,7 +42,7 @@ export class FurnitureDownloader
catch (error) catch (error)
{ {
console.log(); console.log();
console.error(error); console.error(`Error parsing ${ className }: ` + error.message);
} }
} }
} }
@ -66,7 +69,9 @@ export class FurnitureDownloader
catch (error) catch (error)
{ {
console.log(); console.log();
console.error(error); console.error(`Error parsing ${ className }: ` + error.message);
this._logger.logError(`Error parsing ${ className }: ` + error.message);
} }
} }
} }
@ -75,7 +80,7 @@ export class FurnitureDownloader
public async parseFurniData(): Promise<IFurnitureData> public async parseFurniData(): Promise<IFurnitureData>
{ {
const url = this._configuration.getValue('furnidata.url'); const url = this._configuration.getValue('furnidata.load.url');
if(!url || !url.length) return null; if(!url || !url.length) return null;
@ -88,7 +93,7 @@ export class FurnitureDownloader
public async extractFurniture(revision: number, className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void> public async extractFurniture(revision: number, className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
{ {
let url = this._configuration.getValue('dynamic.download.url.furniture'); let url = this._configuration.getValue('dynamic.download.furniture.url');
if(!url || !url.length) return; if(!url || !url.length) return;

View File

@ -0,0 +1,94 @@
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
{
constructor(
private readonly _furnitureDataDownloader: FurnitureDataDownloader,
private readonly _configuration: Configuration,
private readonly _logger: Logger)
{
super();
}
public async convertAsync(): Promise<void>
{
const now = Date.now();
const spinner = ora('Preparing FurnitureData').start();
const directory = this.getDirectory();
try
{
await this._furnitureDataDownloader.download(async (content: string) =>
{
spinner.text = 'Parsing FurnitureData';
spinner.render();
let furnitureDataString = content;
if(!furnitureDataString.startsWith('{'))
{
const xml = await parseStringPromise(furnitureDataString);
const furnitureData = await this.mapXML2JSON(xml);
furnitureDataString = JSON.stringify(furnitureData);
}
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`);
}
catch (error)
{
spinner.fail('FurnitureData 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 + '/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<IFurnitureData>
{
if(!xml) return null;
const output: IFurnitureData = {};
FurnitureDataMapper.mapXML(xml, output);
return output;
}
}

View File

@ -0,0 +1,32 @@
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<void>): Promise<void>
{
const furnitureData = await this.parseFurnitureData();
if(!furnitureData) throw new Error('invalid_furniture_data');
callback(furnitureData);
}
public async parseFurnitureData(): Promise<string>
{
const url = this._configuration.getValue('furnidata.load.url');
if(!url || !url.length) return null;
const content = await FileUtilities.readFileAsString(url);
if(!content || !content.length) return null;
return content;
}
}

View File

@ -7,7 +7,7 @@ import { IAssetData } from '../../mapping/json';
import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers'; import { AssetMapper, IndexMapper, LogicMapper, VisualizationMapper } from '../../mapping/mappers';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import File from '../../utils/File'; import File from '../../utils/File';
import Logger from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import { PetDownloader } from './PetDownloader'; import { PetDownloader } from './PetDownloader';
@singleton() @singleton()
@ -28,9 +28,7 @@ export class PetConverter extends SWFConverter
const spinner = ora('Preparing Pets').start(); const spinner = ora('Preparing Pets').start();
const outputFolder = new File(this._configuration.getValue('output.folder.pet')); const directory = this.getDirectory();
if(!outputFolder.isDirectory()) outputFolder.mkdirs();
try try
{ {
@ -43,7 +41,7 @@ export class PetConverter extends SWFConverter
const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf); const spriteBundle = await this._bundleProvider.generateSpriteSheet(habboAssetSwf);
const assetData = await this.mapXML2JSON(habboAssetSwf, 'pet'); const assetData = await this.mapXML2JSON(habboAssetSwf, 'pet');
await this.fromHabboAsset(habboAssetSwf, outputFolder.path, 'pet', assetData, spriteBundle); await this.fromHabboAsset(habboAssetSwf, directory.path, 'pet', assetData, spriteBundle);
}); });
spinner.succeed(`Pets finished in ${ Date.now() - now }ms`); spinner.succeed(`Pets finished in ${ Date.now() - now }ms`);
@ -55,35 +53,48 @@ export class PetConverter extends SWFConverter
} }
} }
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<IAssetData> private async mapXML2JSON(habboAssetSWF: HabboAssetSWF, assetType: string): Promise<IAssetData>
{ {
if(!habboAssetSWF) return null; if(!habboAssetSWF) return null;
const assetData: IAssetData = {}; const output: IAssetData = {};
assetData.type = assetType; output.type = assetType;
const indexXML = await PetConverter.getIndexXML(habboAssetSWF); const indexXML = await PetConverter.getIndexXML(habboAssetSWF);
if(indexXML) IndexMapper.mapXML(indexXML, assetData); if(indexXML) IndexMapper.mapXML(indexXML, output);
const assetXML = await PetConverter.getAssetsXML(habboAssetSWF); const assetXML = await PetConverter.getAssetsXML(habboAssetSWF);
if(assetXML) if(assetXML)
{ {
AssetMapper.mapXML(assetXML, assetData); AssetMapper.mapXML(assetXML, output);
if(assetData.palettes !== undefined) if(output.palettes !== undefined)
{ {
for(const paletteId in assetData.palettes) for(const paletteId in output.palettes)
{ {
const palette = assetData.palettes[paletteId]; const palette = output.palettes[paletteId];
const paletteColors = PetConverter.getPalette(habboAssetSWF, palette.source); const paletteColors = PetConverter.getPalette(habboAssetSWF, palette.source);
if(!paletteColors) if(!paletteColors)
{ {
delete assetData.palettes[paletteId]; delete output.palettes[paletteId];
continue; continue;
} }
@ -99,12 +110,12 @@ export class PetConverter extends SWFConverter
const logicXML = await PetConverter.getLogicXML(habboAssetSWF); const logicXML = await PetConverter.getLogicXML(habboAssetSWF);
if(logicXML) LogicMapper.mapXML(logicXML, assetData); if(logicXML) LogicMapper.mapXML(logicXML, output);
const visualizationXML = await PetConverter.getVisualizationXML(habboAssetSWF); const visualizationXML = await PetConverter.getVisualizationXML(habboAssetSWF);
if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, assetData); if(visualizationXML) VisualizationMapper.mapXML(visualizationXML, output);
return assetData; return output;
} }
} }

View File

@ -2,13 +2,13 @@ import { singleton } from 'tsyringe';
import { Configuration } from '../../common/config/Configuration'; import { Configuration } from '../../common/config/Configuration';
import { HabboAssetSWF } from '../../swf/HabboAssetSWF'; import { HabboAssetSWF } from '../../swf/HabboAssetSWF';
import { FileUtilities } from '../../utils/FileUtilities'; import { FileUtilities } from '../../utils/FileUtilities';
import Logger from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
@singleton() @singleton()
export class PetDownloader export class PetDownloader
{ {
constructor( constructor(
private readonly _config: Configuration, private readonly _configuration: Configuration,
private readonly _logger: Logger) private readonly _logger: Logger)
{} {}
@ -34,18 +34,18 @@ export class PetDownloader
catch (error) catch (error)
{ {
console.log(); console.log();
console.error(error); console.error(`Error parsing ${ petType }: ` + error.message);
this._logger.logError(`Error parsing ${ petType }: ` + error.message);
} }
} }
} }
public async parsePetTypes(): Promise<string[]> public async parsePetTypes(): Promise<string[]>
{ {
await this._config.loadExternalVariables();
const petTypes: string[] = []; const petTypes: string[] = [];
const pets = this._config.getValue('pet.configuration'); const pets = this._configuration.getValue('pet.configuration');
if(pets) if(pets)
{ {
@ -59,7 +59,7 @@ export class PetDownloader
public async extractPet(className: string, callback: (habboAssetSwf: HabboAssetSWF) => Promise<void>): Promise<void> public async extractPet(className: string, callback: (habboAssetSwf: HabboAssetSWF) => Promise<void>): Promise<void>
{ {
let url = this._config.getValue('dynamic.download.url.pet'); let url = this._configuration.getValue('dynamic.download.pet.url');
if(!url || !url.length) return; if(!url || !url.length) return;

View File

@ -0,0 +1,118 @@
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(): Promise<void>
{
const now = Date.now();
const spinner = ora('Preparing ProductData').start();
const directory = this.getDirectory();
try
{
await 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`);
}
catch (error)
{
spinner.fail('ProductData 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 + '/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<IProductData>
{
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;
}
}

View File

@ -0,0 +1,32 @@
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<void>): Promise<void>
{
const productData = await this.parseProductData();
if(!productData) throw new Error('invalid_product_data');
callback(productData);
}
public async parseProductData(): Promise<string>
{
const url = this._configuration.getValue('productdata.load.url');
if(!url || !url.length) return null;
const content = await FileUtilities.readFileAsString(url);
if(!content || !content.length) return null;
return content;
}
}

View File

@ -2,5 +2,5 @@ import { IEffectMapLibrary } from './IEffectMapLibrary';
export interface IEffectMap export interface IEffectMap
{ {
effects: IEffectMapLibrary[]; effects?: IEffectMapLibrary[];
} }

View File

@ -1,7 +1,7 @@
export interface IEffectMapLibrary export interface IEffectMapLibrary
{ {
id: string; id?: string;
lib: string; lib?: string;
type: string; type?: string;
revision: number; revision?: number;
} }

View File

@ -2,5 +2,5 @@ import { IFigureMapLibrary } from './IFigureMapLibrary';
export interface IFigureMap export interface IFigureMap
{ {
libraries: IFigureMapLibrary[]; libraries?: IFigureMapLibrary[];
} }

View File

@ -2,7 +2,7 @@ import { IFigureMapLibraryPart } from './IFigureMapLibraryPart';
export interface IFigureMapLibrary export interface IFigureMapLibrary
{ {
id: string; id?: string;
revision: number; revision?: number;
parts: IFigureMapLibraryPart[]; parts?: IFigureMapLibraryPart[];
} }

View File

@ -1,5 +1,5 @@
export interface IFigureMapLibraryPart export interface IFigureMapLibraryPart
{ {
id: number; id?: number;
type: string; type?: string;
} }

View File

@ -1,28 +0,0 @@
export interface IFurnitureType
{
id: number;
classname: string;
revision: number;
category: string;
defaultdir: number;
xdim: number;
ydim: number;
partcolors: { color: string[] };
name: string;
description: string;
adurl: string;
offerid: number;
buyout: boolean;
rentofferid: number;
rentbuyout: boolean;
bc: boolean;
excludeddynamic: boolean;
customparams: string;
specialtype: number;
canstandon: boolean;
cansiton: boolean;
canlayon: boolean;
furniline: string;
environment: string;
rare: boolean;
}

View File

@ -2,10 +2,10 @@ import { IFurnitureType } from './IFurnitureType';
export class IFurnitureData export class IFurnitureData
{ {
roomitemtypes: { roomitemtypes?: {
furnitype: IFurnitureType[] furnitype: IFurnitureType[]
}; };
wallitemtypes: { wallitemtypes?: {
furnitype: IFurnitureType[] furnitype: IFurnitureType[]
}; };
} }

View File

@ -0,0 +1,28 @@
export interface IFurnitureType
{
id?: number;
classname?: string;
revision?: number;
category?: string;
defaultdir?: number;
xdim?: number;
ydim?: number;
partcolors?: { color: string[] };
name?: string;
description?: string;
adurl?: string;
offerid?: number;
buyout?: boolean;
rentofferid?: number;
rentbuyout?: boolean;
bc?: boolean;
excludeddynamic?: boolean;
customparams?: string;
specialtype?: number;
canstandon?: boolean;
cansiton?: boolean;
canlayon?: boolean;
furniline?: string;
environment?: string;
rare?: boolean;
}

View File

@ -1,4 +1,5 @@
export * from './asset'; export * from './asset';
export * from './effectmap'; export * from './effectmap';
export * from './figuremap'; export * from './figuremap';
export * from './furnidata'; export * from './furnituredata';
export * from './productdata';

View File

@ -0,0 +1,8 @@
import { IProductType } from './IProductType';
export interface IProductData
{
productdata?: {
product: IProductType[]
};
}

View File

@ -0,0 +1,6 @@
export interface IProductType
{
code: string;
name: string;
description: string;
}

View File

@ -0,0 +1,2 @@
export * from './IProductData';
export * from './IProductType';

View File

@ -0,0 +1,45 @@
import { IEffectMap, IEffectMapLibrary } from '../json';
import { EffectMapEffectXML, EffectMapXML } from '../xml';
import { Mapper } from './asset/Mapper';
export class EffectMapMapper extends Mapper
{
public static mapXML(xml: any, output: IEffectMap): void
{
if(!xml || !output) return;
if(xml.map !== undefined) EffectMapMapper.mapEffectMapXML(new EffectMapXML(xml.map), output);
}
private static mapEffectMapXML(xml: EffectMapXML, output: IEffectMap): void
{
if(!xml || !output) return;
if(xml.effects !== undefined)
{
if(xml.effects.length)
{
output.effects = [];
EffectMapMapper.mapEffectMapLibrariesXML(xml.effects, output.effects);
}
}
}
private static mapEffectMapLibrariesXML(xml: EffectMapEffectXML[], output: IEffectMapLibrary[]): void
{
if(!xml || !xml.length || !output) return;
for(const libraryXML of xml)
{
const library: IEffectMapLibrary = {};
if(libraryXML.id !== undefined) library.id = libraryXML.id;
if(libraryXML.lib !== undefined) library.lib = libraryXML.lib;
if(libraryXML.type !== undefined) library.type = libraryXML.type;
if(libraryXML.revision !== undefined) library.revision = libraryXML.revision;
output.push(library);
}
}
}

View File

@ -0,0 +1,68 @@
import { IFigureMap, IFigureMapLibrary, IFigureMapLibraryPart } from '../json';
import { FigureLibraryPartXML, FigureLibraryXML, FigureMapXML } from '../xml';
import { Mapper } from './asset/Mapper';
export class FigureMapMapper extends Mapper
{
public static mapXML(xml: any, output: IFigureMap): void
{
if(!xml || !output) return;
if(xml.map !== undefined) FigureMapMapper.mapFigureMapXML(new FigureMapXML(xml.map), output);
}
private static mapFigureMapXML(xml: FigureMapXML, output: IFigureMap): void
{
if(!xml || !output) return;
if(xml.libraries !== undefined)
{
if(xml.libraries.length)
{
output.libraries = [];
FigureMapMapper.mapFigureMapLibrariesXML(xml.libraries, output.libraries);
}
}
}
private static mapFigureMapLibrariesXML(xml: FigureLibraryXML[], output: IFigureMapLibrary[]): void
{
if(!xml || !xml.length || !output) return;
for(const libraryXML of xml)
{
const library: IFigureMapLibrary = {};
if(libraryXML.id !== undefined) library.id = libraryXML.id;
if(libraryXML.revision !== undefined) library.revision = libraryXML.revision;
if(libraryXML.parts !== undefined)
{
if(libraryXML.parts.length)
{
library.parts = [];
FigureMapMapper.mapFigureMapLibraryPartsXML(libraryXML.parts, library.parts);
}
}
output.push(library);
}
}
private static mapFigureMapLibraryPartsXML(xml: FigureLibraryPartXML[], output: IFigureMapLibraryPart[]): void
{
if(!xml || !xml.length || !output) return;
for(const libraryPartXML of xml)
{
const libraryPart: IFigureMapLibraryPart = {};
if(libraryPartXML.id !== undefined) libraryPart.id = libraryPartXML.id;
if(libraryPartXML.type !== undefined) libraryPart.type = libraryPartXML.type;
output.push(libraryPart);
}
}
}

View File

@ -0,0 +1,90 @@
import { IFurnitureData, IFurnitureType } from '../json';
import { FurnitureDataXML, FurnitureTypeXML } from '../xml';
import { Mapper } from './asset/Mapper';
export class FurnitureDataMapper extends Mapper
{
public static mapXML(xml: any, output: IFurnitureData): void
{
if(!xml || !output) return;
if(xml.furnidata !== undefined) FurnitureDataMapper.mapFurnitureDataXML(new FurnitureDataXML(xml.furnidata), output);
}
private static mapFurnitureDataXML(xml: FurnitureDataXML, output: IFurnitureData): void
{
if(!xml || !output) return;
if(xml.floorItems !== undefined)
{
if(xml.floorItems.length)
{
output.roomitemtypes = {
furnitype: []
};
FurnitureDataMapper.mapFurnitureTypesXML(xml.floorItems, output.roomitemtypes.furnitype, 'floor');
}
}
if(xml.wallItems !== undefined)
{
if(xml.wallItems.length)
{
output.wallitemtypes = {
furnitype: []
};
FurnitureDataMapper.mapFurnitureTypesXML(xml.wallItems, output.wallitemtypes.furnitype, 'wall');
}
}
}
private static mapFurnitureTypesXML(xml: FurnitureTypeXML[], output: IFurnitureType[], type: string): void
{
if(!xml || !xml.length || !output) return;
for(const typeXML of xml)
{
const furnitureType: IFurnitureType = {};
furnitureType.id = typeXML.id;
furnitureType.classname = typeXML.classname;
furnitureType.revision = typeXML.revision;
furnitureType.category = typeXML.category;
if(type === 'floor')
{
furnitureType.defaultdir = typeXML.defaultdir;
furnitureType.xdim = typeXML.xdim;
furnitureType.ydim = typeXML.ydim;
furnitureType.partcolors = typeXML.partcolors;
}
furnitureType.name = typeXML.name;
furnitureType.description = typeXML.description;
furnitureType.adurl = typeXML.adurl;
furnitureType.offerid = typeXML.offerid;
furnitureType.buyout = typeXML.buyout;
furnitureType.rentofferid = typeXML.rentofferid;
furnitureType.rentbuyout = typeXML.rentbuyout;
furnitureType.bc = typeXML.bc;
furnitureType.excludeddynamic = typeXML.excludeddynamic;
furnitureType.customparams = typeXML.customparams;
furnitureType.specialtype = typeXML.specialtype;
if(type === 'floor')
{
furnitureType.canstandon = typeXML.canstandon;
furnitureType.cansiton = typeXML.cansiton;
furnitureType.canlayon = typeXML.canlayon;
}
furnitureType.furniline = typeXML.furniline;
furnitureType.environment = typeXML.environment;
furnitureType.rare = typeXML.rare;
output.push(furnitureType);
}
}
}

View File

@ -1 +1,4 @@
export * from './asset'; export * from './asset';
export * from './EffectMapMapper';
export * from './FigureMapMapper';
export * from './FurnitureDataMapper';

View File

@ -1,26 +0,0 @@
export class EffectLibraryPartXML
{
private _id: number;
private _type: string;
constructor(xml: any)
{
const attributes = xml.$;
if(attributes)
{
if(attributes.id !== undefined) this._id = parseInt(attributes.id);
if(attributes.type !== undefined) this._type = attributes.type;
}
}
public get id(): number
{
return this._id;
}
public get type(): string
{
return this._type;
}
}

View File

@ -1,46 +0,0 @@
import { EffectLibraryPartXML } from './EffectLibraryPartXML';
export class EffectLibraryXML
{
private _id: string;
private _revision: number;
private _parts: EffectLibraryPartXML[];
constructor(xml: any)
{
const attributes = xml.$;
if(attributes)
{
if(attributes.id !== undefined) this._id = attributes.id;
if(attributes.revision !== undefined) this._revision = parseInt(attributes.revision);
}
if(xml.part !== undefined)
{
this._parts = [];
for(const partId in xml.part)
{
const part = xml.part[partId];
this._parts.push(new EffectLibraryPartXML(part));
}
}
}
public get id(): string
{
return this._id;
}
public get revision(): number
{
return this._revision;
}
public get parts(): EffectLibraryPartXML[]
{
return this._parts;
}
}

View File

@ -0,0 +1,41 @@
export class EffectMapEffectXML
{
private _id: string;
private _lib: string;
private _type: string;
private _revision: number;
constructor(xml: any)
{
const attributes = xml.$;
if(attributes)
{
if(attributes.id !== undefined) this._id = attributes.id;
if(attributes.lib !== undefined) this._lib = attributes.lib;
if(attributes.type !== undefined) this._type = attributes.type;
if(attributes.revision !== undefined) this._revision = parseInt(attributes.revision);
}
}
public get id(): string
{
return this._id;
}
public get lib(): string
{
return this._lib;
}
public get type(): string
{
return this._type;
}
public get revision(): number
{
return this._revision;
}
}

View File

@ -1,29 +1,24 @@
import { EffectLibraryXML } from './EffectLibraryXML'; import { EffectMapEffectXML } from './EffectMapEffectXML';
export class EffectMapXML export class EffectMapXML
{ {
private _librares: EffectLibraryXML[]; private _effect: EffectMapEffectXML[];
constructor(xml: any) constructor(xml: any)
{ {
if(xml.map !== undefined) if(xml.effect !== undefined)
{ {
if(xml.map.lib !== undefined) if(Array.isArray(xml.effect))
{ {
this._librares = []; this._effect = [];
for(const lib in xml.map.lib) for(const library of xml.effect) this._effect.push(new EffectMapEffectXML(library));
{
const library = xml.map.lib[lib];
this._librares.push(new EffectLibraryXML(library));
}
} }
} }
} }
public get libraries(): EffectLibraryXML[] public get effects(): EffectMapEffectXML[]
{ {
return this._librares; return this._effect;
} }
} }

View File

@ -1,3 +1,2 @@
export * from './EffectLibraryPartXML'; export * from './EffectMapEffectXML';
export * from './EffectLibraryXML';
export * from './EffectMapXML'; export * from './EffectMapXML';

View File

@ -18,13 +18,16 @@ export class FigureLibraryXML
if(xml.part !== undefined) if(xml.part !== undefined)
{ {
this._parts = []; if(Array.isArray(xml.part))
for(const partId in xml.part)
{ {
const part = xml.part[partId]; this._parts = [];
this._parts.push(new FigureLibraryPartXML(part)); for(const partId in xml.part)
{
const part = xml.part[partId];
this._parts.push(new FigureLibraryPartXML(part));
}
} }
} }
} }

View File

@ -6,15 +6,15 @@ export class FigureMapXML
constructor(xml: any) constructor(xml: any)
{ {
if(xml.map !== undefined) if(xml.lib !== undefined)
{ {
if(xml.map.lib !== undefined) if(Array.isArray(xml.lib))
{ {
this._librares = []; this._librares = [];
for(const lib in xml.map.lib) for(const lib in xml.lib)
{ {
const library = xml.map.lib[lib]; const library = xml.lib[lib];
this._librares.push(new FigureLibraryXML(library)); this._librares.push(new FigureLibraryXML(library));
} }

View File

@ -13,13 +13,13 @@ export class FurnitureDataXML
{ {
this._floorItems = []; this._floorItems = [];
for(const roomitemtype in xml.roomitemtypes) for(const roomitemtype of xml.roomitemtypes)
{ {
const furniTypes = xml.roomitemtypes[roomitemtype]; const furniTypes = roomitemtype.furnitype;
if(furniTypes !== undefined) if(furniTypes !== undefined)
{ {
for(const furniType in furniTypes) this._floorItems.push(new FurnitureTypeXML('floor', furniType)); if(Array.isArray(furniTypes)) for(const furniType of furniTypes) this._floorItems.push(new FurnitureTypeXML('floor', furniType));
} }
} }
} }
@ -28,13 +28,13 @@ export class FurnitureDataXML
{ {
this._wallItems = []; this._wallItems = [];
for(const wallitemtype in xml.wallitemtypes) for(const wallitemtype of xml.wallitemtypes)
{ {
const furniTypes = xml.wallitemtypes[wallitemtype]; const furniTypes = wallitemtype.furnitype;
if(furniTypes !== undefined) if(furniTypes !== undefined)
{ {
for(const furniType in furniTypes) this._wallItems.push(new FurnitureTypeXML('wall', furniType)); if(Array.isArray(furniTypes)) for(const furniType in furniTypes) this._wallItems.push(new FurnitureTypeXML('wall', furniType));
} }
} }
} }

View File

@ -1,4 +1,4 @@
export * from './asset'; export * from './asset';
export * from './effectmap'; export * from './effectmap';
export * from './figuremap'; export * from './figuremap';
export * from './furnidata'; export * from './furnituredata';

View File

@ -12,6 +12,8 @@ export class FileUtilities
let content: Buffer = null; let content: Buffer = null;
if(url.startsWith('//')) url = ('https:' + url);
if(url.startsWith('http')) if(url.startsWith('http'))
{ {
const data = await fetch.default(url); const data = await fetch.default(url);
@ -33,6 +35,8 @@ export class FileUtilities
let content = null; let content = null;
if(url.startsWith('//')) url = ('https:' + url);
if(url.startsWith('http')) if(url.startsWith('http'))
{ {
const data = await fetch.default(url); const data = await fetch.default(url);

View File

@ -1,22 +1,19 @@
import { createWriteStream, existsSync } from 'fs'; import { WriteStream } from 'fs';
import { appendFile } from 'fs/promises';
import { singleton } from 'tsyringe'; import { singleton } from 'tsyringe';
@singleton() @singleton()
export default class Logger export class Logger
{ {
private _fileName: string = `error-${ Date.now() }.log`;
private _writeStream: WriteStream = null;
constructor() constructor()
{ {
if(!existsSync('error.log'))
{
const createStream = createWriteStream('error.log');
createStream.end();
}
} }
public logErrorAsync(message: string): Promise<void> public logError(message: string): void
{ {
return appendFile('error.log', message + '\n'); //
} }
} }