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 /*****************************************************************************/
15 #define __STORMLIB_SELF__
21 // Include functions from Pkware Data Compression Library
22 #include "pklib/pklib.h"
24 // Include functions from zlib
26 #include "zlib/zlib.h" // Include functions from zlib
28 #include <zlib.h> // If zlib is available on system, use this instead
31 // Include functions from Huffmann compression
32 #include "huffman/huff.h"
34 // Include functions from WAVe compression
35 #include "wave/wave.h"
37 // Include functions from BZip2 compression library
39 #include "bzip2/bzlib.h" // Include functions from bzlib
41 #include <bzlib.h> // If bzlib is available on system, use this instead
44 //-----------------------------------------------------------------------------
47 // Information about the input and output buffers for pklib
50 char * pInBuff
; // Pointer to input data buffer
51 int nInPos
; // Current offset in input data buffer
52 int nInBytes
; // Number of bytes in the input buffer
53 char * pOutBuff
; // Pointer to output data buffer
54 int nOutPos
; // Position in the output buffer
55 int nMaxOut
; // Maximum number of bytes in the output buffer
58 // Table of compression functions
59 typedef int (*COMPRESS
)(char *, int *, char *, int, int *, int);
62 unsigned long dwMask
; // Compression mask
63 COMPRESS Compress
; // Compression function
66 // Table of decompression functions
67 typedef int (*DECOMPRESS
)(char *, int *, char *, int);
70 unsigned long dwMask
; // Decompression bit
71 DECOMPRESS Decompress
; // Decompression function
75 /*****************************************************************************/
77 /* Support functions for Pkware Data Compression Library */
79 /*****************************************************************************/
81 // Function loads data from the input buffer. Used by Pklib's "implode"
82 // and "explode" function as user-defined callback
83 // Returns number of bytes loaded
85 // char * buf - Pointer to a buffer where to store loaded data
86 // unsigned int * size - Max. number of bytes to read
87 // void * param - Custom pointer, parameter of implode/explode
89 static unsigned int ReadInputData(char * buf
, unsigned int * size
, void * param
)
91 TDataInfo
* pInfo
= (TDataInfo
*)param
;
92 unsigned int nMaxAvail
= (pInfo
->nInBytes
- pInfo
->nInPos
);
93 unsigned int nToRead
= *size
;
95 // Check the case when not enough data available
96 if(nToRead
> nMaxAvail
)
99 // Load data and increment offsets
100 memcpy(buf
, pInfo
->pInBuff
+ pInfo
->nInPos
, nToRead
);
101 pInfo
->nInPos
+= nToRead
;
106 // Function for store output data. Used by Pklib's "implode" and "explode"
107 // as user-defined callback
109 // char * buf - Pointer to data to be written
110 // unsigned int * size - Number of bytes to write
111 // void * param - Custom pointer, parameter of implode/explode
113 static void WriteOutputData(char * buf
, unsigned int * size
, void * param
)
115 TDataInfo
* pInfo
= (TDataInfo
*)param
;
116 unsigned int nMaxWrite
= (pInfo
->nMaxOut
- pInfo
->nOutPos
);
117 unsigned int nToWrite
= *size
;
119 // Check the case when not enough space in the output buffer
120 if(nToWrite
> nMaxWrite
)
121 nToWrite
= nMaxWrite
;
123 // Write output data and increments offsets
124 memcpy(pInfo
->pOutBuff
+ pInfo
->nOutPos
, buf
, nToWrite
);
125 pInfo
->nOutPos
+= nToWrite
;
128 /*****************************************************************************/
130 /* "80" is IMA ADPCM stereo (de)compression */
131 /* "40" is IMA ADPCM mono (de)compression */
133 /*****************************************************************************/
135 int Compress_wave_mono(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * pCmpType
, int nCmpLevel
)
137 // Prepare the compression level for the next compression
138 // (After us, the Huffmann compression will be called)
139 if(0 < nCmpLevel
&& nCmpLevel
<= 2)
144 else if(nCmpLevel
== 3)
154 *pdwOutLength
= CompressWave((unsigned char *)pbOutBuffer
, *pdwOutLength
, (short *)pbInBuffer
, dwInLength
, 1, nCmpLevel
);
158 int Decompress_wave_mono(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
160 *pdwOutLength
= DecompressWave((unsigned char *)pbOutBuffer
, *pdwOutLength
, (unsigned char *)pbInBuffer
, dwInLength
, 1);
164 int Compress_wave_stereo(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * pCmpType
, int nCmpLevel
)
166 // Prepare the compression type for the next compression
167 // (After us, the Huffmann compression will be called)
168 if(0 < nCmpLevel
&& nCmpLevel
<= 2)
173 else if(nCmpLevel
== 3)
183 *pdwOutLength
= CompressWave((unsigned char *)pbOutBuffer
, *pdwOutLength
, (short *)pbInBuffer
, dwInLength
, 2, nCmpLevel
);
187 int Decompress_wave_stereo(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
189 *pdwOutLength
= DecompressWave((unsigned char *)pbOutBuffer
, *pdwOutLength
, (unsigned char *)pbInBuffer
, dwInLength
, 2);
193 /*****************************************************************************/
195 /* The "01" (de)compression is the Huffman (de)compression */
197 /*****************************************************************************/
200 int Compress_huff(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * pCmpType
, int /* nCmpLevel */)
202 THuffmannTree ht
; // Huffmann tree for compression
203 TOutputStream os
; // Output stream
205 // Initialize output stream
206 os
.pbOutBuffer
= (unsigned char *)pbOutBuffer
;
207 os
.dwOutSize
= *pdwOutLength
;
208 os
.pbOutPos
= (unsigned char *)pbOutBuffer
;
212 // Initialize the Huffmann tree for compression
215 *pdwOutLength
= ht
.DoCompression(&os
, (unsigned char *)pbInBuffer
, dwInLength
, *pCmpType
);
217 // The following code is not necessary to run, because it has no
218 // effect on the output data. It only clears the huffmann tree, but when
219 // the tree is on the stack, who cares ?
225 int Decompress_huff(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int /* dwInLength */)
230 // Initialize input stream
231 // is.pbInBuffer = (unsigned char *)pbInBuffer;
232 is
.dwBitBuff
= BSWAP_INT32_UNSIGNED(*(unsigned long *)pbInBuffer
);
233 pbInBuffer
+= sizeof(unsigned long);
234 is
.pbInBuffer
= (unsigned char *)pbInBuffer
;
237 // Initialize the Huffmann tree for compression
239 *pdwOutLength
= ht
.DoDecompression((unsigned char *)pbOutBuffer
, *pdwOutLength
, &is
);
241 // The following code is not necessary to run, because it has no
242 // effect on the output data. It only clears the huffmann tree, but when
243 // the tree is on the stack, who cares ?
248 /*****************************************************************************/
250 /* The "02" (de)compression is the ZLIB (de)compression */
252 /*****************************************************************************/
254 int Compress_zlib(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * /* pCmpType */, int /* nCmpLevel */)
256 z_stream z
; // Stream information for zlib
259 // Fill the stream structure for zlib
260 z
.next_in
= (Bytef
*)pbInBuffer
;
261 z
.avail_in
= (uInt
)dwInLength
;
262 z
.total_in
= dwInLength
;
263 z
.next_out
= (Bytef
*)pbOutBuffer
;
264 z
.avail_out
= *pdwOutLength
;
269 // Initialize the compression structure. Storm.dll uses zlib version 1.1.3
271 if((nResult
= deflateInit(&z
, Z_DEFAULT_COMPRESSION
)) == 0)
273 // Call zlib to compress the data
274 nResult
= deflate(&z
, Z_FINISH
);
276 if(nResult
== Z_OK
|| nResult
== Z_STREAM_END
)
277 *pdwOutLength
= z
.total_out
;
284 int Decompress_zlib(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
286 z_stream z
; // Stream information for zlib
289 // Fill the stream structure for zlib
290 z
.next_in
= (Bytef
*)pbInBuffer
;
291 z
.avail_in
= (uInt
)dwInLength
;
292 z
.total_in
= dwInLength
;
293 z
.next_out
= (Bytef
*)pbOutBuffer
;
294 z
.avail_out
= *pdwOutLength
;
299 // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3
300 if((nResult
= inflateInit(&z
)) == 0)
302 // Call zlib to decompress the data
303 nResult
= inflate(&z
, Z_FINISH
);
304 *pdwOutLength
= z
.total_out
;
310 /*****************************************************************************/
312 /* The "08" (de)compression is the Pkware DCL (de)compression */
314 /*****************************************************************************/
316 int Compress_pklib(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * pCmpType
, int /* nCmpLevel */)
318 TDataInfo Info
; // Data information
319 char * work_buf
= ALLOCMEM(char, CMP_BUFFER_SIZE
);// Pklib's work buffer
320 unsigned int dict_size
; // Dictionary size
321 unsigned int ctype
; // Compression type
323 // Fill data information structure
324 Info
.pInBuff
= pbInBuffer
;
326 Info
.nInBytes
= dwInLength
;
327 Info
.pOutBuff
= pbOutBuffer
;
329 Info
.nMaxOut
= *pdwOutLength
;
331 // Set the compression type and dictionary size
332 ctype
= (*pCmpType
== 2) ? CMP_ASCII
: CMP_BINARY
;
333 if (dwInLength
< 0x600)
335 else if(0x600 <= dwInLength
&& dwInLength
< 0xC00)
340 // Do the compression
341 implode(ReadInputData
, WriteOutputData
, work_buf
, &Info
, &ctype
, &dict_size
);
342 *pdwOutLength
= Info
.nOutPos
;
347 int Decompress_pklib(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
349 TDataInfo Info
; // Data information
350 char * work_buf
= ALLOCMEM(char, EXP_BUFFER_SIZE
);// Pklib's work buffer
352 // Fill data information structure
353 Info
.pInBuff
= pbInBuffer
;
355 Info
.nInBytes
= dwInLength
;
356 Info
.pOutBuff
= pbOutBuffer
;
358 Info
.nMaxOut
= *pdwOutLength
;
360 // Do the decompression
361 explode(ReadInputData
, WriteOutputData
, work_buf
, &Info
);
363 // Fix: If PKLIB is unable to decompress the data, they are uncompressed
364 if(Info
.nOutPos
== 0)
366 Info
.nOutPos
= min(*pdwOutLength
, dwInLength
);
367 memcpy(pbOutBuffer
, pbInBuffer
, Info
.nOutPos
);
370 *pdwOutLength
= Info
.nOutPos
;
375 /*****************************************************************************/
377 /* The "10" (de)compression is the Bzip2 (de)compression */
379 /*****************************************************************************/
381 int Compress_bzip2(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
, int * pCmpType
, int nCmpLevel
)
387 // Keep compiler happy
388 nCmpLevel
= nCmpLevel
;
390 // Initialize the BZLIB compression
394 // Adjust the block size
395 blockSize100k
= *pCmpType
;
396 if(blockSize100k
< 1 || blockSize100k
> 9)
399 // Blizzard uses 9 as blockSize100k, (0 as workFactor)
400 if(BZ2_bzCompressInit(&strm
, blockSize100k
, 0, workFactor
) == 0)
402 strm
.next_in
= pbInBuffer
;
403 strm
.avail_in
= dwInLength
;
404 strm
.next_out
= pbOutBuffer
;
405 strm
.avail_out
= *pdwOutLength
;
407 // Perform the compression
408 while(BZ2_bzCompress(&strm
, (strm
.avail_in
!= 0) ? BZ_RUN
: BZ_FINISH
) != BZ_STREAM_END
);
410 // Put the stream into idle state
411 BZ2_bzCompressEnd(&strm
);
412 *pdwOutLength
= strm
.total_out_lo32
;
422 int Decompress_bzip2(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
426 // Initialize the BZLIB decompression
429 if(BZ2_bzDecompressInit(&strm
, 0, 0) == 0)
431 strm
.next_in
= pbInBuffer
;
432 strm
.avail_in
= dwInLength
;
433 strm
.next_out
= pbOutBuffer
;
434 strm
.avail_out
= *pdwOutLength
;
436 // Perform the decompression
437 while(BZ2_bzDecompress(&strm
) != BZ_STREAM_END
);
439 // Put the stream into idle state
440 BZ2_bzDecompressEnd(&strm
);
441 *pdwOutLength
= strm
.total_out_lo32
;
445 // Set zero output length
452 /*****************************************************************************/
456 /*****************************************************************************/
458 // This table contains compress functions which can be applied to
459 // uncompressed blocks. Each bit set means the corresponding
460 // compression method/function must be applied.
462 // WAVes compression Data compression
463 // ------------------ -------------------
464 // 1st block - 0x08 0x08 (D, HF, W2, SC, D2)
465 // Rest blocks - 0x81 0x02 (W3)
467 static TCompressTable cmp_table
[] =
469 {MPQ_COMPRESSION_WAVE_MONO
, Compress_wave_mono
}, // IMA ADPCM mono compression
470 {MPQ_COMPRESSION_WAVE_STEREO
, Compress_wave_stereo
}, // IMA ADPCM stereo compression
471 {MPQ_COMPRESSION_HUFFMANN
, Compress_huff
}, // Huffmann compression
472 {MPQ_COMPRESSION_ZLIB
, Compress_zlib
}, // Compression with the "zlib" library
473 {MPQ_COMPRESSION_PKWARE
, Compress_pklib
}, // Compression with Pkware DCL
474 {MPQ_COMPRESSION_BZIP2
, Compress_bzip2
} // Compression Bzip2 library
477 int WINAPI
SCompCompress(char * pbCompressed
, int * pdwOutLength
, char * pbUncompressed
, int dwInLength
,
478 int uCompressions
, int nCmpType
, int nCmpLevel
)
480 char * pbTempBuff
= NULL
; // Temporary storage for decompressed data
481 char * pbOutput
= pbCompressed
; // Current output buffer
482 char * pbInput
; // Current input buffer
484 int dwCompressCount
= 0;
487 int dwInSize
= dwInLength
;
488 int dwEntries
= (sizeof(cmp_table
) / sizeof(TCompressTable
));
492 // Check for valid parameters
493 if(!pdwOutLength
|| *pdwOutLength
< dwInLength
|| !pbCompressed
|| !pbUncompressed
)
495 SetLastError(ERROR_INVALID_PARAMETER
);
499 // Count the compressions
500 for(i
= 0, uCompressions2
= uCompressions
; i
< dwEntries
; i
++)
502 if(uCompressions
& cmp_table
[i
].dwMask
)
505 uCompressions2
&= ~cmp_table
[i
].dwMask
;
508 // If a compression remains, do nothing
509 if(uCompressions2
!= 0)
511 SetLastError(ERROR_INVALID_PARAMETER
);
515 // If more that one compression, allocate intermediate buffer
516 if(dwCompressCount
>= 2)
517 pbTempBuff
= ALLOCMEM(char, *pdwOutLength
+ 1);
519 // Perform the compressions
520 pbInput
= pbUncompressed
;
521 dwInSize
= dwInLength
;
522 for(i
= 0, uCompressions2
= uCompressions
; i
< dwEntries
; i
++)
524 if(uCompressions2
& cmp_table
[i
].dwMask
)
526 // Set the right output buffer
528 pbOutput
= (dwCompressCount
& 1) ? pbTempBuff
: pbCompressed
;
530 // Perform the partial compression
531 dwOutSize
= *pdwOutLength
- 1;
533 cmp_table
[i
].Compress(pbOutput
+ 1, &dwOutSize
, pbInput
, dwInSize
, &nCmpType
, nCmpLevel
);
536 SetLastError(ERROR_GEN_FAILURE
);
542 // If the compression failed, copy the block instead
543 if(dwOutSize
>= dwInSize
- 1)
548 memcpy(pbOutput
, pbInput
, dwInSize
);
550 uCompressions
&= ~cmp_table
[i
].dwMask
;
551 dwOutSize
= dwInSize
;
555 pbInput
= pbOutput
+ 1;
556 dwInSize
= dwOutSize
;
562 // Copy the compressed data to the correct output buffer
565 if(uCompressions
&& (dwInSize
+ 1) < *pdwOutLength
)
567 if(pbOutput
!= pbCompressed
&& pbOutput
!= pbCompressed
+ 1)
568 memcpy(pbCompressed
, pbOutput
, dwInSize
);
569 *pbCompressed
= (char)uCompressions
;
570 *pdwOutLength
= dwInSize
+ 1;
574 memmove(pbCompressed
, pbUncompressed
, dwInSize
);
575 *pdwOutLength
= dwInSize
;
579 // Cleanup and return
580 if(pbTempBuff
!= NULL
)
585 /*****************************************************************************/
587 /* SCompDecompress */
589 /*****************************************************************************/
591 // This table contains decompress functions which can be applied to
592 // uncompressed blocks. The compression mask is stored in the first byte
593 // of compressed block
594 static TDecompressTable dcmp_table
[] =
596 {MPQ_COMPRESSION_BZIP2
, Decompress_bzip2
}, // Decompression with Bzip2 library
597 {MPQ_COMPRESSION_PKWARE
, Decompress_pklib
}, // Decompression with Pkware Data Compression Library
598 {MPQ_COMPRESSION_ZLIB
, Decompress_zlib
}, // Decompression with the "zlib" library
599 {MPQ_COMPRESSION_HUFFMANN
, Decompress_huff
}, // Huffmann decompression
600 {MPQ_COMPRESSION_WAVE_STEREO
, Decompress_wave_stereo
}, // IMA ADPCM stereo decompression
601 {MPQ_COMPRESSION_WAVE_MONO
, Decompress_wave_mono
} // IMA ADPCM mono decompression
604 int WINAPI
SCompDecompress(char * pbOutBuffer
, int * pdwOutLength
, char * pbInBuffer
, int dwInLength
)
606 char * pbTempBuff
= NULL
; // Temporary storage for decompressed data
607 char * pbWorkBuff
= NULL
; // Where to store decompressed data
608 int dwOutLength
= *pdwOutLength
; // For storage number of output bytes
609 unsigned fDecompressions1
; // Decompressions applied to the block
610 unsigned fDecompressions2
; // Just another copy of decompressions applied to the block
611 int dwCount
= 0; // Counter for every use
612 int dwEntries
= (sizeof(dcmp_table
) / sizeof(TDecompressTable
));
616 // If the input length is the same as output, do nothing.
617 if(dwInLength
== dwOutLength
)
619 if(pbInBuffer
== pbOutBuffer
)
622 memcpy(pbOutBuffer
, pbInBuffer
, dwInLength
);
623 *pdwOutLength
= dwInLength
;
627 // Get applied compression types and decrement data length
628 fDecompressions1
= fDecompressions2
= (unsigned char)*pbInBuffer
++;
631 // Search decompression table type and get all types of compression
632 for(i
= 0; i
< dwEntries
; i
++)
634 // We have to apply this decompression ?
635 if(fDecompressions1
& dcmp_table
[i
].dwMask
)
638 // Clear this flag from temporary variable.
639 fDecompressions2
&= ~dcmp_table
[i
].dwMask
;
642 // Check if there is some method unhandled
643 // (E.g. compressed by future versions)
644 if(fDecompressions2
!= 0)
646 SetLastError(ERROR_INVALID_PARAMETER
);
650 // If there is more than only one compression, we have to allocate extra buffer
652 pbTempBuff
= ALLOCMEM(char, dwOutLength
);
654 // Apply all decompressions
655 for(i
= 0, dwCount
= 0; i
< dwEntries
; i
++)
657 // If not used this kind of compression, skip the loop
658 if(fDecompressions1
& dcmp_table
[i
].dwMask
)
660 // If odd case, use target buffer for output, otherwise use allocated tempbuffer
661 pbWorkBuff
= (dwCount
++ & 1) ? pbTempBuff
: pbOutBuffer
;
662 dwOutLength
= *pdwOutLength
;
664 // Decompress buffer using corresponding function
665 dcmp_table
[i
].Decompress(pbWorkBuff
, &dwOutLength
, pbInBuffer
, dwInLength
);
668 SetLastError(ERROR_GEN_FAILURE
);
673 // Move output length to src length for next compression
674 dwInLength
= dwOutLength
;
675 pbInBuffer
= pbWorkBuff
;
679 // If output buffer is not the same like target buffer, we have to copy data
682 if(pbWorkBuff
!= pbOutBuffer
)
683 memcpy(pbOutBuffer
, pbInBuffer
, dwOutLength
);
687 // Delete temporary buffer, if necessary
688 if(pbTempBuff
!= NULL
)
691 *pdwOutLength
= dwOutLength
;
695 /*****************************************************************************/
697 /* SCompSetDataCompression */
699 /*****************************************************************************/
701 int WINAPI
SCompSetDataCompression(int nDataCompression
)
703 int nValidMask
= (MPQ_COMPRESSION_ZLIB
| MPQ_COMPRESSION_PKWARE
| MPQ_COMPRESSION_BZIP2
);
705 if((nDataCompression
& nValidMask
) != nDataCompression
)
707 SetLastError(ERROR_INVALID_PARAMETER
);
711 SetDataCompression(nDataCompression
);