Subversion Repositories spk

Rev

Rev 4 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "hippack.h"
#include <math.h>
#include <stdio.h>
#include <crtdbg.h>
#include "LiteralHuffTree.h"
#include "CRC32.h"


// #pragma optimize( "", off )

//
// constants/macros
//
#define BUFF ((PBYTE)m_pBuff)

HipPack::HipPack( void* pInBuff, DWORD cbInBuff )
{
        //
        // member variable initializations
        //
        this->m_pBuff      = pInBuff;
        this->m_cbBuff     = cbInBuff;

        m_pOut             = NULL;
        m_pbyValidHEntries = NULL;
        m_Callback         = NULL;
}

HipPack::~HipPack()
{
        Dispose();
}

//
// Purpose:
//   Perform all class-internal cleanups
//
void HipPack::Dispose()
{
        FreeOutBuff();

        return;
}

//
// Purpose:
//   Deallocate output buffer's member if allocated
//
void HipPack::FreeOutBuff()
{
        if ( m_pOut ) // sth allocated ?
        {
                free( m_pOut );
                m_pOut = NULL;
        }

        return;
}

//
// Purpose:
//   Stamps the decompressed data block into a buffer
//   and returns its pointer and size
//
// Returns:
//   FALSE - not enough memory
//
BOOL HipPack::PerformDecompression( OUT void** ppCompBlock, OUT DWORD* pdwcbBlock )
{
        BYTE*           p, *pAlloc;
        DWORD           dwcbMax, cbOut, cbRef, dwDelta;
        BYTE            byChar;
        PByteReader     reader;
        LiteralHuffTree tree;
        PHIP_HEADER     pHdr;
        BYTE              byCBIteration = 0;

        //
        // allocate enough memory
        //
        FreeOutBuff();
        pHdr    = (PHIP_HEADER)m_pBuff;
        dwcbMax = pHdr->cbUncompressed;
        pAlloc  = p = (PBYTE)malloc( dwcbMax );
        if ( !p )
                return FALSE; // ERR

#define BUFF ((PBYTE)m_pBuff)

        //
        // get Huffman table
        //
        DWORD cbHuffTbl;
//      cbHuffTbl = tree.RipEmittedTree(
//              BUFF + sizeof(HIP_HEADER), m_cbBuff - sizeof(HIP_HEADER) );

        tree.GenerateAdaptiveModel();
        cbHuffTbl = 0;

        bool notdone = false;
        //
        // Decompress !
        //
        cbOut                 = 0;
        reader                = new ByteReader(
                MakePtr( PVOID, m_pBuff, sizeof(HIP_HEADER) + cbHuffTbl ),
                m_cbBuff - sizeof(HIP_HEADER) + cbHuffTbl );
        DWORD dwHuffIteration = 0;
        while( cbOut < dwcbMax )
        {
                if ( m_Callback )
                {
                        byCBIteration++;
                        if ( byCBIteration == HIP_ITERATION_FOR_CALLBACK || cbOut == dwcbMax ) 
                        {
                                byCBIteration = 0;
                                if ( !m_Callback( cbOut, dwcbMax ) )
                                {
                                        notdone = true;
                                        break;
                                }
                        }
                }


                // firstly we read the indicator bit
                switch( reader->ReadBit() )
                {
                case HP_LITERAL_PREFIX:
                        //
                        // grab a literal from the compressed buffer
                        //
                        byChar = (BYTE)tree.QueryLiteralByBitSequence( reader );
                        *p = byChar;

                        // adapt the Huffman tree
                        tree.m_dwOccurr[ byChar ]++;
                        dwHuffIteration++;
                        if ( dwHuffIteration % HP_HUFF_ADAPT_ITERATION == 0 )
                                tree.GenerateWithOccurrences();

                        ++p;
                        ++cbOut;
                        break;

                case HP_PTRSIZE_PREFIX:
                        //
                        // grab a ptr/size pair from the compressed buffer
                        //
                        switch( reader->ReadBit() )
                        {
                        case 0: // short ref
                                dwDelta = reader->ReadBits( HP_SHORTREF_PTR_BIT_COUNT );
                                if ( dwDelta == 0 )
                                {
                                        //
                                        // decode a literal block
                                        //
                                        DWORD cb = GammaDecodeLength( reader ) - 2 + HP_MIN_LITERALS_IN_BLOCK;
                                        for ( UINT i = 0; i < cb; i++ )
                                        {
                                                BYTE by = reader->ReadByte();
                                                p[i] = by;
                                        }
                                        cbRef    = cb;
                                }
                                else
                                {
                                        //
                                        // decode usual short reference
                                        //
                    cbRef   = reader->ReadBits( 2 ) + 2;                                
                                }
                                break;

                        case 1: // normal ref
                                DWORD cbitBase;
                                if ( cbOut > HP_BASE3_WIN )
                                        cbitBase = HP_BASE3;
                                if ( cbOut > HP_BASE2_WIN )
                                        cbitBase = HP_BASE2;
                                if ( cbOut > HP_BASE1_WIN )
                                        cbitBase = HP_BASE1;
                                else 
                                        cbitBase = HP_INITAL_BASE;

                                dwDelta = GammaDecodeDistance( reader, cbitBase ) + HP_NORMALREF_BASE;

                                DWORD dwSizeInc;
                                if ( dwDelta >= HP_NREF_SIZE_DEC_INDEX4 )
                                        dwSizeInc = 4;
                                else if ( dwDelta >= HP_NREF_SIZE_DEC_INDEX3 )
                                        dwSizeInc = 3;
                                else if ( dwDelta >= HP_NREF_SIZE_DEC_INDEX2 )
                                        dwSizeInc = 2;
                                else
                                        dwSizeInc = 1;
                cbRef   = GammaDecodeLength( reader ) + dwSizeInc;
                                break;
                        }

                        // paste the referenced bytes a P
                        if ( dwDelta != 0 )
                MyMemCpy( p, (void*)((DWORD)p - dwDelta), cbRef );
                        // adjust vars
                        p      += cbRef;
                        cbOut  += cbRef;
                        break;
                }
        }

        //
        // check CRC32
        //
        if ( !notdone )
        {
                DWORD dwCurCRC = CRC32::Generate( pAlloc, cbOut );
                if ( dwCurCRC != pHdr->CRC )
                        notdone = true;
        }

        //
        // handle output variables
        //
        *pdwcbBlock   = cbOut;
        *ppCompBlock  = pAlloc;

        //
        // tidy up
        //
        delete reader;

        if ( m_pbyValidHEntries )
                delete [] m_pbyValidHEntries;

        return !notdone; 
}

//
// Purpose:
//   Decode Gamma encoded delta
//
DWORD HipPack::GammaDecodeDistance( PByteReader reader, DWORD dwcBaseBits )
{
        DWORD dwValue = GammaDecodeLength( reader ) - 2;

    dwValue <<= dwcBaseBits; 
        dwValue  |= reader->ReadBits( dwcBaseBits );

        return dwValue;
}

//
// Purpose:
//   Decode gamma encoded length
//
DWORD HipPack::GammaDecodeLength( PByteReader reader )
{
        DWORD  dwValue = 1; // MSB always 1

        do
        {
                dwValue <<= 1;
                dwValue  |= reader->ReadBit();
        }
        while( reader->ReadBit() != 0);

        return dwValue;
}

//
// Returns:
//   The address of the callback that was set before
//
hpCompressCallback HipPack::SetCallback( hpCompressCallback proc )
{
        hpCompressCallback  procOldCB = m_Callback;

        m_Callback = proc;

        return procOldCB;
}

// Remarks:
//   Because the memcpy runtime function performs special operations
//   when pSrc + cb > pDest - the memory regions overlap - we hold it
//   simply - thus working - and do a REP MOVSB by hand
//
void HipPack::MyMemCpy( void* pDest, const void* pSrc, DWORD cb )
{
        __asm
        {
                mov     esi, pSrc
                mov     edi, pDest
                mov     ecx, cb
                rep     movsb
        }
        return;
}

#undef BUFF