Weird swf reading...

This commit is contained in:
SpreedBLood 2021-02-03 04:02:14 +01:00
parent 6349a4f0d2
commit 9e983a0e21
14 changed files with 214 additions and 124 deletions

View File

@ -1,6 +1,6 @@
output.folder.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/furniture/ 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.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/ output.folder.pet=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/pet/
furnidata.url=http://assets.nitro.se/game/gamedata/furnidata-entry.xml furnidata.url=http://assets.nitro.se/game/gamedata/furnidata-entry.xml
figuremap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/figuremap.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.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.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 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.figure=0
convert.effect=1 convert.effect=0
convert.pet=1 convert.pet=0
figure.rotation.enabled=0 figure.rotation.enabled=0
figure.skip.non-existing.asset.images=0 figure.skip.non-existing.asset.images=0

8
package-lock.json generated
View File

@ -4,6 +4,14 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "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": { "@jvitela/mustache-wax": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@jvitela/mustache-wax/-/mustache-wax-1.0.3.tgz",

View File

@ -9,6 +9,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@gizeta/swf-reader": "^1.0.0",
"@types/node": "^14.14.22", "@types/node": "^14.14.22",
"bytebuffer": "^5.0.1", "bytebuffer": "^5.0.1",
"free-tex-packer-core": "^0.3.2", "free-tex-packer-core": "^0.3.2",

View File

@ -108,15 +108,15 @@ import PetConverter from "./converters/pet/PetConverter";
} }
} catch (e) { } catch (e) {
console.log(e); console.log(e);
console.log("Effect error: " + habboAssetSwf.getDocumentClass()); console.log("Pet error: " + habboAssetSwf.getDocumentClass());
} }
}); });
} }
console.log('finished!'); console.log('finished!');
/*
outputFolderFurniture.rmdir({ /*outputFolderEffect.rmdir({
recursive: true, recursive: true,
force: true force: true
});*/ });*/

View File

@ -10,7 +10,7 @@ export default class Configuration {
} }
async init() { 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")); this.parseContent(content.toString("utf-8"));
} }

View File

@ -55,7 +55,6 @@ export default class SpriteSheetConverter {
contents: imageTag.imgData contents: imageTag.imgData
}); });
} }
} }
if (images.length === 0) { if (images.length === 0) {

View File

@ -44,12 +44,11 @@ export default class EffectDownloader {
} }
try { try {
const buffer: Buffer = await readFile(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url);
const habboAssetSWF = new HabboAssetSWF(buffer); await newHabboAssetSWF.setupAsync();
await habboAssetSWF.setupAsync();
EffectDownloader.types.set(className, info.type); EffectDownloader.types.set(className, info.type);
await callback(habboAssetSWF); await callback(newHabboAssetSWF);
} catch (e) { } catch (e) {
console.log(className); console.log(className);
console.log(e); console.log(e);

View File

@ -48,12 +48,11 @@ export default class FigureDownloader {
return; return;
} }
const buffer: Buffer = await readFile(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url);
const habboAssetSWF = new HabboAssetSWF(buffer); await newHabboAssetSWF.setupAsync();
await habboAssetSWF.setupAsync();
FigureDownloader.types.set(className, lib.part[0]['$'].type); FigureDownloader.types.set(className, lib.part[0]['$'].type);
await callback(habboAssetSWF); await callback(newHabboAssetSWF);
} }
} }
} }

View File

@ -73,11 +73,10 @@ export default class FurnitureDownloader {
} }
try { try {
const buffer: Buffer = await readFile(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url);
const habboAssetSWF = new HabboAssetSWF(buffer); await newHabboAssetSWF.setupAsync();
await habboAssetSWF.setupAsync();
await callback(habboAssetSWF, className); await callback(newHabboAssetSWF, className);
} catch (e) { } catch (e) {
console.log("Error with furniture: " + url); console.log("Error with furniture: " + url);
console.log(e); console.log(e);

View File

@ -36,11 +36,10 @@ export default class PetDownloader {
continue; continue;
} }
const buffer: Buffer = await readFile(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url);
const habboAssetSWF = new HabboAssetSWF(buffer); await newHabboAssetSWF.setupAsync();
await habboAssetSWF.setupAsync();
await callback(habboAssetSWF); await callback(newHabboAssetSWF);
} }
itemClassNames.push(pet); itemClassNames.push(pet);

View File

@ -1,77 +1,74 @@
import SymbolClassTag from "./tags/SymbolClassTag"; import {readImagesDefineBitsLossless, readImagesJPEG, readSwfAsync} from "../utils/SwfReader";
import ImageTag from "./tags/ImageTag";
import ITag from "./tags/ITag"; 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 CustomIterator from "../utils/CustomIterator";
import CharacterTag from "./tags/CharacterTag"; 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<Tag>,
version: number,
fileLength: SWFFileLength,
frameSize: SWFFrameSize,
frameRate: number,
frameCount: number
}
export default class HabboAssetSWF { export default class HabboAssetSWF {
private swf: SWF | null;
private readonly _tags: Array<ITag>; private readonly _tags: Array<ITag>;
private _documentClass: string | null = null; private _documentClass: string | null = null;
constructor(private readonly _buffer: Buffer) { constructor(
this.swf = null; private readonly _path: string
) {
this._tags = new Array<ITag>(); this._tags = new Array<ITag>();
} }
public async setupAsync() { async setupAsync() {
this.swf = await readFromBufferP(this._buffer); 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) { case 6:
if (tag.code === 76) { console.log(tag);
this._tags.push(new SymbolClassTag(tag)); break;
}
if (tag.code === 87) { case 21:
this._tags.push(new DefineBinaryDataTag(tag)); console.log(tag);
} break;
}
const images = await Promise.all(extractImages(this.swf.tags)); case 35:
for (const image of images) { const imageTag = await readImagesJPEG(35, tag);
const imgObj: any = image;
this._tags.push(new ImageTag({ this._tags.push(new ImageTag({
code: imgObj.code, code: imageTag.code,
characterID: imgObj.characterId, characterID: imageTag.characterId,
imgType: imgObj.imgType, imgType: imageTag.imgType,
imgData: imgObj.imgData 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;
}
} }
this.assignClassesToSymbols(); this.assignClassesToSymbols();
@ -89,15 +86,6 @@ export default class HabboAssetSWF {
return this._tags.filter((tag: ITag) => tag instanceof DefineBinaryDataTag).map(x => x as DefineBinaryDataTag); 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() { private assignClassesToSymbols() {
const classes: Map<number, string> = new Map(); const classes: Map<number, string> = 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 { public getFullClassName(type: string, documentNameTwice: boolean): string {
return this.getFullClassNameSnake(type, documentNameTwice, false); return this.getFullClassNameSnake(type, documentNameTwice, false);
} }
public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string { 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() + "_"; let result: string = this.getDocumentClass() + "_";
if (documentNameTwice) { if (documentNameTwice) {
if (snakeCase) { if (snakeCase) {
@ -181,8 +176,4 @@ export default class HabboAssetSWF {
} }
} }
} }
public setDocumentClass(documentClass: string) {
this._documentClass = documentClass;
}
} }

View File

@ -1,9 +1,6 @@
import {Tag} from "../HabboAssetSWF";
import ITag from "./ITag"; import ITag from "./ITag";
import CharacterTag from "./CharacterTag"; import CharacterTag from "./CharacterTag";
const {SWFBuffer} = require('swf-extract/swf-buffer');
export default class DefineBinaryDataTag extends CharacterTag implements ITag { export default class DefineBinaryDataTag extends CharacterTag implements ITag {
private readonly _tag: number; private readonly _tag: number;
@ -11,15 +8,14 @@ export default class DefineBinaryDataTag extends CharacterTag implements ITag {
private readonly _binaryData: string; private readonly _binaryData: string;
private readonly _binaryDataBuffer: Buffer; private readonly _binaryDataBuffer: Buffer;
constructor(tag: Tag) { constructor(buffer: Buffer) {
super(); super();
const swfBuffer = new SWFBuffer(tag.rawData); this._tag = buffer.readUInt16LE(0);
this._tag = swfBuffer.readUIntLE(16); this._reserved = buffer.readUInt32LE(2);
this._reserved = swfBuffer.readUIntLE(32);
const start = 6; //short 2 + int 4 const start = 6; //short 2 + int 4
const end = tag.rawData.length; const end = buffer.length;
const binary = tag.rawData.slice(start, end); const binary = buffer.slice(start, end);
this._binaryData = binary.toString("utf-8"); this._binaryData = binary.toString("utf-8");
this._binaryDataBuffer = binary; this._binaryDataBuffer = binary;

View File

@ -1,30 +1,22 @@
import {Tag} from "../HabboAssetSWF";
import ITag from "./ITag"; import ITag from "./ITag";
const {SWFBuffer} = require('swf-extract/swf-buffer'); export interface SymbolClass {
id: number,
name: string
}
export default class SymbolClassTag implements ITag { export default class SymbolClassTag implements ITag {
private readonly _tags: Array<number>; private readonly _tags: Array<number>;
private readonly _names: Array<string>; private readonly _names: Array<string>;
constructor(tag: Tag) { constructor(tags: Array<SymbolClass>) {
this._tags = []; this._tags = [];
this._names = []; this._names = [];
this.init(tag); for (const symbolClass of tags) {
} this._tags.push(symbolClass.id);
this._names.push(symbolClass.name);
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);
} }
} }

107
src/utils/SwfReader.ts Normal file
View File

@ -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<any> {
return new Promise<any>(((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);
});
}