Support for both urls

This commit is contained in:
SpreedBLood 2021-02-03 19:02:10 +01:00
parent 9e983a0e21
commit bc60a2b244
10 changed files with 252 additions and 78 deletions

View File

@ -1,18 +1,18 @@
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-test/
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-test/
output.folder.effect=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/effect-test/ 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-test/
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
effectmap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/effectmap.xml effectmap.url=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/effectmap.xml
external_vars.url=http://assets.nitro.se/game/gamedata/external_variables.txt external_vars.url=http://assets.nitro.se/game/gamedata/external_variables.txt
dynamic.download.url.furniture=/home/user/WebstormProjects/sites/assets.nitro.se/game/dcr/endrit/hof_furni/%className%.swf dynamic.download.url.furniture=http://assets.nitro.se/game/dcr/endrit/hof_furni/%className%.swf
dynamic.download.url.figure=/home/user/WebstormProjects/sites/assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf dynamic.download.url.figure=http://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=http://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=http://assets.nitro.se/game/gordon/PRODUCTION-201701242205-837386173/%className%.swf
convert.furniture=1 convert.furniture=0
convert.figure=0 convert.figure=0
convert.effect=0 convert.effect=1
convert.pet=0 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

12
package-lock.json generated
View File

@ -678,18 +678,6 @@
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}, },
"swf-extract": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/swf-extract/-/swf-extract-1.1.0.tgz",
"integrity": "sha1-DS6Q01lKFu9ly8hfuEEiRhdz9t0=",
"requires": {
"concat-frames": "^1.0.3",
"jpg-stream": "^1.1.1",
"lzma-purejs": "~0.9.3",
"png-stream": "^1.0.5",
"stream-to-array": "^2.3.0"
}
},
"tinify": { "tinify": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/tinify/-/tinify-1.5.0.tgz", "resolved": "https://registry.npmjs.org/tinify/-/tinify-1.5.0.tgz",

View File

@ -12,11 +12,14 @@
"@gizeta/swf-reader": "^1.0.0", "@gizeta/swf-reader": "^1.0.0",
"@types/node": "^14.14.22", "@types/node": "^14.14.22",
"bytebuffer": "^5.0.1", "bytebuffer": "^5.0.1",
"concat-frames": "^1.0.3",
"free-tex-packer-core": "^0.3.2", "free-tex-packer-core": "^0.3.2",
"jpg-stream": "^1.1.2",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"node-gzip": "^1.1.2", "node-gzip": "^1.1.2",
"swf-extract": "^1.1.0", "png-stream": "^1.0.5",
"stream-to-array": "^2.3.0",
"xml2js": "^0.4.23" "xml2js": "^0.4.23"
} }
} }

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 (copy)/config.ini"); const content = await fs.readFile("/home/user/git/nitro-asset-converter-node/config.ini");
this.parseContent(content.toString("utf-8")); this.parseContent(content.toString("utf-8"));
} }

View File

@ -2,13 +2,9 @@ import Configuration from "../config/Configuration";
import HabboAssetSWF from "../swf/HabboAssetSWF"; import HabboAssetSWF from "../swf/HabboAssetSWF";
import File from "../utils/File"; import File from "../utils/File";
const fs = require("fs");
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const xml2js = require('xml2js'); const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */); const parser = new xml2js.Parser(/* options */);
const util = require('util');
const readFile = util.promisify(fs.readFile);
export default class EffectDownloader { export default class EffectDownloader {
private readonly _config: Configuration; private readonly _config: Configuration;
@ -25,12 +21,11 @@ export default class EffectDownloader {
const figureMap = await this.parseEffectMap(); const figureMap = await this.parseEffectMap();
const map = figureMap.map; const map = figureMap.map;
let count = 0;
for (const lib of map.effect) { for (const lib of map.effect) {
const info = lib['$']; const info = lib['$'];
const className: string = info.lib; const className: string = info.lib;
//if (className !== 'Hoverboard' && className !== 'Staff' && className !== 'ZombieMask' && className !== 'ESredUntouchable') continue;
const assetOutputFolder = new File(outputFolderEffect + "/" + className); const assetOutputFolder = new File(outputFolderEffect + "/" + className);
if (assetOutputFolder.exists()) { if (assetOutputFolder.exists()) {
continue; continue;
@ -38,13 +33,27 @@ export default class EffectDownloader {
if (!EffectDownloader.types.has(className)) { if (!EffectDownloader.types.has(className)) {
const url = this._config.getValue("dynamic.download.url.effect").replace("%className%", className); const url = this._config.getValue("dynamic.download.url.effect").replace("%className%", className);
if (!fs.existsSync(url)) { let buffer: Buffer | null = null;
if (url.includes("http")) {
const fetchData = await fetch(url);
if (fetchData.status === 404) {
console.log("SWF File does not exist: " + url); console.log("SWF File does not exist: " + url);
continue; continue;
} }
const arrayBuffer = await fetchData.arrayBuffer();
buffer = Buffer.from(arrayBuffer);
} else {
const file = new File(url);
if (!file.exists()) {
console.log("SWF File does not exist: " + file.path);
return;
}
}
try { try {
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(buffer !== null ? buffer : url);
await newHabboAssetSWF.setupAsync(); await newHabboAssetSWF.setupAsync();
EffectDownloader.types.set(className, info.type); EffectDownloader.types.set(className, info.type);

View File

@ -6,9 +6,6 @@ const fs = require("fs");
const fetch = require('node-fetch'); const fetch = require('node-fetch');
const xml2js = require('xml2js'); const xml2js = require('xml2js');
const parser = new xml2js.Parser(/* options */); const parser = new xml2js.Parser(/* options */);
const util = require('util');
const readFile = util.promisify(fs.readFile);
export default class FigureDownloader { export default class FigureDownloader {
@ -43,12 +40,26 @@ export default class FigureDownloader {
className !== "jacket_U_snowwar4_team2") { //TODO: Figure out why snowstorm assets aren't converting... className !== "jacket_U_snowwar4_team2") { //TODO: Figure out why snowstorm assets aren't converting...
const url = this._config.getValue("dynamic.download.url.figure").replace("%className%", className); const url = this._config.getValue("dynamic.download.url.figure").replace("%className%", className);
if (!fs.existsSync(url)) { let buffer: Buffer | null = null;
if (url.includes("http")) {
const fetchData = await fetch(url);
if (fetchData.status === 404) {
console.log("SWF File does not exist: " + url); console.log("SWF File does not exist: " + url);
return; continue;
} }
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); const arrayBuffer = await fetchData.arrayBuffer();
buffer = Buffer.from(arrayBuffer);
} else {
const file = new File(url);
if (!file.exists()) {
console.log("SWF File does not exist: " + file.path);
return;
}
}
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(buffer !== null ? buffer : url);
await newHabboAssetSWF.setupAsync(); await newHabboAssetSWF.setupAsync();
FigureDownloader.types.set(className, lib.part[0]['$'].type); FigureDownloader.types.set(className, lib.part[0]['$'].type);

View File

@ -29,12 +29,16 @@ export default class FurnitureDownloader {
const roomitemtypes = furniData.roomitemtypes; const roomitemtypes = furniData.roomitemtypes;
const wallitemtypes = furniData.wallitemtypes; const wallitemtypes = furniData.wallitemtypes;
const classNames: Array<string> = new Array<string>();
for (const roomItem of roomitemtypes) { for (const roomItem of roomitemtypes) {
for (const furnitype of roomItem.furnitype) { for (const furnitype of roomItem.furnitype) {
const attributes = furnitype['$']; const attributes = furnitype['$'];
const className = attributes.classname.split("\*")[0]; const className = attributes.classname.split("\*")[0];
const revision = furnitype.revision[0]; const revision = furnitype.revision[0];
if (classNames.includes(className)) continue;
else classNames.push(className);
const assetOuputFolder = new File(outputFolderFurniture.path + "/" + className); const assetOuputFolder = new File(outputFolderFurniture.path + "/" + className);
if (assetOuputFolder.isDirectory()) { if (assetOuputFolder.isDirectory()) {
continue; continue;
@ -49,6 +53,8 @@ export default class FurnitureDownloader {
const attributes = furnitype['$']; const attributes = furnitype['$'];
const className = attributes.classname.split("\*")[0]; const className = attributes.classname.split("\*")[0];
const revision = furnitype.revision[0]; const revision = furnitype.revision[0];
if (classNames.includes(className)) continue;
else classNames.push(className);
const assetOuputFolder = new File(outputFolderFurniture + "/" + className); const assetOuputFolder = new File(outputFolderFurniture + "/" + className);
if (assetOuputFolder.isDirectory()) { if (assetOuputFolder.isDirectory()) {
@ -66,14 +72,27 @@ export default class FurnitureDownloader {
//if (className !== 'scifidoor') return; //if (className !== 'scifidoor') return;
const url = this._config.getValue("dynamic.download.url.furniture").replace("%revision%", revision).replace("%className%", className); const url = this._config.getValue("dynamic.download.url.furniture").replace("%revision%", revision).replace("%className%", className);
let buffer: Buffer | null = null;
if (url.includes("http")) {
const fetchData = await fetch(url);
if (fetchData.status === 404) {
console.log("SWF File does not exist: " + url);
return;
}
const arrayBuffer = await fetchData.arrayBuffer();
buffer = Buffer.from(arrayBuffer);
} else {
const file = new File(url); const file = new File(url);
if (!file.exists()) { if (!file.exists()) {
console.log("SWF File does not exist: " + file.path); console.log("SWF File does not exist: " + file.path);
return; return;
} }
}
try { try {
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(buffer !== null ? buffer : url);
await newHabboAssetSWF.setupAsync(); await newHabboAssetSWF.setupAsync();
await callback(newHabboAssetSWF, className); await callback(newHabboAssetSWF, className);

View File

@ -2,9 +2,7 @@ import Configuration from "../config/Configuration";
import HabboAssetSWF from "../swf/HabboAssetSWF"; import HabboAssetSWF from "../swf/HabboAssetSWF";
import File from "../utils/File"; import File from "../utils/File";
const util = require('util'); const fetch = require('node-fetch');
const fs = require("fs");
const readFile = util.promisify(fs.readFile);
export default class PetDownloader { export default class PetDownloader {
private readonly _config: Configuration; private readonly _config: Configuration;
@ -30,13 +28,26 @@ export default class PetDownloader {
if (!itemClassNames.includes(pet)) { if (!itemClassNames.includes(pet)) {
const url = this._config.getValue("dynamic.download.url.pet").replace("%className%", pet); const url = this._config.getValue("dynamic.download.url.pet").replace("%className%", pet);
const file = new File(url); let buffer: Buffer | null = null;
if (!file.exists()) {
console.log("SWF File does not exist: " + file.path); if (url.includes("http")) {
const fetchData = await fetch(url);
if (fetchData.status === 404) {
console.log("SWF File does not exist: " + url);
continue; continue;
} }
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(url); const arrayBuffer = await fetchData.arrayBuffer();
buffer = Buffer.from(arrayBuffer);
} else {
const file = new File(url);
if (!file.exists()) {
console.log("SWF File does not exist: " + file.path);
return;
}
}
const newHabboAssetSWF: HabboAssetSWF = new HabboAssetSWF(buffer !== null ? buffer : url);
await newHabboAssetSWF.setupAsync(); await newHabboAssetSWF.setupAsync();
await callback(newHabboAssetSWF); await callback(newHabboAssetSWF);

View File

@ -12,13 +12,13 @@ export default class HabboAssetSWF {
private _documentClass: string | null = null; private _documentClass: string | null = null;
constructor( constructor(
private readonly _path: string private readonly _data: string | Buffer
) { ) {
this._tags = new Array<ITag>(); this._tags = new Array<ITag>();
} }
async setupAsync() { async setupAsync() {
const swf = await readSwfAsync(this._path); 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) {
@ -38,22 +38,22 @@ export default class HabboAssetSWF {
break; break;
case 35: case 35:
const imageTag = await readImagesJPEG(35, tag); const jpegTag = await readImagesJPEG(35, tag);
this._tags.push(new ImageTag({ this._tags.push(new ImageTag({
code: imageTag.code, code: jpegTag.code,
characterID: imageTag.characterId, characterID: jpegTag.characterId,
imgType: imageTag.imgType, imgType: jpegTag.imgType,
imgData: imageTag.imgData imgData: jpegTag.imgData
})); }));
break; break;
case 36: case 36:
const imageTag: any = await readImagesDefineBitsLossless(tag); const pngTag: any = await readImagesDefineBitsLossless(tag);
this._tags.push(new ImageTag({ this._tags.push(new ImageTag({
code: imageTag.code, code: pngTag.code,
characterID: imageTag.characterId, characterID: pngTag.characterId,
imgType: imageTag.imgType, imgType: pngTag.imgType,
imgData: imageTag.imgData imgData: pngTag.imgData
})); }));
break; break;

View File

@ -1,23 +1,71 @@
const SWFReader = require('@gizeta/swf-reader'); const SWFReader = require('@gizeta/swf-reader');
const {extractImage, test} = require("swf-extract");
var _encoder = require('png-stream/encoder'); const _encoder = require('png-stream/encoder');
var _encoder2 = _interopRequireDefault(_encoder); const _encoder2 = _interopRequireDefault(_encoder);
var _zlib = require('zlib'); const _zlib = require('zlib');
var _zlib2 = _interopRequireDefault(_zlib); const _zlib2 = _interopRequireDefault(_zlib);
var _streamToArray = require('stream-to-array'); const _streamToArray = require('stream-to-array');
var _streamToArray2 = _interopRequireDefault(_streamToArray); const _streamToArray2 = _interopRequireDefault(_streamToArray);
function _interopRequireDefault(obj: any) { return obj && obj.__esModule ? obj : { default: obj }; } const _stream = require('stream');
export function readSwfAsync(path: string): Promise<any> { const _stream2 = _interopRequireDefault(_stream);
const _decoder = require('jpg-stream/decoder');
const _decoder2 = _interopRequireDefault(_decoder);
function _interopRequireDefault(obj: any) {
return obj && obj.__esModule ? obj : {default: obj};
}
const _concatFrames = require('concat-frames');
const _concatFrames2 = _interopRequireDefault(_concatFrames);
const _slicedToArray = function () {
function sliceIterator(arr: any, i: any) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr: any, i: any) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
export function readSwfAsync(data: string | Buffer): Promise<any> {
return new Promise<any>(((resolve, reject) => { return new Promise<any>(((resolve, reject) => {
SWFReader.read(path, function (err: Error, swf: any) { SWFReader.read(data, function (err: Error, swf: any) {
if (err) { if (err) {
reject(err); reject(err);
} }
@ -26,9 +74,94 @@ export function readSwfAsync(path: string): 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 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';
};
export async function readImagesJPEG(code: number, tag: any) { export async function readImagesJPEG(code: number, tagData: any): Promise<any> {
return test(code)(tag); var characterId = tagData.characterId,
imageData = tagData.imageData;
var imgType = recognizeHeader(imageData);
if (imgType !== 'jpeg') {
return {
code: code,
characterId: characterId,
imgType: imgType,
imgData: imageData
};
}
var bitmapAlphaData = tagData.bitmapAlphaData;
return new Promise(function (resolve, reject) {
var enc = new _encoder2.default(undefined, undefined, {colorSpace: 'rgba'});
_zlib2.default.unzip(bitmapAlphaData, function (err: any, alphaBufPre: any) {
// INVARIANT: alphaBuf is either null or a non-empty buffer
let alphaBuf: any = null;
if (err) {
/*
Due to a bug present in node zlib (https://github.com/nodejs/node/issues/17041)
unzipping an empty buffer can raise "unexpected end of file" error.
We fix this here so that our impl does not depend on the version of node
being used.
Theoretically every zlib.unzip call needs to be guarded, but for this package,
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) {
return reject(new Error(err));
}
// leaving alphaBuf as null
} else {
// ensure alphaBuf is only assigned an non-empty Buffer
if (alphaBufPre.length > 0) alphaBuf = alphaBufPre;
}
var bufferStream = new _stream2.default.PassThrough();
bufferStream.end(imageData);
bufferStream.pipe(new _decoder2.default()).pipe((_concatFrames2.default)(function (_ref: any) {
var _ref2 = _slicedToArray(_ref, 1),
frame = _ref2[0];
var input = frame.pixels;
var pCount = frame.width * frame.height;
var output = Buffer.alloc(pCount * 4);
if (alphaBuf !== null && alphaBuf.length !== pCount) {
console.error('expect alphaBuf to have size ' + pCount + ' while getting ' + alphaBuf.length);
}
var getAlphaBuf = alphaBuf === null ? function (_ignored: any) {
return 0xff;
} : function (i: any) {
return alphaBuf[i];
};
for (var i = 0; i < pCount; ++i) {
output[4 * i] = input[3 * i];
output[4 * i + 1] = input[3 * i + 1];
output[4 * i + 2] = input[3 * i + 2];
output[4 * i + 3] = getAlphaBuf(i);
}
enc.format.width = frame.width;
enc.format.height = frame.height;
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: code,
characterId: characterId,
imgType: 'png',
imgData: Buffer.concat(buffers)
});
});
});
} }
export function readImagesDefineBitsLossless(tag: any) { export function readImagesDefineBitsLossless(tag: any) {
@ -47,7 +180,7 @@ export function readImagesDefineBitsLossless(tag: any) {
if (err) { if (err) {
return reject(new Error(err)); return reject(new Error(err));
} }
var output = new Buffer(bitmapWidth * bitmapHeight * 4); var output = Buffer.alloc(bitmapWidth * bitmapHeight * 4);
var index = 0; var index = 0;
var ptr = 0; var ptr = 0;
if (bitmapFormat === 5) { if (bitmapFormat === 5) {