nitro-converter/src/utils/SwfReader.ts

306 lines
9.6 KiB
TypeScript
Raw Normal View History

2021-02-03 04:02:14 +01:00
const SWFReader = require('@gizeta/swf-reader');
2021-02-03 19:02:10 +01:00
const _encoder = require('png-stream/encoder');
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _encoder2 = _interopRequireDefault(_encoder);
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _zlib = require('zlib');
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _zlib2 = _interopRequireDefault(_zlib);
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _streamToArray = require('stream-to-array');
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _streamToArray2 = _interopRequireDefault(_streamToArray);
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _stream = require('stream');
2021-02-03 04:02:14 +01:00
2021-02-03 19:02:10 +01:00
const _stream2 = _interopRequireDefault(_stream);
const _decoder = require('jpg-stream/decoder');
const _decoder2 = _interopRequireDefault(_decoder);
2021-02-17 00:30:41 -05:00
function _interopRequireDefault(obj: any)
{
return obj && obj.__esModule ? obj : { default: obj };
2021-02-03 19:02:10 +01:00
}
const _concatFrames = require('concat-frames');
const _concatFrames2 = _interopRequireDefault(_concatFrames);
2021-02-17 00:30:41 -05:00
const _slicedToArray = function ()
{
function sliceIterator(arr: any, i: any)
{
const _arr = [];
let _n = true;
let _d = false;
let _e = undefined;
try
{
for(var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true)
{
2021-02-03 19:02:10 +01:00
_arr.push(_s.value);
2021-02-17 00:30:41 -05:00
if(i && _arr.length === i) break;
2021-02-03 19:02:10 +01:00
}
2021-02-17 00:30:41 -05:00
}
catch (err)
{
2021-02-03 19:02:10 +01:00
_d = true;
_e = err;
2021-02-17 00:30:41 -05:00
}
finally
{
try
{
if(!_n && _i['return']) _i['return']();
}
finally
{
if(_d) throw _e;
2021-02-03 19:02:10 +01:00
}
}
return _arr;
}
2021-02-17 00:30:41 -05:00
return function (arr: any, i: any)
{
if(Array.isArray(arr))
{
2021-02-03 19:02:10 +01:00
return arr;
2021-02-17 00:30:41 -05:00
}
else if(Symbol.iterator in Object(arr))
{
2021-02-03 19:02:10 +01:00
return sliceIterator(arr, i);
2021-02-17 00:30:41 -05:00
}
else
{
throw new TypeError('Invalid attempt to destructure non-iterable instance');
2021-02-03 19:02:10 +01:00
}
};
}();
2021-02-17 00:30:41 -05:00
export function readSwfAsync(data: string | Buffer): Promise<any>
{
return new Promise<any>(((resolve, reject) =>
{
SWFReader.read(data, function (err: Error, swf: any)
{
if(err)
{
2021-02-03 04:02:14 +01:00
reject(err);
}
resolve(swf);
});
}));
}
2021-02-03 19:02:10 +01:00
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));
2021-02-17 00:30:41 -05:00
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';
2021-02-03 19:02:10 +01:00
return 'jpeg';
};
2021-02-03 04:02:14 +01:00
2021-02-17 00:30:41 -05:00
export async function readImagesJPEG(code: number, tagData: any): Promise<any>
{
const characterId = tagData.characterId,
2021-02-03 19:02:10 +01:00
imageData = tagData.imageData;
2021-02-17 00:30:41 -05:00
const imgType = recognizeHeader(imageData);
if(imgType !== 'jpeg')
{
2021-02-03 19:02:10 +01:00
return {
code: code,
characterId: characterId,
imgType: imgType,
imgData: imageData
};
}
2021-02-17 00:30:41 -05:00
const bitmapAlphaData = tagData.bitmapAlphaData;
2021-02-03 19:02:10 +01:00
2021-02-17 00:30:41 -05:00
return new Promise(function (resolve, reject)
{
const enc = new _encoder2.default(undefined, undefined, { colorSpace: 'rgba' });
_zlib2.default.unzip(bitmapAlphaData, function (err: any, alphaBufPre: any)
{
2021-02-03 19:02:10 +01:00
// INVARIANT: alphaBuf is either null or a non-empty buffer
let alphaBuf: any = null;
2021-02-17 00:30:41 -05:00
if(err)
{
2021-02-03 19:02:10 +01:00
/*
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.
*/
2021-02-17 00:30:41 -05:00
if(bitmapAlphaData.length > 0)
{
2021-02-03 19:02:10 +01:00
return reject(new Error(err));
}
// leaving alphaBuf as null
2021-02-17 00:30:41 -05:00
}
else
{
2021-02-03 19:02:10 +01:00
// ensure alphaBuf is only assigned an non-empty Buffer
2021-02-17 00:30:41 -05:00
if(alphaBufPre.length > 0) alphaBuf = alphaBufPre;
2021-02-03 19:02:10 +01:00
}
2021-02-17 00:30:41 -05:00
const bufferStream = new _stream2.default.PassThrough();
2021-02-03 19:02:10 +01:00
bufferStream.end(imageData);
2021-02-17 00:30:41 -05:00
bufferStream.pipe(new _decoder2.default()).pipe((_concatFrames2.default)(function (_ref: any)
{
const _ref2 = _slicedToArray(_ref, 1),
2021-02-03 19:02:10 +01:00
frame = _ref2[0];
2021-02-17 00:30:41 -05:00
const input = frame.pixels;
const pCount = frame.width * frame.height;
const output = Buffer.alloc(pCount * 4);
if(alphaBuf !== null && alphaBuf.length !== pCount)
{
2021-02-03 19:02:10 +01:00
console.error('expect alphaBuf to have size ' + pCount + ' while getting ' + alphaBuf.length);
}
2021-02-17 00:30:41 -05:00
const getAlphaBuf = alphaBuf === null ? function (_ignored: any)
{
2021-02-03 19:02:10 +01:00
return 0xff;
2021-02-17 00:30:41 -05:00
} : function (i: any)
{
2021-02-03 19:02:10 +01:00
return alphaBuf[i];
};
2021-02-17 00:30:41 -05:00
for(let i = 0; i < pCount; ++i)
{
2021-02-03 19:02:10 +01:00
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);
}));
});
2021-02-17 00:30:41 -05:00
(_streamToArray2.default)(enc).then(function (parts: any)
{
const buffers = parts.map(function (part: any)
{
2021-02-03 19:02:10 +01:00
return Buffer.isBuffer(part) ? part : Buffer.from(part);
});
resolve({
code: code,
characterId: characterId,
imgType: 'png',
imgData: Buffer.concat(buffers)
});
});
});
2021-02-03 04:02:14 +01:00
}
export interface ImageTagData {
code: number,
characterId: number,
imgType: string,
imgData: Buffer,
bitmapWidth: number,
bitmapHeight: number
}
2021-02-17 00:30:41 -05:00
export function readImagesDefineBitsLossless(tag: any)
{
2021-02-03 04:02:14 +01:00
const characterId = tag.characterId,
bitmapFormat = tag.bitmapFormat,
bitmapWidth = tag.bitmapWidth,
bitmapHeight = tag.bitmapHeight,
bitmapColorTableSize = tag.bitmapColorTableSize,
zlibBitmapData = tag.zlibBitmapData;
2021-02-17 00:30:41 -05:00
return new Promise(function (resolve, reject)
{
const enc = new _encoder2.default(bitmapWidth, bitmapHeight, { colorSpace: 'rgba' });
2021-02-03 04:02:14 +01:00
2021-02-17 00:30:41 -05:00
_zlib2.default.unzip(zlibBitmapData, function (err: any, dataBuf: any)
{
if(err)
{
2021-02-03 04:02:14 +01:00
return reject(new Error(err));
}
2021-02-17 00:30:41 -05:00
const output = Buffer.alloc(bitmapWidth * bitmapHeight * 4);
let index = 0;
let ptr = 0;
if(bitmapFormat === 5)
{
2021-02-03 04:02:14 +01:00
// 32-bit ARGB image
2021-02-17 00:30:41 -05:00
for(let y = 0; y < bitmapHeight; ++y)
{
for(let x = 0; x < bitmapWidth; ++x)
{
const alpha = dataBuf[ptr];
output[index] = dataBuf[ptr + 1] * (255 / alpha);
output[index + 1] = dataBuf[ptr + 2] * (255 / alpha);
output[index + 2] = dataBuf[ptr + 3] * (255 / alpha);
2021-02-03 04:02:14 +01:00
output[index + 3] = alpha;
index += 4;
ptr += 4;
}
}
2021-02-17 00:30:41 -05:00
}
else if(bitmapFormat === 3)
{
2021-02-03 04:02:14 +01:00
// 8-bit colormapped image
2021-02-17 00:30:41 -05:00
const colorMap = [];
for(let i = 0; i < bitmapColorTableSize + 1; ++i)
{
2021-02-03 04:02:14 +01:00
colorMap.push([dataBuf[ptr], dataBuf[ptr + 1], dataBuf[ptr + 2], dataBuf[ptr + 3]]);
ptr += 4;
}
2021-02-17 00:30:41 -05:00
for(let _y2 = 0; _y2 < bitmapHeight; ++_y2)
{
for(let _x2 = 0; _x2 < bitmapWidth; ++_x2)
{
const idx = dataBuf[ptr];
const color = idx < colorMap.length ? colorMap[idx] : [0, 0, 0, 0];
2021-02-03 04:02:14 +01:00
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;
}
2021-02-17 00:30:41 -05:00
}
else
{
2021-02-03 04:02:14 +01:00
return reject(new Error('unhandled bitmapFormat: ' + bitmapFormat));
}
enc.end(output);
});
2021-02-17 00:30:41 -05:00
(_streamToArray2.default)(enc).then(function (parts: any)
{
const buffers = parts.map(function (part: any)
{
2021-02-03 04:02:14 +01:00
return Buffer.isBuffer(part) ? part : Buffer.from(part);
});
resolve({
code: 36,
characterId: characterId,
imgType: 'png',
imgData: Buffer.concat(buffers),
bitmapWidth: bitmapWidth,
bitmapHeight: bitmapHeight
2021-02-03 04:02:14 +01:00
});
});
2021-02-17 00:30:41 -05:00
}).catch(function (e)
{
2021-02-03 04:02:14 +01:00
console.error(e);
});
}