diff options
Diffstat (limited to 'StormLib/src/SCompression.cpp')
| -rw-r--r-- | StormLib/src/SCompression.cpp | 1124 |
1 files changed, 0 insertions, 1124 deletions
diff --git a/StormLib/src/SCompression.cpp b/StormLib/src/SCompression.cpp deleted file mode 100644 index 1d0f67979..000000000 --- a/StormLib/src/SCompression.cpp +++ /dev/null @@ -1,1124 +0,0 @@ -/*****************************************************************************/ -/* 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 "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local structures - -// Information about the input and output buffers for pklib -typedef struct -{ - unsigned char * pbInBuff; // Pointer to input data buffer - unsigned char * pbInBuffEnd; // End of the input buffer - unsigned char * pbOutBuff; // Pointer to output data buffer - unsigned char * pbOutBuffEnd; // Pointer to output data buffer -} TDataInfo; - -// Prototype of the compression function -// Function doesn't return an error. A success means that the size of compressed buffer -// is lower than size of uncompressed buffer. -typedef void (*COMPRESS)( - void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored - int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer - void * pvInBuffer, // [in] Pointer to the buffer with data to compress - int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer - int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression - int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero. - -// Prototype of the decompression function -// Returns 1 if success, 0 if failure -typedef int (*DECOMPRESS)( - void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data - int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer - // [out] Contains length of the decompressed data - void * pvInBuffer, // [in] Pointer to data to be decompressed - int cbInBuffer); // [in] Length of the data to be decompressed - -// Table of compression functions -typedef struct -{ - unsigned long uMask; // Compression mask - COMPRESS Compress; // Compression function -} TCompressTable; - -// Table of decompression functions -typedef struct -{ - unsigned long uMask; // Decompression bit - DECOMPRESS Decompress; // Decompression function -} TDecompressTable; - - -/*****************************************************************************/ -/* */ -/* Support for Huffman compression (0x01) */ -/* */ -/*****************************************************************************/ - -void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - THuffmannTree ht(true); - TOutputStream os(pvOutBuffer, *pcbOutBuffer); - - STORMLIB_UNUSED(nCmpLevel); - *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType); -} - -int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - THuffmannTree ht(false); - TInputStream is(pvInBuffer, cbInBuffer); - - *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is); - return (*pcbOutBuffer == 0) ? 0 : 1; -} - -/******************************************************************************/ -/* */ -/* Support for ZLIB compression (0x02) */ -/* */ -/******************************************************************************/ - -void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - z_stream z; // Stream information for zlib - int windowBits; - int nResult; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Fill the stream structure for zlib - z.next_in = (Bytef *)pvInBuffer; - z.avail_in = (uInt)cbInBuffer; - z.total_in = cbInBuffer; - z.next_out = (Bytef *)pvOutBuffer; - z.avail_out = *pcbOutBuffer; - z.total_out = 0; - z.zalloc = NULL; - z.zfree = NULL; - - // Determine the proper window bits (WoW.exe build 12694) - if(cbInBuffer <= 0x100) - windowBits = 8; - else if(cbInBuffer <= 0x200) - windowBits = 9; - else if(cbInBuffer <= 0x400) - windowBits = 10; - else if(cbInBuffer <= 0x800) - windowBits = 11; - else if(cbInBuffer <= 0x1000) - windowBits = 12; - else if(cbInBuffer <= 0x2000) - windowBits = 13; - else if(cbInBuffer <= 0x4000) - windowBits = 14; - else - windowBits = 15; - - // Initialize the compression. - // Storm.dll uses zlib version 1.1.3 - // Wow.exe uses zlib version 1.2.3 - nResult = deflateInit2(&z, - 6, // Compression level used by WoW MPQs - Z_DEFLATED, - windowBits, - 8, - Z_DEFAULT_STRATEGY); - if(nResult == Z_OK) - { - // Call zlib to compress the data - nResult = deflate(&z, Z_FINISH); - - if(nResult == Z_OK || nResult == Z_STREAM_END) - *pcbOutBuffer = z.total_out; - - deflateEnd(&z); - } -} - -int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - z_stream z; // Stream information for zlib - int nResult; - - // Fill the stream structure for zlib - z.next_in = (Bytef *)pvInBuffer; - z.avail_in = (uInt)cbInBuffer; - z.total_in = cbInBuffer; - z.next_out = (Bytef *)pvOutBuffer; - z.avail_out = *pcbOutBuffer; - 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)) == Z_OK) - { - // Call zlib to decompress the data - nResult = inflate(&z, Z_FINISH); - *pcbOutBuffer = z.total_out; - inflateEnd(&z); - } - - return (nResult >= Z_OK); -} - -/******************************************************************************/ -/* */ -/* Support functions for PKWARE Data Compression Library compression (0x08) */ -/* */ -/******************************************************************************/ - -// 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 = (unsigned int)(pInfo->pbInBuffEnd - pInfo->pbInBuff); - 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->pbInBuff, nToRead); - pInfo->pbInBuff += nToRead; - assert(pInfo->pbInBuff <= pInfo->pbInBuffEnd); - 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 = (unsigned int)(pInfo->pbOutBuffEnd - pInfo->pbOutBuff); - 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->pbOutBuff, buf, nToWrite); - pInfo->pbOutBuff += nToWrite; - assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd); -} - -static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - TDataInfo Info; // Data information - char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer - unsigned int dict_size; // Dictionary size - unsigned int ctype = CMP_BINARY; // Compression type - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Handle no-memory condition - if(work_buf != NULL) - { - // Fill data information structure - memset(work_buf, 0, CMP_BUFFER_SIZE); - Info.pbInBuff = (unsigned char *)pvInBuffer; - Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; - Info.pbOutBuff = (unsigned char *)pvOutBuffer; - Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; - - // - // Set the dictionary size - // - // Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 - // Starcraft I uses the variable dictionary size based on algorithm below - // - - if (cbInBuffer < 0x600) - dict_size = CMP_IMPLODE_DICT_SIZE1; - else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) - dict_size = CMP_IMPLODE_DICT_SIZE2; - else - dict_size = CMP_IMPLODE_DICT_SIZE3; - - // Do the compression - if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) - *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); - - STORM_FREE(work_buf); - } -} - -static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - TDataInfo Info; // Data information - char * work_buf; - int nResult = 0; - - // Allocate Pklib's work buffer - if((work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE)) != NULL) - { - // Fill data information structure - memset(work_buf, 0, EXP_BUFFER_SIZE); - Info.pbInBuff = (unsigned char *)pvInBuffer; - Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; - Info.pbOutBuff = (unsigned char *)pvOutBuffer; - Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; - - // Do the decompression - if(explode(ReadInputData, WriteOutputData, work_buf, &Info) == CMP_NO_ERROR) - nResult = 1; - - // Give away the number of decompressed bytes - *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); - STORM_FREE(work_buf); - } - - return nResult; -} - -/******************************************************************************/ -/* */ -/* Support for Bzip2 compression (0x10) */ -/* */ -/******************************************************************************/ - -static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - bz_stream strm; - int blockSize100k = 9; - int workFactor = 30; - int bzError; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Initialize the BZIP2 compression - strm.bzalloc = NULL; - strm.bzfree = NULL; - strm.opaque = NULL; - - // Blizzard uses 9 as blockSize100k, (0x30 as workFactor) - // Last checked on Starcraft II - if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK) - { - strm.next_in = (char *)pvInBuffer; - strm.avail_in = cbInBuffer; - strm.next_out = (char *)pvOutBuffer; - strm.avail_out = *pcbOutBuffer; - - // Perform the compression - for(;;) - { - bzError = BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH); - if(bzError == BZ_STREAM_END || bzError < 0) - break; - } - - // Put the stream into idle state - BZ2_bzCompressEnd(&strm); - - if(bzError > 0) - *pcbOutBuffer = strm.total_out_lo32; - } -} - -static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - bz_stream strm; - int nResult; - - // Initialize the BZIP2 decompression - strm.next_in = (char *)pvInBuffer; - strm.avail_in = cbInBuffer; - strm.next_out = (char *)pvOutBuffer; - strm.avail_out = *pcbOutBuffer; - strm.bzalloc = NULL; - strm.bzfree = NULL; - strm.opaque = NULL; - - // Initialize decompression - if((nResult = BZ2_bzDecompressInit(&strm, 0, 0)) == BZ_OK) - { - // Perform the decompression - nResult = BZ2_bzDecompress(&strm); - *pcbOutBuffer = strm.total_out_lo32; - BZ2_bzDecompressEnd(&strm); - } - - return (nResult >= BZ_OK); -} - -/******************************************************************************/ -/* */ -/* Support functions for LZMA compression (0x12) */ -/* */ -/******************************************************************************/ - -#define LZMA_HEADER_SIZE (1 + LZMA_PROPS_SIZE + 8) - -static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 /* outSize */) -{ - return SZ_OK; -} - -static void * LZMA_Callback_Alloc(void *p, size_t size) -{ - p = p; - return STORM_ALLOC(BYTE, size); -} - -/* address can be 0 */ -static void LZMA_Callback_Free(void *p, void *address) -{ - p = p; - if(address != NULL) - STORM_FREE(address); -} - -// -// Note: So far, I haven't seen any files compressed by LZMA. -// This code haven't been verified against code ripped from Starcraft II Beta, -// but we know that Starcraft LZMA decompression code is able to decompress -// the data compressed by StormLib. -// - -static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - ICompressProgress Progress; - CLzmaEncProps props; - ISzAlloc SzAlloc; - Byte * pbOutBuffer = (Byte *)pvOutBuffer; - Byte * destBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - Byte encodedProps[LZMA_PROPS_SIZE]; - size_t encodedPropsSize = LZMA_PROPS_SIZE; - SRes nResult; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Fill the callbacks in structures - Progress.Progress = LZMA_Callback_Progress; - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Initialize properties - LzmaEncProps_Init(&props); - - // Perform compression - destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE; - destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; - nResult = LzmaEncode(destBuffer, - &destLen, - (Byte *)pvInBuffer, - srcLen, - &props, - encodedProps, - &encodedPropsSize, - 0, - &Progress, - &SzAlloc, - &SzAlloc); - if(nResult != SZ_OK) - return; - - // If we failed to compress the data - if(destLen >= (SizeT)(*pcbOutBuffer - LZMA_HEADER_SIZE)) - return; - - // Write "useFilter" variable. Blizzard MPQ must not use filter. - *pbOutBuffer++ = 0; - - // Copy the encoded properties to the output buffer - memcpy(pvOutBuffer, encodedProps, encodedPropsSize); - pbOutBuffer += encodedPropsSize; - - // Copy the size of the data - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x00); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x08); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x10); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x18); - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - - // Give the size of the data to the caller - *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE); -} - -static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - ELzmaStatus LzmaStatus; - ISzAlloc SzAlloc; - Byte * destBuffer = (Byte *)pvOutBuffer; - Byte * srcBuffer = (Byte *)pvInBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - SRes nResult; - - // There must be at least 0x0E bytes in the buffer - if(srcLen <= LZMA_HEADER_SIZE) - return 0; - - // We only accept blocks that have no filter used - if(*srcBuffer != 0) - return 0; - - // Fill the callbacks in structures - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Perform compression - srcLen = cbInBuffer - LZMA_HEADER_SIZE; - nResult = LzmaDecode(destBuffer, - &destLen, - srcBuffer + LZMA_HEADER_SIZE, - &srcLen, - srcBuffer + 1, - LZMA_PROPS_SIZE, - LZMA_FINISH_END, - &LzmaStatus, - &SzAlloc); - if(nResult != SZ_OK) - return 0; - - *pcbOutBuffer = (unsigned int)destLen; - return 1; -} - -static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - ELzmaStatus LzmaStatus; - ISzAlloc SzAlloc; - Byte * destBuffer = (Byte *)pvOutBuffer; - Byte * srcBuffer = (Byte *)pvInBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - SRes nResult; - BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01}; - - // There must be at least 0x0E bytes in the buffer - if(srcLen <= sizeof(LZMA_Props)) - return 0; - - // Verify the props header - if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props))) - return 0; - - // Fill the callbacks in structures - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Perform compression - srcLen = cbInBuffer - sizeof(LZMA_Props); - nResult = LzmaDecode(destBuffer, - &destLen, - srcBuffer + sizeof(LZMA_Props), - &srcLen, - srcBuffer, - sizeof(LZMA_Props), - LZMA_FINISH_END, - &LzmaStatus, - &SzAlloc); - if(nResult != SZ_OK) - return 0; - - *pcbOutBuffer = (unsigned int)destLen; - return 1; -} - -/******************************************************************************/ -/* */ -/* Support functions for SPARSE compression (0x20) */ -/* */ -/******************************************************************************/ - -void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} - -int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} - -/******************************************************************************/ -/* */ -/* Support for ADPCM mono compression (0x40) */ -/* */ -/******************************************************************************/ - -static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Prepare the compression level for Huffmann compression, - // which will be called as next step - if(0 < nCmpLevel && nCmpLevel <= 2) - { - nCmpLevel = 4; - *pCmpType = 6; - } - else if(nCmpLevel == 3) - { - nCmpLevel = 6; - *pCmpType = 8; - } - else - { - nCmpLevel = 5; - *pCmpType = 7; - } - *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel); -} - -static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); - return 1; -} - -/******************************************************************************/ -/* */ -/* Support for ADPCM stereo compression (0x80) */ -/* */ -/******************************************************************************/ - -static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Prepare the compression level for Huffmann compression, - // which will be called as next step - if(0 < nCmpLevel && nCmpLevel <= 2) - { - nCmpLevel = 4; - *pCmpType = 6; - } - else if(nCmpLevel == 3) - { - nCmpLevel = 6; - *pCmpType = 8; - } - else - { - nCmpLevel = 5; - *pCmpType = 7; - } - *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel); -} - -static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); - return 1; -} - -/*****************************************************************************/ -/* */ -/* SCompImplode */ -/* */ -/*****************************************************************************/ - -int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - int cbOutBuffer; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // Perform the compression - cbOutBuffer = *pcbOutBuffer; - Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0); - - // If the compression was unsuccessful, copy the data as-is - if(cbOutBuffer >= *pcbOutBuffer) - { - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - cbOutBuffer = *pcbOutBuffer; - } - - *pcbOutBuffer = cbOutBuffer; - return 1; -} - -/*****************************************************************************/ -/* */ -/* SCompExplode */ -/* */ -/*****************************************************************************/ - -int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - int cbOutBuffer; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // If the input length is the same as output length, do nothing. - cbOutBuffer = *pcbOutBuffer; - if(cbInBuffer == cbOutBuffer) - { - // If the buffers are equal, don't copy anything. - if(pvInBuffer == pvOutBuffer) - return 1; - - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Perform decompression - if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer)) - { - SetLastError(ERROR_FILE_CORRUPT); - return 0; - } - - *pcbOutBuffer = cbOutBuffer; - return 1; -} - -/*****************************************************************************/ -/* */ -/* SCompCompress */ -/* */ -/*****************************************************************************/ - -// This table contains compress functions which can be applied to -// uncompressed data. Each bit means the corresponding -// compression method/function must be applied. -// -// WAVes compression Data compression -// ------------------ ------------------- -// 1st sector - 0x08 0x08 (D, HF, W2, SC, D2) -// Next sectors - 0x81 0x02 (W3) - -static TCompressTable cmp_table[] = -{ - {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression - {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression - {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_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(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel) -{ - COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially - unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array - unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data - unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer - unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer - int nCompressCount = 0; - int nCompressIndex = 0; - int nAtLeastOneCompressionDone = 0; - int cbOutBuffer = 0; - int cbInLength = cbInBuffer; - int nResult = 1; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // Zero input length brings zero output length - if(cbInBuffer == 0) - { - *pcbOutBuffer = 0; - return true; - } - - // Setup the compression function array - if(uCompressionMask == MPQ_COMPRESSION_LZMA) - { - CompressFuncArray[0] = Compress_LZMA; - CompressByte[0] = (char)uCompressionMask; - nCompressCount = 1; - } - else - { - // Fill the compressions array - for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++) - { - // If the mask agrees, insert the compression function to the array - if(uCompressionMask & cmp_table[i].uMask) - { - CompressFuncArray[nCompressCount] = cmp_table[i].Compress; - CompressByte[nCompressCount] = (unsigned char)cmp_table[i].uMask; - uCompressionMask &= ~cmp_table[i].uMask; - nCompressCount++; - } - } - - // If at least one of the compressions remaing unknown, return an error - if(uCompressionMask != 0) - { - SetLastError(ERROR_NOT_SUPPORTED); - return 0; - } - } - - // If there is at least one compression, do it - if(nCompressCount > 0) - { - // If we need to do more than 1 compression, allocate intermediate buffer - if(nCompressCount > 1) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Get the current compression index - nCompressIndex = nCompressCount - 1; - - // Perform all compressions in the array - for(int i = 0; i < nCompressCount; i++) - { - // Choose the proper output buffer - pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; - nCompressIndex--; - - // Perform the (next) compression - // Note that if the compression method is unable to compress the input data block - // by at least 2 bytes, we consider it as failure and will use source data instead - cbOutBuffer = *pcbOutBuffer - 1; - CompressFuncArray[i](pbOutput + 1, &cbOutBuffer, pbInput, cbInLength, &nCmpType, nCmpLevel); - - // If the compression failed, we copy the input buffer as-is. - // Note that there is one extra byte at the end of the intermediate buffer, so it should be OK - if(cbOutBuffer > (cbInLength - 2)) - { - memcpy(pbOutput + nAtLeastOneCompressionDone, pbInput, cbInLength); - cbOutBuffer = cbInLength; - } - else - { - // Remember that we have done at least one compression - nAtLeastOneCompressionDone = 1; - uCompressionMask |= CompressByte[i]; - } - - // Now point input buffer to the output buffer - pbInput = pbOutput + nAtLeastOneCompressionDone; - cbInLength = cbOutBuffer; - } - - // If at least one compression succeeded, put the compression - // mask to the begin of the output buffer - if(nAtLeastOneCompressionDone) - *pbOutBuffer = (unsigned char)uCompressionMask; - *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone; - } - else - { - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - *pcbOutBuffer = cbInBuffer; - } - - // Cleanup and return - if(pbWorkBuffer != NULL) - STORM_FREE(pbWorkBuffer); - return nResult; -} - -/*****************************************************************************/ -/* */ -/* SCompDecompress */ -/* */ -/*****************************************************************************/ - -// This table contains decompress functions which can be applied to -// uncompressed data. The compression mask is stored in the first byte -// of compressed data -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_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression - {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression - {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression -}; - -int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - unsigned char * pbWorkBuffer = NULL; - unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; - unsigned char * pbOutput = (unsigned char *)pvOutBuffer; - unsigned char * pbInput; - unsigned uCompressionMask; // Decompressions applied to the data - unsigned uCompressionCopy; // Decompressions applied to the data - int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer - int cbInLength; // Current size of the input buffer - int nCompressCount = 0; // Number of compressions to be applied - int nCompressIndex = 0; - int nResult = 1; - - // Verify buffer sizes - if(cbOutBuffer < cbInBuffer || cbInBuffer < 1) - return 0; - - // If the input length is the same as output length, do nothing. - if(cbOutBuffer == cbInBuffer) - { - // If the buffers are equal, don't copy anything. - if(pvInBuffer != pvOutBuffer) - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Get applied compression types and decrement data length - uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; - cbInBuffer--; - - // Get current compressed data and length of it - pbInput = pbInBuffer; - cbInLength = cbInBuffer; - - // This compression function doesn't support LZMA - assert(uCompressionMask != MPQ_COMPRESSION_LZMA); - - // Parse the compression mask - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) - { - // If the mask agrees, insert the compression function to the array - if(uCompressionMask & dcmp_table[i].uMask) - { - uCompressionCopy &= ~dcmp_table[i].uMask; - nCompressCount++; - } - } - - // If at least one of the compressions remaing unknown, return an error - if(nCompressCount == 0 || uCompressionCopy != 0) - { - SetLastError(ERROR_NOT_SUPPORTED); - return 0; - } - - // If there is more than one compression, we have to allocate extra buffer - if(nCompressCount > 1) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Get the current compression index - nCompressIndex = nCompressCount - 1; - - // Apply all decompressions - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) - { - // Perform the (next) decompression - if(uCompressionMask & dcmp_table[i].uMask) - { - // Get the correct output buffer - pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; - nCompressIndex--; - - // Perform the decompression - cbOutBuffer = *pcbOutBuffer; - nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); - if(nResult == 0 || cbOutBuffer == 0) - { - SetLastError(ERROR_FILE_CORRUPT); - nResult = 0; - break; - } - - // Switch buffers - cbInLength = cbOutBuffer; - pbInput = pbOutput; - } - } - - // Put the length of the decompressed data to the output buffer - *pcbOutBuffer = cbOutBuffer; - - // Cleanup and return - if(pbWorkBuffer != NULL) - STORM_FREE(pbWorkBuffer); - return nResult; -} - -int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - DECOMPRESS pfnDecompress1 = NULL; - DECOMPRESS pfnDecompress2 = NULL; - unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; - int cbWorkBuffer = *pcbOutBuffer; - int nResult; - char CompressionMethod; - - // Verify buffer sizes - if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) - return 0; - - // If the outputbuffer is as big as input buffer, just copy the block - if(*pcbOutBuffer == cbInBuffer) - { - if(pvOutBuffer != pvInBuffer) - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Get the compression methods - CompressionMethod = *pbInBuffer++; - cbInBuffer--; - - // We only recognize a fixed set of compression methods - switch((unsigned char)CompressionMethod) - { - case MPQ_COMPRESSION_ZLIB: - pfnDecompress1 = Decompress_ZLIB; - break; - - case MPQ_COMPRESSION_PKWARE: - pfnDecompress1 = Decompress_PKLIB; - break; - - case MPQ_COMPRESSION_BZIP2: - pfnDecompress1 = Decompress_BZIP2; - break; - - case MPQ_COMPRESSION_LZMA: - pfnDecompress1 = Decompress_LZMA; - break; - - case MPQ_COMPRESSION_SPARSE: - pfnDecompress1 = Decompress_SPARSE; - break; - - case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB): - pfnDecompress1 = Decompress_ZLIB; - pfnDecompress2 = Decompress_SPARSE; - break; - - case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2): - pfnDecompress1 = Decompress_BZIP2; - pfnDecompress2 = Decompress_SPARSE; - break; - - // - // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO, - // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN - // is not supported by newer MPQs. - // - - case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN): - pfnDecompress1 = Decompress_huff; - pfnDecompress2 = Decompress_ADPCM_mono; - break; - - case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN): - pfnDecompress1 = Decompress_huff; - pfnDecompress2 = Decompress_ADPCM_stereo; - break; - - default: - SetLastError(ERROR_FILE_CORRUPT); - return 0; - } - - // If we have to use two decompressions, allocate temporary buffer - if(pfnDecompress2 != NULL) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Apply the first decompression method - nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer); - - // Apply the second decompression method, if any - if(pfnDecompress2 != NULL && nResult != 0) - { - cbInBuffer = cbWorkBuffer; - cbWorkBuffer = *pcbOutBuffer; - nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); - } - - // Supply the output buffer size - *pcbOutBuffer = cbWorkBuffer; - - // Free temporary buffer - if(pbWorkBuffer != pvOutBuffer) - STORM_FREE(pbWorkBuffer); - - if(nResult == 0) - SetLastError(ERROR_FILE_CORRUPT); - return nResult; -} - -/*****************************************************************************/ -/* */ -/* File decompression for MPK archives */ -/* */ -/*****************************************************************************/ - -int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} - |
