#include "ArchiveFile.h"



ArchiveFile::ArchiveFile( const Ogre::String& file ):
    File( file )
{
    Extract();
}



ArchiveFile::ArchiveFile(File* file, const u32& offset, const u32& length ):
    File( file, offset, length )
{
    Extract();
}



ArchiveFile::ArchiveFile( u8* buffer, const u32& offset, const u32& length ):
    File( buffer, offset, length )
{
    Extract();
}



ArchiveFile::ArchiveFile( File *file ):
    File( file )
{
    Extract();
}



ArchiveFile::~ArchiveFile()
{
}



void
ArchiveFile::Extract()
{
    u16 circle_size = 4096;
    u16 F = 18;

    u8 circle_buffer[ 4096 ];
    int r = 0;
    for( u16 i = 0; i < circle_size - F; ++i )
    {
        circle_buffer[ i ] = 0;
    }
    r = circle_size - F;

    // current position in mBuffer (compressed)
    u32 comp_position = 0;

    u32 decomp_length = GetU32LE( comp_position );
    comp_position += 0x04;
    u8 *decomp_buffer = ( u8* )malloc( sizeof( u8 ) * decomp_length );

    // current position in decomp_buffer
    u32 decomp_position = 0;

    // decompression
    while( decomp_position < decomp_length )
    {
        // Control byte
        u8 control_byte = GetU8( comp_position++ );

        for( u8 shift = 0; shift < 8; ++shift )
        {
            if( ( control_byte & ( 1 << shift ) ) == 0 )
            {
                // not compressed
                // copy byte in circle burrer and in decomp_buffer
                u8 temp = GetU8( comp_position++ );
                decomp_buffer[ decomp_position++ ] = temp;
                circle_buffer[ r++ ]               = temp;
                r &= circle_size - 1;
            }
            else
            {
                // compressed
                // get 2 bytes
                u8 reference1 = GetU8( comp_position++ );
                u8 reference2 = GetU8( comp_position++ );
                // offset for circle_buffer
                u16 reference_offset = reference1 | ( ( reference2 & 0x0f ) << 8 );
                // length are always lesser than real by 3
                u8 reference_length = ( ( reference2 & 0xf0 ) >> 4 ) + 3;

                // copy bytes
                for( int i = 0; i < reference_length; ++i )
                {
                    u8 temp = circle_buffer[ ( r - reference_offset ) & ( circle_size - 1 ) ];
                    decomp_buffer[ decomp_position++ ] = temp;
                    circle_buffer[ r++ ]               = temp;
                    r &= circle_size - 1;
                }
            }
        }
    }

    free( m_Buffer );
    m_Buffer = decomp_buffer;
    m_BufferSize = decomp_length;
}
