refactor: replace custom MPQ decompression implementation with StormLib.

This commit is contained in:
aomizu 2025-12-23 09:34:11 +09:00
parent b528deaf11
commit 872bfe024a
13 changed files with 81 additions and 2858 deletions

View File

@ -29,6 +29,33 @@ include(lib/system/cmake/system.cmake)
find_package(ZLIB REQUIRED)
find_package(BZip2 REQUIRED)
include(ExternalProject)
set(STORMLIB_SOURCE_DIR ${CMAKE_SOURCE_DIR}/lib/stormlib)
set(STORMLIB_BINARY_DIR ${CMAKE_BINARY_DIR}/stormlib)
set(STORMLIB_LIBRARY ${STORMLIB_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}storm${CMAKE_STATIC_LIBRARY_SUFFIX})
ExternalProject_Add(stormlib_external
SOURCE_DIR ${STORMLIB_SOURCE_DIR}
BINARY_DIR ${STORMLIB_BINARY_DIR}
BUILD_BYPRODUCTS ${STORMLIB_LIBRARY}
INSTALL_COMMAND ""
CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DSTORM_BUILD_TESTS=OFF
-DSTORM_SKIP_INSTALL=ON
-DBUILD_SHARED_LIBS=OFF
-DSTORM_USE_BUNDLED_LIBRARIES=OFF
-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
)
add_library(stormlib STATIC IMPORTED)
set_target_properties(stormlib PROPERTIES
IMPORTED_LOCATION ${STORMLIB_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${STORMLIB_SOURCE_DIR}/src
)
add_dependencies(stormlib stormlib_external)
# Some templates abuse offsetof
if(WHOA_SYSTEM_LINUX OR WHOA_SYSTEM_MAC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof")

View File

@ -1,7 +1,5 @@
file(GLOB PRIVATE_SOURCES
"*.cpp"
"mpq/*.cpp"
"mpq/*.c"
)
if(WHOA_SYSTEM_MAC)
@ -21,7 +19,6 @@ add_library(util STATIC
target_include_directories(util
PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/util/mpq
${ZLIB_INCLUDE_DIRS}
${BZIP2_INCLUDE_DIRS}
)
@ -32,11 +29,16 @@ target_link_libraries(util
lua-5.1
common
storm
stormlib
tempest
${ZLIB_LIBRARIES}
${BZIP2_LIBRARIES}
)
if(TARGET stormlib_external)
add_dependencies(util stormlib_external)
endif()
if(WHOA_SYSTEM_LINUX OR WHOA_SYSTEM_MAC)
target_link_libraries(util
PRIVATE

View File

@ -1,21 +1,22 @@
#include "util/Mpq.hpp"
#include "util/mpq/Decompress.hpp"
#include <StormLib.h>
#include <algorithm>
#include <cctype>
#include <cstring>
#include <fstream>
#include <limits>
#include <string>
#include <vector>
#include <sys/stat.h>
namespace {
constexpr uint32_t ID_MPQ = 0x1A51504D;
constexpr uint32_t ID_MPQ_USERDATA = 0x1B51504D;
constexpr uint32_t kMpqHeaderId = 0x1A51504D;
constexpr uint32_t kMpqUserDataId = 0x1B51504D;
constexpr uint32_t MPQ_FILE_IMPLODE = 0x00000100;
constexpr uint32_t MPQ_FILE_COMPRESSED = 0x00000200;
constexpr uint32_t MPQ_FILE_ENCRYPTED = 0x00010000;
constexpr uint32_t MPQ_FILE_SINGLE_UNIT = 0x01000000;
constexpr uint32_t kMpqFileImplode = 0x00000100;
constexpr uint32_t kMpqFileCompressed = 0x00000200;
constexpr uint32_t kMpqFileEncrypted = 0x00010000;
constexpr uint32_t kMpqFileSingleUnit = 0x01000000;
#pragma pack(push, 1)
struct MpqUserDataHeader {
@ -142,6 +143,38 @@ namespace {
return 0;
}
bool DecompressBlock(const uint8_t* in, size_t inSize, uint8_t* out, size_t outSize) {
if (inSize == outSize) {
std::memcpy(out, in, outSize);
return true;
}
if (inSize == 0) {
return false;
}
if (inSize > static_cast<size_t>(std::numeric_limits<int>::max()) ||
outSize > static_cast<size_t>(std::numeric_limits<int>::max())) {
return false;
}
uint8_t type = in[0];
if (type == 0) {
if (inSize - 1 != outSize) {
return false;
}
std::memcpy(out, in + 1, outSize);
return true;
}
int outLen = static_cast<int>(outSize);
if (!SCompDecompress(out, &outLen, const_cast<uint8_t*>(in), static_cast<int>(inSize))) {
return false;
}
return static_cast<size_t>(outLen) == outSize;
}
struct MpqArchive {
std::ifstream stream;
uint64_t baseOffset = 0;
@ -165,7 +198,7 @@ namespace {
return false;
}
if (userHeader.id == ID_MPQ_USERDATA) {
if (userHeader.id == kMpqUserDataId) {
baseOffset = userHeader.headerOffset;
} else {
baseOffset = 0;
@ -177,7 +210,7 @@ namespace {
return false;
}
if (header.id != ID_MPQ || header.headerSize < 32) {
if (header.id != kMpqHeaderId || header.headerSize < 32) {
return false;
}
@ -263,11 +296,11 @@ namespace {
return false;
}
if (block->flags & MPQ_FILE_ENCRYPTED) {
if (block->flags & kMpqFileEncrypted) {
return false;
}
if (block->flags & MPQ_FILE_IMPLODE) {
if (block->flags & kMpqFileImplode) {
return false;
}
@ -276,7 +309,7 @@ namespace {
std::vector<uint8_t> buffer(fileSize);
if (block->flags & MPQ_FILE_SINGLE_UNIT) {
if (block->flags & kMpqFileSingleUnit) {
std::vector<uint8_t> raw(block->compressedSize);
stream.seekg(static_cast<std::streamoff>(filePos), std::ios::beg);
stream.read(reinterpret_cast<char*>(raw.data()), raw.size());
@ -284,8 +317,8 @@ namespace {
return false;
}
if (block->flags & MPQ_FILE_COMPRESSED) {
if (!MpqCompression::DecompressBlock(raw.data(), raw.size(), buffer.data(), buffer.size())) {
if (block->flags & kMpqFileCompressed) {
if (!DecompressBlock(raw.data(), raw.size(), buffer.data(), buffer.size())) {
return false;
}
} else {
@ -294,7 +327,7 @@ namespace {
}
std::memcpy(buffer.data(), raw.data(), buffer.size());
}
} else if ((block->flags & MPQ_FILE_COMPRESSED) == 0) {
} else if ((block->flags & kMpqFileCompressed) == 0) {
stream.seekg(static_cast<std::streamoff>(filePos), std::ios::beg);
stream.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
if (!stream.good()) {
@ -330,7 +363,7 @@ namespace {
}
uint8_t* outPtr = buffer.data() + i * sectorSize;
if (!MpqCompression::DecompressBlock(raw.data(), raw.size(), outPtr, outSize)) {
if (!DecompressBlock(raw.data(), raw.size(), outPtr, outSize)) {
if (raw.size() == outSize) {
std::memcpy(outPtr, raw.data(), outSize);
} else {

View File

@ -1,247 +0,0 @@
#include "util/mpq/Decompress.hpp"
#include <bzlib.h>
#include <zlib.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <vector>
#include "adpcm.h"
#include "huff.h"
#include "pklib.h"
#include "sparse.h"
namespace MpqCompression {
namespace {
constexpr uint8_t MPQ_COMP_HUFFMANN = 0x01;
constexpr uint8_t MPQ_COMP_ZLIB = 0x02;
constexpr uint8_t MPQ_COMP_PKWARE = 0x08;
constexpr uint8_t MPQ_COMP_BZIP2 = 0x10;
constexpr uint8_t MPQ_COMP_SPARSE = 0x20;
constexpr uint8_t MPQ_COMP_ADPCM_MONO = 0x40;
constexpr uint8_t MPQ_COMP_ADPCM_STEREO = 0x80;
constexpr uint8_t MPQ_COMP_LZMA = 0x12;
struct MpqDataInfo {
unsigned char* in;
unsigned char* inEnd;
unsigned char* out;
unsigned char* outEnd;
};
unsigned int ReadInputData(char* buf, unsigned int* size, void* param) {
MpqDataInfo* info = static_cast<MpqDataInfo*>(param);
unsigned int maxAvail = static_cast<unsigned int>(info->inEnd - info->in);
unsigned int toRead = *size;
if (toRead > maxAvail) {
toRead = maxAvail;
}
std::memcpy(buf, info->in, toRead);
info->in += toRead;
return toRead;
}
void WriteOutputData(char* buf, unsigned int* size, void* param) {
MpqDataInfo* info = static_cast<MpqDataInfo*>(param);
unsigned int maxWrite = static_cast<unsigned int>(info->outEnd - info->out);
unsigned int toWrite = *size;
if (toWrite > maxWrite) {
toWrite = maxWrite;
}
std::memcpy(info->out, buf, toWrite);
info->out += toWrite;
}
int DecompressZlib(void* out, int* outSize, void* in, int inSize) {
z_stream z = {};
z.next_in = reinterpret_cast<Bytef*>(in);
z.avail_in = static_cast<uInt>(inSize);
z.total_in = inSize;
z.next_out = reinterpret_cast<Bytef*>(out);
z.avail_out = static_cast<uInt>(*outSize);
z.total_out = 0;
z.zalloc = nullptr;
z.zfree = nullptr;
int result = inflateInit(&z);
if (result == Z_OK) {
result = inflate(&z, Z_FINISH);
*outSize = static_cast<int>(z.total_out);
inflateEnd(&z);
}
return result >= Z_OK;
}
int DecompressBzip2(void* out, int* outSize, void* in, int inSize) {
bz_stream strm = {};
strm.next_in = reinterpret_cast<char*>(in);
strm.avail_in = inSize;
strm.next_out = reinterpret_cast<char*>(out);
strm.avail_out = *outSize;
strm.bzalloc = nullptr;
strm.bzfree = nullptr;
strm.opaque = nullptr;
int result = BZ2_bzDecompressInit(&strm, 0, 0);
if (result == BZ_OK) {
result = BZ2_bzDecompress(&strm);
*outSize = strm.total_out_lo32;
BZ2_bzDecompressEnd(&strm);
}
return result >= BZ_OK;
}
int DecompressPKLIB(void* out, int* outSize, void* in, int inSize) {
std::vector<char> work(EXP_BUFFER_SIZE);
std::memset(work.data(), 0, work.size());
MpqDataInfo info = {
static_cast<unsigned char*>(in),
static_cast<unsigned char*>(in) + inSize,
static_cast<unsigned char*>(out),
static_cast<unsigned char*>(out) + *outSize
};
unsigned int result = explode(ReadInputData, WriteOutputData, work.data(), &info);
*outSize = static_cast<int>(info.out - static_cast<unsigned char*>(out));
return result == CMP_NO_ERROR ? 1 : 0;
}
int DecompressHuff(void* out, int* outSize, void* in, int inSize) {
THuffmannTree tree(false);
TInputStream stream(in, static_cast<size_t>(inSize));
unsigned int result = tree.Decompress(out, static_cast<unsigned int>(*outSize), &stream);
*outSize = static_cast<int>(result);
return result != 0;
}
int DecompressAdpcmMono(void* out, int* outSize, void* in, int inSize) {
int result = DecompressADPCM(out, *outSize, in, inSize, 1);
*outSize = result;
return result > 0;
}
int DecompressAdpcmStereo(void* out, int* outSize, void* in, int inSize) {
int result = DecompressADPCM(out, *outSize, in, inSize, 2);
*outSize = result;
return result > 0;
}
int DecompressSparseWrapper(void* out, int* outSize, void* in, int inSize) {
return DecompressSparse(out, outSize, in, inSize);
}
struct DecompressEntry {
uint8_t mask;
int (*fn)(void* out, int* outSize, void* in, int inSize);
};
constexpr DecompressEntry kDecompressTable[] = {
{MPQ_COMP_BZIP2, DecompressBzip2},
{MPQ_COMP_PKWARE, DecompressPKLIB},
{MPQ_COMP_ZLIB, DecompressZlib},
{MPQ_COMP_HUFFMANN, DecompressHuff},
{MPQ_COMP_ADPCM_STEREO, DecompressAdpcmStereo},
{MPQ_COMP_ADPCM_MONO, DecompressAdpcmMono},
{MPQ_COMP_SPARSE, DecompressSparseWrapper}
};
constexpr uint8_t kKnownMask =
MPQ_COMP_HUFFMANN |
MPQ_COMP_ZLIB |
MPQ_COMP_PKWARE |
MPQ_COMP_BZIP2 |
MPQ_COMP_SPARSE |
MPQ_COMP_ADPCM_MONO |
MPQ_COMP_ADPCM_STEREO;
}
bool DecompressBlock(const uint8_t* in, size_t inSize, uint8_t* out, size_t outSize) {
if (inSize == outSize) {
std::memcpy(out, in, outSize);
return true;
}
if (inSize < 1) {
return false;
}
if (inSize > static_cast<size_t>(std::numeric_limits<int>::max())) {
return false;
}
if (outSize > static_cast<size_t>(std::numeric_limits<int>::max())) {
return false;
}
uint8_t type = in[0];
const uint8_t* payload = in + 1;
size_t payloadSize = inSize - 1;
if (type == 0) {
if (payloadSize != outSize) {
return false;
}
std::memcpy(out, payload, outSize);
return true;
}
if (type == MPQ_COMP_LZMA) {
return false;
}
if ((type & ~kKnownMask) != 0) {
return false;
}
int steps = 0;
for (const auto& entry : kDecompressTable) {
if (type & entry.mask) {
++steps;
}
}
if (steps == 0) {
return false;
}
std::vector<uint8_t> work;
uint8_t* workBuf = nullptr;
if (steps > 1) {
work.resize(outSize);
workBuf = work.data();
}
const uint8_t* currentInput = payload;
size_t currentSize = payloadSize;
int remaining = steps - 1;
for (const auto& entry : kDecompressTable) {
if ((type & entry.mask) == 0) {
continue;
}
uint8_t* output = (remaining & 1) ? workBuf : out;
int outputSize = static_cast<int>(outSize);
int inputSize = static_cast<int>(currentSize);
if (!entry.fn(output, &outputSize, const_cast<uint8_t*>(currentInput), inputSize)) {
return false;
}
if (outputSize <= 0) {
return false;
}
currentInput = output;
currentSize = static_cast<size_t>(outputSize);
--remaining;
}
return currentSize == outSize;
}
}

View File

@ -1,8 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
namespace MpqCompression {
bool DecompressBlock(const uint8_t* in, size_t inSize, uint8_t* out, size_t outSize);
}

View File

@ -1,539 +0,0 @@
/*****************************************************************************/
/* adpcm.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* This module contains implementation of adpcm decompression method used by */
/* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing */
/* his sources. */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
/* 20.05.03 2.00 Lad Added compression */
/* 19.11.03 2.01 Dan Big endian handling */
/* 10.01.13 3.00 Lad Refactored, beautified, documented :-) */
/*****************************************************************************/
#include <assert.h>
#include <stddef.h>
#include "adpcm.h"
//-----------------------------------------------------------------------------
// Tables necessary dor decompression
static const int NextStepTable[] =
{
-1, 0, -1, 4, -1, 2, -1, 6,
-1, 1, -1, 5, -1, 3, -1, 7,
-1, 1, -1, 5, -1, 3, -1, 7,
-1, 2, -1, 4, -1, 6, -1, 8
};
static const int StepSizeTable[] =
{
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411,
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767
};
//-----------------------------------------------------------------------------
// Helper class for writing output ADPCM data
class TADPCMStream
{
public:
TADPCMStream(void * pvBuffer, size_t cbBuffer)
{
pbBufferEnd = (unsigned char *)pvBuffer + cbBuffer;
pbBuffer = (unsigned char *)pvBuffer;
}
bool ReadByteSample(unsigned char & ByteSample)
{
// Check if there is enough space in the buffer
if(pbBuffer >= pbBufferEnd)
return false;
ByteSample = *pbBuffer++;
return true;
}
bool WriteByteSample(unsigned char ByteSample)
{
// Check if there is enough space in the buffer
if(pbBuffer >= pbBufferEnd)
return false;
*pbBuffer++ = ByteSample;
return true;
}
bool ReadWordSample(short & OneSample)
{
// Check if we have enough space in the output buffer
if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short))
return false;
// Write the sample
OneSample = pbBuffer[0] + (((short)pbBuffer[1]) << 0x08);
pbBuffer += sizeof(short);
return true;
}
bool WriteWordSample(short OneSample)
{
// Check if we have enough space in the output buffer
if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short))
return false;
// Write the sample
*pbBuffer++ = (unsigned char)(OneSample & 0xFF);
*pbBuffer++ = (unsigned char)(OneSample >> 0x08);
return true;
}
int LengthProcessed(void * pvOutBuffer)
{
return (int)((unsigned char *)pbBuffer - (unsigned char *)pvOutBuffer);
}
unsigned char * pbBufferEnd;
unsigned char * pbBuffer;
};
//----------------------------------------------------------------------------
// Local functions
static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample)
{
// Get the next step index
StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F];
// Don't make the step index overflow
if(StepIndex < 0)
StepIndex = 0;
else if(StepIndex > 88)
StepIndex = 88;
return (short)StepIndex;
}
static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference, int BitMask = 0x40)
{
// Is the sign bit set?
if(EncodedSample & BitMask)
{
PredictedSample -= Difference;
if(PredictedSample <= -32768)
PredictedSample = -32768;
}
else
{
PredictedSample += Difference;
if(PredictedSample >= 32767)
PredictedSample = 32767;
}
return PredictedSample;
}
static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference)
{
if(EncodedSample & 0x01)
Difference += (StepSize >> 0);
if(EncodedSample & 0x02)
Difference += (StepSize >> 1);
if(EncodedSample & 0x04)
Difference += (StepSize >> 2);
if(EncodedSample & 0x08)
Difference += (StepSize >> 3);
if(EncodedSample & 0x10)
Difference += (StepSize >> 4);
if(EncodedSample & 0x20)
Difference += (StepSize >> 5);
return UpdatePredictedSample(PredictedSample, EncodedSample, Difference);
}
//----------------------------------------------------------------------------
// Compression routine
int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel)
{
TADPCMStream os(pvOutBuffer, cbOutBuffer); // The output stream
TADPCMStream is(pvInBuffer, cbInBuffer); // The input stream
unsigned char BitShift = (unsigned char)(CompressionLevel - 1);
short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel
short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Step indexes for each channel
short InputSample; // Input sample for the current channel
int TotalStepSize;
int ChannelIndex;
int AbsDifference;
int Difference;
int MaxBitMask;
int StepSize;
// First byte in the output stream contains zero. The second one contains the compression level
os.WriteByteSample(0);
if(!os.WriteByteSample(BitShift))
return 2;
// Set the initial step index for each channel
PredictedSamples[0] = PredictedSamples[1] = 0;
StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX;
// Next, InitialSample value for each channel follows
for(int i = 0; i < ChannelCount; i++)
{
// Get the initial sample from the input stream
if(!is.ReadWordSample(InputSample))
return os.LengthProcessed(pvOutBuffer);
// Store the initial sample to our sample array
PredictedSamples[i] = InputSample;
// Also store the loaded sample to the output stream
if(!os.WriteWordSample(InputSample))
return os.LengthProcessed(pvOutBuffer);
}
// Get the initial index
ChannelIndex = ChannelCount - 1;
// Now keep reading the input data as long as there is something in the input buffer
while(is.ReadWordSample(InputSample))
{
int EncodedSample = 0;
// If we have two channels, we need to flip the channel index
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
// Get the difference from the previous sample.
// If the difference is negative, set the sign bit to the encoded sample
AbsDifference = InputSample - PredictedSamples[ChannelIndex];
if(AbsDifference < 0)
{
AbsDifference = -AbsDifference;
EncodedSample |= 0x40;
}
// If the difference is too low (higher that difference treshold),
// write a step index modifier marker
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
if(AbsDifference < (StepSize >> CompressionLevel))
{
if(StepIndexes[ChannelIndex] != 0)
StepIndexes[ChannelIndex]--;
os.WriteByteSample(0x80);
}
else
{
// If the difference is too high, write marker that
// indicates increase in step size
while(AbsDifference > (StepSize << 1))
{
if(StepIndexes[ChannelIndex] >= 0x58)
break;
// Modify the step index
StepIndexes[ChannelIndex] += 8;
if(StepIndexes[ChannelIndex] > 0x58)
StepIndexes[ChannelIndex] = 0x58;
// Write the "modify step index" marker
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
os.WriteByteSample(0x81);
}
// Get the limit bit value
MaxBitMask = (1 << (BitShift - 1));
MaxBitMask = (MaxBitMask > 0x20) ? 0x20 : MaxBitMask;
Difference = StepSize >> BitShift;
TotalStepSize = 0;
for(int BitVal = 0x01; BitVal <= MaxBitMask; BitVal <<= 1)
{
if((TotalStepSize + StepSize) <= AbsDifference)
{
TotalStepSize += StepSize;
EncodedSample |= BitVal;
}
StepSize >>= 1;
}
PredictedSamples[ChannelIndex] = (short)UpdatePredictedSample(PredictedSamples[ChannelIndex],
EncodedSample,
Difference + TotalStepSize);
// Write the encoded sample to the output stream
if(!os.WriteByteSample((unsigned char)EncodedSample))
break;
// Calculates the step index to use for the next encode
StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample);
}
}
return os.LengthProcessed(pvOutBuffer);
}
//----------------------------------------------------------------------------
// Decompression routine
int DecompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount)
{
TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream
TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream
unsigned char EncodedSample;
unsigned char BitShift;
short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; // Predicted sample for each channel
short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Predicted step index for each channel
int ChannelIndex; // Current channel index
// Initialize the StepIndex for each channel
PredictedSamples[0] = PredictedSamples[1] = 0;
StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX;
// The first byte is always zero, the second one contains bit shift (compression level - 1)
is.ReadByteSample(BitShift);
is.ReadByteSample(BitShift);
// Next, InitialSample value for each channel follows
for(int i = 0; i < ChannelCount; i++)
{
// Get the initial sample from the input stream
short InitialSample;
// Attempt to read the initial sample
if(!is.ReadWordSample(InitialSample))
return os.LengthProcessed(pvOutBuffer);
// Store the initial sample to our sample array
PredictedSamples[i] = InitialSample;
// Also store the loaded sample to the output stream
if(!os.WriteWordSample(InitialSample))
return os.LengthProcessed(pvOutBuffer);
}
// Get the initial index
ChannelIndex = ChannelCount - 1;
// Keep reading as long as there is something in the input buffer
while(is.ReadByteSample(EncodedSample))
{
// If we have two channels, we need to flip the channel index
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
if(EncodedSample == 0x80)
{
if(StepIndexes[ChannelIndex] != 0)
StepIndexes[ChannelIndex]--;
if(!os.WriteWordSample(PredictedSamples[ChannelIndex]))
return os.LengthProcessed(pvOutBuffer);
}
else if(EncodedSample == 0x81)
{
// Modify the step index
StepIndexes[ChannelIndex] += 8;
if(StepIndexes[ChannelIndex] > 0x58)
StepIndexes[ChannelIndex] = 0x58;
// Next pass, keep going on the same channel
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
}
else
{
int StepIndex = StepIndexes[ChannelIndex];
int StepSize = StepSizeTable[StepIndex];
// Encode one sample
PredictedSamples[ChannelIndex] = (short)DecodeSample(PredictedSamples[ChannelIndex],
EncodedSample,
StepSize,
StepSize >> BitShift);
// Write the decoded sample to the output stream
if(!os.WriteWordSample(PredictedSamples[ChannelIndex]))
break;
// Calculates the step index to use for the next encode
StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndex, EncodedSample);
}
}
// Return total bytes written since beginning of the output buffer
return os.LengthProcessed(pvOutBuffer);
}
//-----------------------------------------------------------------------------
// ADPCM decompression present in Starcraft I BETA
typedef struct _ADPCM_DATA
{
const unsigned int * pValues;
int BitCount;
int field_8;
int field_C;
int field_10;
} ADPCM_DATA, *PADPCM_DATA;
static const unsigned int adpcm_values_2[] = {0x33, 0x66};
static const unsigned int adpcm_values_3[] = {0x3A, 0x3A, 0x50, 0x70};
static const unsigned int adpcm_values_4[] = {0x3A, 0x3A, 0x3A, 0x3A, 0x4D, 0x66, 0x80, 0x9A};
static const unsigned int adpcm_values_6[] =
{
0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A,
0x46, 0x53, 0x60, 0x6D, 0x7A, 0x86, 0x93, 0xA0, 0xAD, 0xBA, 0xC6, 0xD3, 0xE0, 0xED, 0xFA, 0x106
};
static const unsigned int * InitAdpcmData(PADPCM_DATA pData, unsigned char BitCount)
{
switch(BitCount)
{
case 2:
pData->pValues = adpcm_values_2;
break;
case 3:
pData->pValues = adpcm_values_3;
break;
case 4:
pData->pValues = adpcm_values_4;
break;
default:
pData->pValues = NULL;
break;
case 6:
pData->pValues = adpcm_values_6;
break;
}
pData->BitCount = BitCount;
pData->field_C = 0x20000;
pData->field_8 = 1 << BitCount;
pData->field_10 = (1 << BitCount) / 2;
return pData->pValues;
}
int DecompressADPCM_SC1B(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount)
{
TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream
TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream
ADPCM_DATA AdpcmData;
int LowBitValues[MAX_ADPCM_CHANNEL_COUNT];
int UpperBits[MAX_ADPCM_CHANNEL_COUNT];
int BitMasks[MAX_ADPCM_CHANNEL_COUNT];
int PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];
int ChannelIndex;
int ChannelIndexMax;
int OutputSample;
unsigned char BitCount;
unsigned char EncodedSample;
short InputValue16;
int reg_eax;
int Difference;
// The first byte contains number of bits
if(!is.ReadByteSample(BitCount))
return os.LengthProcessed(pvOutBuffer);
if(!InitAdpcmData(&AdpcmData, BitCount))
return os.LengthProcessed(pvOutBuffer);
assert(AdpcmData.pValues != NULL);
// Init bit values
for(int i = 0; i < ChannelCount; i++)
{
unsigned char OneByte;
if(!is.ReadByteSample(OneByte))
return os.LengthProcessed(pvOutBuffer);
LowBitValues[i] = OneByte & 0x01;
UpperBits[i] = OneByte >> 1;
}
//
for(int i = 0; i < ChannelCount; i++)
{
if(!is.ReadWordSample(InputValue16))
return os.LengthProcessed(pvOutBuffer);
BitMasks[i] = InputValue16 << AdpcmData.BitCount;
}
// Next, InitialSample value for each channel follows
for(int i = 0; i < ChannelCount; i++)
{
if(!is.ReadWordSample(InputValue16))
return os.LengthProcessed(pvOutBuffer);
PredictedSamples[i] = InputValue16;
os.WriteWordSample(InputValue16);
}
// Get the initial index
ChannelIndexMax = ChannelCount - 1;
ChannelIndex = 0;
// Keep reading as long as there is something in the input buffer
while(is.ReadByteSample(EncodedSample))
{
reg_eax = ((PredictedSamples[ChannelIndex] * 3) << 3) - PredictedSamples[ChannelIndex];
PredictedSamples[ChannelIndex] = ((reg_eax * 10) + 0x80) >> 8;
Difference = (((EncodedSample >> 1) + 1) * BitMasks[ChannelIndex] + AdpcmData.field_10) >> AdpcmData.BitCount;
PredictedSamples[ChannelIndex] = UpdatePredictedSample(PredictedSamples[ChannelIndex], EncodedSample, Difference, 0x01);
BitMasks[ChannelIndex] = (AdpcmData.pValues[EncodedSample >> 1] * BitMasks[ChannelIndex] + 0x80) >> 6;
if(BitMasks[ChannelIndex] < AdpcmData.field_8)
BitMasks[ChannelIndex] = AdpcmData.field_8;
if(BitMasks[ChannelIndex] > AdpcmData.field_C)
BitMasks[ChannelIndex] = AdpcmData.field_C;
reg_eax = (cbInBuffer - is.LengthProcessed(pvInBuffer)) >> ChannelIndexMax;
OutputSample = PredictedSamples[ChannelIndex];
if(reg_eax < UpperBits[ChannelIndex])
{
if(LowBitValues[ChannelIndex])
{
OutputSample += (UpperBits[ChannelIndex] - reg_eax);
if(OutputSample > 32767)
OutputSample = 32767;
}
else
{
OutputSample += (reg_eax - UpperBits[ChannelIndex]);
if(OutputSample < -32768)
OutputSample = -32768;
}
}
// Write the word sample and swap channel
os.WriteWordSample((short)(OutputSample));
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
}
return os.LengthProcessed(pvOutBuffer);
}

View File

@ -1,27 +0,0 @@
/*****************************************************************************/
/* adpcm.h Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Header file for adpcm decompress functions */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 31.03.03 1.00 Lad The first version of adpcm.h */
/*****************************************************************************/
#ifndef __ADPCM_H__
#define __ADPCM_H__
//-----------------------------------------------------------------------------
// Defines
#define MAX_ADPCM_CHANNEL_COUNT 2
#define INITIAL_ADPCM_STEP_INDEX 0x2C
//-----------------------------------------------------------------------------
// Public functions
int CompressADPCM (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount, int nCmpType);
int DecompressADPCM (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount);
int DecompressADPCM_SC1B(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount);
#endif // __ADPCM_H__

View File

@ -1,521 +0,0 @@
/*****************************************************************************/
/* explode.c Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Implode function of PKWARE Data Compression library */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
/* 08.04.03 1.01 Lad Renamed to explode.c to be compatible with pklib */
/* 02.05.03 1.01 Lad Stress test done */
/* 22.04.10 1.01 Lad Documented */
/*****************************************************************************/
#include <assert.h>
#include <string.h>
#include "pklib.h"
#define PKDCL_OK 0
#define PKDCL_STREAM_END 1 // All data from the input stream is read
#define PKDCL_NEED_DICT 2 // Need more data (dictionary)
#define PKDCL_CONTINUE 10 // Internal flag, not returned to user
#define PKDCL_GET_INPUT 11 // Internal flag, not returned to user
char CopyrightPkware[] = "PKWARE Data Compression Library for Win32\r\n"
"Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n"
"Patent No. 5,051,745\r\n"
"PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n"
"Version 1.11\r\n";
//-----------------------------------------------------------------------------
// Tables
const unsigned char DistBits[0x40] =
{
0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};
const unsigned char DistCode[0x40] =
{
0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A,
0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C,
0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00
};
const unsigned char ExLenBits[0x10] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
};
const unsigned short LenBase[0x10] =
{
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106
};
const unsigned char LenBits[0x10] =
{
0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07
};
const unsigned char LenCode[0x10] =
{
0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00
};
const unsigned char ChBitsAsc[0x100] =
{
0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08,
0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B,
0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06,
0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08,
0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05,
0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D,
0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D
};
const unsigned short ChCodeAsc[0x100] =
{
0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0,
0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0,
0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360,
0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60,
0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8,
0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098,
0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C,
0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710,
0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8,
0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E,
0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8,
0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088,
0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A,
0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D,
0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078,
0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0,
0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040,
0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380,
0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180,
0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280,
0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080,
0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300,
0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0,
0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320,
0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220,
0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0,
0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0,
0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340,
0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900,
0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600,
0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200,
0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000
};
//-----------------------------------------------------------------------------
// Local functions
static void GenDecodeTabs(
unsigned char * positions, // [out] Table of positions
const unsigned char * start_indexes, // [in] Table of start indexes
const unsigned char * length_bits, // [in] Table of lengths. Each length is stored as number of bits
size_t elements) // [in] Number of elements in start_indexes and length_bits
{
unsigned int index;
unsigned int length;
size_t i;
for(i = 0; i < elements; i++)
{
length = 1 << length_bits[i]; // Get the length in bytes
for(index = start_indexes[i]; index < 0x100; index += length)
{
positions[index] = (unsigned char)i;
}
}
}
static void GenAscTabs(TDcmpStruct * pWork)
{
const unsigned short * pChCodeAsc = &ChCodeAsc[0xFF];
unsigned int acc, add;
unsigned short count;
for(count = 0x00FF; pChCodeAsc >= ChCodeAsc; pChCodeAsc--, count--)
{
unsigned char * pChBitsAsc = pWork->ChBitsAsc + count;
unsigned char bits_asc = *pChBitsAsc;
if(bits_asc <= 8)
{
add = (1 << bits_asc);
acc = *pChCodeAsc;
do
{
pWork->offs2C34[acc] = (unsigned char)count;
acc += add;
}
while(acc < 0x100);
}
else if((acc = (*pChCodeAsc & 0xFF)) != 0)
{
pWork->offs2C34[acc] = 0xFF;
if(*pChCodeAsc & 0x3F)
{
bits_asc -= 4;
*pChBitsAsc = bits_asc;
add = (1 << bits_asc);
acc = *pChCodeAsc >> 4;
do
{
pWork->offs2D34[acc] = (unsigned char)count;
acc += add;
}
while(acc < 0x100);
}
else
{
bits_asc -= 6;
*pChBitsAsc = bits_asc;
add = (1 << bits_asc);
acc = *pChCodeAsc >> 6;
do
{
pWork->offs2E34[acc] = (unsigned char)count;
acc += add;
}
while(acc < 0x80);
}
}
else
{
bits_asc -= 8;
*pChBitsAsc = bits_asc;
add = (1 << bits_asc);
acc = *pChCodeAsc >> 8;
do
{
pWork->offs2EB4[acc] = (unsigned char)count;
acc += add;
}
while(acc < 0x100);
}
}
}
//-----------------------------------------------------------------------------
// Removes given number of bits in the bit buffer. New bits are reloaded from
// the input buffer, if needed.
// Returns: PKDCL_OK: Operation was successful
// PKDCL_STREAM_END: There are no more bits in the input buffer
static int WasteBits(TDcmpStruct * pWork, unsigned int nBits)
{
// If number of bits required is less than number of (bits in the buffer) ?
if(nBits <= pWork->extra_bits)
{
pWork->extra_bits -= nBits;
pWork->bit_buff >>= nBits;
return PKDCL_OK;
}
// Load input buffer if necessary
pWork->bit_buff >>= pWork->extra_bits;
if(pWork->in_pos == pWork->in_bytes)
{
pWork->in_pos = sizeof(pWork->in_buff);
if((pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param)) == 0)
return PKDCL_STREAM_END;
pWork->in_pos = 0;
}
// Update bit buffer
pWork->bit_buff |= (pWork->in_buff[pWork->in_pos++] << 8);
pWork->bit_buff >>= (nBits - pWork->extra_bits);
pWork->extra_bits = (pWork->extra_bits - nBits) + 8;
return PKDCL_OK;
}
//-----------------------------------------------------------------------------
// Decodes next literal from the input (compressed) data.
// Returns : 0x000: One byte 0x00
// 0x001: One byte 0x01
// ...
// 0x0FF: One byte 0xFF
// 0x100: Repetition, length of 0x02 bytes
// 0x101: Repetition, length of 0x03 bytes
// ...
// 0x304: Repetition, length of 0x206 bytes
// 0x305: End of stream
// 0x306: Error
static unsigned int DecodeLit(TDcmpStruct * pWork)
{
unsigned int extra_length_bits; // Number of bits of extra literal length
unsigned int length_code; // Length code
unsigned int value;
// Test the current bit in byte buffer. If is not set, simply return the next 8 bits.
if(pWork->bit_buff & 1)
{
// Remove one bit from the input data
if(WasteBits(pWork, 1))
return 0x306;
// The next 8 bits hold the index to the length code table
length_code = pWork->LengthCodes[pWork->bit_buff & 0xFF];
// Remove the apropriate number of bits
if(WasteBits(pWork, pWork->LenBits[length_code]))
return 0x306;
// Are there some extra bits for the obtained length code ?
if((extra_length_bits = pWork->ExLenBits[length_code]) != 0)
{
unsigned int extra_length = pWork->bit_buff & ((1 << extra_length_bits) - 1);
if(WasteBits(pWork, extra_length_bits))
{
if((length_code + extra_length) != 0x10E)
return 0x306;
}
length_code = pWork->LenBase[length_code] + extra_length;
}
// In order to distinguish uncompressed byte from repetition length,
// we have to add 0x100 to the length.
return length_code + 0x100;
}
// Remove one bit from the input data
if(WasteBits(pWork, 1))
return 0x306;
// If the binary compression type, read 8 bits and return them as one byte.
if(pWork->ctype == CMP_BINARY)
{
unsigned int uncompressed_byte = pWork->bit_buff & 0xFF;
if(WasteBits(pWork, 8))
return 0x306;
return uncompressed_byte;
}
// When ASCII compression ...
if(pWork->bit_buff & 0xFF)
{
value = pWork->offs2C34[pWork->bit_buff & 0xFF];
if(value == 0xFF)
{
if(pWork->bit_buff & 0x3F)
{
if(WasteBits(pWork, 4))
return 0x306;
value = pWork->offs2D34[pWork->bit_buff & 0xFF];
}
else
{
if(WasteBits(pWork, 6))
return 0x306;
value = pWork->offs2E34[pWork->bit_buff & 0x7F];
}
}
}
else
{
if(WasteBits(pWork, 8))
return 0x306;
value = pWork->offs2EB4[pWork->bit_buff & 0xFF];
}
return WasteBits(pWork, pWork->ChBitsAsc[value]) ? 0x306 : value;
}
//-----------------------------------------------------------------------------
// Decodes the distance of the repetition, backwards relative to the
// current output buffer position
static unsigned int DecodeDist(TDcmpStruct * pWork, unsigned int rep_length)
{
unsigned int dist_pos_code; // Distance position code
unsigned int dist_pos_bits; // Number of bits of distance position
unsigned int distance; // Distance position
// Next 2-8 bits in the input buffer is the distance position code
dist_pos_code = pWork->DistPosCodes[pWork->bit_buff & 0xFF];
dist_pos_bits = pWork->DistBits[dist_pos_code];
if(WasteBits(pWork, dist_pos_bits))
return 0;
if(rep_length == 2)
{
// If the repetition is only 2 bytes length,
// then take 2 bits from the stream in order to get the distance
distance = (dist_pos_code << 2) | (pWork->bit_buff & 0x03);
if(WasteBits(pWork, 2))
return 0;
}
else
{
// If the repetition is more than 2 bytes length,
// then take "dsize_bits" bits in order to get the distance
distance = (dist_pos_code << pWork->dsize_bits) | (pWork->bit_buff & pWork->dsize_mask);
if(WasteBits(pWork, pWork->dsize_bits))
return 0;
}
return distance + 1;
}
static unsigned int Expand(TDcmpStruct * pWork)
{
unsigned int next_literal; // Literal decoded from the compressed data
unsigned int result; // Value to be returned
unsigned int copyBytes; // Number of bytes to copy to the output buffer
pWork->outputPos = 0x1000; // Initialize output buffer position
// Decode the next literal from the input data.
// The returned literal can either be an uncompressed byte (next_literal < 0x100)
// or an encoded length of the repeating byte sequence that
// is to be copied to the current buffer position
while((result = next_literal = DecodeLit(pWork)) < 0x305)
{
// If the literal is greater than 0x100, it holds length
// of repeating byte sequence
// literal of 0x100 means repeating sequence of 0x2 bytes
// literal of 0x101 means repeating sequence of 0x3 bytes
// ...
// literal of 0x305 means repeating sequence of 0x207 bytes
if(next_literal >= 0x100)
{
unsigned char * source;
unsigned char * target;
unsigned int rep_length; // Length of the repetition, in bytes
unsigned int minus_dist; // Backward distance to the repetition, relative to the current buffer position
// Get the length of the repeating sequence.
// Note that the repeating block may overlap the current output position,
// for example if there was a sequence of equal bytes
rep_length = next_literal - 0xFE;
// Get backward distance to the repetition
if((minus_dist = DecodeDist(pWork, rep_length)) == 0)
{
result = 0x306;
break;
}
// Target and source pointer
target = &pWork->out_buff[pWork->outputPos];
source = target - minus_dist;
// Update buffer output position
pWork->outputPos += rep_length;
// Copy the repeating sequence
while(rep_length-- > 0)
*target++ = *source++;
}
else
{
pWork->out_buff[pWork->outputPos++] = (unsigned char)next_literal;
}
// Flush the output buffer, if number of extracted bytes has reached the end
if(pWork->outputPos >= 0x2000)
{
// Copy decompressed data into user buffer
copyBytes = 0x1000;
pWork->write_buf((char *)&pWork->out_buff[0x1000], &copyBytes, pWork->param);
// Now copy the decompressed data to the first half of the buffer.
// This is needed because the decompression might reuse them as repetitions.
// Note that if the output buffer overflowed previously, the extra decompressed bytes
// are stored in "out_buff_overflow", and they will now be
// within decompressed part of the output buffer.
memmove(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000);
pWork->outputPos -= 0x1000;
}
}
// Flush any remaining decompressed bytes
copyBytes = pWork->outputPos - 0x1000;
pWork->write_buf((char *)&pWork->out_buff[0x1000], &copyBytes, pWork->param);
return result;
}
//-----------------------------------------------------------------------------
// Main exploding function.
unsigned int PKEXPORT explode(
unsigned int (*read_buf)(char *buf, unsigned int *size, void *param),
void (*write_buf)(char *buf, unsigned int *size, void *param),
char *work_buf,
void *param)
{
TDcmpStruct * pWork = (TDcmpStruct *)work_buf;
// Initialize work struct and load compressed data
pWork->read_buf = read_buf;
pWork->write_buf = write_buf;
pWork->param = param;
pWork->in_pos = sizeof(pWork->in_buff);
pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param);
if(pWork->in_bytes <= 4)
return CMP_BAD_DATA;
pWork->ctype = pWork->in_buff[0]; // Get the compression type (CMP_BINARY or CMP_ASCII)
pWork->dsize_bits = pWork->in_buff[1]; // Get the dictionary size
pWork->bit_buff = pWork->in_buff[2]; // Initialize 16-bit bit buffer
pWork->extra_bits = 0; // Extra (over 8) bits
pWork->in_pos = 3; // Position in input buffer
// Test for the valid dictionary size
if(4 > pWork->dsize_bits || pWork->dsize_bits > 6)
return CMP_INVALID_DICTSIZE;
pWork->dsize_mask = 0xFFFF >> (0x10 - pWork->dsize_bits); // Shifted by 'sar' instruction
if(pWork->ctype != CMP_BINARY)
{
if(pWork->ctype != CMP_ASCII)
return CMP_INVALID_MODE;
memcpy(pWork->ChBitsAsc, ChBitsAsc, sizeof(pWork->ChBitsAsc));
GenAscTabs(pWork);
}
memcpy(pWork->LenBits, LenBits, sizeof(pWork->LenBits));
GenDecodeTabs(pWork->LengthCodes, LenCode, LenBits, sizeof(LenBits));
memcpy(pWork->ExLenBits, ExLenBits, sizeof(pWork->ExLenBits));
memcpy(pWork->LenBase, LenBase, sizeof(pWork->LenBase));
memcpy(pWork->DistBits, DistBits, sizeof(pWork->DistBits));
GenDecodeTabs(pWork->DistPosCodes, DistCode, DistBits, sizeof(DistBits));
if(Expand(pWork) != 0x306)
return CMP_NO_ERROR;
return CMP_ABORT;
}

View File

@ -1,895 +0,0 @@
/*****************************************************************************/
/* 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 "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 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;
}
// Gets 7 bits from the stream. DOES NOT remove the bits from input stream
bool TInputStream::Peek7Bits(unsigned int & Value)
{
unsigned int Value8Bits = 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)
{
// Load additional 8 bits. Be careful if we have no more data
if(pbInBuffer >= pbInBufferEnd)
return false;
Value8Bits = *pbInBuffer++;
// Add these 8 bits to the bit buffer
BitBuffer |= Value8Bits << BitCount;
BitCount += 8;
}
// Return 7 bits of data. DO NOT remove them from the input stream
Value = (BitBuffer & 0x7F);
return true;
}
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)
{
pFirst = pLast = LIST_HEAD();
MinValidValue = 1;
ItemsUsed = 0;
bIsCmp0 = 0;
memset(ItemsByByte, 0, sizeof(ItemsByByte));
// 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 = NULL;
// Don't let the item buffer run out of space
if(ItemsUsed < HUFF_ITEM_COUNT)
{
// 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
bool 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
if((CompressionType & 0x0F) > 0x08)
return false;
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);
if(pNewItem == NULL)
return false;
// 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;
return true;
}
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++;
}
}
}
bool 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);
if(pChildHi != NULL)
{
pChildHi->pParent = pLastItem;
ItemsByByte[Value1] = pChildHi;
// Create lower-weight child
pChildLo = CreateNewItem(Value2, 0, InsertBefore);
if(pChildLo != NULL)
{
pChildLo->pParent = pLastItem;
pLastItem->pChildLo = pChildLo;
ItemsByByte[Value2] = pChildLo;
IncWeightsAndRebalance(pChildLo);
return true;
}
}
// No more space in the tree buffer
return false;
}
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;
bool bHasItemLinkIndex;
// Try to retrieve quick link index
bHasItemLinkIndex = is->Peek7Bits(ItemLinkIndex);
// Is the quick-link item valid?
if(bHasItemLinkIndex && 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(bHasItemLinkIndex && 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
// Coverity fix 84457: (x >> 32) has undefined behavior
ItemLinkIndex = (BitCount != 0) ? ItemLinkIndex & (0xFFFFFFFF >> (32 - BitCount)) : 0;
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;
if(!BuildTree(CompressionType))
return 0;
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);
if(!InsertNewBranchAndRebalance(pLast->DecompressedValue, InputByte))
return 0;
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
if(!BuildTree(CompressionType))
return 0;
// 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();
if(!InsertNewBranchAndRebalance(pLast->DecompressedValue, DecompressedValue))
return 0;
if(bIsCmp0 == 0)
IncWeightsAndRebalance(ItemsByByte[DecompressedValue]);
}
// Store the byte to the output stream
if(pbOutBuffer >= pbOutBufferEnd)
break;
*pbOutBuffer++ = (unsigned char)DecompressedValue;
if(bIsCmp0)
{
IncWeightsAndRebalance(ItemsByByte[DecompressedValue]);
}
}
return (unsigned int)(pbOutBuffer - (unsigned char *)pvOutBuffer);
}

View File

@ -1,143 +0,0 @@
/*****************************************************************************/
/* huffman.h Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.xx 1.00 Lad The first version of huffman.h */
/* 03.05.03 2.00 Lad Added compression */
/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */
/*****************************************************************************/
#ifndef __HUFFMAN_H__
#define __HUFFMAN_H__
//-----------------------------------------------------------------------------
// Defines
#define HUFF_ITEM_COUNT 0x203 // Number of items in the item pool
#define LINK_ITEM_COUNT 0x80 // Maximum number of quick-link items
//-----------------------------------------------------------------------------
// Structures and classes
// Input stream for Huffmann decompression
class TInputStream
{
public:
TInputStream(void * pvInBuffer, size_t cbInBuffer);
unsigned int Get1Bit();
unsigned int Get8Bits();
bool Peek7Bits(unsigned int & Value);
void SkipBits(unsigned int BitCount);
unsigned char * pbInBufferEnd; // End position in the the input buffer
unsigned char * pbInBuffer; // Current position in the the input buffer
unsigned int BitBuffer; // Input bit buffer
unsigned int BitCount; // Number of bits remaining in 'dwBitBuff'
};
// Output stream for Huffmann compression
class TOutputStream
{
public:
TOutputStream(void * pvOutBuffer, size_t cbOutLength);
void PutBits(unsigned int dwValue, unsigned int nBitCount);
void Flush();
unsigned char * pbOutBufferEnd; // End position in the output buffer
unsigned char * pbOutBuffer; // Current position in the output buffer
unsigned int BitBuffer; // Bit buffer
unsigned int BitCount; // Number of bits in the bit buffer
};
// A virtual tree item that represents the head of the item list
#define LIST_HEAD() ((THTreeItem *)(&pFirst))
enum TInsertPoint
{
InsertAfter = 1,
InsertBefore = 2
};
// Huffmann tree item
struct THTreeItem
{
THTreeItem() { pPrev = pNext = NULL; DecompressedValue = 0; Weight = 0; pParent = pChildLo = NULL; }
// ~THTreeItem() { RemoveItem(); }
void RemoveItem();
// void RemoveEntry();
THTreeItem * pNext; // Pointer to lower-weight tree item
THTreeItem * pPrev; // Pointer to higher-weight item
unsigned int DecompressedValue; // 08 - Decompressed byte value (also index in the array)
unsigned int Weight; // 0C - Weight
THTreeItem * pParent; // 10 - Pointer to parent item (NULL if none)
THTreeItem * pChildLo; // 14 - Pointer to the child with lower-weight child ("left child")
};
// Structure used for quick navigating in the huffmann tree.
// Allows skipping up to 7 bits in the compressed stream, thus
// decompressing a bit faster. Sometimes it can even get the decompressed
// byte directly.
struct TQuickLink
{
unsigned int ValidValue; // If greater than THuffmannTree::MinValidValue, the entry is valid
unsigned int ValidBits; // Number of bits that are valid for this item link
union
{
THTreeItem * pItem; // Pointer to the item within the Huffmann tree
unsigned int DecompressedValue; // Value for direct decompression
};
};
// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert
// for the decompression, I do not know actually if the class is really a Hufmann
// tree. If someone knows the decompression details, please let me know
class THuffmannTree
{
public:
THuffmannTree(bool bCompression);
~THuffmannTree();
void LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2);
void InsertItem(THTreeItem * item, TInsertPoint InsertPoint, THTreeItem * item2);
THTreeItem * FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight);
THTreeItem * CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint);
unsigned int FixupItemPosByWeight(THTreeItem * pItem, unsigned int MaxWeight);
bool BuildTree(unsigned int CompressionType);
void IncWeightsAndRebalance(THTreeItem * pItem);
bool InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2);
void EncodeOneByte(TOutputStream * os, THTreeItem * pItem);
unsigned int DecodeOneByte(TInputStream * is);
unsigned int Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int nCmpType);
unsigned int Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is);
THTreeItem ItemBuffer[HUFF_ITEM_COUNT]; // Buffer for tree items. No memory allocation is needed
unsigned int ItemsUsed; // Number of tree items used from ItemBuffer
// Head of the linear item list
THTreeItem * pFirst; // Pointer to the highest weight item
THTreeItem * pLast; // Pointer to the lowest weight item
THTreeItem * ItemsByByte[0x102]; // Array of item pointers, one for each possible byte value
TQuickLink QuickLinks[LINK_ITEM_COUNT]; // Array of quick-link items
unsigned int MinValidValue; // A minimum value of TQDecompress::ValidValue to be considered valid
unsigned int bIsCmp0; // 1 if compression type 0
};
#endif // __HUFFMAN_H__

View File

@ -1,160 +0,0 @@
/*****************************************************************************/
/* pklib.h Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Header file for PKWARE Data Compression Library */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 31.03.03 1.00 Lad Created */
/*****************************************************************************/
#ifndef __PKLIB_H__
#define __PKLIB_H__
#pragma once
//-----------------------------------------------------------------------------
// Defines
#define CMP_BINARY 0 // Binary compression
#define CMP_ASCII 1 // Ascii compression
#define CMP_NO_ERROR 0
#define CMP_INVALID_DICTSIZE 1
#define CMP_INVALID_MODE 2
#define CMP_BAD_DATA 3
#define CMP_ABORT 4
#define CMP_IMPLODE_DICT_SIZE1 1024 // Dictionary size of 1024
#define CMP_IMPLODE_DICT_SIZE2 2048 // Dictionary size of 2048
#define CMP_IMPLODE_DICT_SIZE3 4096 // Dictionary size of 4096
//-----------------------------------------------------------------------------
// Define calling convention
#ifndef PKEXPORT
#ifdef WIN32
#define PKEXPORT __cdecl // Use for normal __cdecl calling
#else
#define PKEXPORT
#endif
#endif
//-----------------------------------------------------------------------------
// Internal structures
// Compression structure
typedef struct
{
unsigned int distance; // 0000: Backward distance of the currently found repetition, decreased by 1
unsigned int out_bytes; // 0004: # bytes available in out_buff
unsigned int out_bits; // 0008: # of bits available in the last out byte
unsigned int dsize_bits; // 000C: Number of bits needed for dictionary size. 4 = 0x400, 5 = 0x800, 6 = 0x1000
unsigned int dsize_mask; // 0010: Bit mask for dictionary. 0x0F = 0x400, 0x1F = 0x800, 0x3F = 0x1000
unsigned int ctype; // 0014: Compression type (CMP_ASCII or CMP_BINARY)
unsigned int dsize_bytes; // 0018: Dictionary size in bytes
unsigned char dist_bits[0x40]; // 001C: Distance bits
unsigned char dist_codes[0x40]; // 005C: Distance codes
unsigned char nChBits[0x306]; // 009C: Table of literal bit lengths to be put to the output stream
unsigned short nChCodes[0x306]; // 03A2: Table of literal codes to be put to the output stream
unsigned short offs09AE; // 09AE:
void * param; // 09B0: User parameter
unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // 9B4
void (*write_buf)(char *buf, unsigned int *size, void *param); // 9B8
unsigned short offs09BC[0x204]; // 09BC:
unsigned long offs0DC4; // 0DC4:
unsigned short phash_to_index[0x900]; // 0DC8: Array of indexes (one for each PAIR_HASH) to the "pair_hash_offsets" table
unsigned short phash_to_index_end; // 1FC8: End marker for "phash_to_index" table
char out_buff[0x802]; // 1FCA: Compressed data
unsigned char work_buff[0x2204]; // 27CC: Work buffer
// + DICT_OFFSET => Dictionary
// + UNCMP_OFFSET => Uncompressed data
unsigned short phash_offs[0x2204]; // 49D0: Table of offsets for each PAIR_HASH
} TCmpStruct;
#define CMP_BUFFER_SIZE sizeof(TCmpStruct) // Size of compression structure.
// Defined as 36312 in pkware header file
// Decompression structure
typedef struct
{
unsigned long offs0000; // 0000
unsigned long ctype; // 0004: Compression type (CMP_BINARY or CMP_ASCII)
unsigned long outputPos; // 0008: Position in output buffer
unsigned long dsize_bits; // 000C: Dict size (4, 5, 6 for 0x400, 0x800, 0x1000)
unsigned long dsize_mask; // 0010: Dict size bitmask (0x0F, 0x1F, 0x3F for 0x400, 0x800, 0x1000)
unsigned long bit_buff; // 0014: 16-bit buffer for processing input data
unsigned long extra_bits; // 0018: Number of extra (above 8) bits in bit buffer
unsigned int in_pos; // 001C: Position in in_buff
unsigned long in_bytes; // 0020: Number of bytes in input buffer
void * param; // 0024: Custom parameter
unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // Pointer to function that reads data from the input stream
void (*write_buf)(char *buf, unsigned int *size, void *param);// Pointer to function that writes data to the output stream
unsigned char out_buff[0x2204]; // 0030: Output circle buffer.
// 0x0000 - 0x0FFF: Previous uncompressed data, kept for repetitions
// 0x1000 - 0x1FFF: Currently decompressed data
// 0x2000 - 0x2203: Reserve space for the longest possible repetition
unsigned char in_buff[0x800]; // 2234: Buffer for data to be decompressed
unsigned char DistPosCodes[0x100]; // 2A34: Table of distance position codes
unsigned char LengthCodes[0x100]; // 2B34: Table of length codes
unsigned char offs2C34[0x100]; // 2C34: Buffer for
unsigned char offs2D34[0x100]; // 2D34: Buffer for
unsigned char offs2E34[0x80]; // 2E34: Buffer for
unsigned char offs2EB4[0x100]; // 2EB4: Buffer for
unsigned char ChBitsAsc[0x100]; // 2FB4: Buffer for
unsigned char DistBits[0x40]; // 30B4: Numbers of bytes to skip copied block length
unsigned char LenBits[0x10]; // 30F4: Numbers of bits for skip copied block length
unsigned char ExLenBits[0x10]; // 3104: Number of valid bits for copied block
unsigned short LenBase[0x10]; // 3114: Buffer for
} TDcmpStruct;
#define EXP_BUFFER_SIZE sizeof(TDcmpStruct) // Size of decompression structure
// Defined as 12596 in pkware headers
//-----------------------------------------------------------------------------
// Tables (in explode.c)
extern const unsigned char DistBits[0x40];
extern const unsigned char DistCode[0x40];
extern const unsigned char ExLenBits[0x10];
extern const unsigned short LenBase[0x10];
extern const unsigned char LenBits[0x10];
extern const unsigned char LenCode[0x10];
extern const unsigned char ChBitsAsc[0x100];
extern const unsigned short ChCodeAsc[0x100];
//-----------------------------------------------------------------------------
// Public functions
#ifdef __cplusplus
extern "C" {
#endif
unsigned int PKEXPORT implode(
unsigned int (*read_buf)(char *buf, unsigned int *size, void *param),
void (*write_buf)(char *buf, unsigned int *size, void *param),
char *work_buf,
void *param,
unsigned int *type,
unsigned int *dsize);
unsigned int PKEXPORT explode(
unsigned int (*read_buf)(char *buf, unsigned int *size, void *param),
void (*write_buf)(char *buf, unsigned int *size, void *param),
char *work_buf,
void *param);
// The original name "crc32" was changed to "crc32_pklib" due
// to compatibility with zlib
unsigned long PKEXPORT crc32_pklib(char *buffer, unsigned int *size, unsigned long *old_crc);
#ifdef __cplusplus
} // End of 'extern "C"' declaration
#endif
#endif // __PKLIB_H__

View File

@ -1,282 +0,0 @@
/*****************************************************************************/
/* 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;
}

View File

@ -1,17 +0,0 @@
/*****************************************************************************/
/* sparse.h Copyright (c) Ladislav Zezula 2010 */
/*---------------------------------------------------------------------------*/
/* implementation of Sparse compression, used in Starcraft II */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 05.03.10 1.00 Lad The first version of sparse.h */
/*****************************************************************************/
#ifndef __SPARSE_H__
#define __SPARSE_H__
void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
int DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
#endif // __SPARSE_H__