optimize zlib inflate, the old code would get stuck on malformed swfs

This commit is contained in:
BiFi2000 2021-05-20 17:53:41 +00:00
parent 32435a3c84
commit 7ce2438ea5

View File

@ -1,450 +1,432 @@
/** /**
* Simple module for reading SWF properties * Simple module for reading SWF properties
* *
* (c) 2014 Rafael Leal Dias <rafaeldias.c at gmail dot com> * (c) 2014 Rafael Leal Dias <rafaeldias.c at gmail dot com>
* MIT LICENCE * MIT LICENCE
* *
*/ */
var fs = require('fs') var fs = require('fs')
, zlib = require('zlib') , zlib = require('zlib')
, lzma = require('lzma-purejs') , lzma = require('lzma-purejs')
, Stream = require('stream') , Stream = require('stream')
, SWFBuffer = require('./lib/swf-buffer') , SWFBuffer = require('./lib/swf-buffer')
, SWFTags = require('./lib/swf-tags') , SWFTags = require('./lib/swf-tags')
, SWFReader = exports; , SWFReader = exports;
function readSWFTags(buff, swf) { function readSWFTags(buff, swf) {
var tags = [] var tags = []
, tag , tag
, tagHeader , tagHeader
, flag , flag
, l , l
, sc , sc
, 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 : case SWFTags.FileAttributes :
flag = buff.readUIntLE(32); flag = buff.readUIntLE(32);
fileAttrs = {} 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);
fileAttrs.hasMetaData = tag.hasMetaData = !!(flag & 0x10); fileAttrs.hasMetaData = tag.hasMetaData = !!(flag & 0x10);
fileAttrs.useGPU = tag.useGPU = !!(flag & 0x20); fileAttrs.useGPU = tag.useGPU = !!(flag & 0x20);
fileAttrs.useDirectBit = tag.useDirectBlit = !!(flag & 0x40); fileAttrs.useDirectBit = tag.useDirectBlit = !!(flag & 0x40);
swf.fileAttributes = fileAttrs; swf.fileAttributes = fileAttrs;
break; break;
case SWFTags.Metadata : case SWFTags.Metadata :
swf.metadata = tag.metadata = buff.readString() 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;
case SWFTags.DefineSceneAndFrameLabelData : case SWFTags.DefineSceneAndFrameLabelData :
sc = tag.sceneCount = buff.readEncodedU32(); sc = tag.sceneCount = buff.readEncodedU32();
tag.scenes = []; tag.scenes = [];
while (sc--) while (sc--)
tag.scenes.push({ tag.scenes.push({
offset : buff.readEncodedU32(), offset : buff.readEncodedU32(),
name : buff.readString() name : buff.readString()
}); });
fc = tag.frameLabelCount = buff.readEncodedU32(); fc = tag.frameLabelCount = buff.readEncodedU32();
tag.labels = []; tag.labels = [];
while (fc--) while (fc--)
tag.labels.push({ tag.labels.push({
frameNum : buff.readEncodedU32(), frameNum : buff.readEncodedU32(),
frameLabel : buff.readString() frameLabel : buff.readString()
}); });
break; break;
/** /**
* DefineShape4 extends the capabilities of * DefineShape4 extends the capabilities of
* DefineShape3 by using a new line style * DefineShape3 by using a new line style
* record in the shape * record in the shape
*/ */
//case SWFTags.DefineShape4 : //case SWFTags.DefineShape4 :
// /* id for this character */ // /* id for this character */
// tag.ShapeId = buff.readUIntLE(16); // tag.ShapeId = buff.readUIntLE(16);
// /* bounds of the shape */ // /* bounds of the shape */
// tag.ShapeBounds = buff.readRect(); // tag.ShapeBounds = buff.readRect();
// /* bounds of the shape, excluding the strokes */ // /* bounds of the shape, excluding the strokes */
// tag.EdgeBounds = buff.readRect(); // tag.EdgeBounds = buff.readRect();
// /* reserved, must be 0 */ // /* reserved, must be 0 */
// if (0 !== buff.readBits(5)) // if (0 !== buff.readBits(5))
// throw new Error('Reserved bit used.'); // throw new Error('Reserved bit used.');
// /* if 1, use fill winding. >= SWF 10 */ // /* if 1, use fill winding. >= SWF 10 */
// if (swf.version >= 10) // if (swf.version >= 10)
// tag.UsesFillWindingRule = buff.readBits(1); // tag.UsesFillWindingRule = buff.readBits(1);
// /** // /**
// * if 1, shape contains at least one // * if 1, shape contains at least one
// * non-scaling stroke. // * non-scaling stroke.
// */ // */
// tag.UsesNonScallingStrokes = buff.readBits(1); // tag.UsesNonScallingStrokes = buff.readBits(1);
// /** // /**
// * if 1, shape contains at least one // * if 1, shape contains at least one
// * scaling stroke // * scaling stroke
// */ // */
// tag.UsesScalingStrokes = buff.readBits(1); // tag.UsesScalingStrokes = buff.readBits(1);
// 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)
tag.anchor = buff.readUInt8(); tag.anchor = buff.readUInt8();
break; break;
case SWFTags.DefineSprite : case SWFTags.DefineSprite :
tag.SpriteID = buff.readUIntLE(16); tag.SpriteID = buff.readUIntLE(16);
tag.FrameCount = buff.readUIntLE(16); tag.FrameCount = buff.readUIntLE(16);
tag.ControlTags = readSWFTags(buff, swf); tag.ControlTags = readSWFTags(buff, swf);
break; break;
case SWFTags.ExportAssets : case SWFTags.ExportAssets :
tag.count = buff.readUIntLE(16); tag.count = buff.readUIntLE(16);
tag.assets = []; tag.assets = [];
l = 0; l = 0;
while (l++ < tag.count) while (l++ < tag.count)
tag.assets.push({ tag.assets.push({
id : buff.readUIntLE(16), id : buff.readUIntLE(16),
name : buff.readString() name : buff.readString()
}); });
break; break;
case SWFTags.ImportAssets : case SWFTags.ImportAssets :
/** /**
* URL where the source SWF file can be found * URL where the source SWF file can be found
*/ */
tag.url = buff.readString(); tag.url = buff.readString();
/** /**
* Number of assets to import * Number of assets to import
*/ */
tag.count = buff.readUIntLE(16); tag.count = buff.readUIntLE(16);
tag.assets = []; tag.assets = [];
l = 0; l = 0;
while (l++ < tag.count) while (l++ < tag.count)
tag.assets.push({ tag.assets.push({
/** /**
* Character ID for the l-th item * Character ID for the l-th item
* in importing SWF file * in importing SWF file
*/ */
id : buff.readUIntLE(16), id : buff.readUIntLE(16),
/** /**
* Identifies for the l-th * Identifies for the l-th
* imported character * imported character
*/ */
name : buff.readString() name : buff.readString()
}); });
break; break;
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');
} }
tag.count = buff.readUIntLE(16); tag.count = buff.readUIntLE(16);
tag.assets = []; tag.assets = [];
l = 0; l = 0;
while (l++ < tag.count) while (l++ < tag.count)
tag.assets({ tag.assets({
id : buff.readUIntLE(16), id : buff.readUIntLE(16),
name : buff.readString() name : buff.readString()
}); });
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 :
/** /**
* Maximum recursion Depth * Maximum recursion Depth
*/ */
tag.maxRecursionDepth = buff.readUIntLE(16); tag.maxRecursionDepth = buff.readUIntLE(16);
/** /**
* Maximum ActionScript processing time before script * Maximum ActionScript processing time before script
* stuck dialog box displays * stuck dialog box displays
*/ */
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 = [];
l = 0; l = 0;
while (l++ < tag.numSymbols) while (l++ < tag.numSymbols)
tag.symbols.push({ tag.symbols.push({
id : buff.readUIntLE(16), id : buff.readUIntLE(16),
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);
buff.pointer += alphaDataOffset; buff.pointer += alphaDataOffset;
var restLength = tagHeader.length - 6 - alphaDataOffset; var restLength = tagHeader.length - 6 - 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.DefineBitsJPEG4: case SWFTags.DefineBitsJPEG4:
tag.characterId = buff.readUIntLE(16); tag.characterId = buff.readUIntLE(16);
var alphaDataOffset = buff.readUIntLE(32); var 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; var 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; var 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;
break; break;
} }
tags.push(tag); tags.push(tag);
} }
return tags; return tags;
} }
/** /**
* Reads tags and their contents, passaing a SWF object to callback * Reads tags and their contents, passaing a SWF object to callback
* *
* @param {SWFBuffer} buff * @param {SWFBuffer} buff
* @param {Buffer} compressed_buff * @param {Buffer} compressed_buff
* @param {function} callback * @param {function} callback
* @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."); 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.")); return next(new Error("Buffer is to small, must be greater than 9 bytes."));
} }
var swf = { var swf = {
version : buff.readUInt8(), version : buff.readUInt8(),
fileLength : { fileLength : {
compressed : compressed_buff.length, compressed : compressed_buff.length,
uncompressed : buff.readUIntLE(32) uncompressed : buff.readUIntLE(32)
}, },
frameSize : buff.readRect(), // Returns a RECT object. i.e : { x : 0, y : 0, width : 200, height: 300 } frameSize : buff.readRect(), // Returns a RECT object. i.e : { x : 0, y : 0, width : 200, height: 300 }
frameRate : buff.readUIntLE(16)/256, frameRate : buff.readUIntLE(16)/256,
frameCount : buff.readUIntLE(16) frameCount : buff.readUIntLE(16)
} }
, 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);
} }
return isSync && swf || next( null, swf ); return isSync && swf || next( null, swf );
} }
/** /**
* Concat SWF Header with uncompressed Buffer * Concat SWF Header with uncompressed Buffer
* *
* @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]);
} }
/** /**
* Uncompress SWF and start reading it * Uncompress SWF and start reading it
* *
* @param {Buffer|ArrayBuffer} swf * @param {Buffer|ArrayBuffer} 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);
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf); return readSWFBuff(new SWFBuffer(uncompressed_buff), swf);
} }
const newBuffer = swf.slice(4); zlib.inflate(compressed_buff, function(err, buf) {
const uncompressedLength = newBuffer.readUInt32LE(); readSWFBuff(new SWFBuffer(buf), swf, next);
const chunks = []; });
break;
let readLength = 0; case 0x46 : // uncompressed
var decompressStream = zlib.createInflate() return readSWFBuff(new SWFBuffer( swf ), swf, next);
.on('data', function (chunk) { break;
readLength += chunk.length; case 0x5a : // LZMA compressed
chunks.push(chunk); var lzmaProperties = compressed_buff.slice(4, 9);
compressed_buff = compressed_buff.slice(9);
if (uncompressedLength - 8 === readLength) {
decompressStream.close(); var input_stream = new Stream();
} input_stream.pos = 0;
//decompressStream.pause(); input_stream.readByte = function() {
}).on('error', function(err) { return this.pos >= compressed_buff.length ? -1 : compressed_buff[this.pos++];
console.log(err); };
//next(err);
}).on('close', function() { var output_stream = new Stream();
readSWFBuff(new SWFBuffer( Buffer.concat(chunks) ), swf, next); output_stream.buffer = new Buffer(16384);
}); output_stream.pos = 0;
decompressStream.write(compressed_buff); output_stream.writeByte = function(_byte) {
break; if (this.pos >= this.buffer.length) {
case 0x46 : // uncompressed var newBuffer = new Buffer(this.buffer.length * 2);
return readSWFBuff(new SWFBuffer( swf ), swf, next); this.buffer.copy(newBuffer);
break; this.buffer = newBuffer;
case 0x5a : // LZMA compressed }
var lzmaProperties = compressed_buff.slice(4, 9); this.buffer[this.pos++] = _byte;
compressed_buff = compressed_buff.slice(9); };
output_stream.getBuffer = function() {
var input_stream = new Stream(); // trim buffer
input_stream.pos = 0; if (this.pos !== this.buffer.length) {
input_stream.readByte = function() { var newBuffer = new Buffer(this.pos);
return this.pos >= compressed_buff.length ? -1 : compressed_buff[this.pos++]; this.buffer.copy(newBuffer, 0, 0, this.pos);
}; this.buffer = newBuffer;
}
var output_stream = new Stream(); return this.buffer;
output_stream.buffer = new Buffer(16384); };
output_stream.pos = 0;
output_stream.writeByte = function(_byte) { lzma.decompress(lzmaProperties, input_stream, output_stream, -1);
if (this.pos >= this.buffer.length) { uncompressed_buff = Buffer.concat([swf.slice(0, 8), output_stream.getBuffer()]);
var newBuffer = new Buffer(this.buffer.length * 2);
this.buffer.copy(newBuffer); return readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next);
this.buffer = newBuffer; break;
} default :
this.buffer[this.pos++] = _byte; e = new Error('Unknown SWF compressions');
};
output_stream.getBuffer = function() { if (isSync) {
// trim buffer throw e;
if (this.pos !== this.buffer.length) { } else {
var newBuffer = new Buffer(this.pos); next(e);
this.buffer.copy(newBuffer, 0, 0, this.pos); }
this.buffer = newBuffer; };
} };
return this.buffer;
}; /**
* Check if file is Buffer or ArrayBuffer
lzma.decompress(lzmaProperties, input_stream, output_stream, -1); *
uncompressed_buff = Buffer.concat([swf.slice(0, 8), output_stream.getBuffer()]); * @param {Buffer|ArrayBuffer) b
* @api private
return readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next); *
break; */
default : function isBuffer(b) {
e = new Error('Unknown SWF compressions'); return typeof Buffer !== "undefined" && Buffer.isBuffer(b) || b instanceof ArrayBuffer;
}
if (isSync) {
throw e; /* Exposes Tags constants */
} else { SWFReader.TAGS = SWFTags;
next(e);
} /**
}; * Reads SWF file
}; *
* @param {String|Buffer}} file
/** * @param {function} next - if not a function, uses synchronous algorithm
* Check if file is Buffer or ArrayBuffer * @api public
* *
* @param {Buffer|ArrayBuffer) b */
* @api private SWFReader.read = SWFReader.readSync = function(file, next) {
* if (isBuffer(file)) {
*/ /* File is already a buffer */
function isBuffer(b) { return uncompress(file, next);
return typeof Buffer !== "undefined" && Buffer.isBuffer(b) || b instanceof ArrayBuffer; } else {
} /* Get the buffer */
if ('function' === typeof next) {
/* Exposes Tags constants */ fs.readFile(file, function(err, swf) {
SWFReader.TAGS = SWFTags; if ( err ) {
next(err);
/** return;
* Reads SWF file }
* uncompress(swf, next);
* @param {String|Buffer}} file });
* @param {function} next - if not a function, uses synchronous algorithm } else {
* @api public return uncompress(fs.readFileSync(file));
* }
*/ }
SWFReader.read = SWFReader.readSync = function(file, next) { };
if (isBuffer(file)) {
/* File is already a buffer */
return uncompress(file, next);
} else {
/* Get the buffer */
if ('function' === typeof next) {
fs.readFile(file, function(err, swf) {
if ( err ) {
next(err);
return;
}
uncompress(swf, next);
});
} else {
return uncompress(fs.readFileSync(file));
}
}
};