mirror of
https://github.com/billsonnn/nitro-converter.git
synced 2024-11-26 17:30:52 +01:00
Skip bad buffers
This commit is contained in:
parent
e681a316a1
commit
5603c4a010
@ -5,16 +5,16 @@
|
|||||||
* MIT LICENCE
|
* MIT LICENCE
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
import { fs } from 'fs';
|
||||||
|
import { lzma } from 'lzma-purejs';
|
||||||
|
import { Stream } from 'stream';
|
||||||
|
import { zlib } from 'zlib';
|
||||||
|
import { SWFBuffer } from './lib/swf-buffer';
|
||||||
|
import { SWFTags } from './lib/swf-tags';
|
||||||
|
const SWFReader = exports;
|
||||||
|
|
||||||
var fs = require('fs')
|
function readSWFTags(buff, swf)
|
||||||
, zlib = require('zlib')
|
{
|
||||||
, lzma = require('lzma-purejs')
|
|
||||||
, Stream = require('stream')
|
|
||||||
, SWFBuffer = require('./lib/swf-buffer')
|
|
||||||
, SWFTags = require('./lib/swf-tags')
|
|
||||||
, SWFReader = exports;
|
|
||||||
|
|
||||||
function readSWFTags(buff, swf) {
|
|
||||||
var tags = []
|
var tags = []
|
||||||
, tag
|
, tag
|
||||||
, tagHeader
|
, tagHeader
|
||||||
@ -24,14 +24,16 @@ function readSWFTags(buff, swf) {
|
|||||||
, fc;
|
, fc;
|
||||||
|
|
||||||
/* Reads TagCodeAndLength from Tag's RECORDHEADER */
|
/* Reads TagCodeAndLength from Tag's RECORDHEADER */
|
||||||
while( (tagHeader = buff.readTagCodeAndLength()) ) {
|
while( (tagHeader = buff.readTagCodeAndLength()) )
|
||||||
|
{
|
||||||
tag = {
|
tag = {
|
||||||
header : tagHeader
|
header : tagHeader
|
||||||
};
|
};
|
||||||
switch( tagHeader.code ) {
|
switch( tagHeader.code )
|
||||||
case SWFTags.FileAttributes :
|
{
|
||||||
flag = buff.readUIntLE(32);
|
case SWFTags.FileAttributes: {
|
||||||
fileAttrs = {}
|
const flag = buff.readUIntLE(32);
|
||||||
|
const fileAttrs = {};
|
||||||
|
|
||||||
fileAttrs.useNetwork = tag.useNetwork = !!(flag & 0x1);
|
fileAttrs.useNetwork = tag.useNetwork = !!(flag & 0x1);
|
||||||
fileAttrs.as3 = tag.as3 = !!(flag & 0x8);
|
fileAttrs.as3 = tag.as3 = !!(flag & 0x8);
|
||||||
@ -41,13 +43,16 @@ function readSWFTags(buff, swf) {
|
|||||||
|
|
||||||
swf.fileAttributes = fileAttrs;
|
swf.fileAttributes = fileAttrs;
|
||||||
break;
|
break;
|
||||||
case SWFTags.Metadata :
|
}
|
||||||
swf.metadata = tag.metadata = buff.readString()
|
case SWFTags.Metadata: {
|
||||||
|
swf.metadata = tag.metadata = buff.readString();
|
||||||
break;
|
break;
|
||||||
case SWFTags.SetBackgroundColor :
|
}
|
||||||
|
case SWFTags.SetBackgroundColor: {
|
||||||
tag.RGB = buff.readRGB();
|
tag.RGB = buff.readRGB();
|
||||||
swf.backgroundColor = '#' + (tag.RGB[0]*65536 + tag.RGB[1]*256 + tag.RGB[0]).toString(16);
|
swf.backgroundColor = '#' + (tag.RGB[0]*65536 + tag.RGB[1]*256 + tag.RGB[0]).toString(16);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SWFTags.Protect :
|
case SWFTags.Protect :
|
||||||
swf.protect = tagHeader.length && buff.readString();
|
swf.protect = tagHeader.length && buff.readString();
|
||||||
break;
|
break;
|
||||||
@ -101,7 +106,7 @@ function readSWFTags(buff, swf) {
|
|||||||
// tag.shapes = buff.readShapeWithStyle();
|
// tag.shapes = buff.readShapeWithStyle();
|
||||||
// break;
|
// break;
|
||||||
case SWFTags.FrameLabel :
|
case SWFTags.FrameLabel :
|
||||||
tag.name = buff.readString()
|
tag.name = buff.readString();
|
||||||
l = Buffer.byteLength(tag.name);
|
l = Buffer.byteLength(tag.name);
|
||||||
/* check if it's an named anchor */
|
/* check if it's an named anchor */
|
||||||
if(l & (tagHeader.length - 1) != l)
|
if(l & (tagHeader.length - 1) != l)
|
||||||
@ -154,7 +159,8 @@ function readSWFTags(buff, swf) {
|
|||||||
case SWFTags.ImportAssets2 :
|
case SWFTags.ImportAssets2 :
|
||||||
tag.url = buff.readString();
|
tag.url = buff.readString();
|
||||||
|
|
||||||
if ( !(1 === buff.readUInt8() && 0 === buff.readUInt8()) ) {
|
if( !(1 === buff.readUInt8() && 0 === buff.readUInt8()) )
|
||||||
|
{
|
||||||
throw new Error('Reserved bits for ImportAssets2 used');
|
throw new Error('Reserved bits for ImportAssets2 used');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,13 +176,14 @@ function readSWFTags(buff, swf) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case SWFTags.EnableDebbuger :
|
case SWFTags.EnableDebbuger :
|
||||||
tag.password = buff.readString()
|
tag.password = buff.readString();
|
||||||
break;
|
break;
|
||||||
case SWFTags.EnableDebugger2 :
|
case SWFTags.EnableDebugger2 :
|
||||||
if (0 !== buff.readUIntLE(16)) {
|
if(0 !== buff.readUIntLE(16))
|
||||||
|
{
|
||||||
//throw new Error('Reserved bit for EnableDebugger2 used.');
|
//throw new Error('Reserved bit for EnableDebugger2 used.');
|
||||||
}
|
}
|
||||||
tag.password = buff.readString()
|
tag.password = buff.readString();
|
||||||
break;
|
break;
|
||||||
case SWFTags.ScriptLimits :
|
case SWFTags.ScriptLimits :
|
||||||
/**
|
/**
|
||||||
@ -189,7 +196,7 @@ function readSWFTags(buff, swf) {
|
|||||||
*/
|
*/
|
||||||
tag.scriptTimeoutSeconds = buff.readUIntLE(16);
|
tag.scriptTimeoutSeconds = buff.readUIntLE(16);
|
||||||
break;
|
break;
|
||||||
case SWFTags.SymbolClass :
|
case SWFTags.SymbolClass: {
|
||||||
tag.numSymbols = buff.readUIntLE(16);
|
tag.numSymbols = buff.readUIntLE(16);
|
||||||
tag.symbols = [];
|
tag.symbols = [];
|
||||||
|
|
||||||
@ -201,29 +208,35 @@ function readSWFTags(buff, swf) {
|
|||||||
name : buff.readString()
|
name : buff.readString()
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case SWFTags.DefineScalingGrid :
|
}
|
||||||
|
case SWFTags.DefineScalingGrid: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
tag.splitter = buff.readRect();
|
tag.splitter = buff.readRect();
|
||||||
break;
|
break;
|
||||||
case SWFTags.setTabIndex :
|
}
|
||||||
|
case SWFTags.setTabIndex: {
|
||||||
tag.depth = buff.readUIntLE(16);
|
tag.depth = buff.readUIntLE(16);
|
||||||
tag.tabIndex = buff.readUIntLE(16);
|
tag.tabIndex = buff.readUIntLE(16);
|
||||||
break;
|
break;
|
||||||
case SWFTags.JPEGTables:
|
}
|
||||||
|
case SWFTags.JPEGTables: {
|
||||||
tag.jpegData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length);
|
tag.jpegData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length);
|
||||||
buff.pointer += tagHeader.length;
|
buff.pointer += tagHeader.length;
|
||||||
break;
|
break;
|
||||||
case SWFTags.DefineBits:
|
}
|
||||||
|
case SWFTags.DefineBits: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
tag.jpegData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length - 2);
|
tag.jpegData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length - 2);
|
||||||
buff.pointer += tagHeader.length - 2;
|
buff.pointer += tagHeader.length - 2;
|
||||||
break;
|
break;
|
||||||
case SWFTags.DefineBitsJPEG2:
|
}
|
||||||
|
case SWFTags.DefineBitsJPEG2: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length - 2);
|
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length - 2);
|
||||||
buff.pointer += tagHeader.length - 2;
|
buff.pointer += tagHeader.length - 2;
|
||||||
break;
|
break;
|
||||||
case SWFTags.DefineBitsJPEG3:
|
}
|
||||||
|
case SWFTags.DefineBitsJPEG3: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
var alphaDataOffset = buff.readUIntLE(32);
|
var alphaDataOffset = buff.readUIntLE(32);
|
||||||
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + alphaDataOffset);
|
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + alphaDataOffset);
|
||||||
@ -232,30 +245,34 @@ function readSWFTags(buff, swf) {
|
|||||||
tag.bitmapAlphaData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
tag.bitmapAlphaData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
||||||
buff.pointer += restLength;
|
buff.pointer += restLength;
|
||||||
break;
|
break;
|
||||||
case SWFTags.DefineBitsJPEG4:
|
}
|
||||||
|
case SWFTags.DefineBitsJPEG4: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
var alphaDataOffset = buff.readUIntLE(32);
|
const alphaDataOffset = buff.readUIntLE(32);
|
||||||
tag.deblockParam = buff.readUIntLE(16);
|
tag.deblockParam = buff.readUIntLE(16);
|
||||||
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + alphaDataOffset);
|
tag.imageData = buff.buffer.slice(buff.pointer, buff.pointer + alphaDataOffset);
|
||||||
buff.pointer += alphaDataOffset;
|
buff.pointer += alphaDataOffset;
|
||||||
var restLength = tagHeader.length - 8 - alphaDataOffset;
|
const restLength = tagHeader.length - 8 - alphaDataOffset;
|
||||||
tag.bitmapAlphaData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
tag.bitmapAlphaData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
||||||
buff.pointer += restLength;
|
buff.pointer += restLength;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SWFTags.DefineBitsLossless:
|
case SWFTags.DefineBitsLossless:
|
||||||
case SWFTags.DefineBitsLossless2:
|
case SWFTags.DefineBitsLossless2: {
|
||||||
tag.characterId = buff.readUIntLE(16);
|
tag.characterId = buff.readUIntLE(16);
|
||||||
tag.bitmapFormat = buff.readUInt8();
|
tag.bitmapFormat = buff.readUInt8();
|
||||||
tag.bitmapWidth = buff.readUIntLE(16);
|
tag.bitmapWidth = buff.readUIntLE(16);
|
||||||
tag.bitmapHeight = buff.readUIntLE(16);
|
tag.bitmapHeight = buff.readUIntLE(16);
|
||||||
var restLength = tagHeader.length - 7;
|
let restLength = tagHeader.length - 7;
|
||||||
if (tag.bitmapFormat == 3) {
|
if(tag.bitmapFormat == 3)
|
||||||
|
{
|
||||||
tag.bitmapColorTableSize = buff.readUInt8();
|
tag.bitmapColorTableSize = buff.readUInt8();
|
||||||
restLength--;
|
restLength--;
|
||||||
}
|
}
|
||||||
tag.zlibBitmapData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
tag.zlibBitmapData = buff.buffer.slice(buff.pointer, buff.pointer + restLength);
|
||||||
buff.pointer += restLength;
|
buff.pointer += restLength;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
tag.data = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length);
|
tag.data = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length);
|
||||||
buff.pointer += tagHeader.length;
|
buff.pointer += tagHeader.length;
|
||||||
@ -275,12 +292,14 @@ function readSWFTags(buff, swf) {
|
|||||||
* @api private
|
* @api private
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function readSWFBuff(buff, compressed_buff, next) {
|
function readSWFBuff(buff, compressed_buff, next)
|
||||||
|
{
|
||||||
buff.seek(3);// start
|
buff.seek(3);// start
|
||||||
|
|
||||||
if (buff.length < 9) {
|
if(buff.length < 9)
|
||||||
if (isSync) throw new Error("Buffer is to small, must be greater than 9 bytes.");
|
{
|
||||||
return next(new Error("Buffer is to small, must be greater than 9 bytes."));
|
if(isSync) throw new Error('Buffer is to small, must be greater than 9 bytes.');
|
||||||
|
return next(new Error('Buffer is to small, must be greater than 9 bytes.'));
|
||||||
}
|
}
|
||||||
var swf = {
|
var swf = {
|
||||||
version : buff.readUInt8(),
|
version : buff.readUInt8(),
|
||||||
@ -294,9 +313,12 @@ function readSWFBuff(buff, compressed_buff, next) {
|
|||||||
}
|
}
|
||||||
, isSync = 'function' !== typeof next;
|
, isSync = 'function' !== typeof next;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
swf.tags = readSWFTags(buff, swf);
|
swf.tags = readSWFTags(buff, swf);
|
||||||
} catch(e) {
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
if(isSync) throw e;
|
if(isSync) throw e;
|
||||||
return next(e);
|
return next(e);
|
||||||
}
|
}
|
||||||
@ -310,7 +332,8 @@ function readSWFBuff(buff, compressed_buff, next) {
|
|||||||
* @param {Buffer|ArrayBuffer} buff
|
* @param {Buffer|ArrayBuffer} buff
|
||||||
* @param {Buffer|ArrayBuffer} swf
|
* @param {Buffer|ArrayBuffer} swf
|
||||||
*/
|
*/
|
||||||
function concatSWFHeader(buff, swf) {
|
function concatSWFHeader(buff, swf)
|
||||||
|
{
|
||||||
return Buffer.concat([swf.slice(0, 8), buff]);
|
return Buffer.concat([swf.slice(0, 8), buff]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,51 +344,73 @@ function concatSWFHeader(buff, swf) {
|
|||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function uncompress(swf, next) {
|
function uncompress(swf, next)
|
||||||
|
{
|
||||||
var compressed_buff = swf.slice(8)
|
var compressed_buff = swf.slice(8)
|
||||||
, uncompressed_buff
|
, uncompressed_buff
|
||||||
, isSync = 'function' !== typeof next
|
, isSync = 'function' !== typeof next
|
||||||
, e;
|
, e;
|
||||||
|
|
||||||
// uncompress buffer
|
// uncompress buffer
|
||||||
switch( swf[0] ) {
|
switch(swf[0])
|
||||||
|
{
|
||||||
case 0x43 : // zlib compressed
|
case 0x43 : // zlib compressed
|
||||||
if (isSync) {
|
if(isSync)
|
||||||
|
{
|
||||||
uncompressed_buff = concatSWFHeader(zlib.unzipSync(compressed_buff), swf);
|
uncompressed_buff = concatSWFHeader(zlib.unzipSync(compressed_buff), swf);
|
||||||
|
|
||||||
|
if(!Buffer.isBuffer(uncompressed_buff))
|
||||||
|
{
|
||||||
|
console.log('invalid_buffer');
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf);
|
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf);
|
||||||
}
|
}
|
||||||
|
|
||||||
zlib.inflate(compressed_buff, function(err, buf) {
|
if(!Buffer.isBuffer(compressed_buff))
|
||||||
|
{
|
||||||
|
console.log('invalid_buffer');
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib.inflate(compressed_buff, function(err, buf)
|
||||||
|
{
|
||||||
readSWFBuff(new SWFBuffer(buf), swf, next);
|
readSWFBuff(new SWFBuffer(buf), swf, next);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 0x46 : // uncompressed
|
case 0x46 : // uncompressed
|
||||||
return readSWFBuff(new SWFBuffer( swf ), swf, next);
|
return readSWFBuff(new SWFBuffer( swf ), swf, next);
|
||||||
break;
|
|
||||||
case 0x5a : // LZMA compressed
|
case 0x5a : // LZMA compressed
|
||||||
var lzmaProperties = compressed_buff.slice(4, 9);
|
var lzmaProperties = compressed_buff.slice(4, 9);
|
||||||
compressed_buff = compressed_buff.slice(9);
|
compressed_buff = compressed_buff.slice(9);
|
||||||
|
|
||||||
var input_stream = new Stream();
|
var input_stream = new Stream();
|
||||||
input_stream.pos = 0;
|
input_stream.pos = 0;
|
||||||
input_stream.readByte = function() {
|
input_stream.readByte = function()
|
||||||
|
{
|
||||||
return this.pos >= compressed_buff.length ? -1 : compressed_buff[this.pos++];
|
return this.pos >= compressed_buff.length ? -1 : compressed_buff[this.pos++];
|
||||||
};
|
};
|
||||||
|
|
||||||
var output_stream = new Stream();
|
var output_stream = new Stream();
|
||||||
output_stream.buffer = new Buffer(16384);
|
output_stream.buffer = new Buffer(16384);
|
||||||
output_stream.pos = 0;
|
output_stream.pos = 0;
|
||||||
output_stream.writeByte = function(_byte) {
|
output_stream.writeByte = function(_byte)
|
||||||
if (this.pos >= this.buffer.length) {
|
{
|
||||||
|
if(this.pos >= this.buffer.length)
|
||||||
|
{
|
||||||
var newBuffer = new Buffer(this.buffer.length * 2);
|
var newBuffer = new Buffer(this.buffer.length * 2);
|
||||||
this.buffer.copy(newBuffer);
|
this.buffer.copy(newBuffer);
|
||||||
this.buffer = newBuffer;
|
this.buffer = newBuffer;
|
||||||
}
|
}
|
||||||
this.buffer[this.pos++] = _byte;
|
this.buffer[this.pos++] = _byte;
|
||||||
};
|
};
|
||||||
output_stream.getBuffer = function() {
|
output_stream.getBuffer = function()
|
||||||
|
{
|
||||||
// trim buffer
|
// trim buffer
|
||||||
if (this.pos !== this.buffer.length) {
|
if(this.pos !== this.buffer.length)
|
||||||
|
{
|
||||||
var newBuffer = new Buffer(this.pos);
|
var newBuffer = new Buffer(this.pos);
|
||||||
this.buffer.copy(newBuffer, 0, 0, this.pos);
|
this.buffer.copy(newBuffer, 0, 0, this.pos);
|
||||||
this.buffer = newBuffer;
|
this.buffer = newBuffer;
|
||||||
@ -377,17 +422,19 @@ function uncompress(swf, next) {
|
|||||||
uncompressed_buff = Buffer.concat([swf.slice(0, 8), output_stream.getBuffer()]);
|
uncompressed_buff = Buffer.concat([swf.slice(0, 8), output_stream.getBuffer()]);
|
||||||
|
|
||||||
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next);
|
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next);
|
||||||
break;
|
|
||||||
default :
|
default :
|
||||||
e = new Error('Unknown SWF compressions');
|
e = new Error('Unknown SWF compressions');
|
||||||
|
|
||||||
if (isSync) {
|
if(isSync)
|
||||||
|
{
|
||||||
throw e;
|
throw e;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
next(e);
|
next(e);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if file is Buffer or ArrayBuffer
|
* Check if file is Buffer or ArrayBuffer
|
||||||
@ -396,8 +443,9 @@ function uncompress(swf, next) {
|
|||||||
* @api private
|
* @api private
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function isBuffer(b) {
|
function isBuffer(b)
|
||||||
return typeof Buffer !== "undefined" && Buffer.isBuffer(b) || b instanceof ArrayBuffer;
|
{
|
||||||
|
return typeof Buffer !== 'undefined' && Buffer.isBuffer(b) || b instanceof ArrayBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exposes Tags constants */
|
/* Exposes Tags constants */
|
||||||
@ -411,21 +459,30 @@ SWFReader.TAGS = SWFTags;
|
|||||||
* @api public
|
* @api public
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SWFReader.read = SWFReader.readSync = function(file, next) {
|
SWFReader.read = SWFReader.readSync = function(file, next)
|
||||||
if (isBuffer(file)) {
|
{
|
||||||
|
if(isBuffer(file))
|
||||||
|
{
|
||||||
/* File is already a buffer */
|
/* File is already a buffer */
|
||||||
return uncompress(file, next);
|
return uncompress(file, next);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* Get the buffer */
|
/* Get the buffer */
|
||||||
if ('function' === typeof next) {
|
if('function' === typeof next)
|
||||||
fs.readFile(file, function(err, swf) {
|
{
|
||||||
if ( err ) {
|
fs.readFile(file, function(err, swf)
|
||||||
|
{
|
||||||
|
if( err )
|
||||||
|
{
|
||||||
next(err);
|
next(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uncompress(swf, next);
|
uncompress(swf, next);
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return uncompress(fs.readFileSync(file));
|
return uncompress(fs.readFileSync(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
225
src/swf-reader/lib/SWFBuffer.ts
Normal file
225
src/swf-reader/lib/SWFBuffer.ts
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
export class SWFBuffer
|
||||||
|
{
|
||||||
|
public static RECORDHEADER_LENTH_FULL: number = 0x3f;
|
||||||
|
public static EOS: number = 0x00;
|
||||||
|
public static STYLE_COUNT_EXT: number = 0xFF;
|
||||||
|
|
||||||
|
public buffer: Buffer
|
||||||
|
public pointer: number = 0;
|
||||||
|
public position: number = 1;
|
||||||
|
public current: number = 0;
|
||||||
|
public length: number = 0;
|
||||||
|
|
||||||
|
constructor(buffer: Buffer)
|
||||||
|
{
|
||||||
|
if(!Buffer.isBuffer(buffer))
|
||||||
|
{
|
||||||
|
throw new Error('invalid_buffer');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buffer = buffer;
|
||||||
|
this.length = buffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readUIntLE(bits: number): number
|
||||||
|
{
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
switch(bits)
|
||||||
|
{
|
||||||
|
case 16:
|
||||||
|
value = this.buffer.readUInt16LE(this.pointer);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
value = this.buffer.readUInt32LE(this.pointer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pointer += bits / 8;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readUInt8(): number
|
||||||
|
{
|
||||||
|
return this.buffer.readUInt8(this.pointer++);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readEncodedU32(): number
|
||||||
|
{
|
||||||
|
let i = 5;
|
||||||
|
let result = 0;
|
||||||
|
let nb = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
result += (nb = this.nextByte());
|
||||||
|
} while((nb & 128) && --i);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readRGB(): [ number, number, number ]
|
||||||
|
{
|
||||||
|
return [ this.readUInt8(), this.readUInt8(), this.readUInt8() ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public readRGBA(): [ number, number, number, number ]
|
||||||
|
{
|
||||||
|
return [ ...this.readRGB(), this.readUInt8() ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public readString(encoding: BufferEncoding): string
|
||||||
|
{
|
||||||
|
const init = this.pointer;
|
||||||
|
|
||||||
|
while(this.readUInt8() !== SWFBuffer.EOS);
|
||||||
|
|
||||||
|
return this.buffer.toString(encoding || 'utf8', init, this.pointer - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readStyleArray(buffer: SWFBuffer, next)
|
||||||
|
{
|
||||||
|
let styleArrayCount = buffer.readUInt8();
|
||||||
|
const styles = [];
|
||||||
|
|
||||||
|
if(styleArrayCount === SWFBuffer.STYLE_COUNT_EXT) styleArrayCount = buffer.readUIntLE(16);
|
||||||
|
|
||||||
|
for(let i = 0; i < styleArrayCount; i++) styles.push(next(buffer));
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readFillStyle(buffer: SWFBuffer): { fillStyleType: number, color?: [ number, number, number, number ], bitmapId?: number }
|
||||||
|
{
|
||||||
|
const type = buffer.readUInt8();
|
||||||
|
|
||||||
|
const fillStyle: { fillStyleType: number, color?: [ number, number, number, number ], bitmapId?: number } = {
|
||||||
|
fillStyleType: type
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
fillStyle.color = buffer.readRGBA();
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
case 0x12:
|
||||||
|
case 0x13:
|
||||||
|
console.log('Gradient');
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
case 0x41:
|
||||||
|
case 0x42:
|
||||||
|
case 0x43:
|
||||||
|
fillStyle.bitmapId = buffer.readUIntLE(16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fillStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readLineStyle(buffer: SWFBuffer): { width: number, color: [ number, number, number, number ]}
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
width: buffer.readUIntLE(16)/20,
|
||||||
|
color: buffer.readRGBA()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public readShapeRecords(buffer: SWFBuffer)
|
||||||
|
{
|
||||||
|
let shapeRecords = null;
|
||||||
|
const typeFlag = buffer.readBits(1);
|
||||||
|
let eos = 0;
|
||||||
|
|
||||||
|
while((eos = buffer.readBits(5)))
|
||||||
|
{
|
||||||
|
if(0 === typeFlag)
|
||||||
|
{
|
||||||
|
shapeRecords = {
|
||||||
|
type: 'STYLECHANGERECORD'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapeRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readShapeWithStyle()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
fillStyles : this.readStyleArray(this, this.readFillStyle),
|
||||||
|
lineStyles : this.readStyleArray(this, this.readLineStyle),
|
||||||
|
numFillBits : this.readBits(4),
|
||||||
|
numLineBits : this.readBits(4),
|
||||||
|
shapeRecords: this.readShapeRecords(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public readTagCodeAndLength(): { code: number, length: number }
|
||||||
|
{
|
||||||
|
if(this.pointer === this.length) return null;
|
||||||
|
|
||||||
|
const n = this.readUIntLE(16);
|
||||||
|
const tagType = n >> 6;
|
||||||
|
let tagLength = n & SWFBuffer.RECORDHEADER_LENTH_FULL;
|
||||||
|
|
||||||
|
if(n === 0) return null;
|
||||||
|
|
||||||
|
if(tagLength === SWFBuffer.RECORDHEADER_LENTH_FULL ) tagLength = this.readUIntLE(32);
|
||||||
|
|
||||||
|
return { code: tagType, length: tagLength };
|
||||||
|
}
|
||||||
|
|
||||||
|
public readRect(): { x: number, y: number, width: number, height: number }
|
||||||
|
{
|
||||||
|
this.start();
|
||||||
|
|
||||||
|
const NBits = this.readBits(5);
|
||||||
|
const Xmin = this.readBits(NBits, true) / 20;
|
||||||
|
const Xmax = this.readBits(NBits, true) / 20;
|
||||||
|
const Ymin = this.readBits(NBits, true) / 20;
|
||||||
|
const Ymax = this.readBits(NBits, true) / 20;
|
||||||
|
|
||||||
|
return {
|
||||||
|
x : Xmin,
|
||||||
|
y : Ymin,
|
||||||
|
width : (Xmax > Xmin ? Xmax - Xmin : Xmin - Xmax),
|
||||||
|
height : (Ymax > Ymin ? Ymax - Ymin : Ymin - Ymax)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public seek(pos: number): void
|
||||||
|
{
|
||||||
|
this.pointer = pos % this.buffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public start(): void
|
||||||
|
{
|
||||||
|
this.current = this.nextByte();
|
||||||
|
this.position = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public nextByte(): number
|
||||||
|
{
|
||||||
|
return this.pointer > this.buffer.length ? null : this.buffer[ this.pointer++ ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public readBits(b: number, signed: boolean = false): number
|
||||||
|
{
|
||||||
|
let n = 0;
|
||||||
|
let r = 0;
|
||||||
|
|
||||||
|
const sign = signed && ++n && ((this.current >> (8 - this.position++)) & 1) ? -1 : 1;
|
||||||
|
|
||||||
|
while(n++ < b)
|
||||||
|
{
|
||||||
|
if( this.position > 8 ) this.start();
|
||||||
|
|
||||||
|
r = (r << 1 ) + ((this.current >> (8 - this.position++)) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign * r;
|
||||||
|
}
|
||||||
|
}
|
@ -1,291 +0,0 @@
|
|||||||
|
|
||||||
var RECORDHEADER_LENTH_FULL = 0x3f
|
|
||||||
// null-character
|
|
||||||
, EOS = 0x00
|
|
||||||
, styleCountExt = 0xFF;
|
|
||||||
|
|
||||||
function readStyleArray(buffer, next) {
|
|
||||||
var styleArrayCount = buffer.readUInt8()
|
|
||||||
, styles = [];
|
|
||||||
|
|
||||||
if (styleArrayCount === styleCountExt)
|
|
||||||
styleArrayCount = buffer.readUIntLE(16);
|
|
||||||
|
|
||||||
for (var i = 0; i < styleArrayCount; i++)
|
|
||||||
styles.push(next(buffer));
|
|
||||||
|
|
||||||
return styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readFillStyle(buffer) {
|
|
||||||
var type = buffer.readUInt8()
|
|
||||||
, fillStyle = {
|
|
||||||
/**
|
|
||||||
* 0x00 = solid
|
|
||||||
* 0x10 = linear gradient fill
|
|
||||||
* 0x12 = radial gradient fill
|
|
||||||
* 0x13 = focal radial gradient fill (SWF 8 or later)
|
|
||||||
* 0x40 = repeating bitmap fill
|
|
||||||
* 0x41 = clipped bitmap fill
|
|
||||||
* 0x42 = non-smoothed repeating bitmap
|
|
||||||
* 0x43 = non-smoothed clipped bitmap
|
|
||||||
*/
|
|
||||||
fillStyleType : type
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 0x00:
|
|
||||||
fillStyle.color = buffer.readRGBA();
|
|
||||||
break;
|
|
||||||
case 0x10, 0x12, 0x13:
|
|
||||||
console.log('Gradient');
|
|
||||||
break;
|
|
||||||
case 0x40, 0x41, 0x42, 0x43:
|
|
||||||
fillStyle.bitmapId = buffer.readUIntLE(16);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fillStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readLineStyle(buffer) {
|
|
||||||
return {
|
|
||||||
width: buffer.readUIntLE(16)/20,
|
|
||||||
color: buffer.readRGBA()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readShapeRecords(buffer) {
|
|
||||||
var shapeRecords = []
|
|
||||||
, typeFlag = buffer.readBits(1)
|
|
||||||
, shapeRecord
|
|
||||||
, eos;
|
|
||||||
|
|
||||||
while ((eos = buffer.readBits(5))) {
|
|
||||||
if (0 === typeFlag) {
|
|
||||||
shaperecord = {
|
|
||||||
type: 'STYLECHANGERECORD'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return shapeRecords;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Constructor of SWFBuffer object
|
|
||||||
*
|
|
||||||
* @param {Buffer} buffer
|
|
||||||
* @return Instance of SWFBuffer
|
|
||||||
*/
|
|
||||||
|
|
||||||
function SWFBuffer( buffer ) {
|
|
||||||
if ( !Buffer.isBuffer( buffer ) ) {
|
|
||||||
throw new Error('Invalid buffer');
|
|
||||||
}
|
|
||||||
this.buffer = buffer;
|
|
||||||
this.pointer = 0;
|
|
||||||
this.position = 1;
|
|
||||||
this.current = 0;
|
|
||||||
this.length = buffer.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads unsigned 16 or 32 Little Endian Bits
|
|
||||||
* and advance pointer to next bits / 8 bytes
|
|
||||||
*
|
|
||||||
* @param {Number} bits
|
|
||||||
* @return {Number} Value read from buffer
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readUIntLE = function( bits ) {
|
|
||||||
var value = 0;
|
|
||||||
try {
|
|
||||||
value = this.buffer['readUInt' + bits + 'LE'](this.pointer);
|
|
||||||
this.pointer += bits / 8;
|
|
||||||
} catch ( e ) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads unsigned 8 bit from the buffer
|
|
||||||
*
|
|
||||||
* @return {Number} Value read from buffer
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readUInt8 = function() {
|
|
||||||
return this.buffer.readUInt8( this.pointer++ );
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads 32-bit unsigned integers value encoded (1-5 bytes)
|
|
||||||
*
|
|
||||||
* @return {Number} 32-bit unsigned integer
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readEncodedU32 = function() {
|
|
||||||
var i = 5
|
|
||||||
, result = 0
|
|
||||||
, nb;
|
|
||||||
|
|
||||||
do
|
|
||||||
result += (nb = this.nextByte());
|
|
||||||
while((nb & 128) && --i);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an encoded data from buffer and returns a
|
|
||||||
* string using the specified character set.
|
|
||||||
*
|
|
||||||
* @param {String} encoding - defaults to 'utf8'
|
|
||||||
* @returns {String} Decoded string
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readString = function(encoding) {
|
|
||||||
var init = this.pointer;
|
|
||||||
while(this.readUInt8() !== EOS);
|
|
||||||
return this.buffer.toString(encoding || 'utf8', init, this.pointer - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads RGB value
|
|
||||||
*
|
|
||||||
* @return {Array} Array of RGB value
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readRGB = function() {
|
|
||||||
return [this.readUInt8(), this.readUInt8(), this.readUInt8()];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads RGBA value
|
|
||||||
*
|
|
||||||
* @return {Array} Array of RGBA value
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readRGBA = function() {
|
|
||||||
var rgba = this.readRGB();
|
|
||||||
rgba.push(this.readUInt8());
|
|
||||||
return rgba;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads ShapeWithStyle structure
|
|
||||||
* used by the DefineShape tag.
|
|
||||||
*
|
|
||||||
* @return ShapeWithStyle structure
|
|
||||||
*/
|
|
||||||
SWFBuffer.prototype.readShapeWithStyle = function() {
|
|
||||||
return {
|
|
||||||
fillStyles : readStyleArray(this, readFillStyle),
|
|
||||||
lineStyles : readStyleArray(this, readLineStyle),
|
|
||||||
numFillBits : this.readBits(4),
|
|
||||||
numLineBits : this.readBits(4),
|
|
||||||
shapeRecords: readShapeRecords(this)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads RECORDHEADER from next tag in the buffer
|
|
||||||
*
|
|
||||||
* @return {Object} Tag code and length
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readTagCodeAndLength = function() {
|
|
||||||
if (this.pointer === this.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var n = this.readUIntLE(16)
|
|
||||||
, tagType = n >> 6
|
|
||||||
, tagLength = n & RECORDHEADER_LENTH_FULL;
|
|
||||||
|
|
||||||
if ( n === 0 )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( tagLength === RECORDHEADER_LENTH_FULL )
|
|
||||||
tagLength = this.readUIntLE(32);
|
|
||||||
|
|
||||||
return { code : tagType, length : tagLength };
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads RECT format
|
|
||||||
*
|
|
||||||
* @return {Object} x, y, width and height of the RECT
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readRect = function() {
|
|
||||||
|
|
||||||
this.start();
|
|
||||||
|
|
||||||
var NBits = this.readBits(5)
|
|
||||||
, Xmin = this.readBits(NBits, true)/20
|
|
||||||
, Xmax = this.readBits(NBits, true)/20
|
|
||||||
, Ymin = this.readBits(NBits, true)/20
|
|
||||||
, Ymax = this.readBits(NBits, true)/20;
|
|
||||||
|
|
||||||
return {
|
|
||||||
x : Xmin,
|
|
||||||
y : Ymin,
|
|
||||||
width : (Xmax > Xmin ? Xmax - Xmin : Xmin - Xmax),
|
|
||||||
height : (Ymax > Ymin ? Ymax - Ymin : Ymin - Ymax)
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets internal pointer to the specified position;
|
|
||||||
*
|
|
||||||
* @param {Number} pos
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.seek = function( pos ) {
|
|
||||||
this.pointer = pos % this.buffer.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets position and sets current to next Byte in buffer
|
|
||||||
*/
|
|
||||||
SWFBuffer.prototype.start = function() {
|
|
||||||
this.current = this.nextByte();
|
|
||||||
this.position = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets next Byte in the buffer and Increment internal pointer
|
|
||||||
*
|
|
||||||
* @return {Number} Next byte in buffer
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.nextByte = function() {
|
|
||||||
return this.pointer > this.buffer.length ? null : this.buffer[ this.pointer++ ];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads b bits from current byte in buffer
|
|
||||||
*
|
|
||||||
* @param {Number} b
|
|
||||||
* @return {Number} Bits read from buffer
|
|
||||||
*/
|
|
||||||
|
|
||||||
SWFBuffer.prototype.readBits = function( b, signed ) {
|
|
||||||
var n = 0
|
|
||||||
, r = 0
|
|
||||||
, sign = signed && ++n && ((this.current >> (8-this.position++)) & 1) ? -1 : 1;
|
|
||||||
|
|
||||||
while( n++ < b ) {
|
|
||||||
if ( this.position > 8 ) this.start();
|
|
||||||
|
|
||||||
r = (r << 1 ) + ((this.current >> (8-this.position++)) & 1);
|
|
||||||
}
|
|
||||||
return sign * r;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Exposes class */
|
|
||||||
exports = module.exports = SWFBuffer;
|
|
@ -5,7 +5,8 @@
|
|||||||
* @param {Mixed} value
|
* @param {Mixed} value
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function define(name, value) {
|
function define(name, value)
|
||||||
|
{
|
||||||
Object.defineProperty(exports, name, {
|
Object.defineProperty(exports, name, {
|
||||||
value : value,
|
value : value,
|
||||||
enumerable : true
|
enumerable : true
|
||||||
|
Loading…
Reference in New Issue
Block a user