mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-16 14:35:06 -05:00
b3e299dbde
Some modifications to handle backslashes and forward slashes, along with some optimizations to speed up OTR generation.
283 lines
10 KiB
C++
283 lines
10 KiB
C++
/*****************************************************************************/
|
|
/* 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) */
|
|
/*****************************************************************************/
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
#include "sparse.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public functions
|
|
|
|
void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
|
{
|
|
unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
|
|
unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer;
|
|
unsigned char * pbLastNonZero = (unsigned char *)pvInBuffer;
|
|
unsigned char * pbOutBuffer0 = (unsigned char *)pvOutBuffer;
|
|
unsigned char * pbInBuffPtr = (unsigned char *)pvInBuffer;
|
|
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
|
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
|
size_t NumberOfNonZeros;
|
|
size_t NumberOfZeros;
|
|
|
|
// There must be at least 4 bytes of free space in the output buffer now
|
|
if((pbInBuffer + 4) >= pbInBufferEnd)
|
|
return;
|
|
|
|
// Put the original data length (in little endian)
|
|
*pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x18);
|
|
*pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x10);
|
|
*pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x08);
|
|
*pbOutBuffer++ = (unsigned char)(cbInBuffer >> 0x00);
|
|
|
|
// If there is at least 3 bytes in the input buffer, do this loop
|
|
while(pbInBuffer < (pbInBufferEnd - 3))
|
|
{
|
|
// Reset the zero count and frontal pointer
|
|
pbLastNonZero = pbInBuffer;
|
|
pbInBuffPtr = pbInBuffer;
|
|
NumberOfZeros = 0;
|
|
|
|
if(pbInBuffPtr < pbInBufferEnd)
|
|
{
|
|
do
|
|
{
|
|
// Count number of zeros
|
|
if(*pbInBuffPtr == 0)
|
|
{
|
|
NumberOfZeros++;
|
|
}
|
|
else
|
|
{
|
|
// Were there at least 3 zeros before? If yes, we need to flush the data
|
|
if(NumberOfZeros >= 3)
|
|
break;
|
|
pbLastNonZero = pbInBuffPtr + 1;
|
|
NumberOfZeros = 0;
|
|
}
|
|
}
|
|
while(++pbInBuffPtr < pbInBufferEnd);
|
|
}
|
|
|
|
// Get number of nonzeros that we found so far and flush them
|
|
NumberOfNonZeros = pbLastNonZero - pbInBuffer;
|
|
if(NumberOfNonZeros != 0)
|
|
{
|
|
// Process blocks that are longer than 0x81 nonzero bytes
|
|
while(NumberOfNonZeros > 0x81)
|
|
{
|
|
// Verify if we still have enough space in output buffer
|
|
if((pbOutBuffer + 0x81) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "0x80 of nonzeros"
|
|
*pbOutBuffer++ = 0xFF;
|
|
memcpy(pbOutBuffer, pbInBuffer, 0x80);
|
|
|
|
// Adjust counter of nonzeros and both pointers
|
|
NumberOfNonZeros -= 0x80;
|
|
pbOutBuffer += 0x80;
|
|
pbInBuffer += 0x80;
|
|
}
|
|
|
|
// BUGBUG: The following code will be triggered if the NumberOfNonZeros
|
|
// was 0x81 before. It will copy just one byte. This seems like a bug to me,
|
|
// but since I want StormLib to be exact like Blizzard code is, I will keep
|
|
// it that way here
|
|
if(NumberOfNonZeros > 0x80)
|
|
{
|
|
// Verify if we still have enough space in output buffer
|
|
if((pbOutBuffer + 2) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "1 nonzero byte"
|
|
*pbOutBuffer++ = 0x80;
|
|
memcpy(pbOutBuffer, pbInBuffer, 1);
|
|
|
|
// Adjust counter of nonzeros and both pointers
|
|
NumberOfNonZeros--;
|
|
pbOutBuffer++;
|
|
pbInBuffer++;
|
|
}
|
|
|
|
// If there is 1 nonzero or more, put the block
|
|
if(NumberOfNonZeros >= 0x01)
|
|
{
|
|
// Verify if we still have enough space in output buffer
|
|
if((pbOutBuffer + NumberOfNonZeros + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "Several nonzero bytes"
|
|
*pbOutBuffer++ = (unsigned char)(0x80 | (NumberOfNonZeros - 1));
|
|
memcpy(pbOutBuffer, pbInBuffer, NumberOfNonZeros);
|
|
|
|
// Adjust pointers
|
|
pbOutBuffer += NumberOfNonZeros;
|
|
pbInBuffer += NumberOfNonZeros;
|
|
}
|
|
}
|
|
|
|
// Now flush all zero bytes
|
|
while(NumberOfZeros > 0x85)
|
|
{
|
|
// Do we have at least 2 bytes in the output buffer ?
|
|
if((pbOutBuffer + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "0x82 zeros"
|
|
*pbOutBuffer++ = 0x7F;
|
|
|
|
// Adjust zero counter and input pointer
|
|
NumberOfZeros -= 0x82;
|
|
pbInBuffer += 0x82;
|
|
}
|
|
|
|
// If we got more than 0x82 zeros, flush 3 of them now
|
|
if(NumberOfZeros > 0x82)
|
|
{
|
|
// Do we have at least 2 bytes in the output buffer ?
|
|
if((pbOutBuffer + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "0x03 zeros"
|
|
*pbOutBuffer++ = 0;
|
|
|
|
// Adjust zero counter and input pointer
|
|
NumberOfZeros -= 0x03;
|
|
pbInBuffer += 0x03;
|
|
}
|
|
|
|
// Is there at least three zeros ?
|
|
if(NumberOfZeros >= 3)
|
|
{
|
|
// Do we have at least 2 bytes in the output buffer ?
|
|
if((pbOutBuffer + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Put marker that means "Several zeros"
|
|
*pbOutBuffer++ = (unsigned char)(NumberOfZeros - 3);
|
|
|
|
// Adjust pointer
|
|
pbInBuffer += NumberOfZeros;
|
|
}
|
|
}
|
|
|
|
// Flush last three bytes
|
|
if(pbInBuffer < pbInBufferEnd)
|
|
{
|
|
pbInBuffPtr = pbInBuffer;
|
|
|
|
for(;;)
|
|
{
|
|
if(*pbInBuffPtr++ != 0)
|
|
{
|
|
// Get number of bytes remaining
|
|
NumberOfNonZeros = (pbInBufferEnd - pbInBuffer);
|
|
|
|
// Not enough space in the output buffer ==> exit
|
|
if((pbOutBuffer + NumberOfNonZeros + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Terminate with a marker that means "0x80 of nonzeros"
|
|
*pbOutBuffer++ = 0xFF;
|
|
memcpy(pbOutBuffer, pbInBuffer, NumberOfNonZeros);
|
|
|
|
// Adjust pointer
|
|
pbOutBuffer += NumberOfNonZeros;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Is there are more chars in the input buffer
|
|
if(pbInBuffPtr < pbInBufferEnd)
|
|
continue;
|
|
|
|
// If the compression will not compress it by even 1 byte, do nothing
|
|
if((pbOutBuffer + 1) >= pbOutBufferEnd)
|
|
return;
|
|
|
|
// Terminate with a chunk that means "0x82 of zeros"
|
|
*pbOutBuffer++ = 0x7F;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Out the length of the output buffer
|
|
*pcbOutBuffer = (int)(pbOutBuffer - pbOutBuffer0);
|
|
}
|
|
|
|
int DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
|
{
|
|
unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer;
|
|
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
|
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
|
unsigned int cbChunkSize;
|
|
unsigned int cbOutBuffer = 0;
|
|
unsigned int OneByte;
|
|
|
|
// Don't decompress anything that is shorter than 5 bytes
|
|
if(cbInBuffer < 5)
|
|
return 0;
|
|
|
|
// Get the 32-bits from the input stream
|
|
OneByte = *pbInBuffer++;
|
|
cbOutBuffer |= (OneByte << 0x18);
|
|
OneByte = *pbInBuffer++;
|
|
cbOutBuffer |= (OneByte << 0x10);
|
|
OneByte = *pbInBuffer++;
|
|
cbOutBuffer |= (OneByte << 0x08);
|
|
OneByte = *pbInBuffer++;
|
|
cbOutBuffer |= (OneByte << 0x00);
|
|
|
|
// Verify the size of the stream against the output buffer size
|
|
if(cbOutBuffer > (unsigned int)*pcbOutBuffer)
|
|
return 0;
|
|
|
|
// Put the output size to the buffer
|
|
*pcbOutBuffer = cbOutBuffer;
|
|
|
|
// Process the input buffer
|
|
while(pbInBuffer < pbInBufferEnd)
|
|
{
|
|
// Get (next) byte from the stream
|
|
OneByte = *pbInBuffer++;
|
|
|
|
// If highest bit, it means that that normal data follow
|
|
if(OneByte & 0x80)
|
|
{
|
|
cbChunkSize = (OneByte & 0x7F) + 1;
|
|
cbChunkSize = (cbChunkSize < cbOutBuffer) ? cbChunkSize : cbOutBuffer;
|
|
memcpy(pbOutBuffer, pbInBuffer, cbChunkSize);
|
|
pbInBuffer += cbChunkSize;
|
|
}
|
|
else
|
|
{
|
|
cbChunkSize = (OneByte & 0x7F) + 3;
|
|
cbChunkSize = (cbChunkSize < cbOutBuffer) ? cbChunkSize : cbOutBuffer;
|
|
memset(pbOutBuffer, 0, cbChunkSize);
|
|
}
|
|
|
|
// Increment output buffer pointer
|
|
pbOutBuffer += cbChunkSize;
|
|
cbOutBuffer -= cbChunkSize;
|
|
}
|
|
|
|
return 1;
|
|
}
|