SWF Analyzer
やっつけSWF解析
♥0 |
Line 330 |
Modified 2010-07-08 01:50:12 |
MIT License
archived:2017-03-09 13:37:57
ActionScript3 source code
/**
* Copyright hycro ( http://wonderfl.net/user/hycro )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/tcWK
*/
// やっつけSWF解析
package {
import com.bit101.components.PushButton;
import com.bit101.components.TextArea;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.ByteArray;
[SWF(frameRate="30", width="465", height="465", backgroundColor="0xFFFFFF")]
public class SWFAnalyzerTest extends Sprite {
private var _ta:TextArea;
private var _fileRef:FileReference;
public function SWFAnalyzerTest() {
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(evt:Event=null):void {
new PushButton(this, 5, 5, "load", selectFile);
_ta = new TextArea(this, 0, 30);
_ta.width = stage.stageWidth;
_ta.height = stage.stageHeight - 30;
}
private function selectFile(evt:Event):void {
_fileRef = new FileReference();
_fileRef.addEventListener(Event.SELECT, onSelect);
_fileRef.addEventListener(Event.COMPLETE, onComplete);
_fileRef.browse([new FileFilter("SWF", "*.swf")]);
}
private function onSelect(evt:Event):void {
_fileRef.load();
}
private function onComplete(evt:Event):void {
var analyzer:SWFAnalyzer = new SWFAnalyzer(_fileRef.data);
try {
analyzer.analyze();
} catch (err:Error) {
_ta.text = err.toString();
return;
}
_ta.text = "[header]\n"
_ta.text += "compressed: " + analyzer.compressed + "\n";
_ta.text += "version: " + analyzer.version + "\n";
_ta.text += "filesize: " + analyzer.filesize + "\n";
_ta.text += "stageRect: " + analyzer.stageRect + "\n";
_ta.text += "frameRate: " + analyzer.frameRate + "\n";
_ta.text += "numFrames: " + analyzer.numFrames + "\n";
_ta.text += "\n[tags]\n";
for each (var t:Tag in analyzer.tags) {
_ta.text += t.toString() + "\n";
}
}
}
}
import flash.geom.Rectangle;
import flash.utils.ByteArray;
import flash.utils.Endian;
class SWFAnalyzer {
// header
public var compressed:Boolean;
public var version:uint;
public var filesize:uint;
// header_movie
public var stageRect:Rectangle;
public var frameRate:Number;
public var numFrames:Number;
// タグ
public var tags:Vector.<Tag>;
// input
private var _data:ByteArray;
public function SWFAnalyzer(data:ByteArray) {
_data = data;
_data.endian = Endian.LITTLE_ENDIAN;
tags = new Vector.<Tag>();
}
public function analyze():void {
readHeader();
if (compressed) {
_data = uncompless(_data, 8);
_data.position = 8;
}
readHeader2();
readTags();
}
// header
private function readHeader():void {
// 圧縮の有無(3byte)
var cmp:String = _data.readUTFBytes(3);
if (cmp == "FWS") {
this.compressed = false;
} else if (cmp == "CWS") {
this.compressed = true;
} else {
throw new Error();
}
// SWFのバージョン(1byte)
this.version = _data.readUnsignedByte();
// ファイルサイズ(4byte)
this.filesize = _data.readUnsignedInt();
}
// header2
private function readHeader2():void {
var bitReader:BitReader = new BitReader(_data);
// stageRect
var len:uint = bitReader.readUnsignedInt(5);
var x:uint = bitReader.readUnsignedInt(len);
var w:uint = bitReader.readUnsignedInt(len);
var y:uint = bitReader.readUnsignedInt(len);
var h:uint = bitReader.readUnsignedInt(len);
stageRect = new Rectangle(x/20, y/20, w/20, h/20);
// frameRate
var f:uint = _data.readUnsignedByte();
var d:uint = _data.readUnsignedByte();
frameRate = d + (f/256);
// numFrames
numFrames = _data.readUnsignedShort();
}
// Tag
private function readTags():void {
while (_data.bytesAvailable) {
var tag:Tag = new Tag();
var typeAndLength:uint = _data.readUnsignedShort();
tag.type = (typeAndLength & 0xFFC0) >> 6;
tag.length = (typeAndLength & 0x3F);
if (tag.length == 0x3F) {
tag.length = _data.readUnsignedInt();
}
if (tag.length) {
_data.readBytes(tag.contents, 0, tag.length);
}
tags.push(tag);
}
}
// zlib解凍
private function uncompless(ba:ByteArray, offset:uint):ByteArray {
var rv:ByteArray = new ByteArray();
var tmp:ByteArray = new ByteArray();
rv.endian = ba.endian;
tmp.endian = ba.endian;
// offset 以降をコピーしてzlib解凍
ba.position = offset;
ba.readBytes(tmp);
tmp.uncompress();
if (offset == 0) {
return tmp;
}
// offset 以前と zlib解凍した内容をつなげる
ba.position = 0;
ba.readBytes(rv, 0, offset);
tmp.readBytes(rv, offset);
return rv;
}
private function dump(length:uint=100):void {
var pos:uint = _data.position;
var count:uint = 0;
while (_data.bytesAvailable) {
var str:String = "";
for (var i:uint = 0; i < 16 && _data.bytesAvailable; i++) {
var b:uint = _data.readUnsignedByte();
for (var j:uint = 0; j < (8-b.toString(2).length); j++) {
str += "0";
}
str += b.toString(2);
str += " ";
if (++count >= length) {
trace(str);
_data.position = pos;
return;
}
}
trace(str);
}
_data.position = pos;
}
}
class Tag {
public var type:uint;
public var length:uint;
public var contents:ByteArray;
public function Tag() {
contents = new ByteArray();
}
public function toString():String {
switch (type) {
case 0:
return "End";
case 1:
return "ShowFrame";
case 2:
return "DefineShape";
case 4:
return "PlaceObject";
case 5:
return "RemoveObject";
case 6:
return "DefineBits";
case 7:
return "DefineButton";
case 8:
return "JPEGTables";
case 9:
return "SetBackgroundColor";
case 10:
return "DefineFont"
case 11:
return "DefineText"
case 12:
return "DoAction"
case 13:
return "DefineFontInfo"
case 14:
return "DefineSound"
case 15:
return "StartSound"
case 17:
return "DefineButtonSound"
case 18:
return "SoundStreamHead"
case 19:
return "SoundStreamBlock"
case 20:
return "DefineBitsLossless"
case 21:
return "DefineBitsJPEG2"
case 22:
return "DefineShape2"
case 23:
return "DefineButtonCxform"
case 24:
return "Protect"
case 26:
return "PlaceObject2"
case 28:
return "RemoveObject2"
case 32:
return "DefineShape3"
case 33:
return "DefineText2"
case 34:
return "DefineButton2"
case 35:
return "DefineBitsJPEG3"
case 36:
return "DefineBitsLossless2"
case 37:
return "DefineEditText"
case 39:
return "DefineSprite"
case 43:
return "FrameLabel"
case 45:
return "SoundStreamHead2"
case 46:
return "DefineMorphShape"
case 48:
return "DefineFont2"
case 56:
return "ExportAssets"
case 57:
return "ImportAssets"
case 58:
return "EnableDebugger"
case 59:
return "DoInitAction"
case 60:
return "DefineVideoStream"
case 61:
return "VideoFrame"
case 62:
return "DefineFontInfo2"
case 64:
return "EnableDebugger2"
case 65:
return "ScriptLimits"
case 66:
return "SetTabIndex"
case 69:
return "FileAttributes"
case 70:
return "PlaceObject3"
case 71:
return "ImportAssets2"
case 73:
return "DefineFontAlignZones"
case 74:
return "CSMTextSettings"
case 75:
return "DefineFont3"
case 76:
return "SymbolClass"
case 77:
return "Metadata"
case 78:
return "DefineScalingGrid"
case 82:
return "DoABC"
case 83:
return "DefineShape4"
case 84:
return "DefineMorphShape2"
case 86:
return "DefineSceneAndFrameLabelData"
case 87:
return "DefineBinaryData"
case 88:
return "DefineFontName"
case 89:
return "StartSound2"
case 90:
return "DefineBitsJPEG4"
case 91:
return "DefineFont4"
}
return "undefined? (" + type + ")";
}
}
// ByteArrayをbit単位で読むためのクラス
class BitReader {
private static const ONE:Boolean = true;
private static const ZERO:Boolean = false;
private var _input:ByteArray;
private var _currByte:uint;
private var _mask:uint;
public function BitReader(input:ByteArray) {
_input = input;
_currByte = NaN;
_mask = 0;
}
// bit単位でデータを読みます
public function readUnsignedInt(length:uint=0):uint {
var rv:uint = 0;
length = Math.min(length, 32);
for (var i:int = length-1; i >= 0; i--) {
if (next() == ONE) {
rv += Math.pow(2, i);
}
}
return rv;
}
// 次のビットを取得します
private function next():Boolean {
if ((_mask >>= 1) == 0) {
_currByte = _input.readUnsignedByte();
_mask = 0x80;
}
return (_currByte & _mask) ? ONE : ZERO;
}
// 残りのビットを切り捨てます
public function floor():void {
_mask = 0;
}
}