mirror of
				https://github.com/thunderbrewhq/thunderbrew
				synced 2025-10-31 00:06:05 +03:00 
			
		
		
		
	 f86f6d6d09
			
		
	
	
		f86f6d6d09
		
			
		
	
	
	
	
		
			
			* feat(app): add StormLib * feat(app): add OpenArchives * feat(util): update SFile to work with StormLib * feat(app): update SFile * feat(util): update SFile with logging (Windows only) * feat(ui): implemented termination w/o notice * chore(build): update StormLib * chore(util): replace std::string with SStr* functions * fix(stormlib): dwFlags argument for SFileOpenPatchArchive * chore(ui): add Script_* stubs * chore(util): clean up SFile::OpenEx * chore(build): update StormLib --------- Co-authored-by: Phaneron <superp00t@tutanota.com>
		
			
				
	
	
		
			1184 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1184 lines
		
	
	
		
			42 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 "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(pbOutBuffer, 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;
 | |
| }
 | |
| 
 | |
| /******************************************************************************/
 | |
| /*                                                                            */
 | |
| /*  Support for ADPCM mono & stereo (Starcraft I BETA - like)                 */
 | |
| /*                                                                            */
 | |
| /******************************************************************************/
 | |
| 
 | |
| static int Decompress_ADPCM1_sc1b(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
 | |
| {
 | |
|     *pcbOutBuffer = DecompressADPCM_SC1B(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static int Decompress_ADPCM2_sc1b(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
 | |
| {
 | |
|     *pcbOutBuffer = DecompressADPCM_SC1B(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 < _countof(cmp_table); 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                                                         */
 | |
| /*                                                                           */
 | |
| /*****************************************************************************/
 | |
| 
 | |
| // Decompression table specific for Starcraft I BETA
 | |
| // WAVE files are compressed by different ADPCM compression
 | |
| static TDecompressTable dcmp_table_sc_beta[] =
 | |
| {
 | |
|     {MPQ_COMPRESSION_PKWARE,       Decompress_PKLIB},        // Decompression with Pkware Data Compression Library
 | |
|     {MPQ_COMPRESSION_HUFFMANN,     Decompress_huff},         // Huffmann decompression
 | |
|     {0x10,                         Decompress_ADPCM1_sc1b},  // IMA ADPCM mono decompression
 | |
|     {0x20,                         Decompress_ADPCM2_sc1b},  // IMA ADPCM stereo decompression
 | |
| };
 | |
| 
 | |
| // 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
 | |
| };
 | |
| 
 | |
| static int SCompDecompressInternal(
 | |
|     TDecompressTable * table,
 | |
|     size_t table_length,
 | |
|     void * pvOutBuffer,
 | |
|     int * pcbOutBuffer,
 | |
|     void * pvInBuffer,
 | |
|     int cbInBuffer,
 | |
|     unsigned uValidMask = 0xFF)
 | |
| {
 | |
|     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 uCompressionMask1;             // Decompressions applied to the data
 | |
|     unsigned uCompressionMask2;              // 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
 | |
|     uCompressionMask1 = ((unsigned char)(*pbInBuffer++) & (uValidMask));
 | |
|     uCompressionMask2 = uCompressionMask1;
 | |
|     cbInBuffer--;
 | |
| 
 | |
|     // Get current compressed data and length of it
 | |
|     pbInput = pbInBuffer;
 | |
|     cbInLength = cbInBuffer;
 | |
| 
 | |
|     // This compression function doesn't support LZMA
 | |
|     assert(uCompressionMask1 != MPQ_COMPRESSION_LZMA);
 | |
| 
 | |
|     // Parse the compression mask
 | |
|     for(size_t i = 0; i < table_length; i++)
 | |
|     {
 | |
|         // If the mask agrees, insert the compression function to the array
 | |
|         if(uCompressionMask1 & table[i].uMask)
 | |
|         {
 | |
|             uCompressionMask2 &= ~table[i].uMask;
 | |
|             nCompressCount++;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // If at least one of the compressions remaing unknown, return an error
 | |
|     if(nCompressCount == 0 || uCompressionMask2 != 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 < table_length; i++)
 | |
|     {
 | |
|         // Perform the (next) decompression
 | |
|         if(uCompressionMask1 & table[i].uMask)
 | |
|         {
 | |
|             // Get the correct output buffer
 | |
|             pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
 | |
|             nCompressIndex--;
 | |
| 
 | |
|             // Perform the decompression
 | |
|             cbOutBuffer = *pcbOutBuffer;
 | |
|             nResult = 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 SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
 | |
| {
 | |
|     return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
 | |
| {
 | |
|     // MPQs version 2 use their own fixed list of compression flags.
 | |
|     if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
 | |
|     {
 | |
|         return SCompDecompress2(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
 | |
|     }
 | |
| 
 | |
|     // Starcraft BETA has specific decompression table.
 | |
|     if(ha->dwFlags & MPQ_FLAG_STARCRAFT_BETA)
 | |
|     {
 | |
|         return SCompDecompressInternal(dcmp_table_sc_beta, _countof(dcmp_table_sc_beta), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
 | |
|     }
 | |
| 
 | |
|     // Default: Use the common MPQ v1 decompression routine
 | |
|     return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
 | |
| }
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /*                                                                           */
 | |
| /*   File decompression for MPK archives                                     */
 | |
| /*                                                                           */
 | |
| /*****************************************************************************/
 | |
| 
 | |
| int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
 | |
| {
 | |
|     return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
 | |
| }
 | |
| 
 |