/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
// Filename : OLZW.CPP
// Description : LZW compression and decompression
#include
#include
#include
// --------- define constant ------------//
#define BITS 15
#define MAX_CODE ( ( 1 << BITS ) - 1 )
#define TABLE_SIZE 35023L
#define END_OF_STREAM 256
#define BUMP_CODE 257
#define FLUSH_CODE 258
#define FIRST_CODE 259
#define UNUSED 0xffff
static unsigned short BITS_MASK[] =
{
0x0000,
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff,
};
// --------- define macro ----------//
#define DICT( i ) dict[i]
// --------- define struct Dictionary ---------//
struct Dictionary
{
unsigned short code_value;
unsigned short parent_code;
unsigned char character;
};
BitStream::BitStream() : bit_offset(0)
{
}
BitStream::~BitStream()
{
}
unsigned short BitStream::input_bits(unsigned stringLen)
{
bit_offset += stringLen;
return 0;
}
void BitStream::output_bits(unsigned short stringCode, unsigned stringLen)
{
bit_offset += stringLen;
}
BitMemStream::BitMemStream(unsigned char *p) : BitStream(), bytePtr(p)
{
}
unsigned short BitMemStream::input_bits(unsigned stringLen)
{
unsigned char *p = bytePtr + bit_offset / 8;
int s = bit_offset % 8;
// get longer bits
unsigned long ul = *(unsigned long *)p >> s;
(void) BitStream::input_bits(stringLen);
// mask off leading bits
return (unsigned short)ul & BITS_MASK[stringLen];
}
void BitMemStream::output_bits(unsigned short stringCode, unsigned stringLen)
{
// fill low bit first
unsigned char *p = bytePtr + bit_offset / 8;
int s = bit_offset % 8;
// mask off unused bit
*(unsigned long *)p &= BITS_MASK[s];
// stringCode &= bitMask[stringCode];
// shift stringCode and put them to outPtr
*(unsigned long *)p |= (unsigned long)stringCode << s;
BitStream::output_bits(stringCode, stringLen);
}
BitFileRead::BitFileRead(File *f) : filePtr(f), last_offset(0)
{
f->file_read(&residue, sizeof(residue));
}
unsigned short BitFileRead::input_bits(unsigned stringLen)
{
if( bit_offset + stringLen > (last_offset+sizeof(residue))*8 )
{
// find byte to read
long byteFetch = bit_offset/8 - last_offset;
if( byteFetch >= sizeof(residue) ) // residue >>= 32 does not change to 0
residue = 0;
else
residue >>= 8*byteFetch;
filePtr->file_read( sizeof(residue)-byteFetch+(unsigned char *)&residue, byteFetch );
last_offset += byteFetch;
}
err_when( bit_offset + stringLen > (last_offset+sizeof(residue))*8 );
int s = bit_offset - last_offset*8;
unsigned long ul = residue >> s;
(void) BitStream::input_bits(stringLen);
// mask off leading bits
return (unsigned short)ul & BITS_MASK[stringLen];
}
void BitFileRead::output_bits(unsigned short stringCode, unsigned stringLen)
{
err_here();
BitStream::output_bits(stringCode, stringLen);
}
BitFileWrite::BitFileWrite(File *f) : filePtr(f), residue(0), residue_len(0)
{
}
BitFileWrite::~BitFileWrite()
{
// flush output
filePtr->file_write(&residue, sizeof(residue));
}
unsigned short BitFileWrite::input_bits(unsigned stringLen)
{
err_here();
return BitStream::input_bits(stringLen);
}
void BitFileWrite::output_bits(unsigned short stringCode, unsigned stringLen)
{
if( residue_len + stringLen > sizeof(residue)*8 )
{
// number of byte to write, is residue_len / 8
int byteFlush = residue_len / 8;
filePtr->file_write( &residue, byteFlush );
if( byteFlush >= sizeof(residue)) // if byteFlush == 4, residue >>= 32 does not set residue to 0
residue = 0;
else
residue >>= byteFlush * 8;
residue_len -= byteFlush * 8;
}
err_when( residue_len + stringLen > sizeof(residue)*8 );
residue |= (unsigned long) stringCode << residue_len;
residue_len += stringLen;
BitStream::output_bits(stringCode, stringLen);
}
// --------- begin of function Lzw::Lzw -------------//
Lzw::Lzw()
{
dict = NULL;
decode_stack = NULL;
}
// --------- end of function Lzw::Lzw -------------//
// --------- begin of function Lzw::~Lzw -------------//
Lzw::~Lzw()
{
free_storage();
}
// --------- end of function Lzw::~Lzw -------------//
// --------- begin of function Lzw::initialize_storage -------------//
void Lzw::initialize_storage()
{
if( !dict )
dict = (Dictionary *) mem_add(sizeof(Dictionary) * TABLE_SIZE);
if( !decode_stack )
decode_stack = (unsigned char *)mem_add(sizeof(unsigned char) * TABLE_SIZE);
}
// --------- end of function Lzw::initialize_storage -------------//
// --------- begin of function Lzw::free_storage -------------//
void Lzw::free_storage()
{
if( decode_stack )
{
mem_del(decode_stack);
decode_stack = NULL;
}
if( dict )
{
mem_del(dict);
dict = NULL;
}
}
// --------- end of function Lzw::free_storage -------------//
// --------- begin of function Lzw::initialize_dictionary -------------//
void Lzw::initialize_dictionary()
{
unsigned short i;
for ( i = 0 ; i < TABLE_SIZE ; i++ )
DICT( i ).code_value = UNUSED;
next_code = FIRST_CODE;
// putc( 'F', stdout );
current_code_bits = 9;
next_bump_code = 511;
}
// --------- end of function Lzw::initialize_dictionary -------------//
// nul output, to find output size in bits
long Lzw::compress( unsigned char *inPtr, long inByteLen)
{
BitStream nulStream;
return basic_compress( inPtr, inByteLen, &nulStream);
}
// compressed data in memory
long Lzw::compress( unsigned char *inPtr, long inByteLen, unsigned char *outPtr)
{
BitMemStream memStream(outPtr);
return basic_compress( inPtr, inByteLen, &memStream);
}
// set outPtr to NULL to find the decompressed size
long Lzw::expand( unsigned char *inPtr, long inBitLen, unsigned char *outPtr)
{
BitMemStream memStream(inPtr);
return basic_expand( &memStream, outPtr );
}
// compressed data in file
long Lzw::compress( unsigned char *inPtr, long inByteLen, File *outFile)
{
BitFileWrite fileStream(outFile);
return basic_compress( inPtr, inByteLen, &fileStream);
}
// set outPtr to NULL to find the decompressed size
long Lzw::expand( File *inFile, unsigned char *outPtr)
{
BitFileRead fileStream(inFile);
return basic_expand( &fileStream, outPtr);
}
// --------- begin of function Lzw::basic_compress -------------//
// compress a memory block to another memory block
// inPtr address of decompressed input data
// inByteLen length of input data (in byte)
// outStream output stream, BitMemStream or BitFileWrite
//
// return in no. of bits, the size of the compressed data
// call free_storage after compress to free allocated space, if it will
// not going to compress/decompress soon
long Lzw::basic_compress( unsigned char *inPtr, long inByteLen, BitStream *outStream)
{
unsigned char character;
unsigned short stringCode;
unsigned short index;
initialize_storage();
initialize_dictionary();
if ( inByteLen == 0 )
stringCode = END_OF_STREAM;
else
{
stringCode = *inPtr++;
inByteLen--;
}
while ( inByteLen-- > 0)
{
character = *inPtr++;
index = find_child_node( stringCode, character );
if ( DICT( index ).code_value != UNUSED )
stringCode = DICT( index ).code_value;
else
{
DICT( index ).code_value = next_code++;
DICT( index ).parent_code = stringCode;
DICT( index ).character = character;
outStream->output_bits( stringCode, current_code_bits );
stringCode = character;
if ( next_code > MAX_CODE )
{
outStream->output_bits( FLUSH_CODE, current_code_bits );
initialize_dictionary();
}
else if ( next_code > next_bump_code )
{
outStream->output_bits( BUMP_CODE, current_code_bits );
current_code_bits++;
next_bump_code <<= 1;
next_bump_code |= 1;
}
}
}
outStream->output_bits( stringCode, current_code_bits );
outStream->output_bits( END_OF_STREAM, current_code_bits);
// free_storage();
return outStream->bit_offset;
}
// --------- end of function Lzw::basic_compress -------------//
// --------- begin of function Lzw::basic_expand -------------//
// decompress a memory block to another memory block
// inStream bit stream, can be BitMemStream or BitFileRead
// outPtr address of decompressed output data
// (NULL to find the size of decompressed data)
//
// return the no. of byte of the decompressed data
// call free_storage after decompress to free allocated space, if it will
// not going to compress/decompress soon
long Lzw::basic_expand( BitStream *inStream, unsigned char *outPtr)
{
unsigned short newCode;
unsigned short oldCode;
unsigned char character;
unsigned int count;
long outByteLen = 0;
initialize_storage();
for ( ; ; )
{
initialize_dictionary();
oldCode = inStream->input_bits( current_code_bits );
if ( oldCode == END_OF_STREAM )
{
// free_storage();
return outByteLen;
}
character = (unsigned char) oldCode;
if( outPtr )
outPtr[outByteLen] = character;
outByteLen++;
for ( ; ; )
{
newCode = inStream->input_bits( current_code_bits );
if ( newCode == END_OF_STREAM )
{
// free_storage();
return outByteLen;
}
if ( newCode == FLUSH_CODE )
break;
if ( newCode == BUMP_CODE )
{
current_code_bits++;
continue;
}
if ( newCode >= next_code )
{
decode_stack[ 0 ] = character;
count = decode_string( 1, oldCode );
}
else
count = decode_string( 0, newCode );
character = decode_stack[ count - 1 ];
if( outPtr )
{
while ( count > 0 )
outPtr[outByteLen++] = decode_stack[ --count ];
}
else
{
outByteLen += count;
}
err_when( next_code >= TABLE_SIZE);
DICT( next_code ).parent_code = oldCode;
DICT( next_code ).character = character;
next_code++;
oldCode = newCode;
}
}
// free_storage();
return outByteLen;
}
// --------- end of function Lzw::basic_expand -------------//
// --------- begin of function Lzw::find_child_node -------------//
//
// This hashing routine is responsible for finding the table location
// for a string/character combination. The table index is created
// by using an exclusive OR combination of the prefix and character.
// This code also has to check for collisions, and handles them by
// jumping around in the table.
//
unsigned short Lzw::find_child_node( unsigned short parentCode, unsigned char childChar )
{
unsigned short index;
unsigned short offset;
index = ( (unsigned short) childChar << ( BITS - 8 ) ) ^ parentCode;
if ( index == 0 )
offset = 1;
else
offset = TABLE_SIZE - index;
for ( ; ; )
{
if ( DICT( index ).code_value == UNUSED )
return( index );
if ( DICT( index ).parent_code == parentCode &&
DICT( index ).character == childChar )
return( index );
if ( index >= offset )
index -= offset;
else
index += TABLE_SIZE - offset;
}
}
// --------- end of function Lzw::find_child_node -------------//
// --------- begin of function Lzw::decode_string -------------//
//
// This routine decodes a string from the dictionary, and stores it
// in the decode_stack data structure. It returns a count to the
// calling program of how many characters were placed in the stack.
//
unsigned int Lzw::decode_string( unsigned int count, unsigned short code )
{
unsigned short initCode = code;
while ( code > 255 )
{
err_when(count >= TABLE_SIZE);
decode_stack[ count++ ] = DICT( code ).character;
code = DICT( code ).parent_code;
}
decode_stack[ count++ ] = (char) code;
return( count );
}
// --------- end of function Lzw::decode_string -------------//
/*
// --------- begin of function Lzw::output_bits -------------//
//
// if outPtr is null to find the compressed length
// make sure outPtr buffer is long enough
//
void Lzw::output_bits( unsigned char *outPtr, long &outLen, unsigned short stringCode, unsigned int stringLen )
{
// fill low bit first
if( outPtr )
{
outPtr += outLen / 8;
int s = outLen % 8;
int r = 8 - s;
// mask off unused bit
*(unsigned long *)outPtr &= bitMask[s];
// stringCode &= bitMask[stringCode];
// shift stringCode and put them to outPtr
*(unsigned long *)outPtr |= (unsigned long)stringCode << s;
}
outLen += stringLen;
}
// --------- end of function Lzw::output_bits -------------//
// --------- begin of function Lzw::input_bits -------------//
// inPtr is allocated at least 32 bits longer than inCnt
unsigned short Lzw::input_bits( unsigned char *inPtr, long &inCnt, unsigned int stringLen )
{
inPtr += inCnt / 8;
int s = inCnt % 8;
// get longer bits
unsigned long ul = *(unsigned long *)inPtr;
ul >>= s;
inCnt += stringLen;
// mask off leading bits
return (unsigned short)ul & BITS_MASK[stringLen];
}
// --------- end of function Lzw::input_bits -------------//
*/