JPEG Magic + node version check

This commit is contained in:
SpreedBLood 2021-02-25 00:24:09 +01:00
parent 96825fb963
commit cd5f4a093c
5 changed files with 103 additions and 3999 deletions

3894
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ import { ProductDataConverter } from './converters/productdata/ProductDataConver
(async () =>
{
checkNodeVersion();
const config = container.resolve(Configuration);
await config.init();
@ -30,3 +31,13 @@ import { ProductDataConverter } from './converters/productdata/ProductDataConver
await converter.convertAsync();
}
})();
function checkNodeVersion()
{
const version = process.version.replace('v', '');
const major = version.split('.')[0];
if(parseInt(major) < 14)
{
throw new Error('Invalid node version: ' + version + ' please use >= 14');
}
}

View File

@ -1,21 +0,0 @@
{
"output.folder": "C:/nitro-converted-assets/",
"flash.client.url": "",
"furnidata.load.url": "",
"productdata.load.url": "",
"figuremap.load.url": "${flash.client.url}figuremap.xml",
"effectmap.load.url": "${flash.client.url}effectmap.xml",
"dynamic.download.pet.url": "${flash.client.url}%className%.swf",
"dynamic.download.figure.url": "${flash.client.url}%className%.swf",
"dynamic.download.effect.url": "${flash.client.url}%className%.swf",
"flash.dynamic.download.url": "",
"dynamic.download.furniture.url": "${flash.dynamic.download.url}%revision%/%className%.swf",
"external.variables.url": "https://www.habbo.com/gamedata/external_variables/1",
"external.texts.url": "${external.texts.txt}",
"convert.productdata": "1",
"convert.externaltexts": "1",
"convert.figure": "1",
"convert.effect": "1",
"convert.furniture": "1",
"convert.pet": "1"
}

View File

@ -1,31 +1,27 @@
import CustomIterator from '../utils/CustomIterator';
import { readImagesDefineBitsLossless, readImagesJPEG, readSwfAsync } from '../utils/SwfReader';
import { CharacterTag } from './tags/CharacterTag';
import { DefineBinaryDataTag } from './tags/DefineBinaryDataTag';
import { ImageTag } from './tags/ImageTag';
import { ITag } from './tags/ITag';
import { SymbolClassTag } from './tags/SymbolClassTag';
import {readImagesDefineBitsLossless, readImagesJPEG3or4, readSwfAsync} from '../utils/SwfReader';
import {CharacterTag} from './tags/CharacterTag';
import {DefineBinaryDataTag} from './tags/DefineBinaryDataTag';
import {ImageTag} from './tags/ImageTag';
import {ITag} from './tags/ITag';
import {SymbolClassTag} from './tags/SymbolClassTag';
import {writeFileSync} from "fs";
export class HabboAssetSWF
{
export class HabboAssetSWF {
private readonly _tags: Array<ITag>;
private _documentClass: string | null = null;
constructor(
private readonly _data: string | Buffer
)
{
) {
this._tags = new Array<ITag>();
}
async setupAsync()
{
async setupAsync() {
const swf = await readSwfAsync(this._data);
for(const tag of swf.tags)
{
for (const tag of swf.tags) {
switch(tag.header.code)
{
switch (tag.header.code) {
case 76:
this._tags.push(new SymbolClassTag(tag.symbols));
break;
@ -37,39 +33,57 @@ export class HabboAssetSWF
console.log(tag);
break;
case 21:
console.log(tag);
case 21: {
const jpeg3 = await readImagesJPEG3or4(21, tag);
this._tags.push(new ImageTag({
code: 21,
characterID: jpeg3.characterId,
imgType: 'jpeg',
imgData: jpeg3.imageData,
bitmapWidth: jpeg3.bitmapWidth,
bitmapHeight: jpeg3.bitmapHeight
}));
break;
}
case 35: {
const jpegTag = await readImagesJPEG(35, tag);
const jpeg3 = await readImagesJPEG3or4(35, tag);
this._tags.push(new ImageTag({
code: jpegTag.code,
characterID: jpegTag.characterId,
imgType: jpegTag.imgType,
imgData: jpegTag.imgData,
bitmapWidth: jpegTag.bitmapWidth,
bitmapHeight: jpegTag.bitmapHeight
code: jpeg3.code,
characterID: jpeg3.characterId,
imgType: jpeg3.imgType,
imgData: jpeg3.imgData,
bitmapWidth: jpeg3.bitmapWidth,
bitmapHeight: jpeg3.bitmapHeight
}));
break;
}
case 36: {
const pngTag: any = await readImagesDefineBitsLossless(tag);
const pngTagLossLess2: any = await readImagesDefineBitsLossless(tag);
this._tags.push(new ImageTag({
code: pngTag.code,
characterID: pngTag.characterId,
imgType: pngTag.imgType,
imgData: pngTag.imgData,
bitmapWidth: pngTag.bitmapWidth,
bitmapHeight: pngTag.bitmapHeight
code: pngTagLossLess2.code,
characterID: pngTagLossLess2.characterId,
imgType: pngTagLossLess2.imgType,
imgData: pngTagLossLess2.imgData,
bitmapWidth: pngTagLossLess2.bitmapWidth,
bitmapHeight: pngTagLossLess2.bitmapHeight
}));
break;
}
case 20:
console.log(tag);
case 20: {
const pngTagLossless: any = await readImagesDefineBitsLossless(tag);
this._tags.push(new ImageTag({
code: pngTagLossless.code,
characterID: pngTagLossless.characterId,
imgType: pngTagLossless.imgType,
imgData: pngTagLossless.imgData,
bitmapWidth: pngTagLossless.bitmapWidth,
bitmapHeight: pngTagLossless.bitmapHeight
}));
break;
}
case 90:
console.log(tag);
@ -84,47 +98,37 @@ export class HabboAssetSWF
this.assignClassesToSymbols();
}
public imageTags(): Array<ImageTag>
{
public imageTags(): Array<ImageTag> {
return this._tags.filter((tag: ITag) => tag instanceof ImageTag).map(x => x as ImageTag);
}
public symbolTags(): Array<SymbolClassTag>
{
public symbolTags(): Array<SymbolClassTag> {
return this._tags.filter((tag: ITag) => tag instanceof SymbolClassTag).map(x => x as SymbolClassTag);
}
private binaryTags(): Array<DefineBinaryDataTag>
{
private binaryTags(): Array<DefineBinaryDataTag> {
return this._tags.filter((tag: ITag) => tag instanceof DefineBinaryDataTag).map(x => x as DefineBinaryDataTag);
}
private assignClassesToSymbols()
{
private assignClassesToSymbols() {
const classes: Map<number, string> = new Map();
let iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
// eslint-disable-next-line no-constant-condition
while(true)
{
while (true) {
let t: ITag;
do
{
if(!iterator.hasNext())
{
do {
if (!iterator.hasNext()) {
iterator = new CustomIterator(this._tags);
while(iterator.hasNext())
{
while (iterator.hasNext()) {
t = iterator.next();
if(t instanceof CharacterTag)
{
if (t instanceof CharacterTag) {
const ct = t as CharacterTag;
if(classes.has(ct.characterId))
{
if (classes.has(ct.characterId)) {
// @ts-ignore
ct.className = classes.get(ct.characterId);
}
@ -135,46 +139,37 @@ export class HabboAssetSWF
}
t = iterator.next();
} while(!(t instanceof SymbolClassTag));
} while (!(t instanceof SymbolClassTag));
const sct = t as SymbolClassTag;
for(let i = 0; i < sct.tags.length; ++i)
{
if(!classes.has(sct.tags[i]) && !Array.from(classes.values()).includes(sct.names[i]))
{
for (let i = 0; i < sct.tags.length; ++i) {
if (!classes.has(sct.tags[i]) && !Array.from(classes.values()).includes(sct.names[i])) {
classes.set(sct.tags[i], sct.names[i]);
}
}
}
}
public getBinaryTagByName(name: string): DefineBinaryDataTag | null
{
public getBinaryTagByName(name: string): DefineBinaryDataTag | null {
const streamTag = this.binaryTags()
.filter(tag => tag.className === name)[0];
if(streamTag === undefined) return null;
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);
}
public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string
{
public getFullClassNameSnake(type: string, documentNameTwice: boolean, snakeCase: boolean): string {
let result: string = this.getDocumentClass() + '_';
if(documentNameTwice)
{
if(snakeCase)
{
if (documentNameTwice) {
if (snakeCase) {
//result += CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, this.swf.getDocumentClass()) + "_";
}
else
{
} else {
result += this.getDocumentClass() + '_';
}
}
@ -182,32 +177,26 @@ export class HabboAssetSWF
return result + type;
}
public getDocumentClass(): string
{
if(this._documentClass !== null) return this._documentClass;
public getDocumentClass(): string {
if (this._documentClass !== null) return this._documentClass;
const iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
// eslint-disable-next-line no-constant-condition
while(true)
{
while (true) {
let t: ITag;
do
{
if(!iterator.hasNext())
{
do {
if (!iterator.hasNext()) {
return '';
}
t = iterator.next();
} while(!(t instanceof SymbolClassTag));
} while (!(t instanceof SymbolClassTag));
const sc = t as SymbolClassTag;
for(let i = 0; i < sc.tags.length; ++i)
{
if(sc.tags[i] == 0)
{
for (let i = 0; i < sc.tags.length; ++i) {
if (sc.tags[i] == 0) {
this._documentClass = sc.names[i];
return this._documentClass;
}

View File

@ -1,3 +1,5 @@
import {writeFileSync} from "fs";
const SWFReader = require('@gizeta/swf-reader');
const _encoder = require('png-stream/encoder');
@ -98,14 +100,17 @@ export function readSwfAsync(data: string | Buffer): Promise<any>
const pngMagic = Buffer.from('0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A'.split(' ').map(Number));
const gifMagic = Buffer.from('0x47 0x49 0x46 0x38 0x39 0x61'.split(' ').map(Number));
const jpegMagic = Buffer.from('0xFF 0xD8'.split(' ').map(Number));
const recognizeHeader = function recognizeHeader(buffer: Buffer)
{
if(pngMagic.equals(buffer.slice(0, pngMagic.length))) return 'png';
if(gifMagic.equals(buffer.slice(0, gifMagic.length))) return 'gif';
return 'jpeg';
if(jpegMagic.equals(buffer.slice(0, jpegMagic.length))) return 'jpeg';
throw new Error('unknown format: ' + buffer.slice(0, 8));
};
export async function readImagesJPEG(code: number, tagData: any): Promise<any>
export async function readImagesJPEG3or4(code: number, tagData: any): Promise<any>
{
const characterId = tagData.characterId,
imageData = tagData.imageData;
@ -141,7 +146,7 @@ export async function readImagesJPEG(code: number, tagData: any): Promise<any>
other two zlib.unzip call happens at sites that an empty uncompressed Buffer
does not make sense. So I think the current fix is good enough.
*/
if(bitmapAlphaData.length > 0)
if(bitmapAlphaData && bitmapAlphaData.length > 0)
{
return reject(new Error(err));
}