diff --git a/config.ini b/config.ini index 13af61b..a402d39 100644 --- a/config.ini +++ b/config.ini @@ -1,6 +1,6 @@ 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/ -output.folder.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/effect/ +output.folder.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/effect-test/ output.folder.pet=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/pet/ furnidata.url=http://assets.nitro.se/game/gamedata/furnidata-entry.xml figuremap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/figuremap.xml @@ -10,9 +10,9 @@ dynamic.download.url.furniture=/home/user/WebstormProjects/sites/assets.nitro.se dynamic.download.url.figure=/home/user/WebstormProjects/sites/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=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf -convert.furniture=0 +convert.furniture=1 convert.figure=0 -convert.effect=1 -convert.pet=1 +convert.effect=0 +convert.pet=0 figure.rotation.enabled=0 figure.skip.non-existing.asset.images=0 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 956d265..133b491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,14 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@gizeta/swf-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gizeta/swf-reader/-/swf-reader-1.0.0.tgz", + "integrity": "sha1-34Huyh7J7miWax2Tbd/iH5Hp3IM=", + "requires": { + "lzma-purejs": "~0.9.3" + } + }, "@jvitela/mustache-wax": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.3.tgz", diff --git a/package.json b/package.json index 619719f..16faf99 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "author": "", "license": "ISC", "dependencies": { + "@gizeta/swf-reader": "^1.0.0", "@types/node": "^14.14.22", "bytebuffer": "^5.0.1", "free-tex-packer-core": "^0.3.2", diff --git a/src/Main.ts b/src/Main.ts index f5af61b..8bcda19 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -108,15 +108,15 @@ import PetConverter from "./converters/pet/PetConverter"; } } catch (e) { console.log(e); - console.log("Effect error: " + habboAssetSwf.getDocumentClass()); + console.log("Pet error: " + habboAssetSwf.getDocumentClass()); } }); } console.log('finished!'); - /* - outputFolderFurniture.rmdir({ + + /*outputFolderEffect.rmdir({ recursive: true, force: true });*/ diff --git a/src/config/Configuration.ts b/src/config/Configuration.ts index 879f855..dcc6dd7 100644 --- a/src/config/Configuration.ts +++ b/src/config/Configuration.ts @@ -10,7 +10,7 @@ export default class Configuration { } async init() { - const content = await fs.readFile("/home/user/git/nitro-asset-converter-node/config.ini"); + const content = await fs.readFile("/home/user/git/nitro-asset-converter-node (copy)/config.ini"); this.parseContent(content.toString("utf-8")); } diff --git a/src/converters/util/SpriteSheetConverter.ts b/src/converters/util/SpriteSheetConverter.ts index c22e91c..51fda45 100644 --- a/src/converters/util/SpriteSheetConverter.ts +++ b/src/converters/util/SpriteSheetConverter.ts @@ -55,7 +55,6 @@ export default class SpriteSheetConverter { contents: imageTag.imgData }); } - } if (images.length === 0) { diff --git a/src/downloaders/EffectDownloader.ts b/src/downloaders/EffectDownloader.ts index e141ebb..b224ed0 100644 --- a/src/downloaders/EffectDownloader.ts +++ b/src/downloaders/EffectDownloader.ts @@ -44,12 +44,11 @@ export default class EffectDownloader { } try { - const buffer: Buffer = await readFile(url); - const habboAssetSWF = new HabboAssetSWF(buffer); - await habboAssetSWF.setupAsync(); + const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); + await newHabboAssetSWF.setupAsync(); EffectDownloader.types.set(className, info.type); - await callback(habboAssetSWF); + await callback(newHabboAssetSWF); } catch (e) { console.log(className); console.log(e); diff --git a/src/downloaders/FigureDownloader.ts b/src/downloaders/FigureDownloader.ts index a450f3a..e7598ea 100644 --- a/src/downloaders/FigureDownloader.ts +++ b/src/downloaders/FigureDownloader.ts @@ -48,12 +48,11 @@ export default class FigureDownloader { return; } - const buffer: Buffer = await readFile(url); - const habboAssetSWF = new HabboAssetSWF(buffer); - await habboAssetSWF.setupAsync(); + const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); + await newHabboAssetSWF.setupAsync(); FigureDownloader.types.set(className, lib.part[0]['$'].type); - await callback(habboAssetSWF); + await callback(newHabboAssetSWF); } } } diff --git a/src/downloaders/FurnitureDownloader.ts b/src/downloaders/FurnitureDownloader.ts index 9634937..f3cfd1c 100644 --- a/src/downloaders/FurnitureDownloader.ts +++ b/src/downloaders/FurnitureDownloader.ts @@ -73,11 +73,10 @@ export default class FurnitureDownloader { } try { - const buffer: Buffer = await readFile(url); - const habboAssetSWF = new HabboAssetSWF(buffer); - await habboAssetSWF.setupAsync(); + const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); + await newHabboAssetSWF.setupAsync(); - await callback(habboAssetSWF, className); + await callback(newHabboAssetSWF, className); } catch (e) { console.log("Error with furniture: " + url); console.log(e); diff --git a/src/downloaders/PetDownloader.ts b/src/downloaders/PetDownloader.ts index 4ca2951..2c67569 100644 --- a/src/downloaders/PetDownloader.ts +++ b/src/downloaders/PetDownloader.ts @@ -36,11 +36,10 @@ export default class PetDownloader { continue; } - const buffer: Buffer = await readFile(url); - const habboAssetSWF = new HabboAssetSWF(buffer); - await habboAssetSWF.setupAsync(); + const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); + await newHabboAssetSWF.setupAsync(); - await callback(habboAssetSWF); + await callback(newHabboAssetSWF); } itemClassNames.push(pet); diff --git a/src/swf/HabboAssetSWF.ts b/src/swf/HabboAssetSWF.ts index 63ca029..6ca459c 100644 --- a/src/swf/HabboAssetSWF.ts +++ b/src/swf/HabboAssetSWF.ts @@ -1,77 +1,74 @@ -import SymbolClassTag from "./tags/SymbolClassTag"; -import ImageTag from "./tags/ImageTag"; +import {readImagesDefineBitsLossless, readImagesJPEG, readSwfAsync} from "../utils/SwfReader"; import ITag from "./tags/ITag"; +import SymbolClassTag from "./tags/SymbolClassTag"; +import DefineBinaryDataTag from "./tags/DefineBinaryDataTag"; +import ImageTag from "./tags/ImageTag"; import CustomIterator from "../utils/CustomIterator"; import CharacterTag from "./tags/CharacterTag"; -import DefineBinaryDataTag from "./tags/DefineBinaryDataTag"; - -const {readFromBufferP, extractImages} = require('swf-extract'); - -export interface Tag { - code: number, - length: number, - rawData: Buffer -} - -export interface SWFFrameSize { - x: number, - y: number, - width: number, - height: number -} - -export interface SWFFileLength { - compressed: number, - uncompressed: number -} - -export interface SWF { - tags: Array, - version: number, - fileLength: SWFFileLength, - frameSize: SWFFrameSize, - frameRate: number, - frameCount: number -} export default class HabboAssetSWF { - private swf: SWF | null; - private readonly _tags: Array; - private _documentClass: string | null = null; - constructor(private readonly _buffer: Buffer) { - this.swf = null; - + constructor( + private readonly _path: string + ) { this._tags = new Array(); } - public async setupAsync() { - this.swf = await readFromBufferP(this._buffer); + async setupAsync() { + const swf = await readSwfAsync(this._path); + for (const tag of swf.tags) { - if (this.swf === null) throw new Error("SWF Can't be null!"); + switch (tag.header.code) { + case 76: + this._tags.push(new SymbolClassTag(tag.symbols)); + break; + case 87: + this._tags.push(new DefineBinaryDataTag(tag.data)) + break; - for (const tag of this.swf.tags) { - if (tag.code === 76) { - this._tags.push(new SymbolClassTag(tag)); + case 6: + console.log(tag); + break; + + case 21: + console.log(tag); + break; + + case 35: + const imageTag = await readImagesJPEG(35, tag); + this._tags.push(new ImageTag({ + code: imageTag.code, + characterID: imageTag.characterId, + imgType: imageTag.imgType, + imgData: imageTag.imgData + })); + break; + + case 36: + const imageTag: any = await readImagesDefineBitsLossless(tag); + this._tags.push(new ImageTag({ + code: imageTag.code, + characterID: imageTag.characterId, + imgType: imageTag.imgType, + imgData: imageTag.imgData + })); + break; + + case 20: + console.log(tag); + break; + + case 90: + console.log(tag); + break; + + default: + //console.log(tag.header.code); + break; } - - if (tag.code === 87) { - this._tags.push(new DefineBinaryDataTag(tag)); - } - } - - const images = await Promise.all(extractImages(this.swf.tags)); - for (const image of images) { - const imgObj: any = image; - this._tags.push(new ImageTag({ - code: imgObj.code, - characterID: imgObj.characterId, - imgType: imgObj.imgType, - imgData: imgObj.imgData - })); } this.assignClassesToSymbols(); @@ -89,15 +86,6 @@ export default class HabboAssetSWF { return this._tags.filter((tag: ITag) => tag instanceof DefineBinaryDataTag).map(x => x as DefineBinaryDataTag); } - public getBinaryTagByName(name: string): DefineBinaryDataTag | null { - const streamTag = this.binaryTags() - .filter(tag => tag.className === name)[0]; - - if (streamTag === undefined) return null; - - return streamTag; - } - private assignClassesToSymbols() { const classes: Map = new Map(); @@ -137,13 +125,20 @@ export default class HabboAssetSWF { } } + public getBinaryTagByName(name: string): DefineBinaryDataTag | null { + const streamTag = this.binaryTags() + .filter(tag => tag.className === name)[0]; + + if (streamTag === undefined) return null; + + return streamTag; + } + public getFullClassName(type: string, documentNameTwice: boolean): string { return this.getFullClassNameSnake(type, documentNameTwice, false); } public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string { - if (this.swf === null) throw new Error("SWF Can't be null!"); - let result: string = this.getDocumentClass() + "_"; if (documentNameTwice) { if (snakeCase) { @@ -181,8 +176,4 @@ export default class HabboAssetSWF { } } } - - public setDocumentClass(documentClass: string) { - this._documentClass = documentClass; - } } \ No newline at end of file diff --git a/src/swf/tags/DefineBinaryDataTag.ts b/src/swf/tags/DefineBinaryDataTag.ts index c7e959d..bf1df90 100644 --- a/src/swf/tags/DefineBinaryDataTag.ts +++ b/src/swf/tags/DefineBinaryDataTag.ts @@ -1,9 +1,6 @@ -import {Tag} from "../HabboAssetSWF"; import ITag from "./ITag"; import CharacterTag from "./CharacterTag"; -const {SWFBuffer} = require('swf-extract/swf-buffer'); - export default class DefineBinaryDataTag extends CharacterTag implements ITag { private readonly _tag: number; @@ -11,15 +8,14 @@ export default class DefineBinaryDataTag extends CharacterTag implements ITag { private readonly _binaryData: string; private readonly _binaryDataBuffer: Buffer; - constructor(tag: Tag) { + constructor(buffer: Buffer) { super(); - const swfBuffer = new SWFBuffer(tag.rawData); - this._tag = swfBuffer.readUIntLE(16); - this._reserved = swfBuffer.readUIntLE(32); + this._tag = buffer.readUInt16LE(0); + this._reserved = buffer.readUInt32LE(2); const start = 6; //short 2 + int 4 - const end = tag.rawData.length; - const binary = tag.rawData.slice(start, end); + const end = buffer.length; + const binary = buffer.slice(start, end); this._binaryData = binary.toString("utf-8"); this._binaryDataBuffer = binary; diff --git a/src/swf/tags/SymbolClassTag.ts b/src/swf/tags/SymbolClassTag.ts index 3cacb14..a790ed0 100644 --- a/src/swf/tags/SymbolClassTag.ts +++ b/src/swf/tags/SymbolClassTag.ts @@ -1,30 +1,22 @@ -import {Tag} from "../HabboAssetSWF"; import ITag from "./ITag"; -const {SWFBuffer} = require('swf-extract/swf-buffer'); +export interface SymbolClass { + id: number, + name: string +} export default class SymbolClassTag implements ITag { private readonly _tags: Array; private readonly _names: Array; - constructor(tag: Tag) { + constructor(tags: Array) { this._tags = []; this._names = []; - this.init(tag); - } - - init(tag: Tag) { - const swfBuffer = new SWFBuffer(tag.rawData); - - const numSymbols = swfBuffer.readUIntLE(16); - for (let i = 0; i < numSymbols; i++) { - const tagId = swfBuffer.readUIntLE(16); - const tagName = swfBuffer.readString("utf-8"); - - this._tags.push(tagId); - this._names.push(tagName); + for (const symbolClass of tags) { + this._tags.push(symbolClass.id); + this._names.push(symbolClass.name); } } diff --git a/src/utils/SwfReader.ts b/src/utils/SwfReader.ts new file mode 100644 index 0000000..bf61a45 --- /dev/null +++ b/src/utils/SwfReader.ts @@ -0,0 +1,107 @@ +const SWFReader = require('@gizeta/swf-reader'); +const {extractImage, test} = require("swf-extract"); + +var _encoder = require('png-stream/encoder'); + +var _encoder2 = _interopRequireDefault(_encoder); + +var _zlib = require('zlib'); + +var _zlib2 = _interopRequireDefault(_zlib); + +var _streamToArray = require('stream-to-array'); + +var _streamToArray2 = _interopRequireDefault(_streamToArray); + +function _interopRequireDefault(obj: any) { return obj && obj.__esModule ? obj : { default: obj }; } + +export function readSwfAsync(path: string): Promise { + return new Promise(((resolve, reject) => { + SWFReader.read(path, function (err: Error, swf: any) { + if (err) { + reject(err); + } + resolve(swf); + }); + })); +} + + +export async function readImagesJPEG(code: number, tag: any) { + return test(code)(tag); +} + +export function readImagesDefineBitsLossless(tag: any) { + const characterId = tag.characterId, + bitmapFormat = tag.bitmapFormat, + bitmapWidth = tag.bitmapWidth, + bitmapHeight = tag.bitmapHeight, + bitmapColorTableSize = tag.bitmapColorTableSize, + zlibBitmapData = tag.zlibBitmapData; + + + return new Promise(function (resolve, reject) { + const enc = new _encoder2.default(bitmapWidth, bitmapHeight, {colorSpace: 'rgba'}); + + _zlib2.default.unzip(zlibBitmapData, function (err: any, dataBuf: any) { + if (err) { + return reject(new Error(err)); + } + var output = new Buffer(bitmapWidth * bitmapHeight * 4); + var index = 0; + var ptr = 0; + if (bitmapFormat === 5) { + // 32-bit ARGB image + for (var y = 0; y < bitmapHeight; ++y) { + for (var x = 0; x < bitmapWidth; ++x) { + var alpha = dataBuf[ptr]; + output[index] = dataBuf[ptr + 1]; + output[index + 1] = dataBuf[ptr + 2]; + output[index + 2] = dataBuf[ptr + 3]; + output[index + 3] = alpha; + index += 4; + ptr += 4; + } + } + } else if (bitmapFormat === 3) { + // 8-bit colormapped image + var colorMap = []; + for (var i = 0; i < bitmapColorTableSize + 1; ++i) { + colorMap.push([dataBuf[ptr], dataBuf[ptr + 1], dataBuf[ptr + 2], dataBuf[ptr + 3]]); + ptr += 4; + } + for (var _y2 = 0; _y2 < bitmapHeight; ++_y2) { + for (var _x2 = 0; _x2 < bitmapWidth; ++_x2) { + var idx = dataBuf[ptr]; + var color = idx < colorMap.length ? colorMap[idx] : [0, 0, 0, 0]; + output[index] = color[0]; + output[index + 1] = color[1]; + output[index + 2] = color[2]; + output[index + 3] = color[3]; + ptr += 1; + index += 4; + } + // skip padding + ptr += (4 - bitmapWidth % 4) % 4; + } + } else { + return reject(new Error('unhandled bitmapFormat: ' + bitmapFormat)); + } + enc.end(output); + }); + + (_streamToArray2.default)(enc).then(function (parts: any) { + var buffers = parts.map(function (part: any) { + return Buffer.isBuffer(part) ? part : Buffer.from(part); + }); + resolve({ + code: 36, + characterId: characterId, + imgType: 'png', + imgData: Buffer.concat(buffers) + }); + }); + }).catch(function (e) { + console.error(e); + }); +} \ No newline at end of file