mirror of
https://github.com/mangosfour/server.git
synced 2025-12-14 07:37:01 +00:00
715 lines
25 KiB
C++
715 lines
25 KiB
C++
/*****************************************************************************/
|
|
/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* This module serves as a bridge between StormLib code and (de)compression */
|
|
/* functions. All (de)compression calls go (and should only go) through this */
|
|
/* module. No system headers should be included in this module to prevent */
|
|
/* compile-time problems. */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 01.04.03 1.00 Lad The first version of SCompression.cpp */
|
|
/* 19.11.03 1.01 Dan Big endian handling */
|
|
/*****************************************************************************/
|
|
|
|
#define __STORMLIB_SELF__
|
|
#include "StormLib.h"
|
|
#include "SCommon.h"
|
|
|
|
#include <string.h>
|
|
|
|
// Include functions from Pkware Data Compression Library
|
|
#include "pklib/pklib.h"
|
|
|
|
// Include functions from zlib
|
|
#ifndef __SYS_ZLIB
|
|
#include "zlib/zlib.h" // Include functions from zlib
|
|
#else
|
|
#include <zlib.h> // If zlib is available on system, use this instead
|
|
#endif
|
|
|
|
// Include functions from Huffmann compression
|
|
#include "huffman/huff.h"
|
|
|
|
// Include functions from WAVe compression
|
|
#include "wave/wave.h"
|
|
|
|
// Include functions from BZip2 compression library
|
|
#ifndef __SYS_BZLIB
|
|
#include "bzip2/bzlib.h" // Include functions from bzlib
|
|
#else
|
|
#include <bzlib.h> // If bzlib is available on system, use this instead
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local structures
|
|
|
|
// Information about the input and output buffers for pklib
|
|
typedef struct
|
|
{
|
|
char * pInBuff; // Pointer to input data buffer
|
|
int nInPos; // Current offset in input data buffer
|
|
int nInBytes; // Number of bytes in the input buffer
|
|
char * pOutBuff; // Pointer to output data buffer
|
|
int nOutPos; // Position in the output buffer
|
|
int nMaxOut; // Maximum number of bytes in the output buffer
|
|
} TDataInfo;
|
|
|
|
// Table of compression functions
|
|
typedef int (*COMPRESS)(char *, int *, char *, int, int *, int);
|
|
typedef struct
|
|
{
|
|
unsigned long dwMask; // Compression mask
|
|
COMPRESS Compress; // Compression function
|
|
} TCompressTable;
|
|
|
|
// Table of decompression functions
|
|
typedef int (*DECOMPRESS)(char *, int *, char *, int);
|
|
typedef struct
|
|
{
|
|
unsigned long dwMask; // Decompression bit
|
|
DECOMPRESS Decompress; // Decompression function
|
|
} TDecompressTable;
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* Support functions for Pkware Data Compression Library */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
// Function loads data from the input buffer. Used by Pklib's "implode"
|
|
// and "explode" function as user-defined callback
|
|
// Returns number of bytes loaded
|
|
//
|
|
// char * buf - Pointer to a buffer where to store loaded data
|
|
// unsigned int * size - Max. number of bytes to read
|
|
// void * param - Custom pointer, parameter of implode/explode
|
|
|
|
static unsigned int ReadInputData(char * buf, unsigned int * size, void * param)
|
|
{
|
|
TDataInfo * pInfo = (TDataInfo *)param;
|
|
unsigned int nMaxAvail = (pInfo->nInBytes - pInfo->nInPos);
|
|
unsigned int nToRead = *size;
|
|
|
|
// Check the case when not enough data available
|
|
if(nToRead > nMaxAvail)
|
|
nToRead = nMaxAvail;
|
|
|
|
// Load data and increment offsets
|
|
memcpy(buf, pInfo->pInBuff + pInfo->nInPos, nToRead);
|
|
pInfo->nInPos += nToRead;
|
|
|
|
return nToRead;
|
|
}
|
|
|
|
// Function for store output data. Used by Pklib's "implode" and "explode"
|
|
// as user-defined callback
|
|
//
|
|
// char * buf - Pointer to data to be written
|
|
// unsigned int * size - Number of bytes to write
|
|
// void * param - Custom pointer, parameter of implode/explode
|
|
|
|
static void WriteOutputData(char * buf, unsigned int * size, void * param)
|
|
{
|
|
TDataInfo * pInfo = (TDataInfo *)param;
|
|
unsigned int nMaxWrite = (pInfo->nMaxOut - pInfo->nOutPos);
|
|
unsigned int nToWrite = *size;
|
|
|
|
// Check the case when not enough space in the output buffer
|
|
if(nToWrite > nMaxWrite)
|
|
nToWrite = nMaxWrite;
|
|
|
|
// Write output data and increments offsets
|
|
memcpy(pInfo->pOutBuff + pInfo->nOutPos, buf, nToWrite);
|
|
pInfo->nOutPos += nToWrite;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* "80" is IMA ADPCM stereo (de)compression */
|
|
/* "40" is IMA ADPCM mono (de)compression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
int Compress_wave_mono(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel)
|
|
{
|
|
// Prepare the compression level for the next compression
|
|
// (After us, the Huffmann compression will be called)
|
|
if(0 < nCmpLevel && nCmpLevel <= 2)
|
|
{
|
|
nCmpLevel = 4;
|
|
*pCmpType = 6;
|
|
}
|
|
else if(nCmpLevel == 3)
|
|
{
|
|
nCmpLevel = 6;
|
|
*pCmpType = 8;
|
|
}
|
|
else
|
|
{
|
|
nCmpLevel = 5;
|
|
*pCmpType = 7;
|
|
}
|
|
*pdwOutLength = CompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (short *)pbInBuffer, dwInLength, 1, nCmpLevel);
|
|
return 0;
|
|
}
|
|
|
|
int Decompress_wave_mono(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
*pdwOutLength = DecompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (unsigned char *)pbInBuffer, dwInLength, 1);
|
|
return 1;
|
|
}
|
|
|
|
int Compress_wave_stereo(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel)
|
|
{
|
|
// Prepare the compression type for the next compression
|
|
// (After us, the Huffmann compression will be called)
|
|
if(0 < nCmpLevel && nCmpLevel <= 2)
|
|
{
|
|
nCmpLevel = 4;
|
|
*pCmpType = 6;
|
|
}
|
|
else if(nCmpLevel == 3)
|
|
{
|
|
nCmpLevel = 6;
|
|
*pCmpType = 8;
|
|
}
|
|
else
|
|
{
|
|
nCmpLevel = 5;
|
|
*pCmpType = 7;
|
|
}
|
|
*pdwOutLength = CompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (short *)pbInBuffer, dwInLength, 2, nCmpLevel);
|
|
return 0;
|
|
}
|
|
|
|
int Decompress_wave_stereo(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
*pdwOutLength = DecompressWave((unsigned char *)pbOutBuffer, *pdwOutLength, (unsigned char *)pbInBuffer, dwInLength, 2);
|
|
return 1;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* The "01" (de)compression is the Huffman (de)compression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
// 1500F4C0
|
|
int Compress_huff(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int /* nCmpLevel */)
|
|
{
|
|
THuffmannTree ht; // Huffmann tree for compression
|
|
TOutputStream os; // Output stream
|
|
|
|
// Initialize output stream
|
|
os.pbOutBuffer = (unsigned char *)pbOutBuffer;
|
|
os.dwOutSize = *pdwOutLength;
|
|
os.pbOutPos = (unsigned char *)pbOutBuffer;
|
|
os.dwBitBuff = 0;
|
|
os.nBits = 0;
|
|
|
|
// Initialize the Huffmann tree for compression
|
|
ht.InitTree(true);
|
|
|
|
*pdwOutLength = ht.DoCompression(&os, (unsigned char *)pbInBuffer, dwInLength, *pCmpType);
|
|
|
|
// The following code is not necessary to run, because it has no
|
|
// effect on the output data. It only clears the huffmann tree, but when
|
|
// the tree is on the stack, who cares ?
|
|
// ht.UninitTree();
|
|
return 0;
|
|
}
|
|
|
|
// 1500F5F0
|
|
int Decompress_huff(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int /* dwInLength */)
|
|
{
|
|
THuffmannTree ht;
|
|
TInputStream is;
|
|
|
|
// Initialize input stream
|
|
// is.pbInBuffer = (unsigned char *)pbInBuffer;
|
|
is.dwBitBuff = BSWAP_INT32_UNSIGNED(*(unsigned long *)pbInBuffer);
|
|
pbInBuffer += sizeof(unsigned long);
|
|
is.pbInBuffer = (unsigned char *)pbInBuffer;
|
|
is.nBits = 32;
|
|
|
|
// Initialize the Huffmann tree for compression
|
|
ht.InitTree(false);
|
|
*pdwOutLength = ht.DoDecompression((unsigned char *)pbOutBuffer, *pdwOutLength, &is);
|
|
|
|
// The following code is not necessary to run, because it has no
|
|
// effect on the output data. It only clears the huffmann tree, but when
|
|
// the tree is on the stack, who cares ?
|
|
// ht.UninitTree();
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* The "02" (de)compression is the ZLIB (de)compression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
int Compress_zlib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * /* pCmpType */, int /* nCmpLevel */)
|
|
{
|
|
z_stream z; // Stream information for zlib
|
|
int nResult;
|
|
|
|
// Fill the stream structure for zlib
|
|
z.next_in = (Bytef *)pbInBuffer;
|
|
z.avail_in = (uInt)dwInLength;
|
|
z.total_in = dwInLength;
|
|
z.next_out = (Bytef *)pbOutBuffer;
|
|
z.avail_out = *pdwOutLength;
|
|
z.total_out = 0;
|
|
z.zalloc = NULL;
|
|
z.zfree = NULL;
|
|
|
|
// Initialize the compression structure. Storm.dll uses zlib version 1.1.3
|
|
*pdwOutLength = 0;
|
|
if((nResult = deflateInit(&z, Z_DEFAULT_COMPRESSION)) == 0)
|
|
{
|
|
// Call zlib to compress the data
|
|
nResult = deflate(&z, Z_FINISH);
|
|
|
|
if(nResult == Z_OK || nResult == Z_STREAM_END)
|
|
*pdwOutLength = z.total_out;
|
|
|
|
deflateEnd(&z);
|
|
}
|
|
return nResult;
|
|
}
|
|
|
|
int Decompress_zlib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
z_stream z; // Stream information for zlib
|
|
int nResult;
|
|
|
|
// Fill the stream structure for zlib
|
|
z.next_in = (Bytef *)pbInBuffer;
|
|
z.avail_in = (uInt)dwInLength;
|
|
z.total_in = dwInLength;
|
|
z.next_out = (Bytef *)pbOutBuffer;
|
|
z.avail_out = *pdwOutLength;
|
|
z.total_out = 0;
|
|
z.zalloc = NULL;
|
|
z.zfree = NULL;
|
|
|
|
// Initialize the decompression structure. Storm.dll uses zlib version 1.1.3
|
|
if((nResult = inflateInit(&z)) == 0)
|
|
{
|
|
// Call zlib to decompress the data
|
|
nResult = inflate(&z, Z_FINISH);
|
|
*pdwOutLength = z.total_out;
|
|
inflateEnd(&z);
|
|
}
|
|
return nResult;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* The "08" (de)compression is the Pkware DCL (de)compression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
int Compress_pklib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int /* nCmpLevel */)
|
|
{
|
|
TDataInfo Info; // Data information
|
|
char * work_buf = ALLOCMEM(char, CMP_BUFFER_SIZE);// Pklib's work buffer
|
|
unsigned int dict_size; // Dictionary size
|
|
unsigned int ctype; // Compression type
|
|
|
|
// Fill data information structure
|
|
Info.pInBuff = pbInBuffer;
|
|
Info.nInPos = 0;
|
|
Info.nInBytes = dwInLength;
|
|
Info.pOutBuff = pbOutBuffer;
|
|
Info.nOutPos = 0;
|
|
Info.nMaxOut = *pdwOutLength;
|
|
|
|
// Set the compression type and dictionary size
|
|
ctype = (*pCmpType == 2) ? CMP_ASCII : CMP_BINARY;
|
|
if (dwInLength < 0x600)
|
|
dict_size = 0x400;
|
|
else if(0x600 <= dwInLength && dwInLength < 0xC00)
|
|
dict_size = 0x800;
|
|
else
|
|
dict_size = 0x1000;
|
|
|
|
// Do the compression
|
|
implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size);
|
|
*pdwOutLength = Info.nOutPos;
|
|
FREEMEM(work_buf);
|
|
return 0;
|
|
}
|
|
|
|
int Decompress_pklib(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
TDataInfo Info; // Data information
|
|
char * work_buf = ALLOCMEM(char, EXP_BUFFER_SIZE);// Pklib's work buffer
|
|
|
|
// Fill data information structure
|
|
Info.pInBuff = pbInBuffer;
|
|
Info.nInPos = 0;
|
|
Info.nInBytes = dwInLength;
|
|
Info.pOutBuff = pbOutBuffer;
|
|
Info.nOutPos = 0;
|
|
Info.nMaxOut = *pdwOutLength;
|
|
|
|
// Do the decompression
|
|
explode(ReadInputData, WriteOutputData, work_buf, &Info);
|
|
|
|
// Fix: If PKLIB is unable to decompress the data, they are uncompressed
|
|
if(Info.nOutPos == 0)
|
|
{
|
|
Info.nOutPos = min(*pdwOutLength, dwInLength);
|
|
memcpy(pbOutBuffer, pbInBuffer, Info.nOutPos);
|
|
}
|
|
|
|
*pdwOutLength = Info.nOutPos;
|
|
FREEMEM(work_buf);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* The "10" (de)compression is the Bzip2 (de)compression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
int Compress_bzip2(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int * pCmpType, int nCmpLevel)
|
|
{
|
|
bz_stream strm;
|
|
int blockSize100k;
|
|
int workFactor = 30;
|
|
|
|
// Keep compiler happy
|
|
nCmpLevel = nCmpLevel;
|
|
|
|
// Initialize the BZLIB compression
|
|
strm.bzalloc = NULL;
|
|
strm.bzfree = NULL;
|
|
|
|
// Adjust the block size
|
|
blockSize100k = *pCmpType;
|
|
if(blockSize100k < 1 || blockSize100k > 9)
|
|
blockSize100k = 9;
|
|
|
|
// Blizzard uses 9 as blockSize100k, (0 as workFactor)
|
|
if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == 0)
|
|
{
|
|
strm.next_in = pbInBuffer;
|
|
strm.avail_in = dwInLength;
|
|
strm.next_out = pbOutBuffer;
|
|
strm.avail_out = *pdwOutLength;
|
|
|
|
// Perform the compression
|
|
while(BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH) != BZ_STREAM_END);
|
|
|
|
// Put the stream into idle state
|
|
BZ2_bzCompressEnd(&strm);
|
|
*pdwOutLength = strm.total_out_lo32;
|
|
}
|
|
else
|
|
{
|
|
*pdwOutLength = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Decompress_bzip2(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
bz_stream strm;
|
|
|
|
// Initialize the BZLIB decompression
|
|
strm.bzalloc = NULL;
|
|
strm.bzfree = NULL;
|
|
if(BZ2_bzDecompressInit(&strm, 0, 0) == 0)
|
|
{
|
|
strm.next_in = pbInBuffer;
|
|
strm.avail_in = dwInLength;
|
|
strm.next_out = pbOutBuffer;
|
|
strm.avail_out = *pdwOutLength;
|
|
|
|
// Perform the decompression
|
|
while(BZ2_bzDecompress(&strm) != BZ_STREAM_END);
|
|
|
|
// Put the stream into idle state
|
|
BZ2_bzDecompressEnd(&strm);
|
|
*pdwOutLength = strm.total_out_lo32;
|
|
}
|
|
else
|
|
{
|
|
// Set zero output length
|
|
*pdwOutLength = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* SCompCompress */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
// This table contains compress functions which can be applied to
|
|
// uncompressed blocks. Each bit set means the corresponding
|
|
// compression method/function must be applied.
|
|
//
|
|
// WAVes compression Data compression
|
|
// ------------------ -------------------
|
|
// 1st block - 0x08 0x08 (D, HF, W2, SC, D2)
|
|
// Rest blocks - 0x81 0x02 (W3)
|
|
|
|
static TCompressTable cmp_table[] =
|
|
{
|
|
{MPQ_COMPRESSION_WAVE_MONO, Compress_wave_mono}, // IMA ADPCM mono compression
|
|
{MPQ_COMPRESSION_WAVE_STEREO, Compress_wave_stereo}, // IMA ADPCM stereo compression
|
|
{MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
|
|
{MPQ_COMPRESSION_ZLIB, Compress_zlib}, // Compression with the "zlib" library
|
|
{MPQ_COMPRESSION_PKWARE, Compress_pklib}, // Compression with Pkware DCL
|
|
{MPQ_COMPRESSION_BZIP2, Compress_bzip2} // Compression Bzip2 library
|
|
};
|
|
|
|
int WINAPI SCompCompress(char * pbCompressed, int * pdwOutLength, char * pbUncompressed, int dwInLength,
|
|
int uCompressions, int nCmpType, int nCmpLevel)
|
|
{
|
|
char * pbTempBuff = NULL; // Temporary storage for decompressed data
|
|
char * pbOutput = pbCompressed; // Current output buffer
|
|
char * pbInput; // Current input buffer
|
|
int uCompressions2;
|
|
int dwCompressCount = 0;
|
|
int dwDoneCount = 0;
|
|
int dwOutSize = 0;
|
|
int dwInSize = dwInLength;
|
|
int dwEntries = (sizeof(cmp_table) / sizeof(TCompressTable));
|
|
int nResult = 1;
|
|
int i;
|
|
|
|
// Check for valid parameters
|
|
if(!pdwOutLength || *pdwOutLength < dwInLength || !pbCompressed || !pbUncompressed)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// Count the compressions
|
|
for(i = 0, uCompressions2 = uCompressions; i < dwEntries; i++)
|
|
{
|
|
if(uCompressions & cmp_table[i].dwMask)
|
|
dwCompressCount++;
|
|
|
|
uCompressions2 &= ~cmp_table[i].dwMask;
|
|
}
|
|
|
|
// If a compression remains, do nothing
|
|
if(uCompressions2 != 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// If more that one compression, allocate intermediate buffer
|
|
if(dwCompressCount >= 2)
|
|
pbTempBuff = ALLOCMEM(char, *pdwOutLength + 1);
|
|
|
|
// Perform the compressions
|
|
pbInput = pbUncompressed;
|
|
dwInSize = dwInLength;
|
|
for(i = 0, uCompressions2 = uCompressions; i < dwEntries; i++)
|
|
{
|
|
if(uCompressions2 & cmp_table[i].dwMask)
|
|
{
|
|
// Set the right output buffer
|
|
dwCompressCount--;
|
|
pbOutput = (dwCompressCount & 1) ? pbTempBuff : pbCompressed;
|
|
|
|
// Perform the partial compression
|
|
dwOutSize = *pdwOutLength - 1;
|
|
|
|
cmp_table[i].Compress(pbOutput + 1, &dwOutSize, pbInput, dwInSize, &nCmpType, nCmpLevel);
|
|
if(dwOutSize == 0)
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
*pdwOutLength = 0;
|
|
nResult = 0;
|
|
break;
|
|
}
|
|
|
|
// If the compression failed, copy the block instead
|
|
if(dwOutSize >= dwInSize - 1)
|
|
{
|
|
if(dwDoneCount > 0)
|
|
pbOutput++;
|
|
|
|
memcpy(pbOutput, pbInput, dwInSize);
|
|
pbInput = pbOutput;
|
|
uCompressions &= ~cmp_table[i].dwMask;
|
|
dwOutSize = dwInSize;
|
|
}
|
|
else
|
|
{
|
|
pbInput = pbOutput + 1;
|
|
dwInSize = dwOutSize;
|
|
dwDoneCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the compressed data to the correct output buffer
|
|
if(nResult != 0)
|
|
{
|
|
if(uCompressions && (dwInSize + 1) < *pdwOutLength)
|
|
{
|
|
if(pbOutput != pbCompressed && pbOutput != pbCompressed + 1)
|
|
memcpy(pbCompressed, pbOutput, dwInSize);
|
|
*pbCompressed = (char)uCompressions;
|
|
*pdwOutLength = dwInSize + 1;
|
|
}
|
|
else
|
|
{
|
|
memmove(pbCompressed, pbUncompressed, dwInSize);
|
|
*pdwOutLength = dwInSize;
|
|
}
|
|
}
|
|
|
|
// Cleanup and return
|
|
if(pbTempBuff != NULL)
|
|
FREEMEM(pbTempBuff);
|
|
return nResult;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* SCompDecompress */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
// This table contains decompress functions which can be applied to
|
|
// uncompressed blocks. The compression mask is stored in the first byte
|
|
// of compressed block
|
|
static TDecompressTable dcmp_table[] =
|
|
{
|
|
{MPQ_COMPRESSION_BZIP2, Decompress_bzip2}, // Decompression with Bzip2 library
|
|
{MPQ_COMPRESSION_PKWARE, Decompress_pklib}, // Decompression with Pkware Data Compression Library
|
|
{MPQ_COMPRESSION_ZLIB, Decompress_zlib}, // Decompression with the "zlib" library
|
|
{MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
|
|
{MPQ_COMPRESSION_WAVE_STEREO, Decompress_wave_stereo}, // IMA ADPCM stereo decompression
|
|
{MPQ_COMPRESSION_WAVE_MONO, Decompress_wave_mono} // IMA ADPCM mono decompression
|
|
};
|
|
|
|
int WINAPI SCompDecompress(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength)
|
|
{
|
|
char * pbTempBuff = NULL; // Temporary storage for decompressed data
|
|
char * pbWorkBuff = NULL; // Where to store decompressed data
|
|
int dwOutLength = *pdwOutLength; // For storage number of output bytes
|
|
unsigned fDecompressions1; // Decompressions applied to the block
|
|
unsigned fDecompressions2; // Just another copy of decompressions applied to the block
|
|
int dwCount = 0; // Counter for every use
|
|
int dwEntries = (sizeof(dcmp_table) / sizeof(TDecompressTable));
|
|
int nResult = 1;
|
|
int i;
|
|
|
|
// If the input length is the same as output, do nothing.
|
|
if(dwInLength == dwOutLength)
|
|
{
|
|
if(pbInBuffer == pbOutBuffer)
|
|
return 1;
|
|
|
|
memcpy(pbOutBuffer, pbInBuffer, dwInLength);
|
|
*pdwOutLength = dwInLength;
|
|
return 1;
|
|
}
|
|
|
|
// Get applied compression types and decrement data length
|
|
fDecompressions1 = fDecompressions2 = (unsigned char)*pbInBuffer++;
|
|
dwInLength--;
|
|
|
|
// Search decompression table type and get all types of compression
|
|
for(i = 0; i < dwEntries; i++)
|
|
{
|
|
// We have to apply this decompression ?
|
|
if(fDecompressions1 & dcmp_table[i].dwMask)
|
|
dwCount++;
|
|
|
|
// Clear this flag from temporary variable.
|
|
fDecompressions2 &= ~dcmp_table[i].dwMask;
|
|
}
|
|
|
|
// Check if there is some method unhandled
|
|
// (E.g. compressed by future versions)
|
|
if(fDecompressions2 != 0)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
// If there is more than only one compression, we have to allocate extra buffer
|
|
if(dwCount >= 2)
|
|
pbTempBuff = ALLOCMEM(char, dwOutLength);
|
|
|
|
// Apply all decompressions
|
|
for(i = 0, dwCount = 0; i < dwEntries; i++)
|
|
{
|
|
// If not used this kind of compression, skip the loop
|
|
if(fDecompressions1 & dcmp_table[i].dwMask)
|
|
{
|
|
// If odd case, use target buffer for output, otherwise use allocated tempbuffer
|
|
pbWorkBuff = (dwCount++ & 1) ? pbTempBuff : pbOutBuffer;
|
|
dwOutLength = *pdwOutLength;
|
|
|
|
// Decompress buffer using corresponding function
|
|
dcmp_table[i].Decompress(pbWorkBuff, &dwOutLength, pbInBuffer, dwInLength);
|
|
if(dwOutLength == 0)
|
|
{
|
|
SetLastError(ERROR_GEN_FAILURE);
|
|
nResult = 0;
|
|
break;
|
|
}
|
|
|
|
// Move output length to src length for next compression
|
|
dwInLength = dwOutLength;
|
|
pbInBuffer = pbWorkBuff;
|
|
}
|
|
}
|
|
|
|
// If output buffer is not the same like target buffer, we have to copy data
|
|
if(nResult != 0)
|
|
{
|
|
if(pbWorkBuff != pbOutBuffer)
|
|
memcpy(pbOutBuffer, pbInBuffer, dwOutLength);
|
|
|
|
}
|
|
|
|
// Delete temporary buffer, if necessary
|
|
if(pbTempBuff != NULL)
|
|
FREEMEM(pbTempBuff);
|
|
|
|
*pdwOutLength = dwOutLength;
|
|
return nResult;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* SCompSetDataCompression */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
int WINAPI SCompSetDataCompression(int nDataCompression)
|
|
{
|
|
int nValidMask = (MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_PKWARE | MPQ_COMPRESSION_BZIP2);
|
|
|
|
if((nDataCompression & nValidMask) != nDataCompression)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
SetDataCompression(nDataCompression);
|
|
return TRUE;
|
|
}
|
|
|
|
|