Created
November 6, 2012 16:25
-
-
Save tmshv/4025805 to your computer and use it in GitHub Desktop.
SWF File Format Action Script Parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.gotoandstop.bytes{ | |
import flash.utils.ByteArray; | |
/** | |
* @author: Roman Timashev (roman@tmshv.ru) | |
**/ | |
public class ByteArrayUtil{ | |
private static var __bitPosition:uint; | |
public function ByteArrayUtil(){ | |
} | |
public static function readBitSequence(bytes:ByteArray, position:uint, bitLength:uint, length:uint, bitOffset:uint=0):Object{ | |
if(!bytes){ | |
throw new ArgumentError('Error #1507 Argument bytes connot be null.'); | |
} | |
var result:Object = 0; | |
var pos:uint = bytes.position; | |
if(length){ | |
bytes.position = position; | |
ByteArrayUtil.__bitPosition = bitOffset; | |
//var needed:uint = Math.ceil((bitLength * number) / 8); | |
//if((needed + bitOffset) > (bytes.length - bytes.position)){ | |
// trace('needed:', needed, ', available:', bytes.length - bytes.position) | |
// return 0; | |
//} | |
if(length == 1){ | |
result = ByteArrayUtil.readBits(bitLength, bytes); | |
}else{ | |
result = new Array(); | |
for(var i:uint; i < length; i++){ | |
result.push(ByteArrayUtil.readBits(bitLength, bytes)); | |
} | |
} | |
} | |
bytes.position = pos; | |
return result; | |
} | |
private static function readBits(n:uint, bytes:ByteArray):uint{ | |
var result:uint; | |
var byte:uint = bytes[bytes.position + Math.floor(__bitPosition / 8)]; | |
var localBitPosition:uint = __bitPosition % 8; | |
var a:uint = 8 - localBitPosition; | |
if(a > n){ | |
result = byte >> (8 - n); | |
__bitPosition += n; | |
}else{ | |
var upper:uint = ((byte << localBitPosition) & 0xFF) >> localBitPosition; | |
__bitPosition += a; | |
var lower:uint = readBits(n - a, bytes); | |
result = upper << (n - a) | lower; | |
} | |
return result; | |
} | |
public static function writeStringBytes(bytes:ByteArray, string:String):void{ | |
trace("ByteArrayUtils.writeStringByte(): I'm shitty!") | |
var length:uint = string.length; | |
var byte:int; | |
for(var i:uint; i < length; i += 2){ | |
byte = parseInt(string.substr(i, 2), 0x10); | |
bytes.writeByte(byte); | |
} | |
} | |
public static function findStringEnd(bytes:ByteArray, offset:uint=0):uint{ | |
if(!bytes) throw new ArgumentError('Error #1507 Argument bytes connot be null.'); | |
var end:uint; | |
var pos:uint = bytes.position; | |
bytes.position = offset; | |
while(bytes.bytesAvailable){ | |
var zero:uint = bytes.readUnsignedByte(); | |
if(!zero){ | |
end = bytes.position; | |
break; | |
} | |
} | |
bytes.position = pos; | |
return end; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.gotoandstop.bytes{ | |
import flash.geom.Rectangle; | |
import flash.utils.ByteArray; | |
import flash.utils.Endian; | |
/** | |
* @author: Roman Timashev (roman@tmshv.ru) | |
* @version: 1.1 | |
* @date: 01.08.2009 | |
**/ | |
public class SWFRipper extends ByteArray{ | |
private static const SWF_SIGNATURE_UNCOMPRESSED:String = 'FWS'; | |
private static const SWF_SIGNATURE_COMPRESSED:String = 'CWS'; | |
private var _signature:String; | |
public function get signature():String{ | |
return this._signature; | |
} | |
private var _version:uint; | |
public function get version():uint{ | |
return this._version; | |
} | |
private var _length:uint; | |
public function get swfLength():uint{ | |
return this._length; | |
} | |
private var _frameSize:Rectangle; | |
public function get frameSize():Rectangle{ | |
return this._frameSize; | |
} | |
private var _frameRate:uint; | |
public function get frameRate():uint{ | |
return this._frameRate; | |
} | |
private var _frameNumber:uint; | |
public function get frameNumber():uint{ | |
return this._frameNumber; | |
} | |
private var _compressed:Boolean; | |
public function get compressed():Boolean{ | |
return this._compressed; | |
} | |
private var __firstTagPosition:uint; | |
public function SWFRipper(bytes:ByteArray=null){ | |
super(); | |
if(bytes) this.parse(bytes); | |
} | |
public function parse(bytes:ByteArray):void{ | |
bytes.position = 0; | |
var valid:Boolean = SWFRipper.isSWF(bytes); | |
if(valid) super.writeBytes(bytes); | |
else throw new ArgumentError('Error #1508: The value specifed for argument bytes is invalid.'); | |
bytes.position = 0; | |
var sign:String = bytes.readUTFBytes(3); | |
if(sign == SWFRipper.SWF_SIGNATURE_COMPRESSED){ | |
this.uncompress(); | |
}else{ | |
this._compressed = false; | |
} | |
this.parseHeader(); | |
} | |
public override function uncompress():void{ | |
var endian:String = super.endian; | |
super.endian = Endian.LITTLE_ENDIAN; | |
super.position = 0; | |
var sign:String = super.readUTFBytes(3); | |
if(sign == SWFRipper.SWF_SIGNATURE_COMPRESSED){ | |
var uncompressed:ByteArray = new ByteArray(); | |
uncompressed.writeUTFBytes(SWFRipper.SWF_SIGNATURE_UNCOMPRESSED); | |
uncompressed.writeBytes(this, 3, 5); | |
super.position = 8; | |
super.readBytes(this); | |
super.uncompress(); | |
uncompressed.writeBytes(this); | |
super.length = 0; | |
super.writeBytes(uncompressed); | |
}else if(sign == SWFRipper.SWF_SIGNATURE_COMPRESSED){ | |
}else{ | |
throw new ArgumentError('Error #1508: The value specifed for argument bytes is invalid.'); | |
} | |
this._compressed = false; | |
super.endian = endian; | |
super.position = 0; | |
} | |
public override function compress():void{ | |
var endian:String = super.endian; | |
super.endian = Endian.LITTLE_ENDIAN; | |
super.position = 0; | |
var sign:String = super.readUTFBytes(3); | |
if(sign == SWFRipper.SWF_SIGNATURE_UNCOMPRESSED){ | |
var compressed:ByteArray = new ByteArray(); | |
compressed.writeUTFBytes(SWFRipper.SWF_SIGNATURE_COMPRESSED); | |
compressed.writeBytes(this, 3, 5); | |
super.position = 8; | |
super.readBytes(this); | |
super.compress(); | |
compressed.writeBytes(this); | |
super.length = 0; | |
super.writeBytes(compressed); | |
}else if(sign == SWFRipper.SWF_SIGNATURE_COMPRESSED){ | |
}else{ | |
throw new ArgumentError('Error #1508: The value specifed for argument bytes is invalid.'); | |
} | |
this._compressed = true; | |
super.endian = endian; | |
super.position = 0; | |
} | |
public function removeTags(swfTag:uint):void{ | |
var endian:String = super.endian; | |
var position:uint = super.position; | |
super.endian = Endian.LITTLE_ENDIAN; | |
super.position = this.__firstTagPosition; | |
var result:ByteArray = new ByteArray(); | |
var tag:uint; | |
var type:uint; | |
var length:uint; | |
var long:Boolean; | |
var pos:uint; | |
var bytes:ByteArray; | |
while(super.bytesAvailable){ | |
pos = super.position; | |
tag = super.readUnsignedShort(); | |
type = tag >> 6; | |
long = (tag & 0x3F) == 0x3F; | |
length = long ? super.readUnsignedInt() : (tag & 0x3F); | |
switch(type){ | |
case swfTag: | |
result.writeBytes(this, 0, pos); | |
break; | |
} | |
super.position = super.position + (!long ? (length + 2) : (length + 6)); | |
} | |
result.writeBytes(this, super.position); | |
super.length = 0; | |
super.writeBytes(result); | |
super.position = position; | |
super.endian = endian; | |
} | |
public function getTags(swfTag:uint):Array{ | |
var result:Array = new Array(); | |
var position:uint = super.position; | |
super.position = __firstTagPosition; | |
var endian:String = super.endian; | |
super.endian = Endian.LITTLE_ENDIAN; | |
var tag:uint; | |
var type:uint; | |
var length:uint; | |
var long:Boolean; | |
var pos:uint; | |
var bytes:ByteArray; | |
while(super.bytesAvailable){ | |
pos = super.position; | |
tag = super.readUnsignedShort(); | |
type = tag >> 6; | |
long = ((tag & 0x3F) == 0x3F); | |
length = long ? super.readUnsignedInt() : (tag & 0x3F); | |
switch(type){ | |
case swfTag: | |
bytes = new ByteArray(); | |
bytes.writeBytes(this, pos, (!long ? (length + 2) : (length + 6))); | |
bytes.position = 0; | |
result.push(bytes); | |
break; | |
} | |
super.position = super.position + length; | |
} | |
super.position = position; | |
super.endian = endian; | |
return result; | |
} | |
public function getTag(swfTag:uint, offset:uint=0):ByteArray{ | |
var list:Array = this.getTags(swfTag); | |
var result:ByteArray = (offset < list.length) ? list[offset] as ByteArray : list[list.length - 1] as ByteArray; | |
return result; | |
} | |
public function getMetadata():XML{ | |
var data:Array = this.getTags(SWFTag.METADATA); | |
var bytes:ByteArray = data[0] as ByteArray; | |
bytes.endian = Endian.LITTLE_ENDIAN; | |
var tag:uint = bytes.readUnsignedShort(); | |
var length:uint = ((tag & 0x3F) == 0x3F) ? bytes.readUnsignedInt() : (tag & 0x3F); | |
var meta:String = bytes.readUTFBytes(length); | |
return new XML(meta); | |
} | |
private function parseHeader():void{ | |
var endian:String = super.endian; | |
var pos:uint = super.position; | |
super.endian = Endian.LITTLE_ENDIAN; | |
super.position = 0; | |
this._signature = super.readUTFBytes(3); | |
this._version = super.readUnsignedByte(); | |
this._length = super.readUnsignedInt(); | |
var nBits:uint = ByteArrayUtil.readBitSequence(this, 8, 5, 1) as uint; | |
var rect:Array = ByteArrayUtil.readBitSequence(this, 8, nBits, 4, 5) as Array; | |
super.position = super.position + Math.ceil((5 + nBits * 4) / 8); | |
this._frameSize = new Rectangle(); | |
this._frameSize.x = rect[0] / 20; | |
this._frameSize.y = rect[2] / 20; | |
this._frameSize.width = rect[1] / 20; | |
this._frameSize.height = rect[3] / 20; | |
this._frameRate = super.readUnsignedShort() >> 8; | |
this._frameNumber = super.readUnsignedShort(); | |
__firstTagPosition = super.position; | |
super.position = pos; | |
} | |
private static function isSWF(bytes:ByteArray):Boolean{ | |
var sign:String = bytes.readUTFBytes(3); | |
if(sign == SWFRipper.SWF_SIGNATURE_COMPRESSED || sign == SWFRipper.SWF_SIGNATURE_UNCOMPRESSED) return true; | |
else return false; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.gotoandstop.bytes{ | |
public class SWFTag{ | |
public static const END:uint = 0; | |
public static const SHOW_FRAME:uint = 1; | |
public static const DEFINE_SHAPE:uint = 2; | |
public static const PLACE_OBJECT:uint = 4; | |
public static const REMOVE_OBJECT:uint = 5; | |
public static const DEFINE_BITS:uint = 6; | |
public static const DEFINE_BUTTON:uint = 7; | |
public static const JPEG_TABLES:uint = 8; | |
public static const SET_BACKGROUND_COLOR:uint = 9; | |
public static const DEFINE_FONT:uint = 10; | |
public static const DEFINE_TEXT:uint = 11; | |
public static const DO_ACTION:uint = 12; | |
public static const DEFINE_FONT_INFO:uint = 13; | |
public static const DEFINE_SOUND:uint = 14; | |
public static const START_SOUND:uint = 15; | |
public static const DEFINE_BUTTON_SOUND:uint = 17; | |
public static const SOUND_STREAM_HEAD:uint = 18; | |
public static const SOUND_STREAM_BLOCK:uint = 19; | |
public static const DEFINE_BITS_LOSSLESS:uint = 20; | |
public static const DEFINE_BITS_JPEG2:uint = 21; | |
public static const DEFINE_SHAPE2:uint = 22; | |
public static const DEFINE_BUTTON_CXFORM:uint = 23; | |
public static const PROTECT:uint = 24; | |
public static const PLACE_OBJECT2:uint = 26; | |
public static const REMOVE_OBJECT2:uint = 28; | |
public static const DEFINE_SHAPE3:uint = 32; | |
public static const DEFINE_TEXT2:uint = 33; | |
public static const DEFINE_BUTTON2:uint = 34; | |
public static const DEFINE_BITS_JPEG3:uint = 35; | |
public static const DEFINE_BITS_LOSSLESS2:uint = 36; | |
public static const DEFINE_EDIT_TEXT:uint = 37; | |
public static const DEFINE_SPRITE:uint = 39; | |
public static const FRAME_LABEL:uint = 43; | |
public static const SOUND_STREAM_HEAD2:uint = 45; | |
public static const DEFINE_MORPH_SHAPE:uint = 46; | |
public static const DEFINE_FONT2:uint = 48; | |
public static const EXPORT_ASSETS:uint = 56; | |
public static const IMPORT_ASSETS:uint = 57; | |
public static const ENABLE_DEBUGGER:uint = 58; | |
public static const DO_INIT_ACTION:uint = 59; | |
public static const DEFINE_VIDEO_STREAM:uint = 60; | |
public static const VIDE0_FRAME:uint = 61; | |
public static const DEFINE_FONT_INFO2:uint = 62; | |
public static const ENABLE_DEBUGGER2:uint = 64; | |
public static const SCRIPTS_LIMITS:uint = 65; | |
public static const SET_TAB_INDEX:uint = 66; | |
public static const FILE_ATTRIBUTES:uint = 69; | |
public static const PLACE_OBJECT3:uint = 70; | |
public static const IMPORT_ASSETS2:uint = 71; | |
public static const DEFINE_FONT_ALIGN_ZONES:uint = 73; | |
public static const CSM_TEXT_SETTINGS:uint = 74; | |
public static const DEFINE_FONT3:uint = 75; | |
public static const SYMBOL_CLASS:uint = 76; | |
public static const METADATA:uint = 77; | |
public static const DEFINE_SCALING_GRID:uint = 78; | |
public static const DO_ABC:uint = 82; | |
public static const DEFINE_SHAPE4:uint = 83; | |
public static const DEFINE_MORPH_SHAPE2:uint = 84; | |
public static const DEFINE_SCENE_AND_FRAME_LABEL_DATA:uint = 86; | |
public static const DEFINE_BINARY_DATA:uint = 87; | |
public static const DEFINE_FONT_NAME:uint = 88; | |
public static const START_SOUND2:uint = 89; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment