/*****************************************************************************/
/* huffman.cpp                       Copyright (c) Ladislav Zezula 1998-2003 */
/*---------------------------------------------------------------------------*/
/* This module contains Huffmann (de)compression methods                     */
/*                                                                           */
/* Authors : Ladislav Zezula (ladik@zezula.net)                              */
/*           ShadowFlare     (BlakFlare@hotmail.com)                         */
/*                                                                           */
/*---------------------------------------------------------------------------*/
/*   Date    Ver   Who  Comment                                              */
/* --------  ----  ---  -------                                              */
/* xx.xx.xx  1.00  Lad  The first version of dcmp.cpp                        */
/* 03.05.03  1.00  Lad  Added compression methods                            */
/* 19.11.03  1.01  Dan  Big endian handling                                  */
/* 08.12.03  2.01  Dan  High-memory handling (> 0x80000000)                  */
/* 09.01.13  3.00  Lad  Refactored, beautified, documented :-)               */
/*****************************************************************************/
 
#include <assert.h>
#include <string.h>
 
//#include "../StormPort.h"
#include "stormlib_huff.h"

//-----------------------------------------------------------------------------
// Table of byte-to-weight values

// Table for (de)compression. Every compression type has 258 entries
static unsigned char ByteToWeight_00[] =
{
    0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
    0x00, 0x00
};

// Data for compression type 0x01
static unsigned char ByteToWeight_01[] =
{
    0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05,
    0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02,
    0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02,
    0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04,
    0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
    0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02,
    0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03,
    0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02,
    0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03,
    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01,
    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
    0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
    0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B,
    0x00, 0x00
};
   
// Data for compression type 0x02
static unsigned char ByteToWeight_02[] =
{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04,
    0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01,
    0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02,
    0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01,
    0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A,
    0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};
   
// Data for compression type 0x03
static unsigned char ByteToWeight_03[] =
{
    0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03,
    0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
    0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
    0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01,
    0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03,
    0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03,
    0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01,
    0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01,
    0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11,
    0x00, 0x00
};
   
// Data for compression type 0x04
static unsigned char ByteToWeight_04[] =
{
    0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};
   
// Data for compression type 0x05
static unsigned char ByteToWeight_05[] =
{
    0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82,
    0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37,
    0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D,
    0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};
   
    // Data for compression type 0x06
static unsigned char ByteToWeight_06[] =
{
    0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};
   
// Data for compression type 0x07
static unsigned char ByteToWeight_07[] =
{
    0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};
   
// Data for compression type 0x08
static unsigned char ByteToWeight_08[] =
{
    0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10,
    0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11,
    0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00
};

static unsigned char * WeightTables[0x09] =
{
    ByteToWeight_00,
    ByteToWeight_01,
    ByteToWeight_02,
    ByteToWeight_03,
    ByteToWeight_04,
    ByteToWeight_05,
    ByteToWeight_06,
    ByteToWeight_07,
    ByteToWeight_08
};

//----------------------------------------------------------------------------- 
// Debug/diagnostics

#ifdef _DEBUG
void DumpHuffmannTree(THTreeItem * pItem)
{
    THTreeItem * pChildLo;                          // Item with the lower weight
    THTreeItem * pChildHi;                          // Item with the higher weight

    // Get the lower-weight branch
    pChildLo = pItem->pChildLo;
    if(pChildLo != NULL)
    {
        // Get the higher-weight branch
        pChildHi = pChildLo->pPrev;

        // Parse the lower-weight branch
        DumpHuffmannTree(pChildHi);
        DumpHuffmannTree(pChildLo);
    }
}
#endif

//-----------------------------------------------------------------------------
// TInputStream functions

TInputStream::TInputStream(void * pvInBuffer, size_t cbInBuffer)
{
    pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer;
    pbInBuffer = (unsigned char *)pvInBuffer;
    BitBuffer = 0;
    BitCount = 0;
}

// Gets 7 bits from the stream. DOES NOT remove the bits from input stream
unsigned int TInputStream::Peek7Bits()
{
    unsigned int dwReloadByte = 0;

    // If there is not enough bits to get the value,
    // we have to add 8 more bits from the input buffer
    if(BitCount < 7)
    {
        dwReloadByte = *pbInBuffer++;
        BitBuffer |= dwReloadByte << BitCount;
        BitCount += 8;
    }

    // Return the first available 7 bits. DO NOT remove them from the input stream
    return (BitBuffer & 0x7F);
}

// Gets one bit from input stream
unsigned int TInputStream::Get1Bit()
{
    unsigned int OneBit = 0;

    // Ensure that the input stream is reloaded, if there are no bits left
    if(BitCount == 0)
    {
        // Refill the bit buffer
        BitBuffer = *pbInBuffer++;
        BitCount = 8;
    }

    // Copy the bit from bit buffer to the variable
    OneBit = (BitBuffer & 0x01);
    BitBuffer >>= 1;
    BitCount--;

    return OneBit;
}   

// Gets the whole byte from the input stream.
unsigned int TInputStream::Get8Bits()
{
    unsigned int dwReloadByte = 0;
    unsigned int dwOneByte = 0;

    // If there is not enough bits to get the value,
    // we have to add 8 more bits from the input buffer
    if(BitCount < 8)
    {
        dwReloadByte = *pbInBuffer++;
        BitBuffer |= dwReloadByte << BitCount;
        BitCount += 8;
    }

    // Return the lowest 8 its
    dwOneByte = (BitBuffer & 0xFF);
    BitBuffer >>= 8;
    BitCount -= 8;
    return dwOneByte;
}

void TInputStream::SkipBits(unsigned int dwBitsToSkip)
{
    unsigned int dwReloadByte = 0;

    // If there is not enough bits in the buffer,
    // we have to add 8 more bits from the input buffer
    if(BitCount < dwBitsToSkip)
    {
        dwReloadByte = *pbInBuffer++;
        BitBuffer |= dwReloadByte << BitCount;
        BitCount += 8;
    }

    // Skip the remaining bits
    BitBuffer >>= dwBitsToSkip;
    BitCount -= dwBitsToSkip;
}

//-----------------------------------------------------------------------------
// TOutputStream functions

TOutputStream::TOutputStream(void * pvOutBuffer, size_t cbOutLength)
{
    pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength;
    pbOutBuffer = (unsigned char *)pvOutBuffer;
    BitBuffer = 0;
    BitCount = 0;
}

void TOutputStream::PutBits(unsigned int dwValue, unsigned int nBitCount)
{
    BitBuffer |= (dwValue << BitCount);
    BitCount += nBitCount;
 
    // Flush completed bytes
    while(BitCount >= 8)
    {
        if(pbOutBuffer < pbOutBufferEnd)
            *pbOutBuffer++ = (unsigned char)BitBuffer;
 
        BitBuffer >>= 8;
        BitCount -= 8;
    }
}

void TOutputStream::Flush()
{
    while(BitCount != 0)
    {
        if(pbOutBuffer < pbOutBufferEnd)
            *pbOutBuffer++ = (unsigned char)BitBuffer;

        BitBuffer >>= 8;
        BitCount -= ((BitCount > 8) ? 8 : BitCount);
    }
}

//-----------------------------------------------------------------------------
// Methods of the THTreeItem struct

void THTreeItem::RemoveItem()
{
    if(pNext != NULL)
    {
        pPrev->pNext = pNext;
        pNext->pPrev = pPrev;
        pNext = pPrev = NULL;
    }
}

//-----------------------------------------------------------------------------
// THuffmannTree class functions
 
THuffmannTree::THuffmannTree(bool bCompression)
{
    // TODO: Obsolete, delete this!!
//  InitializeHTListHead(&ItemLinks);
    pFirst = pLast = LIST_HEAD();
 
    MinValidValue = 1;
    ItemsUsed = 0;
 
    // If we are going to decompress data, we need to invalidate all item links
    // We do so by zeroing their ValidValue, so it becomes lower MinValidValue
    if(bCompression == false)
    {
        memset(QuickLinks, 0, sizeof(QuickLinks));
    }
}

THuffmannTree::~THuffmannTree()
{
    // Our Huffmann tree does not use any memory allocations,
    // so we don't need to do eny code in the destructor
}

void THuffmannTree::LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2)
{
    pItem2->pNext = pItem1->pNext;
    pItem2->pPrev = pItem1->pNext->pPrev;
    pItem1->pNext->pPrev = pItem2;
    pItem1->pNext = pItem2;
}

// Inserts item into the tree (?)
void THuffmannTree::InsertItem(THTreeItem * pNewItem, TInsertPoint InsertPoint, THTreeItem * pInsertPoint)
{
    // Remove the item from the tree
    pNewItem->RemoveItem();
 
    if(pInsertPoint == NULL)
        pInsertPoint = LIST_HEAD();

    switch(InsertPoint)
    {
        case InsertAfter:
            LinkTwoItems(pInsertPoint, pNewItem);
            return;
       
        case InsertBefore:
            pNewItem->pNext = pInsertPoint;             // Set next item (or pointer to pointer to first item)
            pNewItem->pPrev = pInsertPoint->pPrev;      // Set prev item (or last item in the tree)
            pInsertPoint->pPrev->pNext = pNewItem;
            pInsertPoint->pPrev = pNewItem;             // Set the next/last item
            return;
    }
}

THTreeItem * THuffmannTree::FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight)
{
    // Parse all existing items
    if(pItem != NULL)
    {
        while(pItem != LIST_HEAD())
        {
            if(pItem->Weight >= Weight)
                return pItem;

            pItem = pItem->pPrev;
        }
    }

    // If not found, we just get the first item
    return LIST_HEAD();
}

THTreeItem * THuffmannTree::CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint)
{
    THTreeItem * pNewItem;

    // Allocate new item from the item pool
    pNewItem = &ItemBuffer[ItemsUsed++];

    // Insert this item to the top of the tree
    InsertItem(pNewItem, InsertPoint, NULL);

    // Fill the rest of the item
    pNewItem->DecompressedValue = DecompressedValue;
    pNewItem->Weight = Weight;
    pNewItem->pParent = NULL;
    pNewItem->pChildLo = NULL;
    return pNewItem;
}

unsigned int THuffmannTree::FixupItemPosByWeight(THTreeItem * pNewItem, unsigned int MaxWeight)
{
    THTreeItem * pHigherItem;

    if(pNewItem->Weight < MaxWeight)
    {
        // Find an item that has higher weight than this one
        pHigherItem = FindHigherOrEqualItem(pLast, pNewItem->Weight);

        // Remove the item and put it to the new position
        pNewItem->RemoveItem();
        LinkTwoItems(pHigherItem, pNewItem);
    }
    else
    {
        MaxWeight = pNewItem->Weight;
    }

    // Return the (updated) maximum weight
    return MaxWeight;
}

// Builds Huffman tree. Called with the first 8 bits loaded from input stream
void THuffmannTree::BuildTree(unsigned int CompressionType)
{
    THTreeItem * pNewItem;
    THTreeItem * pChildLo;
    THTreeItem * pChildHi;
    unsigned char * WeightTable;
    unsigned int MaxWeight;                     // [ESP+10] - The greatest character found in table
 
    // Clear all pointers in HTree item array
    memset(ItemsByByte, 0, sizeof(ItemsByByte));
    MaxWeight = 0;
 
    // Ensure that the compression type is in range
    assert((CompressionType & 0x0F) <= 0x08);
    WeightTable  = WeightTables[CompressionType & 0x0F];
 
    // Build the linear list of entries that is sorted by byte weight
    for(unsigned int i = 0; i < 0x100; i++)
    {
        // Skip all the bytes which are zero.
        if(WeightTable[i] != 0)
        {
            // Create new tree item
            ItemsByByte[i] = pNewItem = CreateNewItem(i, WeightTable[i], InsertAfter);

            // We need to put the item to the right place in the list
            MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight);
        }
    }
 
    // Insert termination entries at the end of the list
    ItemsByByte[0x100] = CreateNewItem(0x100, 1, InsertBefore);
    ItemsByByte[0x101] = CreateNewItem(0x101, 1, InsertBefore);
 
    // Now we need to build the tree. We start at the last entry
    // and go backwards to the first one
    pChildLo = pLast;

    // Work as long as both children are valid
    // pChildHi is child with higher weight, pChildLo is the one with lower weight
    while(pChildLo != LIST_HEAD())
    {
        // Also get and verify the higher-weight child
        pChildHi = pChildLo->pPrev;
        if(pChildHi == LIST_HEAD())
            break;

        // Create new parent item for the children
        pNewItem = CreateNewItem(0, pChildHi->Weight + pChildLo->Weight, InsertAfter);

        // Link both child items to their new parent
        pChildLo->pParent = pNewItem;
        pChildHi->pParent = pNewItem;
        pNewItem->pChildLo = pChildLo;

        // Fixup the item's position by its weight
        MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight);

        // Get the previous lower-weight child
        pChildLo = pChildHi->pPrev;
    }

    // Initialize the MinValidValue to 1, which invalidates all quick-link items
    MinValidValue = 1;
}

void THuffmannTree::IncWeightsAndRebalance(THTreeItem * pItem)
{
    THTreeItem * pHigherItem;           // A previous item with greater or equal weight
    THTreeItem * pChildHi;              // The higher-weight child
    THTreeItem * pChildLo;              // The lower-weight child
    THTreeItem * pParent;
 
    // Climb up the tree and increment weight of each tree item
    for(; pItem != NULL; pItem = pItem->pParent)
    {
        // Increment the item's weight
        pItem->Weight++;

        // Find a previous item with equal or greater weight, which is not equal to this item
        pHigherItem = FindHigherOrEqualItem(pItem->pPrev, pItem->Weight);
        pChildHi = pHigherItem->pNext;

        // If the item is not equal to the tree item, we need to rebalance the tree
        if(pChildHi != pItem)
        {
            // Move the previous item to the RIGHT from the given item
            pChildHi->RemoveItem();
            LinkTwoItems(pItem, pChildHi);
            
            // Move the given item AFTER the greater-weight tree item
            pItem->RemoveItem();
            LinkTwoItems(pHigherItem, pItem);
     
            // We need to maintain the tree so that pChildHi->Weight is >= pChildLo->Weight.
            // Rebalance the tree accordingly.
            pChildLo = pChildHi->pParent->pChildLo;
            pParent = pItem->pParent;
            if(pParent->pChildLo == pItem)
                pParent->pChildLo = pChildHi;
            if(pChildLo == pChildHi)
                pChildHi->pParent->pChildLo = pItem;
            pParent = pItem->pParent;
            pItem->pParent = pChildHi->pParent;
            pChildHi->pParent = pParent;

            // Increment the global valid value. This invalidates all quick-link items.
            MinValidValue++;
        }
    }
}

void THuffmannTree::InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2)
{
    THTreeItem * pLastItem = pLast;
    THTreeItem * pChildHi;
    THTreeItem * pChildLo;

    // Create higher-weight child
    pChildHi = CreateNewItem(Value1, pLastItem->Weight, InsertBefore);
    pChildHi->pParent = pLastItem;
    ItemsByByte[Value1] = pChildHi;

    // Create lower-weight child
    pChildLo = CreateNewItem(Value2, 0, InsertBefore);
    pChildLo->pParent = pLastItem;
    pLastItem->pChildLo = pChildLo;
    ItemsByByte[Value2] = pChildLo;

    IncWeightsAndRebalance(pChildLo);
}

void THuffmannTree::EncodeOneByte(TOutputStream * os, THTreeItem * pItem)
{
    THTreeItem * pParent = pItem->pParent;
    unsigned int BitBuffer = 0;
    unsigned int BitCount = 0;

    // Put 1's as long as there is parent
    while(pParent != NULL)
    {
        // Fill the bit buffer
        BitBuffer = (BitBuffer << 1) | ((pParent->pChildLo != pItem) ? 1 : 0);
        BitCount++;

        // Move to the parent
        pItem = pParent;
        pParent = pParent->pParent;
    }

    // Write the bits to the output stream
    os->PutBits(BitBuffer, BitCount);
}

unsigned int THuffmannTree::DecodeOneByte(TInputStream * is)
{
    THTreeItem * pItemLink = NULL;
    THTreeItem * pItem;
    unsigned int ItemLinkIndex;
    unsigned int BitCount = 0;

    // Check for the end of the input stream
    if(is->pbInBuffer >= is->pbInBufferEnd && is->BitCount < 7)
        return 0x1FF;

    // Get the eventual quick-link index
    ItemLinkIndex = is->Peek7Bits();
    
    // Is the quick-link item valid?
    if(QuickLinks[ItemLinkIndex].ValidValue > MinValidValue)
    {
        // If that item needs less than 7 bits, we can get decompressed value directly
        if(QuickLinks[ItemLinkIndex].ValidBits <= 7)
        {
            is->SkipBits(QuickLinks[ItemLinkIndex].ValidBits);
            return QuickLinks[ItemLinkIndex].DecompressedValue;
        }

        // Otherwise we cannot get decompressed value directly
        // but we can skip 7 levels of tree parsing
        pItem = QuickLinks[ItemLinkIndex].pItem;
        is->SkipBits(7);
    }
    else
    {
        // Just a sanity check
        if(pFirst == LIST_HEAD())
            return 0x1FF;

        // We don't have the quick-link item, we need to parse the tree from its root
        pItem = pFirst;
    }

    // Step down the tree until we find a terminal item
    while(pItem->pChildLo != NULL)
    {
        // If the next bit in the compressed stream is set, we get the higher-weight
        // child. Otherwise, get the lower-weight child.
        pItem = is->Get1Bit() ? pItem->pChildLo->pPrev : pItem->pChildLo;
        BitCount++;

        // If the number of loaded bits reached 7,
        // remember the current item for storing into quick-link item array
        if(BitCount == 7)
            pItemLink = pItem;
    }

    // If we didn't get the item from the quick-link array,
    // set the entry in it
    if(QuickLinks[ItemLinkIndex].ValidValue < MinValidValue)
    {
        // If the current compressed byte was more than 7 bits,
        // set a quick-link item with pointer to tree item
        if(BitCount > 7)
        {
            QuickLinks[ItemLinkIndex].ValidValue = MinValidValue;
            QuickLinks[ItemLinkIndex].ValidBits = BitCount;
            QuickLinks[ItemLinkIndex].pItem = pItemLink;
        }
        else
        {
            // Limit the quick-decompress item to lower amount of bits
            ItemLinkIndex &= (0xFFFFFFFF >> (32 - BitCount));
            while(ItemLinkIndex < LINK_ITEM_COUNT)
            {
                // Fill the quick-decompress item
                QuickLinks[ItemLinkIndex].ValidValue = MinValidValue;
                QuickLinks[ItemLinkIndex].ValidBits  = BitCount;
                QuickLinks[ItemLinkIndex].DecompressedValue = pItem->DecompressedValue;

                // Increment the index
                ItemLinkIndex += (1 << BitCount);
            }
        }
    }

    // Return the decompressed value from the found item
    return pItem->DecompressedValue;
}

unsigned int THuffmannTree::Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int CompressionType)
{
    unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer;
    unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
    unsigned char * pbOutBuff = os->pbOutBuffer;
    unsigned char InputByte;
 
    BuildTree(CompressionType);
    bIsCmp0 = (CompressionType == 0);

    // Store the compression type into output buffer
    os->PutBits(CompressionType, 8);
 
    // Process the entire input buffer
    while(pbInBuffer < pbInBufferEnd)
    {
        // Get the (next) byte from the input buffer
        InputByte = *pbInBuffer++;

        // Do we have an item for such input value?
        if(ItemsByByte[InputByte] == NULL)
        {
            // Encode the relationship
            EncodeOneByte(os, ItemsByByte[0x101]);
 
            // Store the loaded byte into output stream
            os->PutBits(InputByte, 8);
 
            InsertNewBranchAndRebalance(pLast->DecompressedValue, InputByte);

            if(bIsCmp0)
            {
                IncWeightsAndRebalance(ItemsByByte[InputByte]);
                continue;
            }
  
            IncWeightsAndRebalance(ItemsByByte[InputByte]);
        }
        else
        {
            EncodeOneByte(os, ItemsByByte[InputByte]);
        }
 
        if(bIsCmp0)
        {
            IncWeightsAndRebalance(ItemsByByte[InputByte]);
        }
    }
 
    // Put the termination mark to the compressed stream
    EncodeOneByte(os, ItemsByByte[0x100]);
 
    // Flush the remaining bits
    os->Flush();
    return (unsigned int)(os->pbOutBuffer - pbOutBuff);
}
 
// Decompression using Huffman tree (1500E450)
unsigned int THuffmannTree::Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is)
{
    unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength;
    unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
    unsigned int DecompressedValue = 0;
    unsigned int CompressionType = 0;
   
    // Test the output length. Must not be NULL.
    if(cbOutLength == 0)
        return 0;
 
    // Get the compression type from the input stream
    CompressionType = is->Get8Bits();
    bIsCmp0 = (CompressionType == 0) ? 1 : 0;
 
    // Build the Huffman tree
    BuildTree(CompressionType);    
 
    // Process the entire input buffer until end of the stream
    while((DecompressedValue = DecodeOneByte(is)) != 0x100)
    {
        // Did an error occur?
        if(DecompressedValue == 0x1FF)          // An error occurred
            return 0;

        // Huffman tree needs to be modified
        if(DecompressedValue == 0x101)
        {
            // The decompressed byte is stored in the next 8 bits
            DecompressedValue = is->Get8Bits();

            InsertNewBranchAndRebalance(pLast->DecompressedValue, DecompressedValue);

            if(bIsCmp0 == 0)
                IncWeightsAndRebalance(ItemsByByte[DecompressedValue]);
        }
 
        // A byte successfully decoded - store it in the output stream
        *pbOutBuffer++ = (unsigned char)DecompressedValue;
        if(pbOutBuffer >= pbOutBufferEnd)
            break;
 
        if(bIsCmp0)
        {
            IncWeightsAndRebalance(ItemsByByte[DecompressedValue]);
        }
    }
 
    return (unsigned int)(pbOutBuffer - (unsigned char *)pvOutBuffer);
}

