//
// General Data Compression
//

#ifndef _H_GDC
#define _H_GDC


//
//
// CONSTANTS
//
//


//
// Scratch buffer mutex
//
#define GDC_MUTEX_NAME "GDCMutex"


//
// Compression Types (bit flags)
//
#define GCT_NOCOMPRESSION    0x0000
#define GCT_PKZIP            0x0001
#define GCT_PERSIST_PKZIP    0x0002
#define GCT_DEFAULT          (GCT_PKZIP | GCT_PERSIST_PKZIP)

//
// Compression Options for GCT_PKZIP
//
#define GDCCO_MAXSPEED        0
#define GDCCO_MAXCOMPRESSION  1



//
// Data sizes used to determine the saved dictionary space in our work 
// buffer.
//
#define GDC_DATA_SMALL          1024
#define GDC_DATA_MEDIUM         2048
#define GDC_DATA_MAX            4096


//
// Persistent Dictionaries used for compression/decompression
//
enum
{
    GDC_DICT_UPDATES = 0,
    GDC_DICT_MISC,
    GDC_DICT_INPUT,
    GDC_DICT_COUNT
};


typedef struct tagGDC_DICTIONARY
{
    UINT        cbUsed;                     // Amount of saved data
    BYTE        pData[GDC_DATA_MAX];      // Saved uncompressed data
} GDC_DICTIONARY;
typedef GDC_DICTIONARY * PGDC_DICTIONARY;


//
// Byte runs that can be replaced with smaller bit sequences
//
#define GDC_MINREP              2
#define GDC_MAXREP              (GDC_MINREP+(8*1)+2+4+8+16+32+64+128+256-4)
// GDC_MAXREP is 516, 129*4


//
// Holds uncompressed data for both compression/decompression
//
#define GDC_UNCOMPRESSED        (GDC_MAXREP + 2*GDC_DATA_MAX)

// 
// We don't need to double-buffer compressed data--we just read it out
// of the caller's source or write it into the caller's dest directly.
//
// NOTE:  With real PKZIP, which mostly reads from/writes to files,
// they don't have memory pointers containing raw data already.  That's
// whe original code we got used Read/Write routine callbacks.  This is
// no longer necessary.
//



//
// Random, little understood PKZIP table values, codes
//
#define KMP_THRESHOLD       10


#define GDC_LIT_SIZE        (256 + GDC_MAXREP + 2) 
// GDC_LIT_SIZE is 774


// EOF is last index of Lit array
#define EOF_CODE            (GDC_LIT_SIZE-1)
#define ABORT_CODE          (EOF_CODE+1)


//
// EXT_DIST_BITS is the # of bits needed to store an index into a GDC_DIST_SIZE
// array.  That's defined to be 64, which is 2^6, hence 6 bits.  Smaller
// dictionary compressions use fewer bits and hence not all of the DIST
// items.  The mask
// is used to pull the 6-bit sized index out of a byte.
//
#define GDC_DIST_SIZE               64

#define EXT_DIST_BITS_MIN           4
#define EXT_DIST_BITS_MEDIUM        5
#define EXT_DIST_BITS_MAC           6


#define GDC_LEN_SIZE                16
#define GDC_DECODED_SIZE            256


//
// The hash function has 4*256+5*256 different values, which means
// we need that many entries in our hash array.
//
#define GDC_HASHFN(x)               (4*(x)[0] + 5*(x)[1])
#define GDC_HASH_SIZE               (4*256 + 5*256)





//
// Structure:   GDC_IMPLODE
//
// Workspace for compressing our data.  We have simplified and shrunk this
// structure a fair amount, by having constant code/bit tables and not
// double-buffering the compressed result.  PKZIP's implode calculates the
// LitBits & LitCodes every time through (rather than storing 2 774 byte
// arrays in data--which would be a pain to declare anyway!), and makes a 
// private copy of the DistBits & DistCodes.  
//

typedef struct tagGDC_IMPLODE
{
    //
    // NO SOURCE INFO--we copy source chunks and maybe dictionary into 
    // RawData.  Then at the end we copy RawData back into the dictionary
    // if there is one.
    //

    //
    // Destination info
    //
    LPBYTE              pDst;       // Current Dest ptr (advances as we write)
    UINT                cbDst;      // Amount of Dest left (shrinks as we write)
    UINT                iDstBit;    // Current bit pos in Current Dest ptr byte

    //
    // Compression info
    //
    UINT                cbDictSize;
    UINT                cbDictUsed;
    UINT                ExtDistBits;
    UINT                ExtDistMask;

    //
    // Working info
    //
    UINT                Distance;            
    UINT                ibRawData;

    // NOTE: GDC_UNCOMPRESSED is a multiple of 4
    BYTE                RawData[GDC_UNCOMPRESSED];

    // NOTE:  This is DWORD aligned (GDC_MAXREP is a multiple of 4
    // and the extra 2 WORDS == 1 DWORD
    short               Next[2 + GDC_MAXREP];

    // NOTE: GDC_UNCOMPRESED is a multiple of 4
    WORD                SortArray[GDC_UNCOMPRESSED];

    // NOTE: This is DWORD aligned since GDC_HASH_SIZE is a multiple of 4
    WORD                HashArray[GDC_HASH_SIZE];
} GDC_IMPLODE, * PGDC_IMPLODE;



//
// GDC_EXPLODE
// Workspace for uncompressing our data.  We have vastly simplified and
// shrunk this structure as per the comments for GDC_IMPLODE.
//

typedef struct tagGDC_EXPLODE
{
    //
    // Source info
    //
    LPBYTE              pSrc;       // Current Src ptr (advances as we read)
    UINT                cbSrc;      // Amount of Src left (shrinks as we read)
    UINT                SrcByte;    // Look ahead byte in source
    UINT                SrcBits;    // Remainded src bits

    //
    // NO DEST INFO--we copy maybe dictionary into RawData at the beginning.
    // Then at the end we maybe copy RawData back into the dictionary.
    //

    //
    // Compression info
    //
    UINT                ExtDistBits;
    UINT                ExtDistMask;
    UINT                cbDictUsed;

    UINT                Distance;   
    UINT                iRawData;   // Current index into RawData
    BYTE                RawData[GDC_UNCOMPRESSED];
} GDC_EXPLODE, *PGDC_EXPLODE;


#define GDC_WORKBUF_SIZE    max(sizeof(GDC_IMPLODE), sizeof(GDC_EXPLODE))



//
// EXTERNAL FUNCTIONS
//

//
// API FUNCTION: GDC_Init()
//
// DESCRIPTION:
//
// Initialises the General Data Compressor.
// Must be called before any other GDC functions.
//
// PARAMETERS:
//
//
//
void GDC_Init(void);



//
// FUNCTION: GDC_Compress(..)
//
// DESCRIPTION:
//
// Compresses source data into a destination buffer.
//
//
// PARAMETERS:
//
// pDictionary              - NULL if old PKZIP, valid ptr if persistent
//
// Options                  - specifies whether speed of compression or
//      size of the compressed data is the most important factor.  This
//      basically affects the amount of previous data saved for looking
//      backwards.  MAXSPEED means smaller dictionary.  MAXCOMPRESSION
//      means a bigger one.  The dictionary size is basically the amount
//      of overlap in the source data used when calculating the hash
//      index.
//
//   GDCCO_MAXSPEED         - compress the data as quickly as possible, at
//                            the expense of increased compressed data size
//
//   GDCCO_MAXCOMPRESSION   - compress the data as much as possible, at the
//                            expense of increased compression time.
// With a persistent dictionary, only GDCCO_MAXCOMPRESSION is meaningful.
//
// pSrc                     - pointer to the source (uncompressed) data.
//
// cbSrcSize                - the number of bytes of source.
//
// pDst                     - pointer to the destination, where the
//                            compressed result will go.
//
// pcbDstSize               - pointer to the maximum amount the destina-
//                            tion can hold.  If the compressed result ends
//                            up being bigger than this amount, we bail
//                            out and don't compress the source at all.
//                            Otherwise the resulting size is written back.
//
// RETURNS:
//
// TRUE if success, FALSE if failure.
//
//
BOOL GDC_Compress
(
    PGDC_DICTIONARY     pDictionary,
    UINT                Options,
    LPBYTE              pWorkBuf,
    LPBYTE              pSrc,
    UINT                cbSrcSize,
    LPBYTE              pDst,
    UINT *              pcbDstSize
);


//
// API FUNCTION: GDC_Decompress(..)
//
// DESCRIPTION:
//
// Decompresses source data into a destination buffer.
//
//
// PARAMETERS:
//
// pDictionary              - NULL if old PKZIP, ptr to saved data if
//                            persistent.
//
// pSrc                     - pointer to the source (compressed) data.
//
// cbSrcSize                - the number of bytes of source.
//
// pDst                     - pointer to the destination, where the
//                            uncompressed result will go.
//
// pcbDstSize               - pointer to the maximum amount the desina-
//                            tion can hold.  If the uncompressed result
//                            ends up being bigger than this amount, we
//                            bail out since we can't decompress it.
//                            Otherwise the resulting size is written back.
//
// RETURNS:
//
// TRUE if success, FALSE if failure.

//
BOOL GDC_Decompress
(
    PGDC_DICTIONARY     pDictionary,
    LPBYTE              pWorkBuf,
    LPBYTE              pSrc,
    UINT                cbSrcSize,
    LPBYTE              pDst,
    UINT *              pcbDstSize
);




//
// INTERNAL FUNCTIONS
//


void GDCCalcDecode(const BYTE * pBits, const BYTE * pCodes, UINT size, LPBYTE pDecode);

LPBYTE GDCGetWorkBuf(void);
void   GDCReleaseWorkBuf(LPBYTE);


UINT GDCFindRep(PGDC_IMPLODE pgdcImp, LPBYTE Start);

void GDCSortBuffer(PGDC_IMPLODE pgdcImp, LPBYTE low, LPBYTE hi);

BOOL GDCOutputBits(PGDC_IMPLODE pgdcImp, WORD Cnt, WORD Code);



UINT GDCDecodeLit(PGDC_EXPLODE);

UINT GDCDecodeDist(PGDC_EXPLODE pgdcExp, UINT Len);

BOOL GDCWasteBits(PGDC_EXPLODE pgdcExp, UINT Bits);


#endif // _H_GDC