mirror of
https://github.com/billsonnn/nitro-converter.git
synced 2024-11-26 17:30:52 +01:00
Effects are done
This commit is contained in:
parent
bb1381c6db
commit
d7cf4b5849
@ -1,5 +1,5 @@
|
||||
output.folder.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/furniture/
|
||||
output.folder.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/figure-new1/
|
||||
output.folder.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/figure/
|
||||
output.folder.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/effect/
|
||||
output.folder.pet=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/pet/
|
||||
furnidata.url=http://assets.nitro.se/game/gamedata/furnidata-entry.xml
|
||||
@ -8,7 +8,7 @@ effectmap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-8373861
|
||||
external_vars.url=http://assets.nitro.se/game/gamedata/external_variables.txt
|
||||
dynamic.download.url.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/endrit/hof_furni/%className%.swf
|
||||
dynamic.download.url.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
|
||||
dynamic.download.url.effect=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
|
||||
dynamic.download.url.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
|
||||
dynamic.download.url.pet=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
|
||||
convert.furniture=1
|
||||
convert.figure=0
|
||||
|
13
package-lock.json
generated
13
package-lock.json
generated
@ -99,6 +99,14 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
|
||||
"integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
|
||||
},
|
||||
"bytebuffer": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
|
||||
"integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
|
||||
"requires": {
|
||||
"long": "~3"
|
||||
}
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
@ -383,6 +391,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"long": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
|
||||
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
|
||||
},
|
||||
"lzma-purejs": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/lzma-purejs/-/lzma-purejs-0.9.3.tgz",
|
||||
|
@ -10,6 +10,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/node": "^14.14.22",
|
||||
"bytebuffer": "^5.0.1",
|
||||
"free-tex-packer-core": "^0.3.2",
|
||||
"lodash": "^4.17.20",
|
||||
"node-fetch": "^2.6.1",
|
||||
|
44
src/Main.ts
44
src/Main.ts
@ -6,6 +6,8 @@ import FigureConverter from "./converters/figure/FigureConverter";
|
||||
import File from "./utils/File";
|
||||
import FurnitureDownloader from "./downloaders/FurnitureDownloader";
|
||||
import FurnitureConverter from "./converters/furniture/FurnitureConverter";
|
||||
import EffectConverter from "./converters/effect/EffectConverter";
|
||||
import EffectDownloader from "./downloaders/EffectDownloader";
|
||||
|
||||
(async () => {
|
||||
const config = new Configuration();
|
||||
@ -21,9 +23,15 @@ import FurnitureConverter from "./converters/furniture/FurnitureConverter";
|
||||
outputFolderFurniture.mkdirs();
|
||||
}
|
||||
|
||||
const outputFolderEffect = new File(config.getValue("output.folder.effect"));
|
||||
if (!outputFolderEffect.isDirectory()) {
|
||||
outputFolderEffect.mkdirs();
|
||||
}
|
||||
|
||||
const spriteSheetConverter = new SpriteSheetConverter();
|
||||
const figureConverter = new FigureConverter(config);
|
||||
const furnitureConverter= new FurnitureConverter(config);
|
||||
const furnitureConverter = new FurnitureConverter(config);
|
||||
const effectConverter = new EffectConverter(config);
|
||||
|
||||
if (config.getBoolean("convert.figure")) {
|
||||
const figureDownloader = new FigureDownloader(config);
|
||||
@ -38,29 +46,21 @@ import FurnitureConverter from "./converters/furniture/FurnitureConverter";
|
||||
|
||||
} catch (e) {
|
||||
console.log("Figure error: " + habboAssetSwf.getDocumentClass());
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let count = 0;
|
||||
|
||||
if (config.getBoolean("convert.furniture")) {
|
||||
let count = 0;
|
||||
const furnitureDownloader = new FurnitureDownloader(config);
|
||||
await furnitureDownloader.download(async function (habboAssetSwf: HabboAssetSWF, className: string) {
|
||||
console.log("Attempt parsing furniture: " + habboAssetSwf.getDocumentClass());
|
||||
|
||||
try {
|
||||
const assetOuputFolder = new File(outputFolderFurniture.path + "/" + className);
|
||||
if (!assetOuputFolder.isDirectory()) {
|
||||
assetOuputFolder.mkdirs();
|
||||
} else if (assetOuputFolder.list().length > 0) {
|
||||
console.log("Furniture already exists or the directory is not empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
const spriteSheetType = await spriteSheetConverter.generateSpriteSheet(habboAssetSwf, assetOuputFolder.path, "furniture");
|
||||
const spriteSheetType = await spriteSheetConverter.generateSpriteSheet(habboAssetSwf, outputFolderFurniture.path, "furniture");
|
||||
if (spriteSheetType !== null) {
|
||||
await furnitureConverter.fromHabboAsset(habboAssetSwf, assetOuputFolder.path, "furniture", spriteSheetType);
|
||||
await furnitureConverter.fromHabboAsset(habboAssetSwf, outputFolderFurniture.path, "furniture", spriteSheetType);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Furniture error: " + habboAssetSwf.getDocumentClass());
|
||||
@ -71,10 +71,26 @@ import FurnitureConverter from "./converters/furniture/FurnitureConverter";
|
||||
console.log(`Parsed ${++count} furnitures`)
|
||||
}
|
||||
|
||||
if (config.getBoolean("convert.effect")) {
|
||||
const effectDownloader = new EffectDownloader(config);
|
||||
await effectDownloader.download(async function (habboAssetSwf: HabboAssetSWF) {
|
||||
console.log("Attempt parsing figure: " + habboAssetSwf.getDocumentClass());
|
||||
|
||||
try {
|
||||
const spriteSheetType = await spriteSheetConverter.generateSpriteSheet(habboAssetSwf, outputFolderFurniture.path, "effect");
|
||||
if (spriteSheetType !== null) {
|
||||
await effectConverter.fromHabboAsset(habboAssetSwf, outputFolderEffect.path, "effect", spriteSheetType);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.log("Effect error: "+ habboAssetSwf.getDocumentClass());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log('finished!');
|
||||
|
||||
/*
|
||||
|
||||
outputFolderFurniture.rmdir({
|
||||
recursive: true,
|
||||
force: true
|
||||
|
9
src/converters/ArchiveType.ts
Normal file
9
src/converters/ArchiveType.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import {SpriteSheetType} from "./util/SpriteSheetTypes";
|
||||
|
||||
export default interface ArchiveType {
|
||||
spriteSheetType: SpriteSheetType,
|
||||
imageData: {
|
||||
name: string,
|
||||
buffer: Buffer
|
||||
}
|
||||
}
|
522
src/converters/effect/EffectAnimationXMLTypes.ts
Normal file
522
src/converters/effect/EffectAnimationXMLTypes.ts
Normal file
@ -0,0 +1,522 @@
|
||||
export class AnimationXML {
|
||||
private readonly _name: string;
|
||||
private readonly _desc: string;
|
||||
private readonly _resetOnToggle: boolean | undefined;
|
||||
|
||||
private readonly _directions: Array<DirectionOffsetXML>;
|
||||
private readonly _shadows: Array<ShadowXML>;
|
||||
private readonly _adds: Array<AddXML>;
|
||||
private readonly _removes: Array<RemoveXML>;
|
||||
private readonly _sprites: Array<SpriteXML>;
|
||||
private readonly _frames: Array<FrameXML>;
|
||||
private readonly _avatars: Array<AvatarXML>;
|
||||
private readonly _overrides: Array<OverrideXML>;
|
||||
|
||||
constructor(animationXML: any) {
|
||||
const animation = animationXML.animation;
|
||||
const attributes = animation.$;
|
||||
|
||||
this._name = attributes.name;
|
||||
this._desc = attributes.desc;
|
||||
|
||||
if (attributes.resetOnToggle !== undefined) this._resetOnToggle = attributes.resetOnToggle === '1';
|
||||
|
||||
this._directions = new Array<DirectionOffsetXML>();
|
||||
if (animation.direction !== undefined) {
|
||||
for (const direction of animation.direction) {
|
||||
this._directions.push(new DirectionOffsetXML(direction));
|
||||
}
|
||||
}
|
||||
|
||||
this._shadows = new Array<ShadowXML>();
|
||||
if (animation.shadow !== undefined) {
|
||||
for (const shadow of animation.shadow) {
|
||||
this._shadows.push(new ShadowXML(shadow));
|
||||
}
|
||||
}
|
||||
|
||||
this._adds = new Array<AddXML>();
|
||||
if (animation.add !== undefined) {
|
||||
for (const add of animation.add) {
|
||||
this._adds.push(new AddXML(add));
|
||||
}
|
||||
}
|
||||
|
||||
this._removes = new Array<RemoveXML>();
|
||||
if (animation.remove !== undefined) {
|
||||
for (const remove of animation.remove) {
|
||||
this._removes.push(new RemoveXML(remove));
|
||||
}
|
||||
}
|
||||
|
||||
this._sprites = new Array<SpriteXML>();
|
||||
if (animation.sprite !== undefined) {
|
||||
for (const sprite of animation.sprite) {
|
||||
this._sprites.push(new SpriteXML(sprite));
|
||||
}
|
||||
}
|
||||
|
||||
this._frames = new Array<FrameXML>();
|
||||
if (animation.frame !== undefined) {
|
||||
for (const frame of animation.frame) {
|
||||
this._frames.push(new FrameXML(frame));
|
||||
}
|
||||
}
|
||||
|
||||
this._avatars = new Array<AvatarXML>();
|
||||
if (animation.avatar !== undefined) {
|
||||
for (const avatar of animation.avatar) {
|
||||
this._avatars.push(new AvatarXML(avatar));
|
||||
}
|
||||
}
|
||||
|
||||
this._overrides = new Array<OverrideXML>();
|
||||
if (animation.override !== undefined) {
|
||||
for (const override of animation.override) {
|
||||
this._overrides.push(new OverrideXML(override));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get desc(): string {
|
||||
return this._desc;
|
||||
}
|
||||
|
||||
get resetOnToggle(): boolean | undefined {
|
||||
return this._resetOnToggle;
|
||||
}
|
||||
|
||||
get directions(): Array<DirectionOffsetXML> {
|
||||
return this._directions;
|
||||
}
|
||||
|
||||
get shadows(): Array<ShadowXML> {
|
||||
return this._shadows;
|
||||
}
|
||||
|
||||
get adds(): Array<AddXML> {
|
||||
return this._adds;
|
||||
}
|
||||
|
||||
get removes(): Array<RemoveXML> {
|
||||
return this._removes;
|
||||
}
|
||||
|
||||
get sprites(): Array<SpriteXML> {
|
||||
return this._sprites;
|
||||
}
|
||||
|
||||
get frames(): Array<FrameXML> {
|
||||
return this._frames;
|
||||
}
|
||||
|
||||
get avatars(): Array<AvatarXML> {
|
||||
return this._avatars;
|
||||
}
|
||||
|
||||
get overrides(): Array<OverrideXML> {
|
||||
return this._overrides;
|
||||
}
|
||||
}
|
||||
|
||||
export class OverrideXML {
|
||||
private readonly _name: string;
|
||||
private readonly _override: string;
|
||||
|
||||
private readonly _frames: Array<FrameXML>;
|
||||
|
||||
constructor(overrideXML: any) {
|
||||
const attributes = overrideXML.$;
|
||||
|
||||
this._name = attributes.name;
|
||||
this._override = attributes.override;
|
||||
|
||||
this._frames = new Array<FrameXML>();
|
||||
if (overrideXML.frame !== undefined) {
|
||||
for (const frame of overrideXML.frame) {
|
||||
this._frames.push(new FrameXML(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get override(): string {
|
||||
return this._override;
|
||||
}
|
||||
|
||||
get frames(): Array<FrameXML> {
|
||||
return this._frames;
|
||||
}
|
||||
}
|
||||
|
||||
export class AvatarXML {
|
||||
private readonly _ink: number;
|
||||
private readonly _foreground: string;
|
||||
private readonly _background: string;
|
||||
|
||||
constructor(avatarXML: any) {
|
||||
const attributes = avatarXML.$;
|
||||
|
||||
this._ink = attributes.ink;
|
||||
this._foreground = attributes.foreground;
|
||||
this._background = attributes.background;
|
||||
}
|
||||
|
||||
get ink(): number {
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
get foreground(): string {
|
||||
return this._foreground;
|
||||
}
|
||||
|
||||
get background(): string {
|
||||
return this._background;
|
||||
}
|
||||
}
|
||||
|
||||
export class SpriteXML {
|
||||
private readonly _id: string;
|
||||
private readonly _member: string;
|
||||
private readonly _directions: number;
|
||||
private readonly _staticY: number;
|
||||
private readonly _ink: number;
|
||||
|
||||
private readonly _directionList: Array<DirectionXML>;
|
||||
|
||||
constructor(spriteXML: any) {
|
||||
const attributes = spriteXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._member = attributes.member;
|
||||
this._directions = attributes.directions;
|
||||
this._staticY = attributes.staticY;
|
||||
this._ink = attributes.ink;
|
||||
|
||||
this._directionList = new Array<DirectionXML>();
|
||||
if (spriteXML.direction !== undefined) {
|
||||
for (const direction of spriteXML.direction) {
|
||||
this._directionList.push(new DirectionXML(direction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get member(): string {
|
||||
return this._member;
|
||||
}
|
||||
|
||||
get directions(): number {
|
||||
return this._directions;
|
||||
}
|
||||
|
||||
get staticY(): number {
|
||||
return this._staticY;
|
||||
}
|
||||
|
||||
get ink(): number {
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
get directionList(): Array<DirectionXML> {
|
||||
return this._directionList;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectionXML {
|
||||
private readonly _id: number;
|
||||
private readonly _dx: number;
|
||||
private readonly _dy: number;
|
||||
private readonly _dz: number;
|
||||
|
||||
constructor(directionXML: any) {
|
||||
const attributes = directionXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._dx = attributes.dx;
|
||||
this._dy = attributes.dy;
|
||||
this._dz = attributes.dz;
|
||||
}
|
||||
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get dx(): number {
|
||||
return this._dx;
|
||||
}
|
||||
|
||||
get dy(): number {
|
||||
return this._dy;
|
||||
}
|
||||
|
||||
get dz(): number {
|
||||
return this._dz;
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveXML {
|
||||
private readonly _id: string;
|
||||
|
||||
constructor(removeXML: any) {
|
||||
this._id = removeXML.$.id;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
}
|
||||
|
||||
export class AddXML {
|
||||
private readonly _id: string;
|
||||
private readonly _align: string;
|
||||
private readonly _blend: string;
|
||||
private readonly _ink: number;
|
||||
private readonly _base: string;
|
||||
|
||||
constructor(addXML: any) {
|
||||
const attributes = addXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._align = attributes.align;
|
||||
this._blend = attributes.blend;
|
||||
this._ink = attributes.ink;
|
||||
this._base = attributes.base;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get align(): string {
|
||||
return this._align;
|
||||
}
|
||||
|
||||
get blend(): string {
|
||||
return this._blend;
|
||||
}
|
||||
|
||||
get ink(): number {
|
||||
return this._ink;
|
||||
}
|
||||
|
||||
get base(): string {
|
||||
return this._base;
|
||||
}
|
||||
}
|
||||
|
||||
export class FrameXML {
|
||||
private readonly _repeats: number | undefined;
|
||||
private readonly _fxs: Array<FxXML>;
|
||||
private readonly _bodyParts: Array<BodyPartXML>;
|
||||
|
||||
constructor(frameXML: any) {
|
||||
if (frameXML.$ !== undefined)
|
||||
this._repeats = frameXML.$.repeats;
|
||||
|
||||
this._fxs = new Array<FxXML>();
|
||||
if (frameXML.fx !== undefined) {
|
||||
for (const fx of frameXML.fx) {
|
||||
this._fxs.push(new FxXML(fx));
|
||||
}
|
||||
}
|
||||
|
||||
this._bodyParts = new Array<BodyPartXML>();
|
||||
if (frameXML.bodypart !== undefined) {
|
||||
for (const bodypart of frameXML.bodypart) {
|
||||
this._bodyParts.push(new BodyPartXML(bodypart));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get repeats(): number | undefined {
|
||||
return this._repeats;
|
||||
}
|
||||
|
||||
get fxs(): Array<FxXML> {
|
||||
return this._fxs;
|
||||
}
|
||||
|
||||
get bodyParts(): Array<BodyPartXML> {
|
||||
return this._bodyParts;
|
||||
}
|
||||
}
|
||||
|
||||
export class BodyPartXML {
|
||||
private readonly _id: string;
|
||||
private readonly _frame: number;
|
||||
private readonly _dx: number;
|
||||
private readonly _dy: number;
|
||||
private readonly _dz: number;
|
||||
private readonly _dd: number;
|
||||
private readonly _action: string;
|
||||
private readonly _base: string;
|
||||
|
||||
private readonly _items: Array<ItemXML>;
|
||||
|
||||
constructor(bodyPartXML: any) {
|
||||
const attributes = bodyPartXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._frame = attributes.frame;
|
||||
this._dx = attributes.dx;
|
||||
this._dy = attributes.dy;
|
||||
this._dz = attributes.dz;
|
||||
this._dd = attributes.dd;
|
||||
this._action = attributes.action;
|
||||
this._base = attributes.base;
|
||||
|
||||
this._items = new Array<ItemXML>();
|
||||
if (bodyPartXML.item !== undefined) {
|
||||
for (const item of bodyPartXML.item) {
|
||||
this._items.push(new ItemXML(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get frame(): number {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
get dx(): number {
|
||||
return this._dx;
|
||||
}
|
||||
|
||||
get dy(): number {
|
||||
return this._dy;
|
||||
}
|
||||
|
||||
get dz(): number {
|
||||
return this._dz;
|
||||
}
|
||||
|
||||
get dd(): number {
|
||||
return this._dd;
|
||||
}
|
||||
|
||||
get action(): string {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
get base(): string {
|
||||
return this._base;
|
||||
}
|
||||
|
||||
get items(): Array<ItemXML> {
|
||||
return this._items;
|
||||
}
|
||||
}
|
||||
|
||||
export class ItemXML {
|
||||
private readonly _id: string;
|
||||
private readonly _base: string;
|
||||
|
||||
constructor(itemXML: any) {
|
||||
const attributes = itemXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._base = attributes.base;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get base(): string {
|
||||
return this._base;
|
||||
}
|
||||
}
|
||||
|
||||
export class FxXML {
|
||||
private readonly _id: string;
|
||||
private readonly _repeats: number;
|
||||
private readonly _frame: number;
|
||||
private readonly _dx: number;
|
||||
private readonly _dy: number;
|
||||
private readonly _dz: number;
|
||||
private readonly _dd: number;
|
||||
private readonly _action: string;
|
||||
|
||||
constructor(fxXML: any) {
|
||||
const attributes = fxXML.$;
|
||||
|
||||
this._id = attributes.id;
|
||||
this._repeats = attributes.repeats;
|
||||
this._frame = attributes.frame;
|
||||
this._dx = attributes.dx;
|
||||
this._dy = attributes.dy;
|
||||
this._dz = attributes.dz;
|
||||
this._dd = attributes.dd;
|
||||
this._action = attributes.action;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get repeats(): number {
|
||||
return this._repeats;
|
||||
}
|
||||
|
||||
get frame(): number {
|
||||
return this._frame;
|
||||
}
|
||||
|
||||
get dx(): number {
|
||||
return this._dx;
|
||||
}
|
||||
|
||||
get dy(): number {
|
||||
return this._dy;
|
||||
}
|
||||
|
||||
get dz(): number {
|
||||
return this._dz;
|
||||
}
|
||||
|
||||
get dd(): number {
|
||||
return this._dd;
|
||||
}
|
||||
|
||||
get action(): string {
|
||||
return this._action;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShadowXML {
|
||||
private readonly _id: string;
|
||||
|
||||
constructor(shadowXML: any) {
|
||||
this._id = shadowXML.$.id;
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectionOffsetXML {
|
||||
private readonly _offset: number;
|
||||
|
||||
constructor(directionXML: any) {
|
||||
this._offset = directionXML.$.offset;
|
||||
}
|
||||
|
||||
get offset(): number {
|
||||
return this._offset;
|
||||
}
|
||||
}
|
80
src/converters/effect/EffectConverter.ts
Normal file
80
src/converters/effect/EffectConverter.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import HabboAssetSWF from "../../swf/HabboAssetSWF";
|
||||
import DefineBinaryDataTag from "../../swf/tags/DefineBinaryDataTag";
|
||||
import ArchiveType from "../ArchiveType";
|
||||
import File from "../../utils/File";
|
||||
import NitroBundle from "../../utils/NitroBundle";
|
||||
import {EffectJson} from "./EffectTypes";
|
||||
import Configuration from "../../config/Configuration";
|
||||
import EffectJsonMapper from "./EffectJsonMapper";
|
||||
|
||||
const xml2js = require('xml2js');
|
||||
const parser = new xml2js.Parser(/* options */);
|
||||
|
||||
const fs = require('fs').promises;
|
||||
|
||||
export default class EffectConverter {
|
||||
private readonly _effectJsonMapper: EffectJsonMapper;
|
||||
|
||||
constructor(config: Configuration) {
|
||||
this._effectJsonMapper = new EffectJsonMapper();
|
||||
}
|
||||
|
||||
private static getBinaryData(habboAssetSWF: HabboAssetSWF, type: string, documentNameTwice: boolean) {
|
||||
let binaryName: string = habboAssetSWF.getFullClassName(type, documentNameTwice);
|
||||
let tag = habboAssetSWF.getBinaryTagByName(binaryName);
|
||||
if (tag === null) {
|
||||
binaryName = habboAssetSWF.getFullClassNameSnake(type, documentNameTwice, true);
|
||||
tag = habboAssetSWF.getBinaryTagByName(binaryName);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static async getManifestXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
|
||||
const binaryData: DefineBinaryDataTag | null = this.getBinaryData(habboAssetSWF, "manifest", false);
|
||||
if (binaryData !== null) {
|
||||
return await parser.parseStringPromise(binaryData.binaryData);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async getAnimationXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
|
||||
const binaryData: DefineBinaryDataTag | null = this.getBinaryData(habboAssetSWF, "animation", false);
|
||||
if (binaryData !== null) {
|
||||
return await parser.parseStringPromise(binaryData.binaryData);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async convertXML2JSON(habboAssetSWF: HabboAssetSWF): Promise<EffectJson | null> {
|
||||
const manifestXML = await EffectConverter.getManifestXML(habboAssetSWF);
|
||||
const animationXML = await EffectConverter.getAnimationXML(habboAssetSWF);
|
||||
|
||||
return this._effectJsonMapper.mapXML(habboAssetSWF, manifestXML, animationXML);
|
||||
}
|
||||
|
||||
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, archiveType: ArchiveType) {
|
||||
const effectJson = await this.convertXML2JSON(habboAssetSWF);
|
||||
if (effectJson !== null) {
|
||||
effectJson.spritesheet = archiveType.spriteSheetType;
|
||||
effectJson.type = type;
|
||||
|
||||
const path = outputFolder + "/" + habboAssetSWF.getDocumentClass() + ".nitro";
|
||||
const assetOuputFolder = new File(path);
|
||||
if (assetOuputFolder.exists()) {
|
||||
console.log("Effect already exists or the directory is not empty!");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const nitroBundle = new NitroBundle();
|
||||
nitroBundle.addFile(habboAssetSWF.getDocumentClass() + ".json", Buffer.from(JSON.stringify(effectJson)));
|
||||
nitroBundle.addFile(archiveType.imageData.name, archiveType.imageData.buffer);
|
||||
|
||||
const buffer = await nitroBundle.toBufferAsync();
|
||||
await fs.writeFile(path, buffer);
|
||||
}
|
||||
}
|
||||
}
|
338
src/converters/effect/EffectJsonMapper.ts
Normal file
338
src/converters/effect/EffectJsonMapper.ts
Normal file
@ -0,0 +1,338 @@
|
||||
import HabboAssetSWF from "../../swf/HabboAssetSWF";
|
||||
import {
|
||||
Add,
|
||||
Alias,
|
||||
Aliases,
|
||||
Animation,
|
||||
Animations,
|
||||
AssetJSON,
|
||||
AssetsJSON, Avatar,
|
||||
Bodypart, Direction, DirectionOffset,
|
||||
EffectJson,
|
||||
Frame,
|
||||
Fx, Item, Override, Remove, Shadow, Sprite
|
||||
} from "./EffectTypes";
|
||||
import EffectDownloader from "../../downloaders/EffectDownloader";
|
||||
import {ManifestXML} from "./EffectManifestXMLTypes";
|
||||
import SpriteSheetConverter from "../util/SpriteSheetConverter";
|
||||
import {
|
||||
AnimationXML,
|
||||
BodyPartXML
|
||||
} from "./EffectAnimationXMLTypes";
|
||||
|
||||
export default class EffectJsonMapper {
|
||||
public static readonly MUST_START_WITH: string = "h_";
|
||||
|
||||
public mapXML(habboAssetSWF: HabboAssetSWF, manifest: any, animation: any): EffectJson {
|
||||
const name = habboAssetSWF.getDocumentClass();
|
||||
const result = {} as EffectJson;
|
||||
result.name = name;
|
||||
result.type = EffectDownloader.types.get(name) as string;
|
||||
EffectJsonMapper.mapManifestXML(new ManifestXML(manifest), result);
|
||||
EffectJsonMapper.mapAnimationXML(new AnimationXML(animation), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static mapManifestXML(manifestXML: ManifestXML, output: EffectJson) {
|
||||
const assets: AssetsJSON = {};
|
||||
|
||||
for (const assetXML of manifestXML.library.assets) {
|
||||
if (assetXML.name.startsWith(this.MUST_START_WITH)) {
|
||||
|
||||
const asset: AssetJSON = {} as any;
|
||||
if (assetXML.param != undefined && assetXML.param.value !== undefined) {
|
||||
asset.x = parseInt(assetXML.param.value.split(",")[0]);
|
||||
asset.y = parseInt(assetXML.param.value.split(",")[1]);
|
||||
}
|
||||
if (SpriteSheetConverter.imageSource.has(assetXML.name)) {
|
||||
asset.source = SpriteSheetConverter.imageSource.get(assetXML.name) as any;
|
||||
}
|
||||
|
||||
assets[assetXML.name] = asset;
|
||||
}
|
||||
}
|
||||
|
||||
output.assets = assets;
|
||||
|
||||
if (manifestXML.library.aliases.length > 0 || SpriteSheetConverter.imageSource.size > 0) {
|
||||
const aliases: Aliases = {};
|
||||
for (const aliasXML of manifestXML.library.aliases) {
|
||||
if (aliasXML.name.startsWith(this.MUST_START_WITH)) {
|
||||
const alias: Alias = {} as any;
|
||||
|
||||
alias.link = aliasXML.link;
|
||||
if (aliasXML.fliph !== undefined)
|
||||
alias.fliph = parseInt(aliasXML.fliph.toString());
|
||||
if (aliasXML.flipv !== undefined)
|
||||
alias.flipv = parseInt(aliasXML.flipv.toString());
|
||||
|
||||
aliases[aliasXML.name] = alias;
|
||||
}
|
||||
}
|
||||
|
||||
output.aliases = aliases;
|
||||
}
|
||||
}
|
||||
|
||||
private static mapAnimationXML(animationXML: AnimationXML, output: EffectJson) {
|
||||
const animations: Animations = {};
|
||||
const animation: Animation = {} as any;
|
||||
animation.name = animationXML.name;
|
||||
animation.desc = animationXML.desc;
|
||||
animation.resetOnToggle = animationXML.resetOnToggle as any;
|
||||
|
||||
const frames: Array<Frame> = new Array<Frame>();
|
||||
const avatars: Array<Avatar> = new Array<Avatar>();
|
||||
const directions: Array<DirectionOffset> = new Array<DirectionOffset>();
|
||||
const shadows: Array<Shadow> = new Array<Shadow>();
|
||||
const adds: Array<Add> = new Array<Add>();
|
||||
const removes: Array<Remove> = new Array<Remove>();
|
||||
const sprites: Array<Sprite> = new Array<Sprite>();
|
||||
const overrides: Array<Override> = new Array<Override>();
|
||||
if (animationXML.frames.length > 0) {
|
||||
for (const frameXML of animationXML.frames) {
|
||||
const fxs: Array<Fx> = new Array<Fx>();
|
||||
const bodyparts: Array<Bodypart> = new Array<BodyPartXML>();
|
||||
|
||||
const frame: Frame = {} as any;
|
||||
if (frameXML.fxs.length > 0) {
|
||||
for (const fxXML of frameXML.fxs) {
|
||||
const fx: Fx = {} as any;
|
||||
fx.action = fxXML.action;
|
||||
|
||||
if (fxXML.dx !== undefined)
|
||||
fx.dx = parseInt(fxXML.dx.toString());
|
||||
|
||||
if (fxXML.dy !== undefined)
|
||||
fx.dy = parseInt(fxXML.dy.toString());
|
||||
|
||||
if (fxXML.dz !== undefined)
|
||||
fx.dz = parseInt(fxXML.dz.toString());
|
||||
|
||||
if (fxXML.dd !== undefined)
|
||||
fx.dd = parseInt(fxXML.dd.toString());
|
||||
|
||||
if (fxXML.frame !== undefined)
|
||||
fx.frame = parseInt(fxXML.frame.toString());
|
||||
fx.id = fxXML.id;
|
||||
|
||||
fxs.push(fx);
|
||||
}
|
||||
}
|
||||
if (frameXML.bodyParts.length > 0) {
|
||||
for (const bodypartXML of frameXML.bodyParts) {
|
||||
const items: Array<Item> = new Array<Item>();
|
||||
const bodypart: Bodypart = {} as any;
|
||||
if (bodypartXML.items.length > 0) {
|
||||
for (const itemXML of bodypartXML.items) {
|
||||
const item: Item = {} as any;
|
||||
item.id = itemXML.id;
|
||||
item.base = itemXML.base;
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
bodypart.action = bodypartXML.action;
|
||||
|
||||
if (bodypartXML.dx !== undefined)
|
||||
bodypart.dx = parseInt(bodypartXML.dx.toString());
|
||||
|
||||
if (bodypartXML.dy !== undefined)
|
||||
bodypart.dy = parseInt(bodypartXML.dy.toString());
|
||||
|
||||
if (bodypartXML.dz !== undefined)
|
||||
bodypart.dz = parseInt(bodypartXML.dz.toString());
|
||||
|
||||
if (bodypartXML.dd !== undefined)
|
||||
bodypart.dd = parseInt(bodypartXML.dd.toString());
|
||||
|
||||
if (bodypartXML.frame !== undefined)
|
||||
bodypart.frame = parseInt(bodypartXML.frame.toString());
|
||||
bodypart.id = bodypartXML.id;
|
||||
bodypart.base = bodypartXML.base;
|
||||
bodypart.items = items;
|
||||
|
||||
bodyparts.push(bodypart);
|
||||
}
|
||||
}
|
||||
if (frameXML.repeats !== undefined) frame.repeats = parseInt(frameXML.repeats.toString());
|
||||
frame.fxs = fxs;
|
||||
frame.bodyparts = bodyparts;
|
||||
frames.push(frame);
|
||||
}
|
||||
}
|
||||
if (animationXML.avatars.length > 0) {
|
||||
for (const avatarXML of animationXML.avatars) {
|
||||
const avatar: Avatar = {} as any;
|
||||
avatar.background = avatarXML.background;
|
||||
avatar.foreground = avatarXML.foreground;
|
||||
|
||||
if (avatarXML.ink !== undefined)
|
||||
avatar.ink = parseInt(avatarXML.ink.toString());
|
||||
|
||||
avatars.push(avatar);
|
||||
}
|
||||
}
|
||||
if (animationXML.directions.length > 0) {
|
||||
for (const directionXML of animationXML.directions) {
|
||||
const direction: DirectionOffset = {} as any;
|
||||
direction.offset = parseInt(directionXML.offset.toString());
|
||||
|
||||
directions.push(direction);
|
||||
}
|
||||
}
|
||||
if (animationXML.shadows.length > 0) {
|
||||
for (const shadowXML of animationXML.shadows) {
|
||||
const shadow: Shadow = {} as any;
|
||||
shadow.id = shadowXML.id;
|
||||
|
||||
shadows.push(shadow);
|
||||
}
|
||||
}
|
||||
if (animationXML.adds.length > 0) {
|
||||
for (const addXML of animationXML.adds) {
|
||||
const add: Add = {} as any;
|
||||
add.id = addXML.id;
|
||||
add.align = addXML.align;
|
||||
add.blend = addXML.blend;
|
||||
|
||||
if (addXML.ink !== undefined)
|
||||
add.ink = parseInt(addXML.ink.toString());
|
||||
add.base = addXML.base;
|
||||
|
||||
adds.push(add);
|
||||
}
|
||||
}
|
||||
if (animationXML.removes.length > 0) {
|
||||
for (const removeXML of animationXML.removes) {
|
||||
const remove: Remove = {} as any;
|
||||
remove.id = removeXML.id;
|
||||
|
||||
removes.push(remove);
|
||||
}
|
||||
}
|
||||
if (animationXML.sprites.length > 0) {
|
||||
for (const spriteXML of animationXML.sprites) {
|
||||
const sprite: Sprite = {} as any;
|
||||
const directions2: Array<Direction> = new Array<Direction>();
|
||||
if (spriteXML.directionList.length > 0) {
|
||||
for (const directionXML of spriteXML.directionList) {
|
||||
const direction: Direction = {} as any;
|
||||
direction.id = parseInt(directionXML.id.toString());
|
||||
|
||||
if (directionXML.dx !== undefined)
|
||||
direction.dx = parseInt(directionXML.dx.toString());
|
||||
|
||||
if (directionXML.dy !== undefined)
|
||||
direction.dy = parseInt(directionXML.dy.toString());
|
||||
|
||||
if (directionXML.dz !== undefined)
|
||||
direction.dz = parseInt(directionXML.dz.toString());
|
||||
|
||||
directions2.push(direction);
|
||||
}
|
||||
}
|
||||
sprite.directionList = directions2;
|
||||
|
||||
if (spriteXML.directions !== undefined)
|
||||
sprite.directions = parseInt(spriteXML.directions.toString());
|
||||
sprite.id = spriteXML.id;
|
||||
|
||||
if (spriteXML.ink !== undefined)
|
||||
sprite.ink = parseInt(spriteXML.ink.toString());
|
||||
|
||||
if (spriteXML.member !== undefined)
|
||||
sprite.member = spriteXML.member;
|
||||
|
||||
if (spriteXML.staticY !== undefined)
|
||||
sprite.staticY = parseInt(spriteXML.staticY.toString());
|
||||
sprites.push(sprite);
|
||||
}
|
||||
}
|
||||
if (animationXML.overrides.length > 0) {
|
||||
for (const overrideXML of animationXML.overrides) {
|
||||
const override: Override = {} as any;
|
||||
override.name = overrideXML.name;
|
||||
override.override = overrideXML.override;
|
||||
if (overrideXML.frames.length > 0) {
|
||||
const overrideFrames: Array<Frame> = new Array<Frame>();
|
||||
for (const frameXML of overrideXML.frames) {
|
||||
const fxs: Array<Fx> = new Array<Fx>();
|
||||
const bodyparts: Array<Bodypart> = new Array<Bodypart>();
|
||||
const frame: Frame = {} as any;
|
||||
if (frameXML.fxs.length > 0) {
|
||||
for (const fxXML of frameXML.fxs) {
|
||||
const fx: Fx = {} as any;
|
||||
fx.action = fxXML.action;
|
||||
if (fxXML.dx !== undefined)
|
||||
fx.dx = parseInt(fxXML.dx.toString());
|
||||
if (fxXML.dy !== undefined)
|
||||
fx.dy = parseInt(fxXML.dy.toString());
|
||||
if (fxXML.dz !== undefined)
|
||||
fx.dz = parseInt(fxXML.dz.toString());
|
||||
if (fxXML.dd !== undefined)
|
||||
fx.dd = parseInt(fxXML.dd.toString());
|
||||
|
||||
if (fxXML.frame !== undefined)
|
||||
fx.frame = parseInt(fxXML.frame.toString());
|
||||
fx.id = fxXML.id;
|
||||
|
||||
fxs.push(fx);
|
||||
}
|
||||
}
|
||||
if (frameXML.bodyParts.length > 0) {
|
||||
for (const bodypartXML of frameXML.bodyParts) {
|
||||
const items: Array<Item> = new Array<Item>();
|
||||
const bodypart: Bodypart = {} as any;
|
||||
if (bodypartXML.items.length > 0) {
|
||||
for (const itemXML of bodypartXML.items) {
|
||||
const item: Item = {} as any;
|
||||
item.id = itemXML.id;
|
||||
item.base = itemXML.base;
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
bodypart.action = bodypartXML.action;
|
||||
|
||||
if (bodypartXML.dx !== undefined)
|
||||
bodypart.dx = parseInt(bodypartXML.dx.toString());
|
||||
if (bodypartXML.dy !== undefined)
|
||||
bodypart.dy = parseInt(bodypartXML.dy.toString());
|
||||
if (bodypartXML.dz !== undefined)
|
||||
bodypart.dz = parseInt(bodypartXML.dz.toString());
|
||||
if (bodypartXML.dd !== undefined)
|
||||
bodypart.dd = parseInt(bodypartXML.dd.toString());
|
||||
|
||||
if (bodypartXML.frame !== undefined)
|
||||
bodypart.frame = parseInt(bodypartXML.frame.toString());
|
||||
bodypart.id = bodypartXML.id;
|
||||
bodypart.base = bodypartXML.base;
|
||||
bodypart.items = items;
|
||||
|
||||
bodyparts.push(bodypart);
|
||||
}
|
||||
}
|
||||
frame.fxs = fxs;
|
||||
frame.bodyparts = bodyparts;
|
||||
overrideFrames.push(frame);
|
||||
}
|
||||
override.frames = overrideFrames;
|
||||
overrides.push(override);
|
||||
}
|
||||
}
|
||||
}
|
||||
animation.frames = frames;
|
||||
animation.shadows = shadows;
|
||||
animation.adds = adds;
|
||||
animation.directions = directions;
|
||||
animation.avatars = avatars;
|
||||
animation.removes = removes;
|
||||
animation.sprites = sprites;
|
||||
animation.overrides = overrides;
|
||||
animations[output.name] = animation;
|
||||
|
||||
output.animations = animations;
|
||||
}
|
||||
}
|
142
src/converters/effect/EffectManifestXMLTypes.ts
Normal file
142
src/converters/effect/EffectManifestXMLTypes.ts
Normal file
@ -0,0 +1,142 @@
|
||||
export class ManifestXML {
|
||||
private readonly _library: LibraryXML;
|
||||
|
||||
constructor(manifestXML: any) {
|
||||
this._library = new LibraryXML(manifestXML.manifest.library[0]);
|
||||
}
|
||||
|
||||
get library(): LibraryXML {
|
||||
return this._library;
|
||||
}
|
||||
}
|
||||
|
||||
export class LibraryXML {
|
||||
private readonly _name: string;
|
||||
private readonly _version: string;
|
||||
|
||||
private readonly _assets: Array<AssetXML>;
|
||||
private readonly _aliases: Array<AliasXML>;
|
||||
|
||||
constructor(libraryXML: any) {
|
||||
const attributes = libraryXML.$;
|
||||
this._name = attributes.id;
|
||||
this._version = attributes.version;
|
||||
|
||||
this._assets = new Array<AssetXML>();
|
||||
if (libraryXML.assets !== undefined) {
|
||||
for (const assetParent of libraryXML.assets) {
|
||||
for (const asset of assetParent.asset) {
|
||||
this._assets.push(new AssetXML(asset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._aliases = new Array<AliasXML>();
|
||||
if (libraryXML.aliases !== undefined && Array.isArray(libraryXML.aliases)) {
|
||||
for (const aliasParent of libraryXML.aliases) {
|
||||
if (Array.isArray(aliasParent.alias)) {
|
||||
for (const alias of aliasParent.alias) {
|
||||
this._aliases.push(new AliasXML(alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get version(): string {
|
||||
return this._version;
|
||||
}
|
||||
|
||||
get assets(): Array<AssetXML> {
|
||||
return this._assets;
|
||||
}
|
||||
|
||||
get aliases(): Array<AliasXML> {
|
||||
return this._aliases;
|
||||
}
|
||||
}
|
||||
|
||||
export class AliasXML {
|
||||
private readonly _name: string;
|
||||
private readonly _link: string;
|
||||
private readonly _fliph: number;
|
||||
private readonly _flipv: number;
|
||||
|
||||
constructor(aliasXML: any) {
|
||||
const attributes = aliasXML.$;
|
||||
|
||||
this._name = attributes.name;
|
||||
this._link = attributes.link;
|
||||
this._fliph = attributes.fliph;
|
||||
this._flipv = attributes.flipv;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get link(): string {
|
||||
return this._link;
|
||||
}
|
||||
|
||||
get fliph(): number {
|
||||
return this._fliph;
|
||||
}
|
||||
|
||||
get flipv(): number {
|
||||
return this._flipv;
|
||||
}
|
||||
}
|
||||
|
||||
export class AssetXML {
|
||||
private readonly _name: string;
|
||||
private readonly _mimeType: string;
|
||||
private readonly _param: ParamXML | undefined;
|
||||
|
||||
constructor(assetXML: any) {
|
||||
const attributes = assetXML.$;
|
||||
this._name = attributes.name;
|
||||
this._mimeType = attributes.mimeType;
|
||||
|
||||
if (assetXML.param !== undefined) {
|
||||
for (const param of assetXML.param) {
|
||||
this._param = new ParamXML(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get mimeType(): string {
|
||||
return this._mimeType;
|
||||
}
|
||||
|
||||
get param(): ParamXML | undefined {
|
||||
return this._param;
|
||||
}
|
||||
}
|
||||
|
||||
export class ParamXML {
|
||||
private readonly _key: string;
|
||||
private readonly _value: string;
|
||||
|
||||
constructor(paramXML: any) {
|
||||
const attributes = paramXML.$;
|
||||
this._key = attributes.key;
|
||||
this._value = attributes.value;
|
||||
}
|
||||
|
||||
get key(): string {
|
||||
return this._key;
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this._value;
|
||||
}
|
||||
}
|
135
src/converters/effect/EffectTypes.ts
Normal file
135
src/converters/effect/EffectTypes.ts
Normal file
@ -0,0 +1,135 @@
|
||||
import {SpriteSheetType} from "../util/SpriteSheetTypes";
|
||||
|
||||
export interface EffectJson {
|
||||
type: string,
|
||||
name: string,
|
||||
spritesheet: SpriteSheetType,
|
||||
|
||||
assets: AssetsJSON,
|
||||
aliases: Aliases,
|
||||
animations: Animations
|
||||
}
|
||||
|
||||
export interface Animations {
|
||||
[key: string]: Animation
|
||||
}
|
||||
|
||||
export interface Animation {
|
||||
name: string,
|
||||
desc: string,
|
||||
resetOnToggle: boolean,
|
||||
|
||||
directions: Array<DirectionOffset>,
|
||||
shadows: Array<Shadow>,
|
||||
adds: Array<Add>,
|
||||
removes: Array<Remove>,
|
||||
sprites: Array<Sprite>,
|
||||
frames: Array<Frame>,
|
||||
avatars: Array<Avatar>,
|
||||
overrides: Array<Override>
|
||||
}
|
||||
|
||||
export interface Override {
|
||||
name: string,
|
||||
override: string,
|
||||
|
||||
frames: Array<Frame>;
|
||||
}
|
||||
|
||||
export interface Avatar {
|
||||
ink: number,
|
||||
foreground: string,
|
||||
background: string
|
||||
}
|
||||
|
||||
export interface Frame {
|
||||
repeats: number,
|
||||
|
||||
fxs: Array<Fx>,
|
||||
bodyparts: Array<Bodypart>
|
||||
}
|
||||
|
||||
export interface Bodypart {
|
||||
id: string,
|
||||
frame: number,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dz: number,
|
||||
dd: number,
|
||||
action: string,
|
||||
base: string,
|
||||
|
||||
items: Array<Item>
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
id: string,
|
||||
base: string
|
||||
}
|
||||
|
||||
export interface Fx {
|
||||
id: string,
|
||||
frame: number,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dz: number,
|
||||
dd: number,
|
||||
action: string
|
||||
}
|
||||
|
||||
export interface Sprite {
|
||||
id: string,
|
||||
member: string,
|
||||
directions: number,
|
||||
staticY: number,
|
||||
ink: number,
|
||||
|
||||
directionList: Array<Direction>;
|
||||
}
|
||||
|
||||
export interface Direction {
|
||||
id: number,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dz: number,
|
||||
}
|
||||
|
||||
export interface Remove {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface Add {
|
||||
id: string,
|
||||
align: string,
|
||||
blend: string,
|
||||
ink: number,
|
||||
base: string
|
||||
}
|
||||
|
||||
export interface Shadow {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface DirectionOffset {
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface Aliases {
|
||||
[key: string]: Alias
|
||||
}
|
||||
|
||||
export interface Alias {
|
||||
link: string,
|
||||
fliph: number,
|
||||
flipv: number
|
||||
}
|
||||
|
||||
export interface AssetsJSON {
|
||||
[key: string]: AssetJSON;
|
||||
}
|
||||
|
||||
export interface AssetJSON {
|
||||
source: string,
|
||||
x: number,
|
||||
y: number,
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
import HabboAssetSWF from "../../swf/HabboAssetSWF";
|
||||
import {SpriteSheetType} from "../util/SpriteSheetTypes";
|
||||
import DefineBinaryDataTag from "../../swf/tags/DefineBinaryDataTag";
|
||||
import Configuration from "../../config/Configuration";
|
||||
import FigureJsonMapper from "./FigureJsonMapper";
|
||||
import {FigureJson} from "./FigureJsonType";
|
||||
import File from "../../utils/File";
|
||||
import ArchiveType from "../ArchiveType";
|
||||
import NitroBundle from "../../utils/NitroBundle";
|
||||
|
||||
const xml2js = require('xml2js');
|
||||
const parser = new xml2js.Parser(/* options */);
|
||||
|
||||
const fs = require('fs').promises;
|
||||
const {gzip} = require('node-gzip');
|
||||
|
||||
export default class FigureConverter {
|
||||
|
||||
@ -42,10 +41,10 @@ export default class FigureConverter {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, spriteSheetType: SpriteSheetType) {
|
||||
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, archiveType: ArchiveType) {
|
||||
const manifestJson = await this.convertXML2JSON(habboAssetSWF);
|
||||
if (manifestJson !== null) {
|
||||
manifestJson.spritesheet = spriteSheetType;
|
||||
manifestJson.spritesheet = archiveType.spriteSheetType;
|
||||
|
||||
const path = outputFolder + "/" + habboAssetSWF.getDocumentClass() + ".nitro";
|
||||
const assetOuputFolder = new File(path);
|
||||
@ -55,8 +54,12 @@ export default class FigureConverter {
|
||||
return;
|
||||
}
|
||||
|
||||
const compressed = await gzip(JSON.stringify(manifestJson));
|
||||
await fs.writeFile(path, compressed);
|
||||
const nitroBundle = new NitroBundle();
|
||||
nitroBundle.addFile(habboAssetSWF.getDocumentClass() + ".json", Buffer.from(JSON.stringify(manifestJson)));
|
||||
nitroBundle.addFile(archiveType.imageData.name, archiveType.imageData.buffer);
|
||||
|
||||
const buffer = await nitroBundle.toBufferAsync();
|
||||
await fs.writeFile(path, buffer);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
import HabboAssetSWF from "../../swf/HabboAssetSWF";
|
||||
import {SpriteSheetType} from "../util/SpriteSheetTypes";
|
||||
import DefineBinaryDataTag from "../../swf/tags/DefineBinaryDataTag";
|
||||
import Configuration from "../../config/Configuration";
|
||||
import FurniJsonMapper from "./FurniJsonMapper";
|
||||
import {FurniJson} from "./FurniTypes";
|
||||
import File from "../../utils/File";
|
||||
import ArchiveType from "../ArchiveType";
|
||||
import NitroBundle from "../../utils/NitroBundle";
|
||||
|
||||
const xml2js = require('xml2js');
|
||||
const parser = new xml2js.Parser(/* options */);
|
||||
|
||||
const fs = require('fs').promises;
|
||||
const {gzip} = require('node-gzip');
|
||||
|
||||
export default class FurnitureConverter {
|
||||
|
||||
@ -50,7 +50,7 @@ export default class FurnitureConverter {
|
||||
}
|
||||
|
||||
private static async getIndexXML(habboAssetSWF: HabboAssetSWF): Promise<any> {
|
||||
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "index", true);
|
||||
const binaryData: DefineBinaryDataTag | null = FurnitureConverter.getBinaryData(habboAssetSWF, "index", false);
|
||||
if (binaryData !== null) {
|
||||
return await parser.parseStringPromise(binaryData.binaryData);
|
||||
}
|
||||
@ -76,10 +76,10 @@ export default class FurnitureConverter {
|
||||
return this._furniJsonMapper.mapXML(assetXml, indexXml, logicXml, visualizationXml);
|
||||
}
|
||||
|
||||
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, spriteSheetType: SpriteSheetType) {
|
||||
public async fromHabboAsset(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string, archiveType: ArchiveType) {
|
||||
const furnitureJson = await this.convertXML2JSON(habboAssetSWF);
|
||||
if (furnitureJson !== null) {
|
||||
furnitureJson.spritesheet = spriteSheetType;
|
||||
furnitureJson.spritesheet = archiveType.spriteSheetType;
|
||||
furnitureJson.type = type;
|
||||
|
||||
const path = outputFolder + "/" + habboAssetSWF.getDocumentClass() + ".nitro";
|
||||
@ -90,8 +90,12 @@ export default class FurnitureConverter {
|
||||
return;
|
||||
}
|
||||
|
||||
const compressed = await gzip(JSON.stringify(furnitureJson));
|
||||
await fs.writeFile(path, compressed);
|
||||
const nitroBundle = new NitroBundle();
|
||||
nitroBundle.addFile(habboAssetSWF.getDocumentClass() + ".json", Buffer.from(JSON.stringify(furnitureJson)));
|
||||
nitroBundle.addFile(archiveType.imageData.name, archiveType.imageData.buffer);
|
||||
|
||||
const buffer = await nitroBundle.toBufferAsync();
|
||||
await fs.writeFile(path, buffer);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,14 @@
|
||||
import {SpriteSheetType} from "./SpriteSheetTypes";
|
||||
|
||||
const fs = require('fs').promises;
|
||||
let {packAsync} = require("free-tex-packer-core");
|
||||
|
||||
import HabboAssetSWF from "../../swf/HabboAssetSWF";
|
||||
import SymbolClassTag from "../../swf/tags/SymbolClassTag";
|
||||
import ImageTag from "../../swf/tags/ImageTag";
|
||||
import ArchiveType from "../ArchiveType";
|
||||
|
||||
export default class SpriteSheetConverter {
|
||||
public static imageSource: Map<String, String> = new Map<String, String>();
|
||||
public static imageSource: Map<string, string> = new Map<string, string>();
|
||||
|
||||
public async generateSpriteSheet(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string): Promise<SpriteSheetType | null> {
|
||||
public async generateSpriteSheet(habboAssetSWF: HabboAssetSWF, outputFolder: string, type: string): Promise<ArchiveType | null> {
|
||||
const tagList: Array<SymbolClassTag> = habboAssetSWF.symbolTags();
|
||||
const names: Array<string> = new Array<string>();
|
||||
const tags: Array<number> = new Array<number>();
|
||||
@ -19,7 +17,7 @@ export default class SpriteSheetConverter {
|
||||
tags.push(...tag.tags);
|
||||
}
|
||||
|
||||
const images: Array<{ path: String, contents: Buffer }> = new Array<{ path: String, contents: Buffer }>();
|
||||
const images: Array<{ path: string, contents: Buffer }> = new Array<{ path: string, contents: Buffer }>();
|
||||
|
||||
const imageTags: Array<ImageTag> = habboAssetSWF.imageTags();
|
||||
for (const imageTag of imageTags) {
|
||||
@ -67,7 +65,7 @@ export default class SpriteSheetConverter {
|
||||
return await this.packImages(habboAssetSWF.getDocumentClass(), outputFolder + "/", images);
|
||||
}
|
||||
|
||||
async packImages(documentClass: string, outputFolder: string, images: Array<{ path: String, contents: Buffer }>): Promise<SpriteSheetType | null> {
|
||||
async packImages(documentClass: string, outputFolder: string, images: Array<{ path: string, contents: Buffer }>): Promise<ArchiveType | null> {
|
||||
let options = {
|
||||
textureName: documentClass,
|
||||
width: 1024,
|
||||
@ -79,26 +77,33 @@ export default class SpriteSheetConverter {
|
||||
exporter: "Pixi"
|
||||
};
|
||||
|
||||
let spriteSheetType: SpriteSheetType | null = null;
|
||||
let base64 = "";
|
||||
const archiveType: ArchiveType = {} as any;
|
||||
const imageData: {
|
||||
name: string,
|
||||
buffer: Buffer
|
||||
} = {} as any;
|
||||
|
||||
try {
|
||||
const files = await packAsync(images, options);
|
||||
for (let item of files) {
|
||||
if (item.name.endsWith(".json")) {
|
||||
spriteSheetType = JSON.parse(item.buffer.toString('utf8'));
|
||||
archiveType.spriteSheetType = JSON.parse(item.buffer.toString('utf8'));
|
||||
} else {
|
||||
base64 = item.buffer.toString("base64");
|
||||
//await fs.writeFile(outputFolder + item.name, item.buffer);
|
||||
imageData.buffer = item.buffer;
|
||||
imageData.name = item.name;
|
||||
}
|
||||
}
|
||||
|
||||
if (spriteSheetType === null) throw new Error("Failed to parse SpriteSheet. " + images[0].path);
|
||||
if (archiveType.spriteSheetType === null) throw new Error("Failed to parse SpriteSheet. " + images[0].path);
|
||||
} catch (error) {
|
||||
console.log("Image Packing Error: ");
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
if (spriteSheetType !== null) spriteSheetType.meta.image = base64;
|
||||
return spriteSheetType;
|
||||
if (archiveType.spriteSheetType !== null) {
|
||||
archiveType.spriteSheetType.meta.image = imageData.name;
|
||||
archiveType.imageData = imageData;
|
||||
}
|
||||
return archiveType;
|
||||
}
|
||||
}
|
68
src/downloaders/EffectDownloader.ts
Normal file
68
src/downloaders/EffectDownloader.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import Configuration from "../config/Configuration";
|
||||
import HabboAssetSWF from "../swf/HabboAssetSWF";
|
||||
import File from "../utils/File";
|
||||
|
||||
const fs = require("fs");
|
||||
const fetch = require('node-fetch');
|
||||
const xml2js = require('xml2js');
|
||||
const parser = new xml2js.Parser(/* options */);
|
||||
const util = require('util');
|
||||
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
|
||||
export default class EffectDownloader {
|
||||
private readonly _config: Configuration;
|
||||
|
||||
constructor(config: Configuration) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
|
||||
public static types: Map<string, string> = new Map<string, string>();
|
||||
|
||||
public async download(callback: (habboAssetSwf: HabboAssetSWF) => Promise<void>) {
|
||||
const outputFolderEffect = this._config.getValue("output.folder.effect");
|
||||
const figureMap = await this.parseEffectMap();
|
||||
const map = figureMap.map;
|
||||
|
||||
for (const lib of map.effect) {
|
||||
const info = lib['$'];
|
||||
const className: string = info.lib;
|
||||
|
||||
//if (className !== 'Hoverboard' && className !== 'Staff' && className !== 'ZombieMask' && className !== 'ESredUntouchable') continue;
|
||||
|
||||
const assetOutputFolder = new File(outputFolderEffect + "/" + className);
|
||||
if (assetOutputFolder.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!EffectDownloader.types.has(className)) {
|
||||
const url = this._config.getValue("dynamic.download.url.effect").replace("%className%", className);
|
||||
if (!fs.existsSync(url)) {
|
||||
console.log("SWF File does not exist: " + url);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer: Buffer = await readFile(url);
|
||||
const habboAssetSWF = new HabboAssetSWF(buffer);
|
||||
await habboAssetSWF.setupAsync();
|
||||
|
||||
EffectDownloader.types.set(className, info.type);
|
||||
await callback(habboAssetSWF);
|
||||
} catch (e) {
|
||||
console.log(className);
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async parseEffectMap() {
|
||||
const figureMapPath = this._config.getValue("effectmap.url");
|
||||
const figureFetch = await fetch(figureMapPath);
|
||||
const figureMap = await figureFetch.text();
|
||||
|
||||
return await parser.parseStringPromise(figureMap);
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ export default class FigureDownloader {
|
||||
}
|
||||
|
||||
|
||||
public static types: Map<String, String> = new Map<String, String>();
|
||||
public static types: Map<string, string> = new Map<string, string>();
|
||||
|
||||
public async download(callback: (habboAssetSwf: HabboAssetSWF) => Promise<void>) {
|
||||
const outputFolderFigure = this._config.getValue("output.folder.figure");
|
||||
|
38
src/utils/NitroBundle.ts
Normal file
38
src/utils/NitroBundle.ts
Normal file
@ -0,0 +1,38 @@
|
||||
const ByteBuffer = require('bytebuffer');
|
||||
const {gzip} = require('node-gzip');
|
||||
|
||||
export default class NitroBundle {
|
||||
private readonly _files: Map<string, Buffer>;
|
||||
|
||||
constructor() {
|
||||
this._files = new Map<string, Buffer>();
|
||||
}
|
||||
|
||||
addFile(name: string, data: Buffer) {
|
||||
this._files.set(name, data);
|
||||
}
|
||||
|
||||
async toBufferAsync() {
|
||||
const buffer = new ByteBuffer();
|
||||
|
||||
buffer.writeUInt16(this._files.size);
|
||||
|
||||
const iterator = this._files.entries();
|
||||
let result: IteratorResult<[string, Buffer]> = iterator.next();
|
||||
while (!result.done) {
|
||||
const fileName = result.value[0];
|
||||
const file = result.value[1];
|
||||
|
||||
buffer.writeUint16(fileName.length);
|
||||
buffer.writeString(fileName);
|
||||
|
||||
const compressed = await gzip(file);
|
||||
buffer.writeUint32(compressed.length);
|
||||
buffer.append(compressed);
|
||||
|
||||
result = iterator.next();
|
||||
}
|
||||
|
||||
return buffer.flip().toBuffer();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user