Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
660 lines (582 sloc) 14.4 KB
package com.swfwire.decompiler
{
import com.swfwire.utils.ByteArrayUtil;
import flash.utils.ByteArray;
import flash.utils.Endian;
/**
* Adds the ability to read and write core SWF data types to standard ByteArray.
* Abbreviations from the SWF spec:
* SI = Signed Integer
* UI = Unsigned Integer
* SB = Signed Bits
* UB = Unsigned Bits
*/
public class SWFByteArray
{
private static const filter5:uint = ~0 >>> -5;
private static const filter7:uint = ~0 >>> -7;
private static const filter8:uint = ~0 >>> -8;
private static const filter10:uint = ~0 >>> -10;
private static const filter13:uint = ~0 >>> -13;
private static const filter16:uint = ~0 >>> -16;
private static const filter23:uint = ~0 >>> -23;
private static const tempByteArray:ByteArray = new ByteArray();
/**
* Returns the number of bits required to hold <code>number</code> in a UB
*/
public static function calculateUBBits(number:uint):uint
{
if(number == 0) return 0;
var bits:uint = 0;
while(number >>>= 1) bits++;
return bits + 1;
}
/**
* Returns the number of bits required to hold <code>number</code> in an SB
*/
public static function calculateSBBits(number:int):uint
{
return number == 0 ? 1 : calculateUBBits(number < 0 ? ~number : number) + 1;
}
/**
* Returns the number of bits required to hold <code>number</code> in an FB
*/
public static function calculateFBBits(number:Number):uint
{
var integer:int = Math.floor(number);
var decimal:uint = (Math.round(Math.abs(number - integer) * 0xFFFF)) & filter16;
var sbVersion:int = ((integer & filter16) << 16) | (decimal);
return number == 0 ? 1 : calculateSBBits(sbVersion);
}
private static function float32AsUnsignedInt(value:Number):uint
{
tempByteArray.position = 0;
tempByteArray.writeFloat(value);
tempByteArray.position = 0;
return tempByteArray.readUnsignedInt();
}
private static function unsignedIntAsFloat32(value:uint):Number
{
tempByteArray.position = 0;
tempByteArray.writeUnsignedInt(value);
tempByteArray.position = 0;
return tempByteArray.readFloat();
}
public function SWFByteArray(bytes:ByteArray)
{
this.bytes = bytes;
bytes.endian = Endian.LITTLE_ENDIAN;
bytes.position = 0;
}
private var bytes:ByteArray;
private var bitPosition:uint = 0;
/**
* Move forward to the next byte boundary
*/
public function alignBytes():void
{
if(bitPosition != 0)
{
bytes.position++;
bitPosition = 0;
}
}
public function getBytePosition():uint
{
var result:uint = bytes.position;
if(bitPosition > 0)
{
result++;
}
return result;
}
public function setBytePosition(newPosition:uint):void
{
bitPosition = 0;
bytes.position = newPosition;
}
public function getBitPosition():uint
{
return bitPosition;
}
public function setBitPosition(newPosition:uint):void
{
bitPosition = newPosition;
}
public function getBytesAvailable():uint
{
return bytes.bytesAvailable;
}
public function getLength():uint
{
return bytes.length;
}
public function setLength(newLength:uint):void
{
bytes.length = newLength;
}
public function compress():void
{
bytes.compress();
}
public function decompress():void
{
bytes.uncompress();
}
public function clear():void
{
bitPosition = 0;
bytes.clear();
}
public function dump():void
{
var offset:uint = bytes.position;
ByteArrayUtil.dumpHex(bytes);
bytes.position = offset;
}
/**
* Reads data to a ByteArray
*/
public function readBytes(byteArray:ByteArray, offset:uint = 0, length:uint = 0):void
{
alignBytes();
bytes.readBytes(byteArray, offset, length);
}
/**
* Writes data from a ByteArray
*/
public function writeBytes(byteArray:ByteArray, offset:uint = 0, length:uint = 0):void
{
alignBytes();
bytes.writeBytes(byteArray, offset, length);
}
/**
* Shortcut for reading UB[1]
*/
public function readFlag():Boolean
{
return readUB(1) == 1;
}
/**
* Shortcut for writing UB[1]
*/
public function writeFlag(value:Boolean):void
{
writeUB(1, value ? 1 : 0);
}
/*
* Signed int values
*/
public function readSI8():int
{
alignBytes();
return bytes.readByte();
}
public function readSI16():int
{
alignBytes();
return bytes.readShort();
}
public function writeSI16(value:int):void
{
alignBytes();
bytes.writeShort(value);
}
public function readSI32():int
{
alignBytes();
return bytes.readInt();
}
public function writeSI32(value:int):void
{
alignBytes();
return bytes.writeInt(value);
}
public function readSI8Array(length:uint):Vector.<int>
{
var result:Vector.<int> = new Vector.<int>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readSI8();
}
return result;
}
public function readSI16Array(length:uint):Vector.<int>
{
var result:Vector.<int> = new Vector.<int>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readSI16();
}
return result;
}
/*
* Unsigned int values
*/
public function readUI8():uint
{
alignBytes();
return bytes.readUnsignedByte();
}
public function writeUI8(value:uint):void
{
alignBytes();
bytes.writeByte(int(value));
}
public function readUI16():uint
{
alignBytes();
return bytes.readUnsignedShort();
}
public function writeUI16(value:uint):void
{
alignBytes();
bytes.writeShort(int(value));
}
public function readUI24():uint
{
alignBytes();
return bytes.readUnsignedByte() << 16 | bytes.readUnsignedByte() << 8 | bytes.readUnsignedByte();
}
public function writeUI24(value:uint):void
{
alignBytes();
bytes.writeByte(value >> 16);
bytes.writeByte(value >> 8);
bytes.writeByte(value);
}
public function readUI32():uint
{
alignBytes();
return bytes.readUnsignedInt();
}
public function writeUI32(value:uint):void
{
alignBytes();
bytes.writeUnsignedInt(value);
}
public function readUI8Array(length:uint):Vector.<uint>
{
var result:Vector.<uint> = new Vector.<uint>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readUI8();
}
return result;
}
public function readUI16Array(length:uint):Vector.<uint>
{
var result:Vector.<uint> = new Vector.<uint>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readUI16();
}
return result;
}
public function readUI24Array(length:uint):Vector.<uint>
{
alignBytes();
var result:Vector.<uint> = new Vector.<uint>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readUI24();
}
return result;
}
public function readUI32Array(length:uint):Vector.<uint>
{
var result:Vector.<uint> = new Vector.<uint>(length, true);
for(var iter:uint = 0; iter < length; iter++)
{
result[iter] = readUI32();
}
return result;
}
/*
* Fixed point numbers
*/
public function readFixed8_8():Number
{
alignBytes();
var decimal:uint = bytes.readUnsignedByte();
var result:Number = bytes.readByte();
result += decimal / 0xFF;
return result;
}
public function writeFixed8_8(value:Number):void
{
alignBytes();
var integer:uint = int(value);
var decimal:uint = uint((value - integer) * 0xFF);
bytes.writeByte(int(decimal));
bytes.writeByte(int(integer));
}
public function readFixed16_16():Number
{
alignBytes();
var decimal:uint = bytes.readUnsignedShort();
var result:Number = bytes.readShort();
result += decimal / 0xFFFF;
return result;
}
public function writeFixed16_16(value:Number):void
{
alignBytes();
var integer:uint = int(value);
var decimal:uint = uint((value - integer) * 0xFFFF);
bytes.writeShort(int(decimal));
bytes.writeShort(int(integer));
}
/*
* Floating point numbers
*/
public function readFloat16():Number
{
var raw:uint = readUI16();
var sign:uint = raw >> 15;
var exp:int = (raw >> 10) & filter5;
var sig:int = raw & filter10;
//Handle infinity/NaN
if(exp == 31)
{
exp = 255;
}
//Handle normalized values
else if(exp == 0)
{
exp = 0;
sig = 0;
}
else
{
exp += 111;
}
var temp:uint = sign << 31 | exp << 23 | sig << 13;
return unsignedIntAsFloat32(temp);
}
public function writeFloat16(value:Number):void
{
var raw:uint = float32AsUnsignedInt(value);
var sign:uint = raw >> 31;
var exp:int = (raw >> 23) & filter8;
var sig:uint = (raw >> 13) & filter10;
//Handle NaN
if(exp == 255)
{
exp = 31;
}
//Handle underflow
else if(exp < 111)
{
exp = 0;
sig = 0;
}
//Handle overflow
else if(exp > 141)
{
exp = 31;
sig = 0;
}
else
{
exp -= 111;
}
writeUI16(sign << 15 | exp << 10 | sig);
}
public function readFloat():Number
{
return bytes.readFloat();
}
public function writeFloat(value:Number):void
{
bytes.writeFloat(value);
}
public function readDouble():Number
{
return bytes.readDouble();
}
public function writeDouble(value:Number):void
{
bytes.writeDouble(value);
}
/*
* Encoded integers
*/
public function readEncodedUI32():uint
{
alignBytes();
var result:uint;
var bytesRead:uint;
var currentByte:uint;
var shouldContinue:Boolean = true;
while(shouldContinue && bytesRead < 5)
{
currentByte = bytes.readUnsignedByte();
result = ((currentByte & filter7) << (7 * bytesRead)) | result;
shouldContinue = ((currentByte >> 7) == 1);
bytesRead++;
}
return result;
}
public function writeEncodedUI32(value:uint):void
{
var remaining:uint = value;
var bytesWritten:uint;
var currentByte:uint;
var shouldContinue:Boolean = true;
while(shouldContinue && bytesWritten < 5)
{
currentByte = remaining & filter7;
remaining = remaining >> 7;
if(remaining > 0)
{
currentByte = currentByte | (1 << 7);
}
bytes.writeByte(currentByte);
shouldContinue = remaining > 0;
bytesWritten++;
}
}
/*
* Bit values
*/
public function readUB(length:uint):uint
{
if(!length) return 0;
var totalBytes:uint = Math.ceil((bitPosition + length) / 8);
var iter:uint = 0;
var currentByte:uint = 0;
var result:uint = 0;
while(iter < totalBytes)
{
currentByte = bytes.readUnsignedByte();
result = (result << 8) | currentByte;
iter++;
}
var newBitPosition:uint = ((bitPosition + length) % 8);
var excessBits:uint = (totalBytes * 8 - (bitPosition + length));
result = result >> excessBits;
result = result & (~0 >>> -length);
bitPosition = newBitPosition;
if(bitPosition > 0)
{
bytes.position--;
}
return result;
}
public function writeUB(length:uint, value:uint):void
{
if(!length) return;
var totalBytes:uint = Math.ceil((bitPosition + length) / 8);
var iter:int = 0;
var currentByte:uint = 0;
var existing:uint = 0;
var startPosition:uint = bytes.position;
while(iter < totalBytes)
{
currentByte = bytes.bytesAvailable >= 1 ? bytes.readUnsignedByte() : 0;
existing = (existing << 8) | currentByte;
iter++;
}
var newBitPosition:uint = ((bitPosition + length) % 8);
var result:uint;
result = existing >> (totalBytes * 8 - bitPosition);
result = result << length;
result = result | (value & (~0 >>> -length));
var excessBits:uint = (totalBytes * 8 - (bitPosition + length));
if(excessBits > 0)
{
result = result << excessBits;
result = result | (existing & (~0 >>> -excessBits));
}
bytes.position = startPosition;
iter = totalBytes - 1;
while(iter >= 0)
{
bytes.position = startPosition + iter;
currentByte = result & filter8;
result = result >> 8;
bytes.writeByte(int(currentByte));
iter--;
}
bytes.position = startPosition + totalBytes;
bitPosition = newBitPosition;
if(bitPosition > 0)
{
bytes.position--;
}
}
public function readSB(length:uint):int
{
if(!length) return 0;
var result:int = readUB(length);
var leadingDigit:uint = result >>> (length - 1);
if(leadingDigit == 1)
{
return -((~result & (~0 >>> -length)) + 1);
}
return result;
}
public function writeSB(length:uint, value:int):void
{
if(!length) return;
if(value < 0)
{
writeUB(length, ~Math.abs(value) + 1);
}
else
{
writeUB(1, 0);
writeUB(length - 1, value);
}
}
public function readFB(length:uint):Number
{
if(!length) return 0;
var raw:int = readSB(length);
var integer:int = raw >> 16;
var decimal:Number = (raw & filter16)/0xFFFF;
return integer + decimal;
}
public function writeFB(length:uint, value:Number):void
{
if(!length) return;
var integer:int = Math.floor(value);
var decimal:uint = (Math.round(Math.abs(value - integer) * 0xFFFF)) & filter16;
var raw:int = (integer << 16) | decimal;
writeSB(length, raw);
}
/*
* String values
*/
public function readString():String
{
alignBytes();
var byteCount:uint = 1;
while(bytes.readUnsignedByte())
{
byteCount++;
}
bytes.position -= byteCount;
var result:String = bytes.readUTFBytes(byteCount);
return result;
}
public function writeString(value:String):void
{
alignBytes();
bytes.writeUTFBytes(value);
bytes.writeByte(0);
}
public function readStringWithLength(length:uint):String
{
alignBytes();
return bytes.readUTFBytes(length);
}
public function writeStringWithLength(value:String, length:uint):void
{
alignBytes();
value = value.substr(0, length);
bytes.writeUTFBytes(value);
}
}
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.