In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Flash解析 (一部)

ヘッダー情報と背景色(SetBackgroundColor)とストリーミングサウンドの開始位置(SoundStreamBlock)を表示します。
Get Adobe Flash player
by fujista 10 Sep 2012
/**
 * 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;
        }
    }
}