package de.popforge.format.furnace
{
import flash.utils.ByteArray;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;
/**
* The FurnaceFormat class can read and write the popforge furnace format.
* Since the class implements Using The furnace file format supports streaming and has been designed for fast and
* easy read access. The file structure is like this:
* IExternalizable it can be passed
* to a class that implements IDataOutput.
*
* registerClassAlias makes calls to IDataInput.readObject()
* or IDataOutput.writeObject() possible.
* Data contains the raw GZip compressed bytes of the actual file.
*
* Header: *
| Length | Type | Description |
|---|---|---|
| 3b | String | Identifier (should be "FUR") |
| 4b | uint | Total filesize in bytes (including header) |
| 4b | Number | Format version |
| 4b | uint | Number of files |
* Item: *
| Length | Type | Description |
|---|---|---|
| 2b | uint | Length of UTF encoded name in bytes |
| LEN bytes | String | Filename |
| 4b | uint | Position of file data |
| 4b | uint | Length of the file in bytes |
* Data: * Raw GZip encoded file data. *
* * @author Joa Ebert */ public class FurnaceFormat implements IExternalizable { /** * Actual version of the format. */ internal static const VERSION: Number = 1.0; /** * A reference to the file header. */ internal var _header: FurnaceHeader; /** * Array ofFurnaceItem items.
*/
internal var _items: Array;
/**
* Array of file content represented as ByteArray objects.
*/
internal var _library: Array;
/**
* Creates a new FurnaceFormat object.
*/
public function FurnaceFormat()
{
_header = new FurnaceHeader;
_items = new Array;
_library = new Array;
}
/**
* The number of items in the package.
*/
public function get numFiles(): uint
{
return _header.numFiles;
}
/**
* Returns the file at given item index.
*
* @param index The item index.
* @return Content of the file.
* @throws RangeError If index is out of bounds.
*/
public function fileById( index: uint ): ByteArray
{
testBounds( index );
return _library[ index ];
}
/**
* Returns the file with given name.
*
* In order to find the file all items are searched
* for the given name and returns the first occurence.
*
* If no file is found an error will be thrown.
*
* @param fileName A filename.
* @throws Error If no file with given name exists.
* @return Content of the file.
*/
public function fileByName( fileName: String ): ByteArray
{
var i: int, n: uint;
i = -1;
n = _items.length;
for (;iIDataOutput stream.
*
* @param output The stream to write to.
*/
public function writeExternal( output: IDataOutput ): void
{
var i: uint, n: uint;
var totalLength: uint = 0;
var headerAndItems: uint = 0;
var item: FurnaceItem;
var bytes: ByteArray;
if ( _items.length != _library.length )
throw new Error( 'Items and data must have the same length.' );
compressLibrary();
//-- BEGIN COMPUTE TOTAL LENGTH
n = _items.length;
totalLength += _header.computeLength();
for ( i = 0; i < n; ++i )
{
totalLength += FurnaceItem( _items[ i ] ).computeLength();
}
headerAndItems = totalLength;
for ( i = 0; i < n; ++i )
{
totalLength += ByteArray( _library[ i ] ).length;
}
//-- END COMPUTE TOTAL LENGTH
_header._size = totalLength;
_header._numFiles = n;
_header.writeExternal( output );
for ( i = 0; i < n; ++i )
{
var index: uint = headerAndItems;
var j: uint = 0;
for (;jIDataInput stream.
*
* @param input The stream to read from.
*/
public function readExternal( input: IDataInput ): void
{
var i: uint, n: uint;
if ( input.bytesAvailable < _header.computeLength() )
throw new Error( 'Can not read header (streaming not implemented yet).' );
_header.readExternal( input );
if ( input.bytesAvailable < ( _header.size - _header.computeLength() ) )
throw new Error( 'Can not read body (streaming not implemented yet).' );
n = _header.numFiles;
_items = new Array( n );
_library = new Array( n );
for ( i = 0; i < n; ++i )
{
var item: FurnaceItem = new FurnaceItem;
item.readExternal( input );
_items[ i ] = item;
}
for ( i = 0; i < n; ++i )
{
var bytes: ByteArray = new ByteArray;
input.readBytes( bytes, _items[ i ], FurnaceItem( _items[ i ] )._size );
_library[ i ] = bytes;
}
uncompressLibrary();
}
/**
* Compresses all objects that are in the library.
*/
protected function compressLibrary(): void
{
var i: uint, n: uint;
i = 0;
n = _library.length;
for (;i