/**
* 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
}
}