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 () => (async () =>
{ {
checkNodeVersion();
const config = container.resolve(Configuration); const config = container.resolve(Configuration);
await config.init(); await config.init();
@ -30,3 +31,13 @@ import { ProductDataConverter } from './converters/productdata/ProductDataConver
await converter.convertAsync(); 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 CustomIterator from '../utils/CustomIterator';
import { readImagesDefineBitsLossless, readImagesJPEG, readSwfAsync } from '../utils/SwfReader'; import {readImagesDefineBitsLossless, readImagesJPEG3or4, readSwfAsync} from '../utils/SwfReader';
import {CharacterTag} from './tags/CharacterTag'; import {CharacterTag} from './tags/CharacterTag';
import {DefineBinaryDataTag} from './tags/DefineBinaryDataTag'; import {DefineBinaryDataTag} from './tags/DefineBinaryDataTag';
import {ImageTag} from './tags/ImageTag'; import {ImageTag} from './tags/ImageTag';
import {ITag} from './tags/ITag'; import {ITag} from './tags/ITag';
import {SymbolClassTag} from './tags/SymbolClassTag'; import {SymbolClassTag} from './tags/SymbolClassTag';
import {writeFileSync} from "fs";
export class HabboAssetSWF export class HabboAssetSWF {
{
private readonly _tags: Array<ITag>; private readonly _tags: Array<ITag>;
private _documentClass: string | null = null; private _documentClass: string | null = null;
constructor( constructor(
private readonly _data: string | Buffer private readonly _data: string | Buffer
) ) {
{
this._tags = new Array<ITag>(); this._tags = new Array<ITag>();
} }
async setupAsync() async setupAsync() {
{
const swf = await readSwfAsync(this._data); 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: case 76:
this._tags.push(new SymbolClassTag(tag.symbols)); this._tags.push(new SymbolClassTag(tag.symbols));
break; break;
@ -37,39 +33,57 @@ export class HabboAssetSWF
console.log(tag); console.log(tag);
break; break;
case 21: case 21: {
console.log(tag); 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; break;
}
case 35: { case 35: {
const jpegTag = await readImagesJPEG(35, tag); const jpeg3 = await readImagesJPEG3or4(35, tag);
this._tags.push(new ImageTag({ this._tags.push(new ImageTag({
code: jpegTag.code, code: jpeg3.code,
characterID: jpegTag.characterId, characterID: jpeg3.characterId,
imgType: jpegTag.imgType, imgType: jpeg3.imgType,
imgData: jpegTag.imgData, imgData: jpeg3.imgData,
bitmapWidth: jpegTag.bitmapWidth, bitmapWidth: jpeg3.bitmapWidth,
bitmapHeight: jpegTag.bitmapHeight bitmapHeight: jpeg3.bitmapHeight
})); }));
break; break;
} }
case 36: { case 36: {
const pngTag: any = await readImagesDefineBitsLossless(tag); const pngTagLossLess2: any = await readImagesDefineBitsLossless(tag);
this._tags.push(new ImageTag({ this._tags.push(new ImageTag({
code: pngTag.code, code: pngTagLossLess2.code,
characterID: pngTag.characterId, characterID: pngTagLossLess2.characterId,
imgType: pngTag.imgType, imgType: pngTagLossLess2.imgType,
imgData: pngTag.imgData, imgData: pngTagLossLess2.imgData,
bitmapWidth: pngTag.bitmapWidth, bitmapWidth: pngTagLossLess2.bitmapWidth,
bitmapHeight: pngTag.bitmapHeight bitmapHeight: pngTagLossLess2.bitmapHeight
})); }));
break; break;
} }
case 20: case 20: {
console.log(tag); 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; break;
}
case 90: case 90:
console.log(tag); console.log(tag);
@ -84,47 +98,37 @@ export class HabboAssetSWF
this.assignClassesToSymbols(); this.assignClassesToSymbols();
} }
public imageTags(): Array<ImageTag> public imageTags(): Array<ImageTag> {
{
return this._tags.filter((tag: ITag) => tag instanceof ImageTag).map(x => x as 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); 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); 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(); const classes: Map<number, string> = new Map();
let iterator: CustomIterator<ITag> = new CustomIterator(this._tags); let iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while(true) while (true) {
{
let t: ITag; let t: ITag;
do do {
{ if (!iterator.hasNext()) {
if(!iterator.hasNext())
{
iterator = new CustomIterator(this._tags); iterator = new CustomIterator(this._tags);
while(iterator.hasNext()) while (iterator.hasNext()) {
{
t = iterator.next(); t = iterator.next();
if(t instanceof CharacterTag) if (t instanceof CharacterTag) {
{
const ct = t as CharacterTag; const ct = t as CharacterTag;
if(classes.has(ct.characterId)) if (classes.has(ct.characterId)) {
{
// @ts-ignore // @ts-ignore
ct.className = classes.get(ct.characterId); ct.className = classes.get(ct.characterId);
} }
@ -139,18 +143,15 @@ export class HabboAssetSWF
const sct = t as SymbolClassTag; const sct = t as SymbolClassTag;
for(let i = 0; i < sct.tags.length; ++i) for (let i = 0; i < sct.tags.length; ++i) {
{ if (!classes.has(sct.tags[i]) && !Array.from(classes.values()).includes(sct.names[i])) {
if(!classes.has(sct.tags[i]) && !Array.from(classes.values()).includes(sct.names[i]))
{
classes.set(sct.tags[i], 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() const streamTag = this.binaryTags()
.filter(tag => tag.className === name)[0]; .filter(tag => tag.className === name)[0];
@ -159,22 +160,16 @@ export class HabboAssetSWF
return streamTag; 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 {
{
let result: string = this.getDocumentClass() + '_'; let result: string = this.getDocumentClass() + '_';
if(documentNameTwice) if (documentNameTwice) {
{ if (snakeCase) {
if(snakeCase)
{
//result += CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, this.swf.getDocumentClass()) + "_"; //result += CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, this.swf.getDocumentClass()) + "_";
} } else {
else
{
result += this.getDocumentClass() + '_'; result += this.getDocumentClass() + '_';
} }
} }
@ -182,20 +177,16 @@ export class HabboAssetSWF
return result + type; return result + type;
} }
public getDocumentClass(): string public getDocumentClass(): string {
{
if (this._documentClass !== null) return this._documentClass; if (this._documentClass !== null) return this._documentClass;
const iterator: CustomIterator<ITag> = new CustomIterator(this._tags); const iterator: CustomIterator<ITag> = new CustomIterator(this._tags);
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while(true) while (true) {
{
let t: ITag; let t: ITag;
do do {
{ if (!iterator.hasNext()) {
if(!iterator.hasNext())
{
return ''; return '';
} }
@ -204,10 +195,8 @@ export class HabboAssetSWF
const sc = t as SymbolClassTag; const sc = t as SymbolClassTag;
for(let i = 0; i < sc.tags.length; ++i) for (let i = 0; i < sc.tags.length; ++i) {
{ if (sc.tags[i] == 0) {
if(sc.tags[i] == 0)
{
this._documentClass = sc.names[i]; this._documentClass = sc.names[i];
return this._documentClass; return this._documentClass;
} }

View File

@ -1,3 +1,5 @@
import {writeFileSync} from "fs";
const SWFReader = require('@gizeta/swf-reader'); const SWFReader = require('@gizeta/swf-reader');
const _encoder = require('png-stream/encoder'); 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 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 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) const recognizeHeader = function recognizeHeader(buffer: Buffer)
{ {
if(pngMagic.equals(buffer.slice(0, pngMagic.length))) return 'png'; if(pngMagic.equals(buffer.slice(0, pngMagic.length))) return 'png';
if(gifMagic.equals(buffer.slice(0, gifMagic.length))) return 'gif'; 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, const characterId = tagData.characterId,
imageData = tagData.imageData; 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 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. 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)); return reject(new Error(err));
} }