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.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

8
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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
});*/

View File

@ -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"));
}

View File

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

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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<Tag>,
version: number,
fileLength: SWFFileLength,
frameSize: SWFFrameSize,
frameRate: number,
frameCount: number
}
export default class HabboAssetSWF {
private swf: SWF | null;
private readonly _tags: Array<ITag>;
private _documentClass: string | null = null;
constructor(private readonly _buffer: Buffer) {
this.swf = null;
constructor(
private readonly _path: string
) {
this._tags = new Array<ITag>();
}
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<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 {
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;
}
}

View File

@ -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;

View File

@ -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<number>;
private readonly _names: Array<string>;
constructor(tag: Tag) {
constructor(tags: Array<SymbolClass>) {
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);
}
}

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);
});
}