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 IExternalizable it can be passed * to a class that implements IDataInput or IDataOutput. * *

Using registerClassAlias makes calls to IDataInput.readObject() * or IDataOutput.writeObject() possible.

* *

The furnace file format supports streaming and has been designed for fast and * easy read access.

* *

The file structure is like this: *

* Data contains the raw GZip compressed bytes of the actual file. *

* *

* Header: * * * * * * *
LengthTypeDescription
3bStringIdentifier (should be "FUR")
4buintTotal filesize in bytes (including header)
4bNumberFormat version
4buintNumber of files
*

* *

* Item: * * * * * * *
LengthTypeDescription
2buintLength of UTF encoded name in bytes
LEN bytesStringFilename
4buintPosition of file data
4buintLength 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 of FurnaceItem 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 (;i= _library.length ) { throw new RangeError( 'index is out of bounds.' ); } } /** * Writes the furnace format to a given IDataOutput 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