├── .gitignore
├── LICENSE.txt
├── README.md
├── index.js
├── lib
├── swf-buffer.js
└── swf-tags.js
├── package.json
└── test
├── test.spec.js
└── test.swf
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Rafael Leal Dias
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## SWF Reader
2 |
3 | A simple [node][nodejs] module for reading [SWF format][swf-format].
4 |
5 | ## Installation
6 |
7 | ```sh
8 | $ npm install swf-reader
9 | ```
10 |
11 | ## Usage
12 |
13 | ```js
14 | var SWFReader = require('swf-reader');
15 |
16 | SWFReader.read( 'swf_path.swf', function(err, swf) {
17 | if ( err ) {
18 | // handle error
19 | ...
20 | }
21 | console.log(swf);
22 | });
23 | ```
24 |
25 | ## SWFReader.read(file, callback)
26 |
27 | Returns a [SWF Object](#swf-object) to `callback` function. If it's not possible to read the SWF, an error object is passed as the first argument of `callback`.
28 |
29 | ## SWFReader.readSync(file)
30 |
31 | Returns a [SWF Object](#swf-object) to the caller. If it's not possible to read the SWF, an Exception is thrown.
32 |
33 | The `file` parameter of both methods may be either a file path or a buffer of the SWF file.
34 |
35 | ## SWF Object
36 |
37 | The SWF Object method has the following properties :
38 |
39 | * `version`: The SWF version.
40 | * `fileLength`: An Object containing the following properties :
41 | * `compressed`: The SWF compressed size in bytes.
42 | * `uncompressed`: The SWF uncompressed size in bytes.
43 | * `frameSize`: An Object containing the `width` and `height` of the SWF.
44 | * `frameRate`: The SWF framerate.
45 | * `frameCount`: Number of frames in the SWF.
46 | * `backgroundColor`: The background color of the SWF in the format `#XXXXXX`.
47 | * `fileAttributes`: FileAtributtes defines characteristics of the SWF file.
48 | * `useNetwork`: If `true`, the SWF file is given network file access when loaded locally.
49 | * `as3`: If `true`, the SWF uses ActionScript 3.0. Otherwise it uses ActionScript 1.0 or 2.0.
50 | * `hasMetaData`: If `true`, the SWF file contains the Metadata tag.
51 | * `useGPU`: If `true`, the SWF file uses GPU compositing features when drawing graphics.
52 | * `useDirectBlit`: If `true`, the SWF file uses hardware acceleration to blit graphics to the screen.
53 | * `metadata`: The metadata describes the SWF file to and external process.
54 | * `tags`: An array of `tag`. Each item in the array is an object with a `header` property with the folowing properties:
55 | * `code`: A number indicating the type of the tag. (see [SWF format][swf-format] for more information)
56 | * `length`: The length of the tag in bytes.
57 |
58 | ## Running test
59 |
60 | To run the test invoke the following command within the repo directory :
61 |
62 | ```sh
63 | $ npm test
64 | ```
65 |
66 | ## Todo
67 |
68 | * Read tags fields.
69 | * Write in tags block.
70 |
71 | ## Contributors
72 |
73 | Author: [Rafael Leal Dias][rdleal-git]
74 |
75 | ## License
76 |
77 | MIT
78 |
79 | [nodejs]: http://www.nodejs.org
80 | [swf-format]: http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf-file-format-spec.pdf
81 | [rdleal-git]: https://github.com/rafaeldias
82 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simple module for reading SWF properties
3 | *
4 | * (c) 2014 Rafael Leal Dias
5 | * MIT LICENCE
6 | *
7 | */
8 |
9 | var fs = require('fs')
10 | , zlib = require('zlib')
11 | , lzma = require('lzma-purejs')
12 | , SWFBuffer = require('./lib/swf-buffer')
13 | , SWFTags = require('./lib/swf-tags')
14 | , SWFReader = exports;
15 |
16 | function readSWFTags(buff, swf) {
17 | var tags = []
18 | , tag
19 | , tagHeader
20 | , flag
21 | , l
22 | , sc
23 | , fc;
24 |
25 | /* Reads TagCodeAndLength from Tag's RECORDHEADER */
26 | while( (tagHeader = buff.readTagCodeAndLength()) ) {
27 | tag = {
28 | header : tagHeader
29 | };
30 | switch( tagHeader.code ) {
31 | case SWFTags.FileAttributes :
32 | flag = buff.readUIntLE(32);
33 | fileAttrs = {}
34 |
35 | fileAttrs.useNetwork = tag.useNetwork = !!(flag & 0x1);
36 | fileAttrs.as3 = tag.as3 = !!(flag & 0x8);
37 | fileAttrs.hasMetaData = tag.hasMetaData = !!(flag & 0x10);
38 | fileAttrs.useGPU = tag.useGPU = !!(flag & 0x20);
39 | fileAttrs.useDirectBit = tag.useDirectBlit = !!(flag & 0x40);
40 |
41 | swf.fileAttributes = fileAttrs;
42 | break;
43 | case SWFTags.Metadata :
44 | swf.metadata = tag.metadata = buff.readString()
45 | break;
46 | case SWFTags.SetBackgroundColor :
47 | tag.RGB = buff.readRGB();
48 | swf.backgroundColor = '#' + (tag.RGB[0]*65536 + tag.RGB[1]*256 + tag.RGB[0]).toString(16);
49 | break;
50 | case SWFTags.Protect :
51 | swf.protect = tagHeader.length && buff.readString();
52 | break;
53 | case SWFTags.DefineSceneAndFrameLabelData :
54 | sc = tag.sceneCount = buff.readEncodedU32();
55 | tag.scenes = [];
56 |
57 | while (sc--)
58 | tag.scenes.push({
59 | offset : buff.readEncodedU32(),
60 | name : buff.readString()
61 | });
62 |
63 | fc = tag.frameLabelCount = buff.readEncodedU32();
64 | tag.labels = [];
65 |
66 | while (fc--)
67 | tag.labels.push({
68 | frameNum : buff.readEncodedU32(),
69 | frameLabel : buff.readString()
70 | });
71 | break;
72 | /**
73 | * DefineShape4 extends the capabilities of
74 | * DefineShape3 by using a new line style
75 | * record in the shape
76 | */
77 | //case SWFTags.DefineShape4 :
78 | // /* id for this character */
79 | // tag.ShapeId = buff.readUIntLE(16);
80 | // /* bounds of the shape */
81 | // tag.ShapeBounds = buff.readRect();
82 | // /* bounds of the shape, excluding the strokes */
83 | // tag.EdgeBounds = buff.readRect();
84 | // /* reserved, must be 0 */
85 | // if (0 !== buff.readBits(5))
86 | // throw new Error('Reserved bit used.');
87 | // /* if 1, use fill winding. >= SWF 10 */
88 | // if (swf.version >= 10)
89 | // tag.UsesFillWindingRule = buff.readBits(1);
90 | // /**
91 | // * if 1, shape contains at least one
92 | // * non-scaling stroke.
93 | // */
94 | // tag.UsesNonScallingStrokes = buff.readBits(1);
95 | // /**
96 | // * if 1, shape contains at least one
97 | // * scaling stroke
98 | // */
99 | // tag.UsesScalingStrokes = buff.readBits(1);
100 | // tag.shapes = buff.readShapeWithStyle();
101 | // break;
102 | case SWFTags.FrameLabel :
103 | tag.name = buff.readString()
104 | l = Buffer.byteLength(tag.name);
105 | /* check if it's an named anchor */
106 | if (l & (tagHeader.length - 1) != l)
107 | tag.anchor = buff.readUInt8();
108 | break;
109 | case SWFTags.DefineSprite :
110 | tag.SpriteID = buff.readUIntLE(16);
111 | tag.FrameCount = buff.readUIntLE(16);
112 | tag.ControlTags = readSWFTags(buff, swf);
113 | break;
114 | case SWFTags.ExportAssets :
115 | tag.count = buff.readUIntLE(16);
116 | tag.assets = [];
117 |
118 | l = 0;
119 |
120 | while (l++ < tag.count)
121 | tag.assets.push({
122 | id : buff.readUIntLE(16),
123 | name : buff.readString()
124 | });
125 | break;
126 | case SWFTags.ImportAssets :
127 | /**
128 | * URL where the source SWF file can be found
129 | */
130 | tag.url = buff.readString();
131 | /**
132 | * Number of assets to import
133 | */
134 | tag.count = buff.readUIntLE(16);
135 | tag.assets = [];
136 |
137 | l = 0;
138 |
139 | while (l++ < tag.count)
140 | tag.assets.push({
141 | /**
142 | * Character ID for the l-th item
143 | * in importing SWF file
144 | */
145 | id : buff.readUIntLE(16),
146 | /**
147 | * Identifies for the l-th
148 | * imported character
149 | */
150 | name : buff.readString()
151 | });
152 | break;
153 | case SWFTags.ImportAssets2 :
154 | tag.url = buff.readString();
155 |
156 | if ( !(1 === buff.readUInt8() && 0 === buff.readUInt8()) ) {
157 | throw new Error('Reserved bits for ImportAssets2 used');
158 | }
159 |
160 | tag.count = buff.readUIntLE(16);
161 | tag.assets = [];
162 |
163 | l = 0;
164 |
165 | while (l++ < tag.count)
166 | tag.assets({
167 | id : buff.readUIntLE(16),
168 | name : buff.readString()
169 | });
170 | break;
171 | case SWFTags.EnableDebbuger :
172 | tag.password = buff.readString()
173 | break;
174 | case SWFTags.EnableDebugger2 :
175 | if (0 !== buff.readUIntLE(16)) {
176 | throw new Error('Reserved bit for EnableDebugger2 used.');
177 | }
178 | tag.password = buff.readString()
179 | break;
180 | case SWFTags.ScriptLimits :
181 | /**
182 | * Maximum recursion Depth
183 | */
184 | tag.maxRecursionDepth = buff.readUIntLE(16);
185 | /**
186 | * Maximum ActionScript processing time before script
187 | * stuck dialog box displays
188 | */
189 | tag.scriptTimeoutSeconds = buff.readUIntLE(16);
190 | break;
191 | case SWFTags.SymbolClass :
192 | tag.numSymbols = buff.readUIntLE(16);
193 | tag.symbols = [];
194 |
195 | l = 0;
196 |
197 | while (l++ < tag.numSymbols)
198 | tag.symbols.push({
199 | id : buff.readUIntLE(16),
200 | name : buff.readString()
201 | });
202 | break;
203 | case SWFTags.DefineScalingGrid :
204 | tag.characterId = buff.readUIntLE(16);
205 | tag.splitter = buff.readRect();
206 | break;
207 | case SWFTags.setTabIndex :
208 | tag.depth = buff.readUIntLE(16);
209 | tag.tabIndex = buff.readUIntLE(16);
210 | break;
211 | default:
212 | tag.data = buff.buffer.slice(buff.pointer, buff.pointer + tagHeader.length);
213 | buff.pointer += tagHeader.length;
214 | break;
215 | }
216 | tags.push(tag);
217 | }
218 | return tags;
219 | }
220 |
221 | /**
222 | * Reads tags and their contents, passaing a SWF object to callback
223 | *
224 | * @param {SWFBuffer} buff
225 | * @param {Buffer} compressed_buff
226 | * @param {function} callback
227 | * @api private
228 | *
229 | */
230 | function readSWFBuff(buff, compressed_buff, next) {
231 | buff.seek(3);// start
232 |
233 | var swf = {
234 | version : buff.readUInt8(),
235 | fileLength : {
236 | compressed : compressed_buff.length,
237 | uncompressed : buff.readUIntLE(32)
238 | },
239 | frameSize : buff.readRect(), // Returns a RECT object. i.e : { x : 0, y : 0, width : 200, height: 300 }
240 | frameRate : buff.readUIntLE(16)/256,
241 | frameCount : buff.readUIntLE(16)
242 | }
243 | , isSync = 'function' !== typeof next;
244 |
245 | try {
246 | swf.tags = readSWFTags(buff, swf);
247 | } catch(e) {
248 | if (isSync) throw e;
249 | return next(e);
250 | }
251 |
252 | return isSync && swf || next( null, swf );
253 | }
254 |
255 | /**
256 | * Concat SWF Header with uncompressed Buffer
257 | *
258 | * @param {Buffer|ArrayBuffer} buff
259 | * @param {Buffer|ArrayBuffer} swf
260 | */
261 | function concatSWFHeader(buff, swf) {
262 | return Buffer.concat([swf.slice(0, 8), buff]);
263 | }
264 |
265 | /**
266 | * Uncompress SWF and start reading it
267 | *
268 | * @param {Buffer|ArrayBuffer} swf
269 | * @param {function} callback
270 | *
271 | */
272 | function uncompress(swf, next) {
273 | var compressed_buff = swf.slice(8)
274 | , uncompressed_buff
275 | , isSync = 'function' !== typeof next
276 | , e;
277 |
278 | // uncompress buffer
279 | switch( swf[0] ) {
280 | case 0x43 : // zlib compressed
281 | if (isSync) {
282 | uncompressed_buff = concatSWFHeader(zlib.unzipSync(compressed_buff), swf);
283 | return readSWFBuff(new SWFBuffer(uncompressed_buff), swf);
284 | }
285 |
286 | zlib.unzip( compressed_buff, function(err, result) {
287 | if ( err ) {
288 | next(err);
289 | return;
290 | }
291 | uncompressed_buff = concatSWFHeader(result, swf);
292 | readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next);
293 | });
294 | break;
295 | case 0x46 : // uncompressed
296 | return readSWFBuff(new SWFBuffer( swf ), swf, next);
297 | break;
298 | case 0x5a : // LZMA compressed
299 | uncompressed_buff = Buffer.concat([swf.slice(0, 8), lzma.decompressFile(compressed_buff)]);
300 |
301 | return readSWFBuff(new SWFBuffer(uncompressed_buff), swf, next);
302 | break;
303 | default :
304 | e = new Error('Unknown SWF compressions');
305 |
306 | if (isSync) {
307 | throw e;
308 | } else {
309 | next(e);
310 | }
311 | };
312 | };
313 |
314 | /**
315 | * Check if file is Buffer or ArrayBuffer
316 | *
317 | * @param {Buffer|ArrayBuffer) b
318 | * @api private
319 | *
320 | */
321 | function isBuffer(b) {
322 | return typeof Buffer !== "undefined" && Buffer.isBuffer(b) || b instanceof ArrayBuffer;
323 | }
324 |
325 | /* Exposes Tags constants */
326 | SWFReader.TAGS = SWFTags;
327 |
328 | /**
329 | * Reads SWF file
330 | *
331 | * @param {String|Buffer}} file
332 | * @param {function} next - if not a function, uses synchronous algorithm
333 | * @api public
334 | *
335 | */
336 | SWFReader.read = SWFReader.readSync = function(file, next) {
337 | if (isBuffer(file)) {
338 | /* File is already a buffer */
339 | return uncompress(file, next);
340 | } else {
341 | /* Get the buffer */
342 | if ('function' === typeof next) {
343 | fs.readFile(file, function(err, swf) {
344 | if ( err ) {
345 | next(err);
346 | return;
347 | }
348 | uncompress(swf, next);
349 | });
350 | } else {
351 | return uncompress(fs.readFileSync(file));
352 | }
353 | }
354 | };
355 |
--------------------------------------------------------------------------------
/lib/swf-buffer.js:
--------------------------------------------------------------------------------
1 |
2 | var RECORDHEADER_LENTH_FULL = 0x3f
3 | // null-character
4 | , EOS = 0x00
5 | , styleCountExt = 0xFF;
6 |
7 | function readStyleArray(buffer, next) {
8 | var styleArrayCount = buffer.readUInt8()
9 | , styles = [];
10 |
11 | if (styleArrayCount === styleCountExt)
12 | styleArrayCount = buffer.readUIntLE(16);
13 |
14 | for (var i = 0; i < styleArrayCount; i++)
15 | styles.push(next(buffer));
16 |
17 | return styles;
18 | }
19 |
20 | function readFillStyle(buffer) {
21 | var type = buffer.readUInt8()
22 | , fillStyle = {
23 | /**
24 | * 0x00 = solid
25 | * 0x10 = linear gradient fill
26 | * 0x12 = radial gradient fill
27 | * 0x13 = focal radial gradient fill (SWF 8 or later)
28 | * 0x40 = repeating bitmap fill
29 | * 0x41 = clipped bitmap fill
30 | * 0x42 = non-smoothed repeating bitmap
31 | * 0x43 = non-smoothed clipped bitmap
32 | */
33 | fillStyleType : type
34 | };
35 |
36 | switch (type) {
37 | case 0x00:
38 | fillStyle.color = buffer.readRGBA();
39 | break;
40 | case 0x10, 0x12, 0x13:
41 | console.log('Gradient');
42 | break;
43 | case 0x40, 0x41, 0x42, 0x43:
44 | fillStyle.bitmapId = buffer.readUIntLE(16);
45 | break;
46 | }
47 |
48 | return fillStyle;
49 | }
50 |
51 | function readLineStyle(buffer) {
52 | return {
53 | width: buffer.readUIntLE(16)/20,
54 | color: buffer.readRGBA()
55 | };
56 | }
57 |
58 | function readShapeRecords(buffer) {
59 | var shapeRecords = []
60 | , typeFlag = buffer.readBits(1)
61 | , shapeRecord
62 | , eos;
63 |
64 | while ((eos = buffer.readBits(5))) {
65 | if (0 === typeFlag) {
66 | shaperecord = {
67 | type: 'STYLECHANGERECORD'
68 | };
69 | }
70 | }
71 |
72 | return shapeRecords;
73 | }
74 |
75 | /**
76 | *
77 | * Constructor of SWFBuffer object
78 | *
79 | * @param {Buffer} buffer
80 | * @return Instance of SWFBuffer
81 | */
82 |
83 | function SWFBuffer( buffer ) {
84 | if ( !Buffer.isBuffer( buffer ) ) {
85 | throw new Error('Invalid buffer');
86 | }
87 | this.buffer = buffer;
88 | this.pointer = 0;
89 | this.position = 1;
90 | this.current = 0;
91 | this.length = buffer.length;
92 | }
93 |
94 | /**
95 | * Reads unsigned 16 or 32 Little Endian Bits
96 | * and advance pointer to next bits / 8 bytes
97 | *
98 | * @param {Number} bits
99 | * @return {Number} Value read from buffer
100 | */
101 |
102 | SWFBuffer.prototype.readUIntLE = function( bits ) {
103 | var value = 0;
104 | try {
105 | value = this.buffer['readUInt' + bits + 'LE'](this.pointer);
106 | this.pointer += bits / 8;
107 | } catch ( e ) {
108 | throw e;
109 | }
110 | return value;
111 | };
112 |
113 | /**
114 | * Reads unsigned 8 bit from the buffer
115 | *
116 | * @return {Number} Value read from buffer
117 | */
118 |
119 | SWFBuffer.prototype.readUInt8 = function() {
120 | return this.buffer.readUInt8( this.pointer++ );
121 | };
122 |
123 | /**
124 | * Reads 32-bit unsigned integers value encoded (1-5 bytes)
125 | *
126 | * @return {Number} 32-bit unsigned integer
127 | */
128 |
129 | SWFBuffer.prototype.readEncodedU32 = function() {
130 | var i = 5
131 | , result = 0
132 | , nb;
133 |
134 | do
135 | result += (nb = this.nextByte());
136 | while((nb & 128) && --i);
137 |
138 | return result;
139 | };
140 |
141 | /**
142 | * Reads an encoded data from buffer and returns a
143 | * string using the specified character set.
144 | *
145 | * @param {String} encoding - defaults to 'utf8'
146 | * @returns {String} Decoded string
147 | */
148 |
149 | SWFBuffer.prototype.readString = function(encoding) {
150 | var init = this.pointer;
151 | while(this.readUInt8() !== EOS);
152 | return this.buffer.toString(encoding || 'utf8', init, this.pointer - 1);
153 | };
154 |
155 | /**
156 | * Reads RGB value
157 | *
158 | * @return {Array} Array of RGB value
159 | */
160 |
161 | SWFBuffer.prototype.readRGB = function() {
162 | return [this.readUInt8(), this.readUInt8(), this.readUInt8()];
163 | };
164 |
165 | /**
166 | * Reads RGBA value
167 | *
168 | * @return {Array} Array of RGBA value
169 | */
170 |
171 | SWFBuffer.prototype.readRGBA = function() {
172 | var rgba = this.readRGB();
173 | rgba.push(this.readUInt8());
174 | return rgba;
175 | }
176 |
177 | /**
178 | * Reads ShapeWithStyle structure
179 | * used by the DefineShape tag.
180 | *
181 | * @return ShapeWithStyle structure
182 | */
183 | SWFBuffer.prototype.readShapeWithStyle = function() {
184 | return {
185 | fillStyles : readStyleArray(this, readFillStyle),
186 | lineStyles : readStyleArray(this, readLineStyle),
187 | numFillBits : this.readBits(4),
188 | numLineBits : this.readBits(4),
189 | shapeRecords: readShapeRecords(this)
190 | }
191 | };
192 |
193 | /**
194 | * Reads RECORDHEADER from next tag in the buffer
195 | *
196 | * @return {Object} Tag code and length
197 | */
198 |
199 | SWFBuffer.prototype.readTagCodeAndLength = function() {
200 | var n = this.readUIntLE(16)
201 | , tagType = n >> 6
202 | , tagLength = n & RECORDHEADER_LENTH_FULL;
203 |
204 | if ( n === 0 )
205 | return false;
206 |
207 | if ( tagLength === RECORDHEADER_LENTH_FULL )
208 | tagLength = this.readUIntLE(32);
209 |
210 | return { code : tagType, length : tagLength };
211 | };
212 |
213 | /**
214 | * Reads RECT format
215 | *
216 | * @return {Object} x, y, width and height of the RECT
217 | */
218 |
219 | SWFBuffer.prototype.readRect = function() {
220 |
221 | this.start();
222 |
223 | var NBits = this.readBits(5)
224 | , Xmin = this.readBits(NBits, true)/20
225 | , Xmax = this.readBits(NBits, true)/20
226 | , Ymin = this.readBits(NBits, true)/20
227 | , Ymax = this.readBits(NBits, true)/20;
228 |
229 | return {
230 | x : Xmin,
231 | y : Ymin,
232 | width : (Xmax > Xmin ? Xmax - Xmin : Xmin - Xmax),
233 | height : (Ymax > Ymin ? Ymax - Ymin : Ymin - Ymax)
234 | };
235 |
236 | }
237 |
238 | /**
239 | * Sets internal pointer to the specified position;
240 | *
241 | * @param {Number} pos
242 | */
243 |
244 | SWFBuffer.prototype.seek = function( pos ) {
245 | this.pointer = pos % this.buffer.length;
246 | };
247 |
248 | /**
249 | * Resets position and sets current to next Byte in buffer
250 | */
251 | SWFBuffer.prototype.start = function() {
252 | this.current = this.nextByte();
253 | this.position = 1;
254 | };
255 |
256 | /**
257 | * Gets next Byte in the buffer and Increment internal pointer
258 | *
259 | * @return {Number} Next byte in buffer
260 | */
261 |
262 | SWFBuffer.prototype.nextByte = function() {
263 | return this.pointer > this.buffer.length ? null : this.buffer[ this.pointer++ ];
264 | };
265 |
266 | /**
267 | * Reads b bits from current byte in buffer
268 | *
269 | * @param {Number} b
270 | * @return {Number} Bits read from buffer
271 | */
272 |
273 | SWFBuffer.prototype.readBits = function( b, signed ) {
274 | var n = 0
275 | , r = 0
276 | , sign = signed && ++n && ((this.current >> (8-this.position++)) & 1) ? -1 : 1;
277 |
278 | while( n++ < b ) {
279 | if ( this.position > 8 ) this.start();
280 |
281 | r = (r << 1 ) + ((this.current >> (8-this.position++)) & 1);
282 | }
283 | return sign * r;
284 | };
285 |
286 | /* Exposes class */
287 | exports = module.exports = SWFBuffer;
288 |
--------------------------------------------------------------------------------
/lib/swf-tags.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Defines constants on exports object
3 | *
4 | * @param {String} name
5 | * @param {Mixed} value
6 | */
7 |
8 | function define(name, value) {
9 | Object.defineProperty(exports, name, {
10 | value : value,
11 | enumerable : true
12 | });
13 | }
14 |
15 | /* SWF Tags Type */
16 |
17 | define('End', 0);
18 | define('ShowFrame', 1);
19 | define('DefineShape', 2);
20 | define('PlaceObject', 4);
21 | define('RemoveObject', 5);
22 | define('DefineBits', 6);
23 | define('DefineButton', 7);
24 | define('JPEGTables', 8);
25 | define('SetBackgroundColor', 9);
26 | define('DefineFont', 10);
27 | define('DefineText', 11);
28 | define('DoAction', 12);
29 | define('DefineFontInfo', 13);
30 | define('DefineSound', 14);
31 | define('StartSound', 15);
32 | define('DefineButtonSound', 17);
33 | define('SoundStreamHead', 18);
34 | define('SoundStreamBlock', 19);
35 | define('DefineBitsLossless', 20);
36 | define('DefineBitsJPEG2', 21);
37 | define('DefineShape2', 22);
38 | define('DefineButtonCxform', 23);
39 | define('Protect', 24);
40 | define('PlaceObject2', 26);
41 | define('RemoveObject2', 28);
42 | define('DefineShape3', 32);
43 | define('DefineText2', 33);
44 | define('DefineButton2', 34);
45 | define('DefineBitsJPEG3', 35);
46 | define('DefineBitsLossless2', 36);
47 | define('DefineEditText', 37);
48 | define('DefineSprite', 39);
49 | define('SerialNumber', 41);
50 | define('FrameLabel', 43);
51 | define('SoundStreamHead2', 45);
52 | define('DefineMorphShape', 46);
53 | define('DefineFont2', 48);
54 | define('ExportAssets', 56);
55 | define('ImportAssets', 57);
56 | define('EnableDebugger', 58);
57 | define('DoInitAction', 59);
58 | define('DefineVideoStream', 60);
59 | define('VideoFrame', 61);
60 | define('DefineFontInfo2', 62);
61 | define('EnableDebugger2', 64);
62 | define('ScriptLimits', 65);
63 | define('SetTabIndex', 66);
64 | define('FileAttributes', 69);
65 | define('PlaceObject3', 70);
66 | define('ImportAssets2', 71);
67 | define('DefineFontAlignZones', 73);
68 | define('CSMTextSettings', 74);
69 | define('DefineFont3', 75);
70 | define('SymbolClass', 76);
71 | define('Metadata', 77);
72 | define('DefineScalingGrid', 78);
73 | define('DoABC', 82);
74 | define('DefineShape4', 83);
75 | define('DefineMorphShape2', 84);
76 | define('DefineSceneAndFrameLabelData', 86);
77 | define('DefineBinaryData', 87);
78 | define('DefineFontName', 88);
79 | define('StartSound2', 89);
80 | define('DefineBitsJPEG4', 90);
81 | define('DefineFont4', 91);
82 | //define('TagMax' (DefineFont4 + 1)
83 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "swf-reader",
3 | "version": "1.0.0",
4 | "description": "A simple node module for reading SWF format",
5 | "main": "index.js",
6 | "dependencies": {
7 | "lzma-purejs": "~0.9.3"
8 | },
9 | "directories": {
10 | "test": "test"
11 | },
12 | "scripts": {
13 | "test": "TEST_FILE='test/test.swf' node test/test.spec.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/rafaeldias/swf-reader"
18 | },
19 | "keywords": [
20 | "swf",
21 | "flash",
22 | "adobe"
23 | ],
24 | "author": "Rafael Leal Dias ",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/rafaeldias/swf-reader/issues"
28 | },
29 | "homepage": "https://github.com/rafaeldias/swf-reader"
30 | }
31 |
--------------------------------------------------------------------------------
/test/test.spec.js:
--------------------------------------------------------------------------------
1 | var SWFReader = require('../index')
2 | , fs = require('fs')
3 | , SWFInfo = function(swf) {
4 | console.log('SWF version : ' + swf.version);
5 | console.log('SWF size : ' + swf.frameSize.width + 'x' + swf.frameSize.height);
6 | console.log('SWF background : ' + swf.backgroundColor);
7 | console.log('SWF compressed bytes : ' + swf.fileLength.compressed + ', uncompressed bytes : ' + swf.fileLength.uncompressed);
8 | console.log('SWF frameRate : ' + swf.frameRate);
9 | console.log('SWF frames : ' + swf.frameCount);
10 | console.log('Test OK.');
11 | };
12 |
13 | if ( process.env.TEST_FILE ) {
14 | console.log('Testing file path...');
15 | SWFReader.read(process.env.TEST_FILE, function(err, swf){
16 | if ( err ) {
17 | console.log(err);
18 | return;
19 | }
20 |
21 | SWFInfo(swf);
22 |
23 | console.log('Testing buffer...');
24 |
25 | fs.readFile(process.env.TEST_FILE, function(err, buff) {
26 | if ( err ) {
27 | console.log(err);
28 | return;
29 | }
30 |
31 | SWFReader.read(buff, function(err, swf) {
32 | if ( err ) {
33 | console.log(err);
34 | return;
35 | }
36 |
37 | SWFInfo(swf);
38 |
39 | console.log('Testing Sync...');
40 | SWFInfo(SWFReader.readSync(buff));
41 |
42 |
43 | console.log('Tags:');
44 | console.log(swf.tags);
45 | });
46 | });
47 | });
48 | }else{
49 | throw new Error("TEST_FILE env var not set to .swf");
50 | }
51 |
--------------------------------------------------------------------------------
/test/test.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaeldias/swf-reader/b52ad60b83ba6fcb0c189174a6bca10be63455c0/test/test.swf
--------------------------------------------------------------------------------