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

WonderFLCompiler

Compiles a list of .as files into a single .as file, given a source code directory! By loading a SWF, you can get a list of class names that you may need to load. Once you've specified the classes to attempt to load by loading some .swf, specified the document class and source code directory, hit F2 to run. Note: Run this offline would be necessary if accessing https or url locations that don't have cross-domain access. Also, remember that the generated Output isn't full-proof and you'd probably need to touch up the Output code accordingly to get it to run, depending on the project's complexity.
Get Adobe Flash player
by Glidias 17 Jul 2013
/**
 * Copyright Glidias ( http://wonderfl.net/user/Glidias )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/xcUB
 */

package 
{
    import com.bit101.components.CheckBox;
    import com.bit101.components.HBox;
    import com.bit101.components.InputText;
    import com.bit101.components.Label;
    import com.bit101.components.PushButton;
    import com.bit101.components.TextArea;
    import com.bit101.components.VBox;
    import com.bit101.components.Window;
    import flash.display.DisplayObject;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.IEventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.KeyboardEvent;
    import flash.net.FileFilter;
    import flash.net.FileReference;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.ui.Keyboard;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;
    import flash.utils.setTimeout;
    /**
     * Compiles everything from a .swf into a single .as file, given a source code directory!  WIP...
     * @author Glenn Ko
     */
    public class WonderFLCompiler extends Sprite
    {
        private var btnOutput:PushButton;
        private var btnExcludes:PushButton;
        private var btnIncludes:PushButton;
        private var btnLoad:PushButton;
        
        private var pages:Vector.<TextArea> = new Vector.<TextArea>(4,true);
        private var pageHolder:Window;
        private var fileRef:FileReference;
        private var _includedClasses:Array;
        private var _loadQueue:Array;

        
        // eg. (to exclude/include a specific class in a package) com.bit101::SomeSpecialClass    
        // or  (to exclude/include an entire package) com.bit101.**  
        // or  (to exclude/include a specific package folder only) com.bit101.somepackage.*
        private static const EXCLUDES:Array = [   //
            "flash.**", "private.**", "mx.**", "com.bit101.**", "alternativa.**", "jp.nium.**", "jp.progression.**"
        ]
        
        private static const FIELD_DOC_CLASS:String = ""; //"WonderFLIsles";
        private static const FIELD_SRC_PATH:String = ""; //"https://raw.github.com/Glidias/Asharena/master/src/";
        private var fieldSrcPath:InputText;
        private var fieldDoc:InputText;
        
        
        
        // STUFF TO RESET ON Every loading of new swf
        private var stripClasses:Array = [];
        private var stripPackages:Array = [];
        private var _failedClasses:Array = [];
        private var outputData:String = "";
        private var loaderDict:Dictionary = new Dictionary();
        private var _docClassPath:String;
        private var dataList:Array = [];// = new Dictionary();
        
        
        
        private var excludeLoadPackages:Array = EXCLUDES;
        private var _excludePackageless:CheckBox;
        
        static public const CARRIAGE_RETURN:String = String.fromCharCode(13);
        static public const TXT_ATTEMPT_TO_LOAD:String = "Classes to attempt to load:";
        
        
        public function WonderFLCompiler() 
        {
            var vBox:VBox = new VBox(this);
            var hBox:HBox = new HBox(vBox); 
            btnLoad = new PushButton(hBox, 0, 0, "Load SWF file...", onBtnClick);
            btnIncludes = new PushButton(hBox, 0, 0, "Load Includes >", onBtnClick);
            btnExcludes = new PushButton(hBox, 0, 0, "Load Excludes >", onBtnClick);
            btnOutput = new PushButton(hBox, 0, 0, "View Output >", onBtnClick);
            
            hBox = new HBox(vBox);
            new Label(hBox, 0, 0, "Document class");
            fieldDoc = new InputText(hBox, 0, 0, FIELD_DOC_CLASS);
            new Label(hBox, 0, 0, "Source directory");
            fieldSrcPath = new InputText(hBox, 0, 0, FIELD_SRC_PATH);
            
            _excludePackageless =  new CheckBox(hBox, 0, 0, "X non-'./::'");
            
            fileRef = new FileReference();
            fileRef.addEventListener(Event.SELECT, onFileSelected);
            fileRef.addEventListener(Event.COMPLETE, onFileLoaded);
            
            var len:int = pages.length;
            for (var i:int = 0; i < len; i++) {
                pages[i] = getInputTextArea();
            }
            
            
            
            pages[2].text = EXCLUDES.join(CARRIAGE_RETURN);
            
            pageHolder = new Window(vBox, 0, 0, "");
            pageHolder.width = 400;
            pageHolder.height = 370;
            showPage(0);
            
            setTimeout( vBox.draw, 100);
            
        }
        
        private function popLoad():void {
            if (_loadQueue.length == 0) {
                
                doLoadCompletion();
                return;
            }
            var classPath:String = _loadQueue.pop();
            classPath = classPath.replace("::", ".");
            
            
            var urlLoader:URLLoader = new URLLoader();
            loaderDict[urlLoader] = classPath;
            urlLoader.addEventListener(Event.COMPLETE, onURLLoadComplete);
            urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onURLLoadError);
            //throw new Error( fieldSrcPath.text + classPath.split(".").join("/") );
            urlLoader.load( new URLRequest(fieldSrcPath.text+classPath.split(".").join("/")+".as" ) );
        }
        
        private function doLoadCompletion():void 
        {
            startDataProcessing();
        }
        
        private function startDataProcessing():void 
        {
        //    pageHolder.title = TXT_ATTEMPT_TO_LOAD + " ( Start processing: " + ( _failedClasses.length > 0 ? "with failed loads..." : "No failed loads!") + " )";
            var len:int = dataList.length;
            outputData += processData( dataList[0], false );
            for (var i:int = 1; i < len; i++) {
                outputData += processData( dataList[i], true );
            }
            finishDataProcessing();
        }
        
        private function finishDataProcessing():void 
        {
            pageHolder.title = TXT_ATTEMPT_TO_LOAD + " ( Done! " + ( _failedClasses.length > 0 ? "with failed loads..." : "No failed loads!") + " )";
            
            if (_failedClasses.length > 0) {
                pages[0].text = "Failed:\n_________\n" + _failedClasses.join(CARRIAGE_RETURN) + "\n\n______________________________________\n\n" + pages[0].text;
            }
            //outputData  = 
            pages[3].text = outputData;
        }
        
        private function findBraceIndexFromAbove(arr:Array, pattern:RegExp):int {
            var len:int = arr.length;
            for (var i:int = 0; i < len; i++) {
                if (arr[i].match(pattern)) return i;
            }
            return -1;
        }
        
        private function findBraceIndexFromBelow(arr:Array, pattern:RegExp):int {
            
            var i:int = arr.length;
            while(--i > -1) {
                if (arr[i].match(pattern)) return i;
            }
            return -1;
        }
        
        private function onURLLoadError(e:Event):void {
            (e.currentTarget as IEventDispatcher).removeEventListener(e.type, onURLLoadError);
            (e.currentTarget as IEventDispatcher).removeEventListener(Event.COMPLETE, onURLLoadComplete);
            
            var classPath:String = loaderDict[e.currentTarget];
            delete loaderDict[e.currentTarget];
            _failedClasses.push(classPath);
            
        
            
            
            popLoad();
        }

        
        private function onURLLoadComplete(e:Event):void 
        {
            (e.currentTarget as IEventDispatcher).removeEventListener(e.type, onURLLoadComplete);
            (e.currentTarget as IEventDispatcher).removeEventListener(IOErrorEvent.IO_ERROR, onURLLoadError);
            
            var data:String =  (e.currentTarget.data);
            var classPath:String = loaderDict[e.currentTarget];
            delete loaderDict[e.currentTarget];
            
            
            stripClasses.push(classPath);
            
            dataList.push(data);
            
            
            popLoad();
            
        }
        
        private function countOpeningBraces(str:String):int {

            var len:int = str.length;
            var count:int = 0;
            for (var i:int = 0; i < len; i++ ) {
                var char:String = str.charAt(i);
                if (char === "}") return count;
                count += char != "{" ? 0 : 1;
            }
            return count;
        }
        
        private function countOpeningBraces2(str:String):int {
            var count:int = 0;
            var flagCount:int = 0;
            var closeCount:int = 0;
            var len:int = str.length;
    
            for (var i:int = 0; i < len; i++ ) {
                var char:String = str.charAt(i);
            
                if (char === "{" ) {
                    flagCount++;
                    count++;
                }
                else if (char === "}" ) {
                    closeCount++;
                    count--;
                }
                if (flagCount != 0 && count == 0) {
                    if (closeCount != flagCount) throw new Error("Mismatch count of opening and close braces");
                    return flagCount;
                }
            }
            if (closeCount != flagCount) throw new Error("Mismatch count of opening and close braces");
            return flagCount;
        }
        
        private function processData(data:String, stripPackage:Boolean):String {
            var closingBraces:Array;
            var index:int;
            var openingBraces:Array;
            var str:String;
            
            // strip comments
            data = data.replace(/(\/\*([\s\S]*?)\*\/)|(\/\/(.*)$)/gm, "");
            
            //    data += " // EOF";
            
        //    var fullPathAppend:String = "[^;\\n]+[;\\n]";
            var regex:RegExp;
            for each(str in stripPackages) {   
                regex =  new RegExp("\\bimport\\s+"+str + ".", "g");
                data = data.replace(regex, "//import "+str+".");
            }
            // strip class imports  (these are classes found loaded in the directory)
            for each(str in stripClasses) {
                regex =  new RegExp("\\bimport\\s" + str + "[^$][;\\n]", "g");
                
                data = data.replace(regex, "//import " + str + ";\n");
            }
            

            // Handle package brace
            if (stripPackage) {     // is NOT document class
                openingBraces = data.split("{");
                var packageRegex:RegExp = /\bpackage\b[^{]+{/;
                index = findBraceIndexFromAbove(openingBraces, packageRegex);
                
                var packageBraceOffset:int = openingBraces.length - countOpeningBraces2(data);
            
                closingBraces = data.split("}");    

                closingBraces.splice(closingBraces.length - 2 - index - packageBraceOffset, 1, "\n//"); // [closingBraces.length - 1 - index];
                
                
                data = closingBraces.join("}");
                data = data.replace(packageRegex, "//package {");
                
                // remove public identifier for class
                data = data.replace(/\bpublic\b[^]+\bclass\b/, "/*public*/ class");
            }
            else {           // is document class
                data = data.replace(/\bpackage\b[^{]+{/, "package {");
            }
            
            // remove public identifier for interface
            data = data.replace(/\bpublic\s+interface\b/, "/*public*/ interface");
            
            
            
            // Further post processing ( like strip comments again)
            //data = data.replace(/(\/\*([\s\S]*?)\*\/)|(\/\/(.*)$)/gm, "");
            
            
            return data;
        }
        
        private function updateBoxContents():void 
        {
            pages[0].textField.scrollV = pages[0].textField.maxScrollV;
            pages[0].textField.dispatchEvent( new Event(Event.SCROLL) );
            pages[0].draw();
        }
        
        private function onFileLoaded(e:Event):void 
        {

            stripClasses = [];
            stripPackages = [];
            _failedClasses = [];
            outputData = "";
    
            loaderDict = new Dictionary();
            dataList = [];
            
            _includedClasses = getDefinitionNames(fileRef.data);
            cleanupIncludedClasses();
            pages[0].text = _includedClasses.join(CARRIAGE_RETURN);
            
            
            var docClass:String = fieldDoc.text;
            var srcPath:String = fieldSrcPath.text;
            
            /*
            if (!srcPath || !docClass) {
                pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (Please specify Document Class and Source Path!)";
                return;
            }
            */
            
            
            
            
            //if (_loadQueue.length) {
             pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (Press F2 to confirm start load!)";
             stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            //}
            //else pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (No classes found to load!)";
            
            
        }
        
        private function onKeyDown(e:KeyboardEvent):void 
        {
            
            if (e.keyCode === Keyboard.F2) {
                startLoading();
            }
        }
        
        private function startLoading():void 
        {
            var srcPath:String = fieldSrcPath.text;
                    var docClass:String = fieldDoc.text;
                    
            if (!srcPath || !docClass) {
                pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (Please specify Document Class and Source Path!)";
                return;
            }
            
            
            _docClassPath = docClass.replace("::", ".");
            
             _includedClasses =  pages[0].text.split(CARRIAGE_RETURN);
             
             var docIndex:int;
             if ( (docIndex=_includedClasses.indexOf(docClass)) < 0) {
                 pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (Could not find Document Class in load list!)";
                 return;
             }
            
             
             
            _loadQueue = _includedClasses.slice();
            _loadQueue.splice(docIndex, 1);
            _loadQueue.push(docClass);
            
            pageHolder.title = TXT_ATTEMPT_TO_LOAD + " (Loading...please wait!)";
            stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
                popLoad();
                
        }
        
        private function cleanupIncludedClasses():void 
        {
            var len:int = _includedClasses.length;
            var filters:Array = getExcludeFilters();
            var f:Array;
            var arr:Array = [];
            var myIncludedClasses:Array = pages[1].text.split(CARRIAGE_RETURN);
            for each(var includedStr:String in myIncludedClasses) {
                var tail:String = includedStr.split(".").pop();
                if (tail === "*" ) stripClasses.push(includedStr)
                else if (tail === "**") stripPackages.push(includedStr);
            }
            var fi:int;
            var fValue:String;
            var doContinue:Boolean = false;
            var fSplit:Array;
            var stripPackageless:Boolean  = _excludePackageless.selected;
            var docClass:String = fieldDoc.text;
            
            
            for (var i:int = 0; i < len; i++) {
                var candidate:String = _includedClasses[i];
                // enforce any specific included classes
                fi = myIncludedClasses.length;
                
                doContinue = false;
                while (--fi > -1) {
                    if (myIncludedClasses[fi] === candidate) {
                      if (!stripPackageless || candidate.indexOf("::") >=0 || candidate.indexOf(".") >= 0  || candidate === docClass )    arr.push(candidate);
                        doContinue = true;
                        break;
                    }
                }
                if (doContinue) continue;
                
                // filter away by excluded classes
                f = filters[0];
                fi = f.length;
                while (--fi > -1) {
                    if (f[fi] === candidate) {
                        doContinue = true;
                        break;
                    }
                }
                if (doContinue) continue;
                
                // filter away by excluded package folder
                f = filters[1];
                fi = f.length;
                while (--fi > -1) {
                    fValue = f[fi];
                    if (fValue === candidate.split(":").shift() )    {
                        doContinue = true;
                        break;
                    }
                }
                if (doContinue) continue;
                
                // filter away by excluded entire package
                f = filters[2];
                fi = f.length;
                
                while (--fi > -1) {
                    fValue = f[fi];
                    
                    if ( candidate.substr(0, fValue.length) === fValue) {
                        doContinue = true;
                        break;
                    }
                }
                if (doContinue) continue;
                
               if (!stripPackageless || candidate.indexOf("::") >=0 || candidate.indexOf(".") >= 0  || candidate === docClass )   arr.push(candidate);
            }
            
            _includedClasses = arr;
        }
        
        private function getExcludeFilters():Array 
        {
            var excludes:Array = pages[2].text.split(CARRIAGE_RETURN);
            
            
            var excludedClasses:Array  = [];
            var excludedPackageFolders:Array = [];
            var excludedPackages:Array = [];
            
            var len:int = excludes.length;
            
            for (var i:int = 0; i < len; i++) {
                var str:String = excludes[i];
                if (str.charAt(str.length - 1) === "*") {
                    if (str.charAt(str.length -2) === "*") {
            
                        excludedPackages.push(str.slice(0,str.length-3));  // slice away dot behind * as well
                    }
                    else {
                        excludedPackageFolders.push(str.slice(0,str.length-2));
                    }
                }
                else {
                    if (str.indexOf(":") < 0) {
                        var packageSplit:Array = str.split(".");
                        var className:String = packageSplit.pop();
                        str = packageSplit + ":" + className;
                    }
                    excludedClasses.push(str);
                }
            }

            return [excludedClasses, excludedPackageFolders, excludedPackages];
        }

        
        private function onFileSelected(e:Event):void 
        {
            stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            fileRef.load();
        }
        
        private function getInputTextArea():TextArea 
        {
            var result:TextArea = new TextArea(null, 0, 0, "");
            result.width  = 400;
            result.height = 330;
            return result;
        }
        
        public function showPage(index:int):void {
            
        if (pageHolder.content.numChildren) pageHolder.content.removeChildAt(0);
        var child:DisplayObject =     pageHolder.addChild(pages[index]);
        child.y = 20;
        //child.y = 0;    
            if (index === 0) {
                pageHolder.title = TXT_ATTEMPT_TO_LOAD;
            }
            else if (index === 1) {
                pageHolder.title = "My Includes:";
            }
            else if (index === 2) {
                pageHolder.title = "My Excludes:";
            }
            else {
                pageHolder.title = "Output:";
            }
            pageHolder.draw();
        }
        
        private function onBtnClick(e:Event):void 
        {
            var targ:Object = e.currentTarget;
            if (targ === btnLoad) {
                loadSWFFile();
                showPage(0);
            }
            else if (targ === btnIncludes) {
                showPage(1);
            }
            else if (targ === btnExcludes) {
                showPage(2);
            }
            else {
                showPage(3);
            }
        }
        
        private function loadSWFFile():void 
        {
            
            fileRef.browse([new FileFilter("SWF file", "*.swf")]);
        }
        
      
  

    public function getDefinitionNames(data:Object, extended:Boolean = false, linkedOnly:Boolean = false):Array {
        var bytes:ByteArray;
        
        if (data is LoaderInfo) {
            bytes = (data as LoaderInfo).bytes;
        } else if (data is ByteArray) {
            bytes = data as ByteArray;
        } else throw new ArgumentError('Error #1001: The specified data is invalid');
        
        var position:uint = bytes.position;
        var finder:Finder = new Finder(bytes);
        bytes.position = position;
        return finder.getDefinitionNames(extended, linkedOnly);
    }
    
     }

}






import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.geom.Rectangle;
import flash.system.ApplicationDomain;

/**
 * @private
 */
 class Finder {
        
    public function Finder(bytes:ByteArray) {
        super();
        this._data = new SWFByteArray(bytes);
    }

    /**
     * @private
     */
    private var _data:SWFByteArray;
    
    /**
     * @private
     */
    private var _stringTable:Array;
    
    /**
     * @private
     */
    private var _namespaceTable:Array;

    /**
     * @private
     */
    private var _multinameTable:Array;
            
    public function getDefinitionNames(extended:Boolean, linkedOnly:Boolean):Array {
        var definitions:Array = new Array();
        var tag:uint;
        var id:uint;
        var length:uint;
        var minorVersion:uint;
        var majorVersion:uint;
        var position:uint;
        var name:String;
        var index:int;
        
        while (this._data.bytesAvailable) {
            tag = this._data.readUnsignedShort();
            id = tag >> 6;
            length = tag & 0x3F;
            length = (length == 0x3F) ? this._data.readUnsignedInt() : length;
            position = this._data.position;
            
            if (linkedOnly) {
                if (id == 76) {
                    var count:uint = this._data.readUnsignedShort();
                    
                    while (count--) {
                        this._data.readUnsignedShort(); // Object ID
                        name = this._data.readString();
                        index = name.lastIndexOf('.');
                        if (index >= 0) name = name.substr(0, index) + '::' + name.substr(index + 1); // Fast. Simple. Cheat ;)
                        definitions.push(name);
                    }
                }
            } else {
                switch (id) {
                    case 72:
                    case 82:
                        if (id == 82) {
                            this._data.position += 4;
                            this._data.readString(); // identifier
                        }
                        
                        minorVersion = this._data.readUnsignedShort();
                        majorVersion = this._data.readUnsignedShort();
                        if (minorVersion == 0x0010 && majorVersion == 0x002E) definitions.push.apply(definitions, this.getDefinitionNamesInTag(extended));
                    break;
                }
            }

            this._data.position = position + length;
        }

        return definitions;
    }
    
    /**
     * @private
     */
    private function getDefinitionNamesInTag(extended:Boolean):Array {
        var classesOnly:Boolean = !extended;
        var count:int;
        var kind:uint;
        var id:uint;
        var flags:uint;
        var counter:uint;
        var ns:uint;
        var names:Array = new Array();
        this._stringTable = new Array();
        this._namespaceTable = new Array();
        this._multinameTable = new Array();
        
        // int table
        count = this._data.readASInt() - 1;
        
        while (count > 0 && count--) {
            this._data.readASInt();
        }
        
        // uint table
        count = this._data.readASInt() - 1;
        
        while (count > 0 && count--) {
            this._data.readASInt();
        }

        // Double table
        count = this._data.readASInt() - 1;
        
        while (count > 0 && count--) {
            this._data.readDouble();
        }
        
        // String table
        count = this._data.readASInt()-1;
        id = 1;
        
        while (count > 0 && count--) {
            this._stringTable[id] = this._data.readUTFBytes(this._data.readASInt());
            id++;
        }
        
        // Namespace table
        count = this._data.readASInt() - 1;
        id = 1;
        
        while (count > 0 && count--) {
            kind = this._data.readUnsignedByte();
            ns = this._data.readASInt();
            if (kind == 0x16) this._namespaceTable[id] = ns; // only public
            id++;
        }
        
        // NsSet table
        count = this._data.readASInt() - 1;
        
        while (count > 0 && count--) {
             counter = this._data.readUnsignedByte();
             while (counter--) this._data.readASInt();
        }
        
        // Multiname table
        count = this._data.readASInt() - 1;
        id = 1;
        
        while (count > 0 && count--) {
            kind = this._data.readASInt();

            switch (kind) {
                case 0x07:
                case 0x0D:
                    ns = this._data.readASInt();
                    this._multinameTable[id] = [ns, this._data.readASInt()];
                break;    
                case 0x0F:
                case 0x10:
                    this._multinameTable[id] = [0, this._stringTable[this._data.readASInt()]];
                break;    
                case 0x11:
                case 0x12:
                break;    
                case 0x09:
                case 0x0E:
                    this._multinameTable[id] = [0, this._stringTable[this._data.readASInt()]];
                    this._data.readASInt();
                break;    
                case 0x1B:
                case 0x1C:
                    this._data.readASInt();
                break;
                case 0x1D: // Generic
                    if (extended) {
                        var multinameID:uint = this._data.readASInt(); // u8 or u30, maybe YOU know?
                        var params:uint = this._data.readASInt(); // param count (u8 or u30), should always to be 1 in current ABC versions
                        name = this.getName(multinameID);
                        
                        while (params--) {
                            var paramID:uint = this._data.readASInt();
                            
                            if (name) { // not the best method, i know
                                name = name + '.<' + this.getName(paramID) + '>';
                                names.push(name);
                            }
                        }
                        
                        this._multinameTable[id] = [0, name];
                    } else {
                        this._data.readASInt();
                        this._data.readASInt();
                        this._data.readASInt();
                    }
                break;    
            }
            
            id++;
        }
        
        // Method table
        count = this._data.readASInt();

        while (count > 0 && count--) {
            var paramsCount:int = this._data.readASInt();
            counter = paramsCount;
            this._data.readASInt();
            while (counter--) this._data.readASInt();
            this._data.readASInt();
            flags = this._data.readUnsignedByte();
            
            if (flags & 0x08) {
                counter = this._data.readASInt();
                
                while (counter--) {
                    this._data.readASInt();
                    this._data.readASInt();
                }
            }
            
            if (flags & 0x80) {
                counter = paramsCount;
                while (counter--) this._data.readASInt();
            }
        }

        // Metadata table
        count = this._data.readASInt();

        while (count > 0 && count--) {
            this._data.readASInt();
            counter = this._data.readASInt();
            
            while (counter--) {
                this._data.readASInt();
                this._data.readASInt();
            }
        }

        // Instance table
        count = this._data.readASInt();
        var classCount:uint = count;
        var name:String;
        var isInterface:Boolean;

        while (count > 0 && count--) {
            id = this._data.readASInt();
            this._data.readASInt();
            flags = this._data.readUnsignedByte();
            if (flags & 0x08) ns = this._data.readASInt();
            isInterface = Boolean(flags & 0x04);
            counter = this._data.readASInt();
            while (counter--) this._data.readASInt();
            this._data.readASInt(); // iinit
            this.readTraits();
            
            if (classesOnly && !isInterface) {
                name = this.getName(id);
                if (name) names.push(name);
            }
        }
        
        if (classesOnly) return names;
        
        // Class table
        count = classCount;
        
        while (count && count--) {
            this._data.readASInt(); // cinit
            this.readTraits();
        }
        
        // Script table
        count = this._data.readASInt();
        var traits:Array;
        
        while (count && count--) {
            this._data.readASInt(); // init
            traits = this.readTraits(true);
            if (traits.length) names.push.apply(names, traits);
        }

        return names;
    }
    
    /**
     * @private
     */
    private function readTraits(buildNames:Boolean = false):Array {
        var kind:uint;
        var counter:uint;
        var ns:uint;
        var id:uint;
        var traitCount:uint = this._data.readASInt();
        var names:Array;
        var name:String;
        if (buildNames) names = [];

        while (traitCount--) {
            id = this._data.readASInt(); // name
            kind = this._data.readUnsignedByte();
            var upperBits:uint = kind >> 4;
            var lowerBits:uint = kind & 0xF;
            this._data.readASInt();
            this._data.readASInt();
            
            switch (lowerBits) {
                case 0x00:
                case 0x06:
                    if (this._data.readASInt()) this._data.readASInt();
                    break;
            }

            if (buildNames) {
                name = this.getName(id);
                if (name) names.push(name);
            }
            
            if (upperBits & 0x04) {
                counter = this._data.readASInt();
                while (counter--) this._data.readASInt();
            }
        }
        
        return names;
    }
    
    /**
     * @private
     */
    private function getName(id:uint):String {
        if (!(id in this._multinameTable)) return null;
        var mn:Array = this._multinameTable[id] as Array;
        var ns:uint = mn[0] as uint;
        var nsName:String = this._stringTable[this._namespaceTable[ns] as uint] as String;
        var name:String = mn[1] is String ? mn[1] : (this._stringTable[mn[1] as uint] as String);
        if (nsName && nsName.indexOf('__AS3__') < 0 /* cheat! */) name = nsName + '::' + name;
        return name;
    }

}
    
internal class SWFByteArray extends ByteArray {
    
    /**
     * @private
     */
    private static const TAG_SWF:String = 'FWS';
    
    /**
     * @private
     */
    private static const TAG_SWF_COMPRESSED:String = 'CWS';
    
    public function SWFByteArray(data:ByteArray=null):void {
        super();
        super.endian = Endian.LITTLE_ENDIAN;
        var endian:String;
        var tag:String;
        
        if (data) {
            endian = data.endian;
            data.endian = Endian.LITTLE_ENDIAN;
            
            if (data.bytesAvailable > 26) {
                tag = data.readUTFBytes(3);
                
                if (tag == SWFByteArray.TAG_SWF || tag == SWFByteArray.TAG_SWF_COMPRESSED) {
                    this._version = data.readUnsignedByte();
                    data.readUnsignedInt();
                    data.readBytes(this);
                    if (tag == SWFByteArray.TAG_SWF_COMPRESSED) super.uncompress();
                } else throw new ArgumentError('Error #2124: Loaded file is an unknown type.');
                
                this.readHeader();
            }
            
            data.endian = endian;
        }
    }
        
    /**
     * @private
     */
    private var _bitIndex:uint;
    
    /**
     * @private
     */
    private var _version:uint;
    
    public function get version():uint {
        return this._version;
    }
    
    /**
     * @private
     */
    private var _frameRate:Number;
    
    public function get frameRate():Number {
        return this._frameRate;    
    }
    
    /**
     * @private
     */
    private var _rect:Rectangle;
    
    public function get rect():Rectangle {
        return this._rect;
    }

    public function writeBytesFromString(bytesHexString:String):void {
        var length:uint = bytesHexString.length;
        
        for (var i:uint = 0;i<length;i += 2) {
            var hexByte:String = bytesHexString.substr(i, 2);
            var byte:uint = parseInt(hexByte, 16);
            writeByte(byte);
        }
    }
    
    public function readRect():Rectangle {
        var pos:uint = super.position;
        var byte:uint = this[pos];
        var bits:uint = byte >> 3;
        var xMin:Number = this.readBits(bits, 5) / 20;
        var xMax:Number = this.readBits(bits) / 20;
        var yMin:Number = this.readBits(bits) / 20;
        var yMax:Number = this.readBits(bits) / 20;
        super.position = pos + Math.ceil(((bits * 4) - 3) / 8) + 1;
        return new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin);
    }
    
    public function readBits(length:uint, start:int = -1):Number {
        if (start < 0) start = this._bitIndex;
        this._bitIndex = start;
        var byte:uint = this[super.position];
        var out:Number = 0;
        var shift:Number = 0;
        var currentByteBitsLeft:uint = 8 - start;
        var bitsLeft:Number = length - currentByteBitsLeft;
        
        if (bitsLeft > 0) {
            super.position++;
            out = this.readBits(bitsLeft, 0) | ((byte & ((1 << currentByteBitsLeft) - 1)) << (bitsLeft));
        } else {
            out = (byte >> (8 - length - start)) & ((1 << length) - 1);
            this._bitIndex = (start + length) % 8;
            if (start + length > 7) super.position++;
        }
        
        return out;
    }
    
    public function readASInt():int {
        var result:uint = 0;
        var i:uint = 0, byte:uint;
        do {
            byte = super.readUnsignedByte();
            result |= ( byte & 0x7F ) << ( i*7 );
            i+=1;
        } while ( byte & 1<<7 );
        return result;            
    }

    public function readString():String {
        var i:uint = super.position;
        while (this[i] && (i+=1)) {};
        var str:String = super.readUTFBytes(i - super.position);
        super.position = i+1; 
        return str;
    }

    public function traceArray(array:ByteArray):String { // for debug
        var out:String = '';
        var pos:uint = array.position;
        var i:uint = 0;
        array.position = 0;

        while (array.bytesAvailable) {
            var str:String = array.readUnsignedByte().toString(16).toUpperCase();
            str = str.length < 2 ? '0'+str : str;
            out += str+' ';
        }
        
        array.position = pos;
        return out;
    }

    /**
     * @private
     */
    private function readFrameRate():void {
        if (this._version < 8) {
            this._frameRate = super.readUnsignedShort();
        } else {
            var fixed:Number = super.readUnsignedByte() / 0xFF;
            this._frameRate = super.readUnsignedByte() + fixed;
        }
    }
    
    /**
     * @private
     */
    private function readHeader():void {
        this._rect = this.readRect();
        this.readFrameRate();        
        super.readShort(); // num of frames
    }
}