Flash解析 (一部)
ヘッダー情報と背景色(SetBackgroundColor)とストリーミングサウンドの開始位置(SoundStreamBlock)を表示します。
/**
* Copyright fujista ( http://wonderfl.net/user/fujista )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/5yYo
*/
package {
// Use as3swf Library
import com.codeazur.as3swf.SWFData;
import com.codeazur.as3swf.data.SWFRawTag;
import com.codeazur.as3swf.data.SWFRecordHeader;
import com.codeazur.as3swf.data.SWFRectangle;
import com.codeazur.as3swf.factories.SWFTagFactory;
import com.codeazur.as3swf.tags.IDefinitionTag;
import com.codeazur.as3swf.tags.IDisplayListTag;
import com.codeazur.as3swf.tags.ITag;
import com.codeazur.as3swf.tags.TagDefineSprite;
import com.codeazur.as3swf.tags.TagEnd;
import com.codeazur.as3swf.tags.TagPlaceObject;
import com.codeazur.as3swf.tags.TagPlaceObject2;
import com.codeazur.as3swf.tags.TagPlaceObject3;
import com.codeazur.as3swf.tags.TagSetBackgroundColor;
import com.codeazur.as3swf.tags.TagShowFrame;
import com.codeazur.as3swf.tags.TagSoundStreamHead;
import com.codeazur.as3swf.tags.TagSoundStreamHead2;
import com.codeazur.as3swf.tags.TagSoundStreamBlock;
// Use MinimalComps Library
import com.bit101.components.Label;
import com.bit101.components.ProgressBar;
import com.bit101.components.PushButton;
import com.bit101.components.Style;
import com.bit101.components.Text;
import com.bit101.components.TextArea;
// Use FontLoader Class
import net.wonderfl.utils.FontLoader;
// Other
import flash.display.Sprite;
import flash.errors.IOError;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.net.FileReference;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
public class FlashParser extends Sprite {
// swf headers
public var version:uint;
public var fileLength:uint;
public var frameSize:SWFRectangle;
public var frameRate:Number;
public var frameCount:uint;
public var compressed:Boolean;
// minimalcomps
protected var browse:PushButton;
protected var filename:Text;
protected var log:TextArea;
protected var progress1:ProgressBar;
protected var progress2:ProgressBar;
protected var status:Label;
protected var step:Label;
protected var _file:FileReference;
protected var _maxCharacterId:uint = 0;
protected var _dictionary:Dictionary;
protected var _parentMessage:String;
public function FlashParser() {
_dictionary = new Dictionary();
initFileReference();
initFontLoad();
}
public function start(ba:ByteArray):void {
var source:SWFData = new SWFData();
ba.readBytes(source);
ba.clear();
try {
parse(source);
} catch (e:Error) {
// Nothing do
}
reset();
browse.enabled = true;
status.text = "done.";
}
protected function reset():void {
_dictionary = new Dictionary();
}
protected function parse(data:SWFData):void {
try {
updateStatus("Checking...", false);
parseHeader(data);
setProgressMaximum(fileLength, "Parsing...\n");
parseTimeline(data, frameCount);
setProgressValue(progress2.maximum);
updateStatus("\ndone.", false);
} catch (e:Error) {
updateStatus(e.toString());
}
}
protected function parseHeader(data:SWFData):void {
var header:Dictionary = new Dictionary();
var signature:Array = [data.readUI8(), data.readUI8(), data.readUI8()];
if ((signature[0] != 0x43 && signature[0] != 0x46 && signature[0] != 0x5a) || signature[1] != 0x57 || signature[2] != 0x53) {
throw( new Error("File is not swf.") );
}
this.version = data.readUI8();
this.fileLength = data.readUI32();
this.compressed = (signature[0] == 0x43 || signature[0] == 0x5a) ? true : false;
if (this.compressed) {
updateStatus("Uncompressing...", false);
data.swfUncompress();
if (this.fileLength != data.length) {
throw( new Error("File uncompress error") );
}
}
this.frameSize = data.readRECT();
this.frameRate = data.readFIXED8();
this.frameCount = data.readUI16();
updateStatus("", false);
updateStatus("Version: " + this.version, false);
updateStatus("FileLength: " + this.fileLength.toString().replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g, '$1,') + " bytes", false);
updateStatus("FrameSize: " + (this.frameSize.xmax / 20) + " x " + (this.frameSize.ymax / 20) + " px", false);
updateStatus("FrameRate: " + this.frameRate.toFixed(3) + " fps", false);
updateStatus("FrameCount: " + this.frameCount + " frames", false);
updateStatus("Compressed: " + (this.compressed ? "Yes" + (signature[0] == 0x43 ? " (zlib)" : " (lzma)") : "No"), false);
updateStatus("", false);
}
protected function parseTimeline(data:SWFData, frameCount:uint, parentCharacterId:uint = 0):void {
var pos:uint;
var raw:SWFRawTag;
var tag:ITag;
var currentFrame:uint = 1;
var streamSound:Boolean = false;
do {
pos = parentCharacterId ? data.position + _dictionary[parentCharacterId] : data.position;
raw = data.readRawTag();
data.position += raw.header.contentLength;
tag = parseTag(raw.bytes, pos);
switch (tag.type) {
case TagShowFrame.TYPE:
currentFrame++;
break;
case TagSetBackgroundColor.TYPE:
updateStatus("SetBackgroundColor: #" + (tag as TagSetBackgroundColor).color.toString(16).substr(-6).toUpperCase(), false);
break;
case TagPlaceObject.TYPE:
case TagPlaceObject2.TYPE:
case TagPlaceObject3.TYPE:
// @TODO
break;
case TagSoundStreamHead.TYPE:
case TagSoundStreamHead2.TYPE:
streamSound = true;
updateStatus("SoundStreamHead: " + currentFrame, false);
break;
case TagSoundStreamBlock.TYPE:
if (streamSound) {
streamSound = false;
updateStatus("SoundStreamBlock: " + currentFrame, false);
}
break;
}
setProgressValue(pos + raw.header.contentLength);
} while (tag.type != TagEnd.TYPE);
}
protected function parseTag(data:SWFData, pos:uint):ITag {
var header:SWFRecordHeader = data.readTagHeader();
var tag:ITag = SWFTagFactory.create(header.type, null);
if (tag is IDefinitionTag) {
var characterId:uint = data.readUI16();
_dictionary[characterId] = pos;
_maxCharacterId = Math.max(_maxCharacterId, characterId);
if (tag is TagDefineSprite) {
var frameCount:uint = data.readUI16();
parseTimeline(data, frameCount, characterId);
}
} else if (tag is IDisplayListTag) {
tag.parse(data, header.contentLength, this.version);
} else if (tag is TagSetBackgroundColor) {
tag.parse(data, header.contentLength, this.version);
}
return tag;
}
protected function setProgressMaximum(value:uint, message:String = null):void {
progress2.maximum = value;
progress1.value++;
if (message) {
updateStatus(message, false);
step.text = Math.floor( value / progress2.maximum * 100 ) + "%";
}
}
protected function setProgressValue(value:uint, append:Boolean = false):void {
value += append ? progress2.value : 0;
progress2.value = value;
step.text = Math.floor( value / progress2.maximum * 100 ) + "%";
}
protected function updateStatus(text:String, append:Boolean = true, override:Boolean = true):void {
_parentMessage = (!append && override) ? text : _parentMessage;
status.text = append ? _parentMessage + " " + text : text;
log.text += status.text + "\n";
}
protected function initFileReference():void {
_file = new FileReference();
_file.addEventListener(Event.SELECT, function(e:Event):void{
log.text = "";
browse.enabled = false;
filename.text = e.target.name;
setProgressMaximum(e.target.size, "Loading...");
e.target.load();
});
_file.addEventListener(Event.COMPLETE, function(e:Event):void{
setProgressValue(e.target.data.length);
start(e.target.data);
});
_file.addEventListener(ProgressEvent.PROGRESS, function(e:ProgressEvent):void{
setProgressValue(e.bytesLoaded);
});
_file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOError):void{
updateStatus(e.toString());
});
}
protected function initFontLoad():void {
var font:FontLoader = new FontLoader();
font.addEventListener(Event.COMPLETE, function(e:Event):void {
initMinimalComps();
progress1.maximum = 2;
updateStatus("Please push \"Browse\" button and select target file.", false);
});
font.load("IPAGP");
}
protected function initMinimalComps():void {
var format:TextFormat;
Style.fontSize = 12;
Style.fontName = "IPAGP";
new Label(this, 5, 5, "File:");
browse = new PushButton(this, 360, 25, "Browse", function(e:MouseEvent):void{
_file.browse();
});
filename = new Text(this, 5, 25, null);
filename.width = 350;
filename.height = 20;
filename.editable = false;
new Label(this, 5, 55, "Progress:");
progress1 = new ProgressBar(this, 5, 75);
progress1.width = 455;
progress2 = new ProgressBar(this, 5, 86);
progress2.width = 455;
new Label(this, 5, 100, ">>");
status = new Label(this, 25, 100);
status.width = 400;
status.autoSize = false;
step = new Label(this, 410, 100);
step.width = 50;
step.autoSize = false;
format = step.textField.defaultTextFormat;
format.align = TextFormatAlign.RIGHT;
step.textField.defaultTextFormat = format;
new Label(this, 5, 120, "Information:");
log = new TextArea(this, 5, 140, null);
log.width = 455;
log.height = 320;
log.editable = false;
}
}
}