1 /*****************************************************************************/
2 /* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */
3 /*---------------------------------------------------------------------------*/
4 /* This module serves as a bridge between StormLib code and (de)compression */
5 /* functions. All (de)compression calls go (and should only go) through this */
6 /* module. No system headers should be included in this module to prevent */
7 /* compile-time problems. */
8 /*---------------------------------------------------------------------------*/
9 /* Date Ver Who Comment */
10 /* -------- ---- --- ------- */
11 /* 01.04.03 1.00 Lad The first version of SCompression.cpp */
12 /* 19.11.03 1.01 Dan Big endian handling */
13 /*****************************************************************************/
14
15 #define __STORMLIB_SELF__
16 #include "StormLib.h"
17 #include "StormCommon.h"
18
19 //-----------------------------------------------------------------------------
20 // Local structures
21
22 // Information about the input and output buffers for pklib
23 typedef struct
24 {
25 unsigned char * pbInBuff; // Pointer to input data buffer
26 unsigned char * pbInBuffEnd; // End of the input buffer
27 unsigned char * pbOutBuff; // Pointer to output data buffer
28 unsigned char * pbOutBuffEnd; // Pointer to output data buffer
29 } TDataInfo;
30
31 // Prototype of the compression function
32 // Function doesn't return an error. A success means that the size of compressed buffer
33 // is lower than size of uncompressed buffer.
34 typedef void (*COMPRESS)(
35 void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored
36 int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer
37 void * pvInBuffer, // [in] Pointer to the buffer with data to compress
38 int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer
39 int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression
40 int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero.
41
42 // Prototype of the decompression function
43 // Returns 1 if success, 0 if failure
44 typedef int (*DECOMPRESS)(
45 void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data
46 int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer
47 // [out] Contains length of the decompressed data
48 void * pvInBuffer, // [in] Pointer to data to be decompressed
49 int cbInBuffer); // [in] Length of the data to be decompressed
50
51 // Table of compression functions
52 typedef struct
53 {
54 unsigned long uMask; // Compression mask
55 COMPRESS Compress; // Compression function
56 } TCompressTable;
57
58 // Table of decompression functions
59 typedef struct
60 {
61 unsigned long uMask; // Decompression bit
62 DECOMPRESS Decompress; // Decompression function
63 } TDecompressTable;
64
65
66 /*****************************************************************************/
67 /* */
68 /* Support for Huffman compression (0x01) */
69 /* */
70 /*****************************************************************************/
71
Compress_huff(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)72 void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
73 {
74 THuffmannTree ht(true);
75 TOutputStream os(pvOutBuffer, *pcbOutBuffer);
76
77 STORMLIB_UNUSED(nCmpLevel);
78 *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType);
79 }
80
Decompress_huff(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)81 int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
82 {
83 THuffmannTree ht(false);
84 TInputStream is(pvInBuffer, cbInBuffer);
85
86 *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is);
87 return (*pcbOutBuffer == 0) ? 0 : 1;
88 }
89
90 /******************************************************************************/
91 /* */
92 /* Support for ZLIB compression (0x02) */
93 /* */
94 /******************************************************************************/
95
Compress_ZLIB(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)96 void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
97 {
98 z_stream z; // Stream information for zlib
99 int windowBits;
100 int nResult;
101
102 // Keep compilers happy
103 STORMLIB_UNUSED(pCmpType);
104 STORMLIB_UNUSED(nCmpLevel);
105
106 // Fill the stream structure for zlib
107 z.next_in = (Bytef *)pvInBuffer;
108 z.avail_in = (uInt)cbInBuffer;
109 z.total_in = cbInBuffer;
110 z.next_out = (Bytef *)pvOutBuffer;
111 z.avail_out = *pcbOutBuffer;
112 z.total_out = 0;
113 z.zalloc = NULL;
114 z.zfree = NULL;
115
116 // Determine the proper window bits (WoW.exe build 12694)
117 if(cbInBuffer <= 0x100)
118 windowBits = 8;
119 else if(cbInBuffer <= 0x200)
120 windowBits = 9;
121 else if(cbInBuffer <= 0x400)
122 windowBits = 10;
123 else if(cbInBuffer <= 0x800)
124 windowBits = 11;
125 else if(cbInBuffer <= 0x1000)
126 windowBits = 12;
127 else if(cbInBuffer <= 0x2000)
128 windowBits = 13;
129 else if(cbInBuffer <= 0x4000)
130 windowBits = 14;
131 else
132 windowBits = 15;
133
134 // Initialize the compression.
135 // Storm.dll uses zlib version 1.1.3
136 // Wow.exe uses zlib version 1.2.3
137 nResult = deflateInit2(&z,
138 6, // Compression level used by WoW MPQs
139 Z_DEFLATED,
140 windowBits,
141 8,
142 Z_DEFAULT_STRATEGY);
143 if(nResult == Z_OK)
144 {
145 // Call zlib to compress the data
146 nResult = deflate(&z, Z_FINISH);
147
148 if(nResult == Z_OK || nResult == Z_STREAM_END)
149 *pcbOutBuffer = z.total_out;
150
151 deflateEnd(&z);
152 }
153 }
154
Decompress_ZLIB(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)155 int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
156 {
157 z_stream z; // Stream information for zlib
158 int nResult;
159
160 // Fill the stream structure for zlib
161 z.next_in = (Bytef *)pvInBuffer;
162 z.avail_in = (uInt)cbInBuffer;
163 z.total_in = cbInBuffer;
164 z.next_out = (Bytef *)pvOutBuffer;
165 z.avail_out = *pcbOutBuffer;
166 z.total_out = 0;
167 z.zalloc = NULL;
168 z.zfree = NULL;
169
170 // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3
171 if((nResult = inflateInit(&z)) == Z_OK)
172 {
173 // Call zlib to decompress the data
174 nResult = inflate(&z, Z_FINISH);
175 *pcbOutBuffer = z.total_out;
176 inflateEnd(&z);
177 }
178
179 return (nResult >= Z_OK);
180 }
181
182 /******************************************************************************/
183 /* */
184 /* Support functions for PKWARE Data Compression Library compression (0x08) */
185 /* */
186 /******************************************************************************/
187
188 // Function loads data from the input buffer. Used by Pklib's "implode"
189 // and "explode" function as user-defined callback
190 // Returns number of bytes loaded
191 //
192 // char * buf - Pointer to a buffer where to store loaded data
193 // unsigned int * size - Max. number of bytes to read
194 // void * param - Custom pointer, parameter of implode/explode
195
ReadInputData(char * buf,unsigned int * size,void * param)196 static unsigned int ReadInputData(char * buf, unsigned int * size, void * param)
197 {
198 TDataInfo * pInfo = (TDataInfo *)param;
199 unsigned int nMaxAvail = (unsigned int)(pInfo->pbInBuffEnd - pInfo->pbInBuff);
200 unsigned int nToRead = *size;
201
202 // Check the case when not enough data available
203 if(nToRead > nMaxAvail)
204 nToRead = nMaxAvail;
205
206 // Load data and increment offsets
207 memcpy(buf, pInfo->pbInBuff, nToRead);
208 pInfo->pbInBuff += nToRead;
209 assert(pInfo->pbInBuff <= pInfo->pbInBuffEnd);
210 return nToRead;
211 }
212
213 // Function for store output data. Used by Pklib's "implode" and "explode"
214 // as user-defined callback
215 //
216 // char * buf - Pointer to data to be written
217 // unsigned int * size - Number of bytes to write
218 // void * param - Custom pointer, parameter of implode/explode
219
WriteOutputData(char * buf,unsigned int * size,void * param)220 static void WriteOutputData(char * buf, unsigned int * size, void * param)
221 {
222 TDataInfo * pInfo = (TDataInfo *)param;
223 unsigned int nMaxWrite = (unsigned int)(pInfo->pbOutBuffEnd - pInfo->pbOutBuff);
224 unsigned int nToWrite = *size;
225
226 // Check the case when not enough space in the output buffer
227 if(nToWrite > nMaxWrite)
228 nToWrite = nMaxWrite;
229
230 // Write output data and increments offsets
231 memcpy(pInfo->pbOutBuff, buf, nToWrite);
232 pInfo->pbOutBuff += nToWrite;
233 assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd);
234 }
235
Compress_PKLIB(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)236 static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
237 {
238 TDataInfo Info; // Data information
239 char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer
240 unsigned int dict_size; // Dictionary size
241 unsigned int ctype = CMP_BINARY; // Compression type
242
243 // Keep compilers happy
244 STORMLIB_UNUSED(pCmpType);
245 STORMLIB_UNUSED(nCmpLevel);
246
247 // Handle no-memory condition
248 if(work_buf != NULL)
249 {
250 // Fill data information structure
251 memset(work_buf, 0, CMP_BUFFER_SIZE);
252 Info.pbInBuff = (unsigned char *)pvInBuffer;
253 Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer;
254 Info.pbOutBuff = (unsigned char *)pvOutBuffer;
255 Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
256
257 //
258 // Set the dictionary size
259 //
260 // Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
261 // Starcraft I uses the variable dictionary size based on algorithm below
262 //
263
264 if (cbInBuffer < 0x600)
265 dict_size = CMP_IMPLODE_DICT_SIZE1;
266 else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
267 dict_size = CMP_IMPLODE_DICT_SIZE2;
268 else
269 dict_size = CMP_IMPLODE_DICT_SIZE3;
270
271 // Do the compression
272 if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR)
273 *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer);
274
275 STORM_FREE(work_buf);
276 }
277 }
278
Decompress_PKLIB(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)279 static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
280 {
281 TDataInfo Info; // Data information
282 char * work_buf;
283 int nResult = 0;
284
285 // Allocate Pklib's work buffer
286 if((work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE)) != NULL)
287 {
288 // Fill data information structure
289 memset(work_buf, 0, EXP_BUFFER_SIZE);
290 Info.pbInBuff = (unsigned char *)pvInBuffer;
291 Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer;
292 Info.pbOutBuff = (unsigned char *)pvOutBuffer;
293 Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
294
295 // Do the decompression
296 if(explode(ReadInputData, WriteOutputData, work_buf, &Info) == CMP_NO_ERROR)
297 nResult = 1;
298
299 // Give away the number of decompressed bytes
300 *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer);
301 STORM_FREE(work_buf);
302 }
303
304 return nResult;
305 }
306
307 /******************************************************************************/
308 /* */
309 /* Support for Bzip2 compression (0x10) */
310 /* */
311 /******************************************************************************/
312
Compress_BZIP2(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)313 static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
314 {
315 bz_stream strm;
316 int blockSize100k = 9;
317 int workFactor = 30;
318 int bzError;
319
320 // Keep compilers happy
321 STORMLIB_UNUSED(pCmpType);
322 STORMLIB_UNUSED(nCmpLevel);
323
324 // Initialize the BZIP2 compression
325 strm.bzalloc = NULL;
326 strm.bzfree = NULL;
327 strm.opaque = NULL;
328
329 // Blizzard uses 9 as blockSize100k, (0x30 as workFactor)
330 // Last checked on Starcraft II
331 if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK)
332 {
333 strm.next_in = (char *)pvInBuffer;
334 strm.avail_in = cbInBuffer;
335 strm.next_out = (char *)pvOutBuffer;
336 strm.avail_out = *pcbOutBuffer;
337
338 // Perform the compression
339 for(;;)
340 {
341 bzError = BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH);
342 if(bzError == BZ_STREAM_END || bzError < 0)
343 break;
344 }
345
346 // Put the stream into idle state
347 BZ2_bzCompressEnd(&strm);
348
349 if(bzError > 0)
350 *pcbOutBuffer = strm.total_out_lo32;
351 }
352 }
353
Decompress_BZIP2(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)354 static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
355 {
356 bz_stream strm;
357 int nResult;
358
359 // Initialize the BZIP2 decompression
360 strm.next_in = (char *)pvInBuffer;
361 strm.avail_in = cbInBuffer;
362 strm.next_out = (char *)pvOutBuffer;
363 strm.avail_out = *pcbOutBuffer;
364 strm.bzalloc = NULL;
365 strm.bzfree = NULL;
366 strm.opaque = NULL;
367
368 // Initialize decompression
369 if((nResult = BZ2_bzDecompressInit(&strm, 0, 0)) == BZ_OK)
370 {
371 // Perform the decompression
372 nResult = BZ2_bzDecompress(&strm);
373 *pcbOutBuffer = strm.total_out_lo32;
374 BZ2_bzDecompressEnd(&strm);
375 }
376
377 return (nResult >= BZ_OK);
378 }
379
380 /******************************************************************************/
381 /* */
382 /* Support functions for LZMA compression (0x12) */
383 /* */
384 /******************************************************************************/
385
386 #define LZMA_HEADER_SIZE (1 + LZMA_PROPS_SIZE + 8)
387
LZMA_Callback_Progress(void *,UInt64,UInt64)388 static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 /* outSize */)
389 {
390 return SZ_OK;
391 }
392
LZMA_Callback_Alloc(void * p,size_t size)393 static void * LZMA_Callback_Alloc(void *p, size_t size)
394 {
395 p = p;
396 return STORM_ALLOC(BYTE, size);
397 }
398
399 /* address can be 0 */
LZMA_Callback_Free(void * p,void * address)400 static void LZMA_Callback_Free(void *p, void *address)
401 {
402 p = p;
403 if(address != NULL)
404 STORM_FREE(address);
405 }
406
407 //
408 // Note: So far, I haven't seen any files compressed by LZMA.
409 // This code haven't been verified against code ripped from Starcraft II Beta,
410 // but we know that Starcraft LZMA decompression code is able to decompress
411 // the data compressed by StormLib.
412 //
413
Compress_LZMA(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)414 static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
415 {
416 ICompressProgress Progress;
417 CLzmaEncProps props;
418 ISzAlloc SzAlloc;
419 Byte * pbOutBuffer = (Byte *)pvOutBuffer;
420 Byte * destBuffer;
421 SizeT destLen = *pcbOutBuffer;
422 SizeT srcLen = cbInBuffer;
423 Byte encodedProps[LZMA_PROPS_SIZE];
424 size_t encodedPropsSize = LZMA_PROPS_SIZE;
425 SRes nResult;
426
427 // Keep compilers happy
428 STORMLIB_UNUSED(pCmpType);
429 STORMLIB_UNUSED(nCmpLevel);
430
431 // Fill the callbacks in structures
432 Progress.Progress = LZMA_Callback_Progress;
433 SzAlloc.Alloc = LZMA_Callback_Alloc;
434 SzAlloc.Free = LZMA_Callback_Free;
435
436 // Initialize properties
437 LzmaEncProps_Init(&props);
438
439 // Perform compression
440 destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE;
441 destLen = *pcbOutBuffer - LZMA_HEADER_SIZE;
442 nResult = LzmaEncode(destBuffer,
443 &destLen,
444 (Byte *)pvInBuffer,
445 srcLen,
446 &props,
447 encodedProps,
448 &encodedPropsSize,
449 0,
450 &Progress,
451 &SzAlloc,
452 &SzAlloc);
453 if(nResult != SZ_OK)
454 return;
455
456 // If we failed to compress the data
457 if(destLen >= (SizeT)(*pcbOutBuffer - LZMA_HEADER_SIZE))
458 return;
459
460 // Write "useFilter" variable. Blizzard MPQ must not use filter.
461 *pbOutBuffer++ = 0;
462
463 // Copy the encoded properties to the output buffer
464 memcpy(pvOutBuffer, encodedProps, encodedPropsSize);
465 pbOutBuffer += encodedPropsSize;
466
467 // Copy the size of the data
468 *pbOutBuffer++ = (unsigned char)(srcLen >> 0x00);
469 *pbOutBuffer++ = (unsigned char)(srcLen >> 0x08);
470 *pbOutBuffer++ = (unsigned char)(srcLen >> 0x10);
471 *pbOutBuffer++ = (unsigned char)(srcLen >> 0x18);
472 *pbOutBuffer++ = 0;
473 *pbOutBuffer++ = 0;
474 *pbOutBuffer++ = 0;
475 *pbOutBuffer++ = 0;
476
477 // Give the size of the data to the caller
478 *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE);
479 }
480
Decompress_LZMA(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)481 static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
482 {
483 ELzmaStatus LzmaStatus;
484 ISzAlloc SzAlloc;
485 Byte * destBuffer = (Byte *)pvOutBuffer;
486 Byte * srcBuffer = (Byte *)pvInBuffer;
487 SizeT destLen = *pcbOutBuffer;
488 SizeT srcLen = cbInBuffer;
489 SRes nResult;
490
491 // There must be at least 0x0E bytes in the buffer
492 if(srcLen <= LZMA_HEADER_SIZE)
493 return 0;
494
495 // We only accept blocks that have no filter used
496 if(*srcBuffer != 0)
497 return 0;
498
499 // Fill the callbacks in structures
500 SzAlloc.Alloc = LZMA_Callback_Alloc;
501 SzAlloc.Free = LZMA_Callback_Free;
502
503 // Perform compression
504 srcLen = cbInBuffer - LZMA_HEADER_SIZE;
505 nResult = LzmaDecode(destBuffer,
506 &destLen,
507 srcBuffer + LZMA_HEADER_SIZE,
508 &srcLen,
509 srcBuffer + 1,
510 LZMA_PROPS_SIZE,
511 LZMA_FINISH_END,
512 &LzmaStatus,
513 &SzAlloc);
514 if(nResult != SZ_OK)
515 return 0;
516
517 *pcbOutBuffer = (unsigned int)destLen;
518 return 1;
519 }
520
Decompress_LZMA_MPK(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)521 static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
522 {
523 ELzmaStatus LzmaStatus;
524 ISzAlloc SzAlloc;
525 Byte * destBuffer = (Byte *)pvOutBuffer;
526 Byte * srcBuffer = (Byte *)pvInBuffer;
527 SizeT destLen = *pcbOutBuffer;
528 SizeT srcLen = cbInBuffer;
529 SRes nResult;
530 BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01};
531
532 // There must be at least 0x0E bytes in the buffer
533 if(srcLen <= sizeof(LZMA_Props))
534 return 0;
535
536 // Verify the props header
537 if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props)))
538 return 0;
539
540 // Fill the callbacks in structures
541 SzAlloc.Alloc = LZMA_Callback_Alloc;
542 SzAlloc.Free = LZMA_Callback_Free;
543
544 // Perform compression
545 srcLen = cbInBuffer - sizeof(LZMA_Props);
546 nResult = LzmaDecode(destBuffer,
547 &destLen,
548 srcBuffer + sizeof(LZMA_Props),
549 &srcLen,
550 srcBuffer,
551 sizeof(LZMA_Props),
552 LZMA_FINISH_END,
553 &LzmaStatus,
554 &SzAlloc);
555 if(nResult != SZ_OK)
556 return 0;
557
558 *pcbOutBuffer = (unsigned int)destLen;
559 return 1;
560 }
561
562 /******************************************************************************/
563 /* */
564 /* Support functions for SPARSE compression (0x20) */
565 /* */
566 /******************************************************************************/
567
Compress_SPARSE(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)568 void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
569 {
570 // Keep compilers happy
571 STORMLIB_UNUSED(pCmpType);
572 STORMLIB_UNUSED(nCmpLevel);
573
574 CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
575 }
576
Decompress_SPARSE(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)577 int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
578 {
579 return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
580 }
581
582 /******************************************************************************/
583 /* */
584 /* Support for ADPCM mono compression (0x40) */
585 /* */
586 /******************************************************************************/
587
Compress_ADPCM_mono(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)588 static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
589 {
590 // Prepare the compression level for Huffmann compression,
591 // which will be called as next step
592 if(0 < nCmpLevel && nCmpLevel <= 2)
593 {
594 nCmpLevel = 4;
595 *pCmpType = 6;
596 }
597 else if(nCmpLevel == 3)
598 {
599 nCmpLevel = 6;
600 *pCmpType = 8;
601 }
602 else
603 {
604 nCmpLevel = 5;
605 *pCmpType = 7;
606 }
607 *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel);
608 }
609
Decompress_ADPCM_mono(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)610 static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
611 {
612 *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1);
613 return 1;
614 }
615
616 /******************************************************************************/
617 /* */
618 /* Support for ADPCM stereo compression (0x80) */
619 /* */
620 /******************************************************************************/
621
Compress_ADPCM_stereo(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,int * pCmpType,int nCmpLevel)622 static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
623 {
624 // Prepare the compression level for Huffmann compression,
625 // which will be called as next step
626 if(0 < nCmpLevel && nCmpLevel <= 2)
627 {
628 nCmpLevel = 4;
629 *pCmpType = 6;
630 }
631 else if(nCmpLevel == 3)
632 {
633 nCmpLevel = 6;
634 *pCmpType = 8;
635 }
636 else
637 {
638 nCmpLevel = 5;
639 *pCmpType = 7;
640 }
641 *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel);
642 }
643
Decompress_ADPCM_stereo(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)644 static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
645 {
646 *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2);
647 return 1;
648 }
649
650 /*****************************************************************************/
651 /* */
652 /* SCompImplode */
653 /* */
654 /*****************************************************************************/
655
SCompImplode(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)656 int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
657 {
658 int cbOutBuffer;
659
660 // Check for valid parameters
661 if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
662 {
663 SetLastError(ERROR_INVALID_PARAMETER);
664 return 0;
665 }
666
667 // Perform the compression
668 cbOutBuffer = *pcbOutBuffer;
669 Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0);
670
671 // If the compression was unsuccessful, copy the data as-is
672 if(cbOutBuffer >= *pcbOutBuffer)
673 {
674 memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
675 cbOutBuffer = *pcbOutBuffer;
676 }
677
678 *pcbOutBuffer = cbOutBuffer;
679 return 1;
680 }
681
682 /*****************************************************************************/
683 /* */
684 /* SCompExplode */
685 /* */
686 /*****************************************************************************/
687
SCompExplode(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)688 int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
689 {
690 int cbOutBuffer;
691
692 // Check for valid parameters
693 if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
694 {
695 SetLastError(ERROR_INVALID_PARAMETER);
696 return 0;
697 }
698
699 // If the input length is the same as output length, do nothing.
700 cbOutBuffer = *pcbOutBuffer;
701 if(cbInBuffer == cbOutBuffer)
702 {
703 // If the buffers are equal, don't copy anything.
704 if(pvInBuffer == pvOutBuffer)
705 return 1;
706
707 memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
708 return 1;
709 }
710
711 // Perform decompression
712 if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer))
713 {
714 SetLastError(ERROR_FILE_CORRUPT);
715 return 0;
716 }
717
718 *pcbOutBuffer = cbOutBuffer;
719 return 1;
720 }
721
722 /*****************************************************************************/
723 /* */
724 /* SCompCompress */
725 /* */
726 /*****************************************************************************/
727
728 // This table contains compress functions which can be applied to
729 // uncompressed data. Each bit means the corresponding
730 // compression method/function must be applied.
731 //
732 // WAVes compression Data compression
733 // ------------------ -------------------
734 // 1st sector - 0x08 0x08 (D, HF, W2, SC, D2)
735 // Next sectors - 0x81 0x02 (W3)
736
737 static TCompressTable cmp_table[] =
738 {
739 {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
740 {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
741 {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
742 {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
743 {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
744 {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
745 {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
746 };
747
SCompCompress(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer,unsigned uCompressionMask,int nCmpType,int nCmpLevel)748 int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel)
749 {
750 COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially
751 unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array
752 unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
753 unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
754 unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer
755 unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer
756 int nCompressCount = 0;
757 int nCompressIndex = 0;
758 int nAtLeastOneCompressionDone = 0;
759 int cbOutBuffer = 0;
760 int cbInLength = cbInBuffer;
761 int nResult = 1;
762
763 // Check for valid parameters
764 if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
765 {
766 SetLastError(ERROR_INVALID_PARAMETER);
767 return 0;
768 }
769
770 // Zero input length brings zero output length
771 if(cbInBuffer == 0)
772 {
773 *pcbOutBuffer = 0;
774 return true;
775 }
776
777 // Setup the compression function array
778 if(uCompressionMask == MPQ_COMPRESSION_LZMA)
779 {
780 CompressFuncArray[0] = Compress_LZMA;
781 CompressByte[0] = (char)uCompressionMask;
782 nCompressCount = 1;
783 }
784 else
785 {
786 // Fill the compressions array
787 for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++)
788 {
789 // If the mask agrees, insert the compression function to the array
790 if(uCompressionMask & cmp_table[i].uMask)
791 {
792 CompressFuncArray[nCompressCount] = cmp_table[i].Compress;
793 CompressByte[nCompressCount] = (unsigned char)cmp_table[i].uMask;
794 uCompressionMask &= ~cmp_table[i].uMask;
795 nCompressCount++;
796 }
797 }
798
799 // If at least one of the compressions remaing unknown, return an error
800 if(uCompressionMask != 0)
801 {
802 SetLastError(ERROR_NOT_SUPPORTED);
803 return 0;
804 }
805 }
806
807 // If there is at least one compression, do it
808 if(nCompressCount > 0)
809 {
810 // If we need to do more than 1 compression, allocate intermediate buffer
811 if(nCompressCount > 1)
812 {
813 pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
814 if(pbWorkBuffer == NULL)
815 {
816 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
817 return 0;
818 }
819 }
820
821 // Get the current compression index
822 nCompressIndex = nCompressCount - 1;
823
824 // Perform all compressions in the array
825 for(int i = 0; i < nCompressCount; i++)
826 {
827 // Choose the proper output buffer
828 pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
829 nCompressIndex--;
830
831 // Perform the (next) compression
832 // Note that if the compression method is unable to compress the input data block
833 // by at least 2 bytes, we consider it as failure and will use source data instead
834 cbOutBuffer = *pcbOutBuffer - 1;
835 CompressFuncArray[i](pbOutput + 1, &cbOutBuffer, pbInput, cbInLength, &nCmpType, nCmpLevel);
836
837 // If the compression failed, we copy the input buffer as-is.
838 // Note that there is one extra byte at the end of the intermediate buffer, so it should be OK
839 if(cbOutBuffer > (cbInLength - 2))
840 {
841 memcpy(pbOutput + nAtLeastOneCompressionDone, pbInput, cbInLength);
842 cbOutBuffer = cbInLength;
843 }
844 else
845 {
846 // Remember that we have done at least one compression
847 nAtLeastOneCompressionDone = 1;
848 uCompressionMask |= CompressByte[i];
849 }
850
851 // Now point input buffer to the output buffer
852 pbInput = pbOutput + nAtLeastOneCompressionDone;
853 cbInLength = cbOutBuffer;
854 }
855
856 // If at least one compression succeeded, put the compression
857 // mask to the begin of the output buffer
858 if(nAtLeastOneCompressionDone)
859 *pbOutBuffer = (unsigned char)uCompressionMask;
860 *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone;
861 }
862 else
863 {
864 memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
865 *pcbOutBuffer = cbInBuffer;
866 }
867
868 // Cleanup and return
869 if(pbWorkBuffer != NULL)
870 STORM_FREE(pbWorkBuffer);
871 return nResult;
872 }
873
874 /*****************************************************************************/
875 /* */
876 /* SCompDecompress */
877 /* */
878 /*****************************************************************************/
879
880 // This table contains decompress functions which can be applied to
881 // uncompressed data. The compression mask is stored in the first byte
882 // of compressed data
883 static TDecompressTable dcmp_table[] =
884 {
885 {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library
886 {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
887 {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library
888 {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
889 {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression
890 {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression
891 {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
892 };
893
SCompDecompress(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)894 int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
895 {
896 unsigned char * pbWorkBuffer = NULL;
897 unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
898 unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
899 unsigned char * pbOutput = (unsigned char *)pvOutBuffer;
900 unsigned char * pbInput;
901 unsigned uCompressionMask; // Decompressions applied to the data
902 unsigned uCompressionCopy; // Decompressions applied to the data
903 int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
904 int cbInLength; // Current size of the input buffer
905 int nCompressCount = 0; // Number of compressions to be applied
906 int nCompressIndex = 0;
907 int nResult = 1;
908
909 // Verify buffer sizes
910 if(cbOutBuffer < cbInBuffer || cbInBuffer < 1)
911 return 0;
912
913 // If the input length is the same as output length, do nothing.
914 if(cbOutBuffer == cbInBuffer)
915 {
916 // If the buffers are equal, don't copy anything.
917 if(pvInBuffer != pvOutBuffer)
918 memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
919 return 1;
920 }
921
922 // Get applied compression types and decrement data length
923 uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++;
924 cbInBuffer--;
925
926 // Get current compressed data and length of it
927 pbInput = pbInBuffer;
928 cbInLength = cbInBuffer;
929
930 // This compression function doesn't support LZMA
931 assert(uCompressionMask != MPQ_COMPRESSION_LZMA);
932
933 // Parse the compression mask
934 for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
935 {
936 // If the mask agrees, insert the compression function to the array
937 if(uCompressionMask & dcmp_table[i].uMask)
938 {
939 uCompressionCopy &= ~dcmp_table[i].uMask;
940 nCompressCount++;
941 }
942 }
943
944 // If at least one of the compressions remaing unknown, return an error
945 if(nCompressCount == 0 || uCompressionCopy != 0)
946 {
947 SetLastError(ERROR_NOT_SUPPORTED);
948 return 0;
949 }
950
951 // If there is more than one compression, we have to allocate extra buffer
952 if(nCompressCount > 1)
953 {
954 pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer);
955 if(pbWorkBuffer == NULL)
956 {
957 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
958 return 0;
959 }
960 }
961
962 // Get the current compression index
963 nCompressIndex = nCompressCount - 1;
964
965 // Apply all decompressions
966 for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
967 {
968 // Perform the (next) decompression
969 if(uCompressionMask & dcmp_table[i].uMask)
970 {
971 // Get the correct output buffer
972 pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
973 nCompressIndex--;
974
975 // Perform the decompression
976 cbOutBuffer = *pcbOutBuffer;
977 nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength);
978 if(nResult == 0 || cbOutBuffer == 0)
979 {
980 SetLastError(ERROR_FILE_CORRUPT);
981 nResult = 0;
982 break;
983 }
984
985 // Switch buffers
986 cbInLength = cbOutBuffer;
987 pbInput = pbOutput;
988 }
989 }
990
991 // Put the length of the decompressed data to the output buffer
992 *pcbOutBuffer = cbOutBuffer;
993
994 // Cleanup and return
995 if(pbWorkBuffer != NULL)
996 STORM_FREE(pbWorkBuffer);
997 return nResult;
998 }
999
SCompDecompress2(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)1000 int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
1001 {
1002 DECOMPRESS pfnDecompress1 = NULL;
1003 DECOMPRESS pfnDecompress2 = NULL;
1004 unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer;
1005 unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
1006 int cbWorkBuffer = *pcbOutBuffer;
1007 int nResult;
1008 char CompressionMethod;
1009
1010 // Verify buffer sizes
1011 if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1)
1012 return 0;
1013
1014 // If the outputbuffer is as big as input buffer, just copy the block
1015 if(*pcbOutBuffer == cbInBuffer)
1016 {
1017 if(pvOutBuffer != pvInBuffer)
1018 memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
1019 return 1;
1020 }
1021
1022 // Get the compression methods
1023 CompressionMethod = *pbInBuffer++;
1024 cbInBuffer--;
1025
1026 // We only recognize a fixed set of compression methods
1027 switch((unsigned char)CompressionMethod)
1028 {
1029 case MPQ_COMPRESSION_ZLIB:
1030 pfnDecompress1 = Decompress_ZLIB;
1031 break;
1032
1033 case MPQ_COMPRESSION_PKWARE:
1034 pfnDecompress1 = Decompress_PKLIB;
1035 break;
1036
1037 case MPQ_COMPRESSION_BZIP2:
1038 pfnDecompress1 = Decompress_BZIP2;
1039 break;
1040
1041 case MPQ_COMPRESSION_LZMA:
1042 pfnDecompress1 = Decompress_LZMA;
1043 break;
1044
1045 case MPQ_COMPRESSION_SPARSE:
1046 pfnDecompress1 = Decompress_SPARSE;
1047 break;
1048
1049 case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
1050 pfnDecompress1 = Decompress_ZLIB;
1051 pfnDecompress2 = Decompress_SPARSE;
1052 break;
1053
1054 case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
1055 pfnDecompress1 = Decompress_BZIP2;
1056 pfnDecompress2 = Decompress_SPARSE;
1057 break;
1058
1059 //
1060 // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
1061 // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN
1062 // is not supported by newer MPQs.
1063 //
1064
1065 case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN):
1066 pfnDecompress1 = Decompress_huff;
1067 pfnDecompress2 = Decompress_ADPCM_mono;
1068 break;
1069
1070 case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN):
1071 pfnDecompress1 = Decompress_huff;
1072 pfnDecompress2 = Decompress_ADPCM_stereo;
1073 break;
1074
1075 default:
1076 SetLastError(ERROR_FILE_CORRUPT);
1077 return 0;
1078 }
1079
1080 // If we have to use two decompressions, allocate temporary buffer
1081 if(pfnDecompress2 != NULL)
1082 {
1083 pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
1084 if(pbWorkBuffer == NULL)
1085 {
1086 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1087 return 0;
1088 }
1089 }
1090
1091 // Apply the first decompression method
1092 nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer);
1093
1094 // Apply the second decompression method, if any
1095 if(pfnDecompress2 != NULL && nResult != 0)
1096 {
1097 cbInBuffer = cbWorkBuffer;
1098 cbWorkBuffer = *pcbOutBuffer;
1099 nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
1100 }
1101
1102 // Supply the output buffer size
1103 *pcbOutBuffer = cbWorkBuffer;
1104
1105 // Free temporary buffer
1106 if(pbWorkBuffer != pvOutBuffer)
1107 STORM_FREE(pbWorkBuffer);
1108
1109 if(nResult == 0)
1110 SetLastError(ERROR_FILE_CORRUPT);
1111 return nResult;
1112 }
1113
1114 /*****************************************************************************/
1115 /* */
1116 /* File decompression for MPK archives */
1117 /* */
1118 /*****************************************************************************/
1119
SCompDecompressMpk(void * pvOutBuffer,int * pcbOutBuffer,void * pvInBuffer,int cbInBuffer)1120 int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
1121 {
1122 return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
1123 }
1124
1125