mirror of
https://github.com/billsonnn/nitro-converter.git
synced 2024-11-22 15:40:52 +01:00
Merge branch 'dev' into 'master'
Dev See merge request nitro/nitro-converter!8
This commit is contained in:
commit
f7f78d6fae
26
src/Main.ts
26
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;
|
||||
|
59
src/common/SWFDownloader.ts
Normal file
59
src/common/SWFDownloader.ts
Normal file
@ -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<HabboAssetSWF>
|
||||
{
|
||||
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(`<Downloader> 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<HabboAssetSWF>
|
||||
{
|
||||
if(SWFDownloader.LOG_DOWNLOADS)
|
||||
{
|
||||
console.log();
|
||||
console.log(`<Downloader> Downloading ${ className } from ${url}`);
|
||||
}
|
||||
|
||||
const habboAssetSWF = await this.extractSWF(url);
|
||||
|
||||
if(!habboAssetSWF) return null;
|
||||
|
||||
return habboAssetSWF;
|
||||
}
|
||||
|
||||
public static async extractSWF(url: string): Promise<HabboAssetSWF>
|
||||
{
|
||||
const buffer = await FileUtilities.readFileAsBuffer(url);
|
||||
|
||||
if(!buffer) return null;
|
||||
|
||||
const habboAssetSWF = new HabboAssetSWF(buffer);
|
||||
|
||||
await habboAssetSWF.setupAsync();
|
||||
|
||||
return habboAssetSWF;
|
||||
}
|
||||
}
|
@ -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<string, string> = new Map();
|
||||
|
||||
public async generateSpriteSheet(habboAssetSWF: HabboAssetSWF): Promise<SpriteBundle>
|
||||
public static async generateSpriteSheet(habboAssetSWF: HabboAssetSWF, convertCase: boolean = false): Promise<SpriteBundle>
|
||||
{
|
||||
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<SpriteBundle>
|
||||
private static async packImages(documentClass: string, imageBundle: ImageBundle, convertCase: boolean = false): Promise<SpriteBundle>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<void>
|
||||
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<any>
|
||||
public static async getManifestXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
public static async getIndexXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
public static async getAssetsXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
public static async getLogicXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
public static async getVisualizationXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<any>
|
||||
public static async getAnimationXML(habboAssetSWF: HabboAssetSWF, snakeCase: boolean = false): Promise<any>
|
||||
{
|
||||
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<IAssetData>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
107
src/converters/EffectConverter.ts
Normal file
107
src/converters/EffectConverter.ts
Normal file
@ -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<string, string> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly _effectMapConverter: EffectMapConverter,
|
||||
private readonly _configuration: Configuration)
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public async convertAsync(args: string[] = []): Promise<void>
|
||||
{
|
||||
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`);
|
||||
}
|
||||
}
|
58
src/converters/EffectMapConverter.ts
Normal file
58
src/converters/EffectMapConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<IEffectMap>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IEffectMap = {};
|
||||
|
||||
EffectMapMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
61
src/converters/ExternalTextsConverter.ts
Normal file
61
src/converters/ExternalTextsConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<IExternalTexts>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
105
src/converters/FigureConverter.ts
Normal file
105
src/converters/FigureConverter.ts
Normal file
@ -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<string, string> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly _figureMapConverter: FigureMapConverter,
|
||||
private readonly _configuration: Configuration)
|
||||
{}
|
||||
|
||||
public async convertAsync(args: string[] = []): Promise<void>
|
||||
{
|
||||
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`);
|
||||
}
|
||||
}
|
58
src/converters/FigureDataConverter.ts
Normal file
58
src/converters/FigureDataConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<IFigureData>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFigureData = {};
|
||||
|
||||
FigureDataMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
60
src/converters/FigureMapConverter.ts
Normal file
60
src/converters/FigureMapConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<IFigureMap>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFigureMap = {};
|
||||
|
||||
FigureMapMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
121
src/converters/FurnitureConverter.ts
Normal file
121
src/converters/FurnitureConverter.ts
Normal file
@ -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<string, IAssetData> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly _furniDataConverter: FurnitureDataConverter,
|
||||
private readonly _configuration: Configuration)
|
||||
{}
|
||||
|
||||
public async convertAsync(args: string[] = []): Promise<void>
|
||||
{
|
||||
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`);
|
||||
}
|
||||
}
|
59
src/converters/FurnitureDataConverter.ts
Normal file
59
src/converters/FurnitureDataConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<IFurnitureData>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFurnitureData = {};
|
||||
|
||||
FurnitureDataMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
50
src/converters/OldAssetConverter.ts
Normal file
50
src/converters/OldAssetConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
src/converters/PetConverter.ts
Normal file
98
src/converters/PetConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
86
src/converters/ProductDataConverter.ts
Normal file
86
src/converters/ProductDataConverter.ts
Normal file
@ -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<void>
|
||||
{
|
||||
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<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;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IAssetData>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<string, string> = 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<void>): Promise<void>
|
||||
{
|
||||
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<IEffectMap>
|
||||
{
|
||||
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(`<Downloader> 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<void>): Promise<void>
|
||||
{
|
||||
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(`<Downloader> 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);
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IEffectMap>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IEffectMap = {};
|
||||
|
||||
EffectMapMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
@ -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<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 logDownloads = this._configuration.getBoolean('misc.log_download_urls');
|
||||
|
||||
if(logDownloads) console.log(`<Downloader> Downloading effect map from ${url}`);
|
||||
|
||||
const content = await FileUtilities.readFileAsString(url);
|
||||
|
||||
if(!content || !content.length) return null;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IExternalTexts>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<void>): Promise<void>
|
||||
{
|
||||
const productData = await this.parseExternalTexts();
|
||||
|
||||
if(!productData) throw new Error('invalid_external_texts');
|
||||
|
||||
callback(productData);
|
||||
}
|
||||
|
||||
public async parseExternalTexts(): Promise<string>
|
||||
{
|
||||
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(`<Downloader> Downloading external texts from ${url}`);
|
||||
|
||||
const content = await FileUtilities.readFileAsString(url);
|
||||
|
||||
if(!content || !content.length) return null;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IAssetData>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<string, string> = 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<void>): Promise<void>
|
||||
{
|
||||
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<IFigureMap>
|
||||
{
|
||||
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(`<Downloader> 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<void>): Promise<void>
|
||||
{
|
||||
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(`<Downloader> 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);
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IFigureData>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFigureData = {};
|
||||
|
||||
FigureDataMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
@ -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<void>): Promise<void>
|
||||
{
|
||||
const figureData = await this.parseFigureData();
|
||||
|
||||
if(!figureData) throw new Error('invalid_figure_data');
|
||||
|
||||
callback(figureData);
|
||||
}
|
||||
|
||||
public async parseFigureData(): Promise<string>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IFigureMap>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFigureMap = {};
|
||||
|
||||
FigureMapMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
@ -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<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 logDownloads = this._configuration.getBoolean('misc.log_download_urls');
|
||||
|
||||
if(logDownloads) console.log(`<Downloader> Downloading figure map from ${url}`);
|
||||
|
||||
const content = await FileUtilities.readFileAsString(url);
|
||||
|
||||
if(!content || !content.length) return null;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@ -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<string, IAssetData> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly _furniDownloader: FurnitureDownloader,
|
||||
private readonly _configuration: Configuration,
|
||||
private readonly _bundleProvider: BundleProvider,
|
||||
private readonly _logger: Logger)
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public async convertAsync(): Promise<void>
|
||||
{
|
||||
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<IAssetData>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<void>): Promise<void>
|
||||
{
|
||||
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<IFurnitureData>
|
||||
{
|
||||
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(`<Downloader> 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<void>): Promise<void>
|
||||
{
|
||||
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(`<Downloader> 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;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IFurnitureData>
|
||||
{
|
||||
if(!xml) return null;
|
||||
|
||||
const output: IFurnitureData = {};
|
||||
|
||||
FurnitureDataMapper.mapXML(xml, output);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
@ -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<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 logDownloads = this._configuration.getBoolean('misc.log_download_urls');
|
||||
|
||||
if(logDownloads) console.log(`<Downloader> Downloading furniture data from ${url}`);
|
||||
|
||||
const content = await FileUtilities.readFileAsString(url);
|
||||
|
||||
if(!content || !content.length) return null;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<IAssetData>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<void>): Promise<void>
|
||||
{
|
||||
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<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;
|
||||
}
|
||||
|
||||
public async extractPet(className: string, callback: (habboAssetSwf: HabboAssetSWF, className: string) => Promise<void>): Promise<void>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
@ -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<void>
|
||||
{
|
||||
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<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;
|
||||
}
|
||||
}
|
@ -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<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 logDownloads = this._configuration.getBoolean('misc.log_download_urls');
|
||||
|
||||
if(logDownloads) console.log(`<Downloader> Downloading product data from ${url}`);
|
||||
|
||||
const content = await FileUtilities.readFileAsString(url);
|
||||
|
||||
if(!content || !content.length) return null;
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
@ -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 };
|
||||
|
@ -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';
|
||||
|
4
src/mapping/json/asset/logic/IAssetLogicCustomVars.ts
Normal file
4
src/mapping/json/asset/logic/IAssetLogicCustomVars.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface ICustomVars
|
||||
{
|
||||
variables?: string[];
|
||||
}
|
17
src/mapping/json/asset/logic/IAssetLogicData.ts
Normal file
17
src/mapping/json/asset/logic/IAssetLogicData.ts
Normal file
@ -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;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export interface IPlanetSystem
|
||||
export interface IAssetLogicPlanetSystem
|
||||
{
|
||||
id?: number;
|
||||
name?: string;
|
6
src/mapping/json/asset/logic/index.ts
Normal file
6
src/mapping/json/asset/logic/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './IAssetLogicCustomVars';
|
||||
export * from './IAssetLogicData';
|
||||
export * from './IAssetLogicPlanetSystem';
|
||||
export * from './ISoundSample';
|
||||
export * from './model';
|
||||
export * from './particlesystem';
|
7
src/mapping/json/asset/logic/model/IAssetLogicModel.ts
Normal file
7
src/mapping/json/asset/logic/model/IAssetLogicModel.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { IAssetDimension } from './IAssetDimension';
|
||||
|
||||
export interface IAssetLogicModel
|
||||
{
|
||||
dimensions?: IAssetDimension;
|
||||
directions?: number[];
|
||||
}
|
2
src/mapping/json/asset/logic/model/index.ts
Normal file
2
src/mapping/json/asset/logic/model/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './IAssetDimension';
|
||||
export * from './IAssetLogicModel';
|
@ -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[];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,5 @@ export * from './AssetMapper';
|
||||
export * from './IndexMapper';
|
||||
export * from './LogicMapper';
|
||||
export * from './ManifestMapper';
|
||||
export * from './Mapper';
|
||||
export * from './VisualizationMapper';
|
||||
|
29
src/mapping/xml/asset/logic/CustomVarsXML.ts
Normal file
29
src/mapping/xml/asset/logic/CustomVarsXML.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ export class SoundSampleXML
|
||||
|
||||
constructor(xml: any)
|
||||
{
|
||||
const attributes = xml[0].$;
|
||||
const attributes = xml.$;
|
||||
|
||||
if(attributes !== undefined)
|
||||
{
|
||||
|
@ -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';
|
||||
|
@ -1,4 +1,4 @@
|
||||
export class DimensionsXML
|
||||
export class ModelDimensionsXML
|
||||
{
|
||||
private readonly _x: number;
|
||||
private readonly _y: number;
|
@ -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;
|
||||
}
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from './DimensionsXML';
|
||||
export * from './ModelDimensionsXML';
|
||||
export * from './ModelDirectionXML';
|
||||
export * from './ModelXML';
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
6
src/mapping/xml/asset/logic/particlesystem/index.ts
Normal file
6
src/mapping/xml/asset/logic/particlesystem/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './ParticleSystemEmitterXML';
|
||||
export * from './ParticleSystemObjectXML';
|
||||
export * from './ParticleSystemParticleXML';
|
||||
export * from './ParticleSystemSimulationXML';
|
||||
export * from './ParticleSystemXML';
|
||||
export * from './PlanetSystemObjectXML';
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = [];
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Buffer>
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user