| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * mpq-compression.c -- MPQ compression and decompression orchestration. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003-2026 Maik Broemme <mbroemme@libmpq.org> | ||
| 5 | * | ||
| 6 | * This file is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU Lesser General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2.1 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This file is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public License | ||
| 17 | * along with this file; if not, see <https://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "mpq-compression.h" | ||
| 21 | #include "mpq-endian.h" | ||
| 22 | #include "mpq-huffman.h" | ||
| 23 | #include "mpq-internal.h" | ||
| 24 | #include "mpq-pkware.h" | ||
| 25 | #include "mpq-wave.h" | ||
| 26 | #include <libmpq/mpq.h> | ||
| 27 | |||
| 28 | #include <stdlib.h> | ||
| 29 | #include <string.h> | ||
| 30 | |||
| 31 | #include <bzlib.h> | ||
| 32 | #include <zlib.h> | ||
| 33 | |||
| 34 | /* Map MPQ compression flags to the backend that can decode that payload. */ | ||
| 35 | static decompress_table_s dcmp_table[] = { | ||
| 36 | |||
| 37 | /* Reverse of the canonical writer order. */ | ||
| 38 | { LIBMPQ_COMPRESSION_BZIP2, libmpq__compression_decompress_bzip2 }, | ||
| 39 | { LIBMPQ_COMPRESSION_PKZIP, libmpq__compression_decompress_pkzip }, | ||
| 40 | { LIBMPQ_COMPRESSION_ZLIB, libmpq__compression_decompress_zlib }, | ||
| 41 | { LIBMPQ_COMPRESSION_HUFFMAN, libmpq__compression_decompress_huffman }, | ||
| 42 | { LIBMPQ_COMPRESSION_WAVE_STEREO, libmpq__compression_decompress_wave_stereo }, | ||
| 43 | { LIBMPQ_COMPRESSION_WAVE_MONO, libmpq__compression_decompress_wave_mono } | ||
| 44 | }; | ||
| 45 | |||
| 46 | /* Decompress an MPQ Huffman-compressed stream into the caller-provided buffer. | ||
| 47 | * The stream owns adaptive tree state, so the function initializes and frees | ||
| 48 | * a separate tree and bit reader for each archive block. */ | ||
| 49 | int32_t | ||
| 50 | 165 | libmpq__compression_decompress_huffman( | |
| 51 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 52 | ) | ||
| 53 | { | ||
| 54 | |||
| 55 | /* Huffman state and transferred-byte count for this stream. */ | ||
| 56 | 165 | int32_t tb = 0; | |
| 57 | struct huffman_tree_s *ht; | ||
| 58 | struct huffman_input_stream_s *is; | ||
| 59 | |||
| 60 |
3/6✓ Branch 0 taken 165 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 165 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 165 times.
|
165 | if (in_buf == NULL || out_buf == NULL || in_size < sizeof(uint32_t)) { |
| 61 | ✗ | return LIBMPQ_ERROR_FORMAT; | |
| 62 | } | ||
| 63 | |||
| 64 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 165 times.
|
165 | if ((ht = malloc(sizeof(struct huffman_tree_s))) == NULL) { |
| 65 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 66 | } | ||
| 67 | |||
| 68 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 165 times.
|
165 | if ((is = malloc(sizeof(struct huffman_input_stream_s))) == NULL) { |
| 69 | ✗ | free(ht); | |
| 70 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 71 | } | ||
| 72 | |||
| 73 | /* Start from a clean tree and input stream because both keep adaptive state. */ | ||
| 74 | 165 | memset(ht, 0, sizeof(struct huffman_tree_s)); | |
| 75 | 165 | memset(is, 0, sizeof(struct huffman_input_stream_s)); | |
| 76 | |||
| 77 | /* The first four bytes seed the bit buffer; remaining bytes form the stream. */ | ||
| 78 | 165 | is->bit_buf = libmpq__load_le32(in_buf); | |
| 79 | 165 | in_buf += sizeof(int32_t); | |
| 80 | 165 | is->in_buf = in_buf; | |
| 81 | 165 | is->in_end = in_buf + in_size - sizeof(uint32_t); | |
| 82 | 165 | is->bits = 32; | |
| 83 | |||
| 84 | 165 | libmpq__huffman_tree_init(ht, LIBMPQ_HUFF_DECOMPRESS); | |
| 85 | |||
| 86 | 165 | tb = libmpq__huffman_decode(ht, is, out_buf, out_size); | |
| 87 | |||
| 88 | 165 | free(is); | |
| 89 | 165 | free(ht); | |
| 90 | |||
| 91 | 165 | return tb; | |
| 92 | } | ||
| 93 | |||
| 94 | /* Decompress an MPQ zlib-compressed stream into the caller-provided buffer. | ||
| 95 | * The output count returned by zlib is converted to the libmpq block API's | ||
| 96 | * signed transfer convention, while zlib failures are propagated unchanged. */ | ||
| 97 | int32_t | ||
| 98 | 242 | libmpq__compression_decompress_zlib( | |
| 99 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 100 | ) | ||
| 101 | { | ||
| 102 | |||
| 103 | /* Zlib stream state and transferred-byte count for this stream. */ | ||
| 104 | 242 | int32_t result = 0; | |
| 105 | 242 | int32_t tb = 0; | |
| 106 | z_stream z; | ||
| 107 | |||
| 108 | /* Zlib consumes the complete MPQ block and writes directly to the caller buffer. */ | ||
| 109 | 242 | memset(&z, 0, sizeof(z)); | |
| 110 | 242 | z.next_in = (Bytef *)in_buf; | |
| 111 | 242 | z.avail_in = (uInt)in_size; | |
| 112 | 242 | z.total_in = in_size; | |
| 113 | 242 | z.next_out = (Bytef *)out_buf; | |
| 114 | 242 | z.avail_out = (uInt)out_size; | |
| 115 | 242 | z.total_out = 0; | |
| 116 | 242 | z.zalloc = NULL; | |
| 117 | 242 | z.zfree = NULL; | |
| 118 | |||
| 119 | /* Use zlib's default window handling; MPQ streams are standard zlib payloads. */ | ||
| 120 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 242 times.
|
242 | if ((result = inflateInit(&z)) != Z_OK) { |
| 121 | ✗ | return result; | |
| 122 | } | ||
| 123 | |||
| 124 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 242 times.
|
242 | if ((result = inflate(&z, Z_FINISH)) != Z_STREAM_END) { |
| 125 | ✗ | inflateEnd(&z); | |
| 126 | ✗ | return result; | |
| 127 | } | ||
| 128 | |||
| 129 | 242 | tb = z.total_out; | |
| 130 | |||
| 131 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 242 times.
|
242 | if ((result = inflateEnd(&z)) != Z_OK) { |
| 132 | ✗ | return result; | |
| 133 | } | ||
| 134 | |||
| 135 | 242 | return tb; | |
| 136 | } | ||
| 137 | |||
| 138 | /* Decompress an MPQ PKWARE Data Compression Library stream. | ||
| 139 | * A scratch codec object and callback state isolate the decoder from the | ||
| 140 | * caller's buffers while preserving the exact number of produced bytes. */ | ||
| 141 | int32_t | ||
| 142 | 3 | libmpq__compression_decompress_pkzip( | |
| 143 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 144 | ) | ||
| 145 | { | ||
| 146 | |||
| 147 | /* PKZIP work buffer, callback state and transferred-byte count. */ | ||
| 148 | 3 | int32_t tb = 0; | |
| 149 | pkzip_cmp_s *work_buf; | ||
| 150 | pkzip_data_s info; | ||
| 151 | |||
| 152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if ((work_buf = malloc(sizeof(*work_buf))) == NULL) { |
| 153 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 154 | } | ||
| 155 | |||
| 156 | /* The PKWARE decoder uses caller-provided scratch memory as its full state. */ | ||
| 157 | 3 | memset(work_buf, 0, sizeof(*work_buf)); | |
| 158 | |||
| 159 | /* Callback state tracks input and output positions for the decoder. */ | ||
| 160 | 3 | info.in_buf = in_buf; | |
| 161 | 3 | info.in_pos = 0; | |
| 162 | 3 | info.in_bytes = in_size; | |
| 163 | 3 | info.out_buf = out_buf; | |
| 164 | 3 | info.out_pos = 0; | |
| 165 | 3 | info.max_out = out_size; | |
| 166 | |||
| 167 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((tb = libmpq__pkzip_decompress((uint8_t *)work_buf, &info)) < 0) { |
| 168 | ✗ | free(work_buf); | |
| 169 | ✗ | return tb; | |
| 170 | } | ||
| 171 | |||
| 172 | 3 | tb = info.out_pos; | |
| 173 | |||
| 174 | 3 | free(work_buf); | |
| 175 | |||
| 176 | 3 | return tb; | |
| 177 | } | ||
| 178 | |||
| 179 | /* Decompress an MPQ bzip2-compressed stream into the caller-provided buffer. | ||
| 180 | * The bzip2 state consumes the compressed block and writes decoded bytes | ||
| 181 | * directly to the destination supplied by the archive reader. */ | ||
| 182 | int32_t | ||
| 183 | 77 | libmpq__compression_decompress_bzip2( | |
| 184 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 185 | ) | ||
| 186 | { | ||
| 187 | |||
| 188 | /* Bzip2 stream state and transferred-byte count for this stream. */ | ||
| 189 | 77 | int32_t result = 0; | |
| 190 | 77 | int32_t tb = 0; | |
| 191 | bz_stream strm; | ||
| 192 | |||
| 193 | /* Default bzip2 allocators are sufficient for MPQ block decompression. */ | ||
| 194 | 77 | strm.bzalloc = NULL; | |
| 195 | 77 | strm.bzfree = NULL; | |
| 196 | |||
| 197 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 77 times.
|
77 | if ((result = BZ2_bzDecompressInit(&strm, 0, 0)) != BZ_OK) { |
| 198 | ✗ | return result; | |
| 199 | } | ||
| 200 | |||
| 201 | /* Bzip2 consumes the complete MPQ block and writes directly to the caller buffer. */ | ||
| 202 | 77 | strm.next_in = (char *)in_buf; | |
| 203 | 77 | strm.avail_in = in_size; | |
| 204 | 77 | strm.next_out = (char *)out_buf; | |
| 205 | 77 | strm.avail_out = out_size; | |
| 206 | |||
| 207 | ✗ | for (;;) { | |
| 208 | 77 | uint32_t available_in = strm.avail_in; | |
| 209 | 77 | uint32_t available_out = strm.avail_out; | |
| 210 | |||
| 211 | 77 | result = BZ2_bzDecompress(&strm); | |
| 212 |
1/2✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
|
77 | if (result == BZ_STREAM_END) { |
| 213 | 77 | break; | |
| 214 | } | ||
| 215 | ✗ | if (result != BZ_OK || (strm.avail_in == available_in && strm.avail_out == available_out)) { | |
| 216 | ✗ | BZ2_bzDecompressEnd(&strm); | |
| 217 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 218 | } | ||
| 219 | ✗ | if (strm.avail_out == 0) { | |
| 220 | ✗ | BZ2_bzDecompressEnd(&strm); | |
| 221 | ✗ | return LIBMPQ_ERROR_SIZE; | |
| 222 | } | ||
| 223 | ✗ | if (strm.avail_in == 0) { | |
| 224 | ✗ | BZ2_bzDecompressEnd(&strm); | |
| 225 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | 77 | tb = strm.total_out_lo32; | |
| 230 | |||
| 231 | 77 | BZ2_bzDecompressEnd(&strm); | |
| 232 | |||
| 233 | 77 | return tb; | |
| 234 | } | ||
| 235 | |||
| 236 | /* Decompress an MPQ mono WAVE-compressed stream. | ||
| 237 | * The channel count is fixed to one so the shared WAVE decoder can validate | ||
| 238 | * the payload format and reconstruct the original PCM byte stream. */ | ||
| 239 | int32_t | ||
| 240 | 3 | libmpq__compression_decompress_wave_mono( | |
| 241 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 242 | ) | ||
| 243 | { | ||
| 244 | |||
| 245 | /* Transferred-byte count reported by the shared WAVE decoder. */ | ||
| 246 | 3 | int32_t tb = 0; | |
| 247 | |||
| 248 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((tb = libmpq__wave_decompress(out_buf, out_size, in_buf, in_size, 1)) < 0) { |
| 249 | ✗ | return tb; | |
| 250 | } | ||
| 251 | |||
| 252 | 3 | return tb; | |
| 253 | } | ||
| 254 | |||
| 255 | /* Decompress an MPQ stereo WAVE-compressed stream. | ||
| 256 | * The channel count is fixed to two, matching the stereo ADPCM framing and | ||
| 257 | * predictor state expected by the shared WAVE decoder. */ | ||
| 258 | int32_t | ||
| 259 | ✗ | libmpq__compression_decompress_wave_stereo( | |
| 260 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 261 | ) | ||
| 262 | { | ||
| 263 | |||
| 264 | /* Transferred-byte count reported by the shared WAVE decoder. */ | ||
| 265 | ✗ | int32_t tb = 0; | |
| 266 | |||
| 267 | ✗ | if ((tb = libmpq__wave_decompress(out_buf, out_size, in_buf, in_size, 2)) < 0) { | |
| 268 | ✗ | return tb; | |
| 269 | } | ||
| 270 | |||
| 271 | ✗ | return tb; | |
| 272 | } | ||
| 273 | |||
| 274 | /* Decode a Blizzard multi-compression stream by applying each flagged backend in order. | ||
| 275 | * The leading mask selects supported codecs, and intermediate buffers preserve | ||
| 276 | * each stage's output while the next stage consumes it. */ | ||
| 277 | int32_t | ||
| 278 | 412 | libmpq__compression_decompress_multi( | |
| 279 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size | ||
| 280 | ) | ||
| 281 | { | ||
| 282 | |||
| 283 | /* Compression mask state, temporary buffers and transferred-byte count. */ | ||
| 284 | 412 | int32_t tb = 0; | |
| 285 | 412 | uint32_t count = 0; | |
| 286 | 412 | uint32_t entries = (sizeof(dcmp_table) / sizeof(decompress_table_s)); | |
| 287 | 412 | uint8_t *temp_buf = NULL; | |
| 288 | 412 | uint8_t *work_buf = 0; | |
| 289 | uint8_t decompress_flag; | ||
| 290 | uint8_t decompress_unsupp; | ||
| 291 | uint32_t i; | ||
| 292 | |||
| 293 |
3/6✓ Branch 0 taken 412 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 412 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 412 times.
|
412 | if (in_buf == NULL || out_buf == NULL || in_size == 0) { |
| 294 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 295 | } | ||
| 296 | |||
| 297 | /* First byte selects the chained decompression backends for this block. */ | ||
| 298 | 412 | decompress_flag = decompress_unsupp = *in_buf++; | |
| 299 | |||
| 300 | 412 | in_size--; | |
| 301 | |||
| 302 | /* Count supported algorithms and remember flags that have no local backend. */ | ||
| 303 |
2/2✓ Branch 0 taken 2472 times.
✓ Branch 1 taken 412 times.
|
2884 | for (i = 0; i < entries; i++) { |
| 304 |
2/2✓ Branch 0 taken 487 times.
✓ Branch 1 taken 1985 times.
|
2472 | if (decompress_flag & dcmp_table[i].mask) { |
| 305 | 487 | count++; | |
| 306 | 487 | decompress_unsupp &= ~dcmp_table[i].mask; | |
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Refuse streams that use a compression method from a newer unsupported format. */ | ||
| 311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
|
412 | if (decompress_unsupp) { |
| 312 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 313 | } | ||
| 314 | |||
| 315 | /* Multiple backends need a temporary buffer between decompression stages. */ | ||
| 316 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 337 times.
|
412 | if (count > 1) { |
| 317 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | if ((temp_buf = malloc(out_size)) == NULL) { |
| 318 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 319 | } | ||
| 320 | |||
| 321 | 75 | memset(temp_buf, 0, out_size); | |
| 322 | } | ||
| 323 | |||
| 324 | /* Apply selected backends in table order, alternating buffers between stages. */ | ||
| 325 |
2/2✓ Branch 0 taken 2472 times.
✓ Branch 1 taken 412 times.
|
2884 | for (i = 0, count = 0; i < entries; i++) { |
| 326 | |||
| 327 | /* Apply this decompressor if its bit is present in the stream header. */ | ||
| 328 |
2/2✓ Branch 0 taken 487 times.
✓ Branch 1 taken 1985 times.
|
2472 | if (decompress_flag & dcmp_table[i].mask) { |
| 329 | |||
| 330 | /* Chained stages ping-pong between output and temporary storage. */ | ||
| 331 |
2/2✓ Branch 0 taken 412 times.
✓ Branch 1 taken 75 times.
|
487 | if (count == 0) |
| 332 |
2/2✓ Branch 0 taken 75 times.
✓ Branch 1 taken 337 times.
|
412 | work_buf = temp_buf != NULL ? temp_buf : out_buf; |
| 333 | else | ||
| 334 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | work_buf = (in_buf == out_buf) ? temp_buf : out_buf; |
| 335 | |||
| 336 | /* Decompress the current stage with the mapped backend. */ | ||
| 337 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 487 times.
|
487 | if ((tb = dcmp_table[i].decompress(in_buf, in_size, work_buf, out_size)) < 0) { |
| 338 | ✗ | free(temp_buf); | |
| 339 | ✗ | return tb; | |
| 340 | } | ||
| 341 | |||
| 342 | /* Feed this stage's output into the next decompression stage. */ | ||
| 343 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 487 times.
|
487 | if (tb < 0) { |
| 344 | ✗ | free(temp_buf); | |
| 345 | ✗ | return tb; | |
| 346 | } | ||
| 347 | 487 | in_size = (uint32_t)tb; | |
| 348 | 487 | in_buf = work_buf; | |
| 349 | |||
| 350 | 487 | count++; | |
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | /* Copy the final stage back if it ended in the temporary buffer. */ | ||
| 355 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 412 times.
|
412 | if (work_buf != out_buf) { |
| 356 | ✗ | memcpy(out_buf, in_buf, (size_t)tb); | |
| 357 | } | ||
| 358 | |||
| 359 | 412 | free(temp_buf); | |
| 360 | |||
| 361 | 412 | return tb; | |
| 362 | } | ||
| 363 | |||
| 364 | /* Return whether every requested compression bit has a local implementation. | ||
| 365 | * Unknown bits are rejected before any codec or archive state is modified. */ | ||
| 366 | int | ||
| 367 | 1318 | libmpq__compression_supported_mask(uint32_t mask) | |
| 368 | { | ||
| 369 | 1318 | return (mask & ~(LIBMPQ_COMPRESSION_HUFFMAN | LIBMPQ_COMPRESSION_ZLIB | | |
| 370 | LIBMPQ_COMPRESSION_PKZIP | LIBMPQ_COMPRESSION_BZIP2 | | ||
| 371 | 1318 | LIBMPQ_COMPRESSION_WAVE_MONO | LIBMPQ_COMPRESSION_WAVE_STEREO)) == 0; | |
| 372 | } | ||
| 373 | |||
| 374 | /* Apply one selected compression backend and replace the current buffer. | ||
| 375 | * Each backend receives the current stage output and returns a newly owned | ||
| 376 | * buffer, allowing the caller to retain the previous stage on fallback. */ | ||
| 377 | static int32_t | ||
| 378 | 1029 | compression_stage(uint8_t **data, size_t *size, uint32_t mask) | |
| 379 | { | ||
| 380 | uint8_t *out; | ||
| 381 | 1029 | size_t out_size = *size + (*size / 100) + 1024; | |
| 382 | z_stream z; | ||
| 383 | bz_stream b; | ||
| 384 | int result; | ||
| 385 | |||
| 386 |
2/2✓ Branch 0 taken 213 times.
✓ Branch 1 taken 816 times.
|
1029 | if (mask == LIBMPQ_COMPRESSION_HUFFMAN) |
| 387 | 213 | out_size = *size * 2 + 64; | |
| 388 |
1/2✓ Branch 0 taken 1029 times.
✗ Branch 1 not taken.
|
1029 | out = malloc(out_size ? out_size : 1); |
| 389 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
|
1029 | if (out == NULL) |
| 390 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 391 |
2/2✓ Branch 0 taken 210 times.
✓ Branch 1 taken 819 times.
|
1029 | if (mask == LIBMPQ_COMPRESSION_PKZIP) { |
| 392 | 210 | uint8_t *candidate = NULL; | |
| 393 | 210 | uint32_t candidate_size = 0; | |
| 394 | int32_t status; | ||
| 395 | 210 | free(out); | |
| 396 | 210 | status = libmpq__pkzip_compress(*data, (uint32_t)*size, &candidate, &candidate_size); | |
| 397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
|
210 | if (status < 0) |
| 398 | ✗ | return status; | |
| 399 | 210 | free(*data); | |
| 400 | 210 | *data = candidate; | |
| 401 | 210 | *size = candidate_size; | |
| 402 | 210 | return LIBMPQ_SUCCESS; | |
| 403 |
2/2✓ Branch 0 taken 213 times.
✓ Branch 1 taken 606 times.
|
819 | } else if (mask == LIBMPQ_COMPRESSION_HUFFMAN) { |
| 404 | 213 | struct huffman_tree_s *tree = calloc(1, sizeof(*tree)); | |
| 405 | struct huffman_output_stream_s stream; | ||
| 406 | int32_t encoded; | ||
| 407 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 213 times.
|
213 | if (tree == NULL) { |
| 408 | ✗ | free(out); | |
| 409 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 410 | } | ||
| 411 | 213 | stream.out_buf = out; | |
| 412 | 213 | stream.capacity = (uint32_t)out_size; | |
| 413 | 213 | encoded = libmpq__huffman_encode(tree, &stream, *data, (uint32_t)*size); | |
| 414 | 213 | free(tree); | |
| 415 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 213 times.
|
213 | if (encoded < 0) { |
| 416 | ✗ | free(out); | |
| 417 | ✗ | return encoded; | |
| 418 | } | ||
| 419 | 213 | free(*data); | |
| 420 | 213 | *data = out; | |
| 421 | 213 | *size = (size_t)encoded; | |
| 422 | 213 | return LIBMPQ_SUCCESS; | |
| 423 |
3/4✓ Branch 0 taken 603 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 603 times.
|
606 | } else if (mask == LIBMPQ_COMPRESSION_WAVE_MONO || mask == LIBMPQ_COMPRESSION_WAVE_STEREO) { |
| 424 | 3 | uint8_t *candidate = NULL; | |
| 425 | 3 | uint32_t candidate_size = 0; | |
| 426 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | int32_t status = libmpq__wave_compress( |
| 427 | 3 | *data, (uint32_t)*size, &candidate, &candidate_size, | |
| 428 | mask == LIBMPQ_COMPRESSION_WAVE_MONO ? 1 : 2 | ||
| 429 | ); | ||
| 430 | 3 | free(out); | |
| 431 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (status < 0) |
| 432 | ✗ | return status; | |
| 433 | 3 | free(*data); | |
| 434 | 3 | *data = candidate; | |
| 435 | 3 | *size = candidate_size; | |
| 436 | 3 | return LIBMPQ_SUCCESS; | |
| 437 |
2/2✓ Branch 0 taken 358 times.
✓ Branch 1 taken 245 times.
|
603 | } else if (mask == LIBMPQ_COMPRESSION_ZLIB) { |
| 438 | 358 | memset(&z, 0, sizeof(z)); | |
| 439 | 358 | z.next_in = *data; | |
| 440 | 358 | z.avail_in = (uInt)*size; | |
| 441 | 358 | z.next_out = out; | |
| 442 | 358 | z.avail_out = (uInt)out_size; | |
| 443 | 358 | result = deflateInit(&z, Z_DEFAULT_COMPRESSION); | |
| 444 |
1/2✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
|
358 | if (result == Z_OK) |
| 445 | 358 | result = deflate(&z, Z_FINISH); | |
| 446 |
1/2✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
|
358 | if (result == Z_STREAM_END) |
| 447 | 358 | result = deflateEnd(&z); | |
| 448 | else | ||
| 449 | ✗ | deflateEnd(&z); | |
| 450 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 358 times.
|
358 | if (result != Z_OK) { |
| 451 | ✗ | free(out); | |
| 452 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 453 | } | ||
| 454 | 358 | out_size = z.total_out; | |
| 455 | } else { | ||
| 456 | 245 | memset(&b, 0, sizeof(b)); | |
| 457 | 245 | b.next_in = (char *)*data; | |
| 458 | 245 | b.avail_in = (unsigned int)*size; | |
| 459 | 245 | b.next_out = (char *)out; | |
| 460 | 245 | b.avail_out = (unsigned int)out_size; | |
| 461 | 245 | result = BZ2_bzCompressInit(&b, 9, 0, 30); | |
| 462 |
1/2✓ Branch 0 taken 245 times.
✗ Branch 1 not taken.
|
245 | if (result == BZ_OK) { |
| 463 | do { | ||
| 464 | 245 | result = BZ2_bzCompress(&b, BZ_FINISH); | |
| 465 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 245 times.
|
245 | } while (result == BZ_FINISH_OK); |
| 466 | } | ||
| 467 |
1/2✓ Branch 0 taken 245 times.
✗ Branch 1 not taken.
|
245 | if (result == BZ_STREAM_END) |
| 468 | 245 | result = BZ2_bzCompressEnd(&b); | |
| 469 | else | ||
| 470 | ✗ | BZ2_bzCompressEnd(&b); | |
| 471 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 245 times.
|
245 | if (result != BZ_OK) { |
| 472 | ✗ | free(out); | |
| 473 | ✗ | return LIBMPQ_ERROR_UNPACK; | |
| 474 | } | ||
| 475 | 245 | out_size = b.total_out_lo32; | |
| 476 | } | ||
| 477 | 603 | free(*data); | |
| 478 | 603 | *data = out; | |
| 479 | 603 | *size = out_size; | |
| 480 | 603 | return LIBMPQ_SUCCESS; | |
| 481 | } | ||
| 482 | |||
| 483 | /* Apply the selected compression chain and return its actual successful mask. | ||
| 484 | * Stages run in canonical Storm order, and a stage is kept only when it saves | ||
| 485 | * at least two bytes; the emitted mask therefore describes actual reductions. */ | ||
| 486 | int32_t | ||
| 487 | 614 | libmpq__compression_encode_sector( | |
| 488 | const uint8_t *input, size_t input_size, uint32_t requested, uint8_t **output, | ||
| 489 | size_t *output_size, uint8_t *emitted_mask | ||
| 490 | ) | ||
| 491 | { | ||
| 492 | uint8_t *data; | ||
| 493 | size_t size; | ||
| 494 | 614 | uint32_t masks[] = { LIBMPQ_COMPRESSION_WAVE_MONO, LIBMPQ_COMPRESSION_WAVE_STEREO, | |
| 495 | LIBMPQ_COMPRESSION_HUFFMAN, LIBMPQ_COMPRESSION_ZLIB, | ||
| 496 | LIBMPQ_COMPRESSION_PKZIP, LIBMPQ_COMPRESSION_BZIP2 }; | ||
| 497 | size_t i; | ||
| 498 | |||
| 499 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 614 times.
|
614 | if (!libmpq__compression_supported_mask(requested)) |
| 500 | ✗ | return LIBMPQ_ERROR_FORMAT; | |
| 501 |
1/2✓ Branch 0 taken 614 times.
✗ Branch 1 not taken.
|
614 | data = malloc(input_size ? input_size : 1); |
| 502 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 614 times.
|
614 | if (data == NULL) |
| 503 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 504 | 614 | memcpy(data, input, input_size); | |
| 505 | 614 | size = input_size; | |
| 506 | 614 | *emitted_mask = 0; | |
| 507 | |||
| 508 | /* Try each requested codec independently so expansion does not poison later stages. */ | ||
| 509 |
2/2✓ Branch 0 taken 3684 times.
✓ Branch 1 taken 614 times.
|
4298 | for (i = 0; i < sizeof(masks) / sizeof(masks[0]); i++) { |
| 510 |
2/2✓ Branch 0 taken 1029 times.
✓ Branch 1 taken 2655 times.
|
3684 | if (requested & masks[i]) { |
| 511 | 1029 | size_t before = size; | |
| 512 |
1/2✓ Branch 0 taken 1029 times.
✗ Branch 1 not taken.
|
1029 | uint8_t *saved = malloc(before ? before : 1); |
| 513 | int32_t result; | ||
| 514 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
|
1029 | if (saved == NULL) { |
| 515 | ✗ | free(data); | |
| 516 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 517 | } | ||
| 518 | 1029 | memcpy(saved, data, before); | |
| 519 | 1029 | result = compression_stage(&data, &size, masks[i]); | |
| 520 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1029 times.
|
1029 | if (result < 0) { |
| 521 | ✗ | free(saved); | |
| 522 | ✗ | continue; | |
| 523 | } | ||
| 524 |
2/2✓ Branch 0 taken 495 times.
✓ Branch 1 taken 534 times.
|
1029 | if (size <= before - (before >= 2 ? 2 : before)) |
| 525 | 495 | *emitted_mask |= (uint8_t)masks[i]; | |
| 526 | else { | ||
| 527 | 534 | free(data); | |
| 528 | 534 | data = saved; | |
| 529 | 534 | size = before; | |
| 530 | 534 | saved = NULL; | |
| 531 | } | ||
| 532 | 1029 | free(saved); | |
| 533 | } | ||
| 534 | } | ||
| 535 |
3/4✓ Branch 0 taken 420 times.
✓ Branch 1 taken 194 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 420 times.
|
614 | if (*emitted_mask == 0 || size + 1 >= input_size) { |
| 536 | 194 | *emitted_mask = 0; | |
| 537 | 194 | *output = data; | |
| 538 | 194 | *output_size = size; | |
| 539 | } else { | ||
| 540 | 420 | uint8_t *packed = realloc(data, size + 1); | |
| 541 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 420 times.
|
420 | if (packed == NULL) { |
| 542 | ✗ | free(data); | |
| 543 | ✗ | return LIBMPQ_ERROR_MALLOC; | |
| 544 | } | ||
| 545 | 420 | memmove(packed + 1, packed, size); | |
| 546 | 420 | packed[0] = *emitted_mask; | |
| 547 | 420 | *output = packed; | |
| 548 | 420 | *output_size = size + 1; | |
| 549 | } | ||
| 550 | 614 | return LIBMPQ_SUCCESS; | |
| 551 | } | ||
| 552 | |||
| 553 | /* Decompress one archive block according to its MPQ compression flags. | ||
| 554 | * Raw data is copied directly, while PKWARE and multi-compression payloads | ||
| 555 | * are dispatched to the codec layer with MPQ-compatible expansion semantics. */ | ||
| 556 | int32_t | ||
| 557 | 943 | libmpq__compression_decompress_block( | |
| 558 | uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size, | ||
| 559 | uint32_t compression_type | ||
| 560 | ) | ||
| 561 | { | ||
| 562 | 943 | int32_t tb = 0; | |
| 563 | |||
| 564 |
2/2✓ Branch 0 taken 192 times.
✓ Branch 1 taken 751 times.
|
943 | if (compression_type == LIBMPQ_FLAG_COMPRESS_NONE) { |
| 565 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 192 times.
|
192 | if (in_size < out_size) |
| 566 | ✗ | return LIBMPQ_ERROR_SIZE; | |
| 567 | 192 | memcpy(out_buf, in_buf, out_size); | |
| 568 | 192 | tb = out_size; | |
| 569 |
3/4✓ Branch 0 taken 613 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 613 times.
✗ Branch 3 not taken.
|
751 | } else if (compression_type == LIBMPQ_FLAG_COMPRESS_PKZIP || |
| 570 | compression_type == LIBMPQ_FLAG_COMPRESS_MULTI) { | ||
| 571 |
2/2✓ Branch 0 taken 138 times.
✓ Branch 1 taken 613 times.
|
751 | if (compression_type == LIBMPQ_FLAG_COMPRESS_PKZIP) { |
| 572 |
1/2✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
|
138 | if (in_size >= out_size) { |
| 573 | 138 | memcpy(out_buf, in_buf, out_size); | |
| 574 | 138 | tb = out_size; | |
| 575 | ✗ | } else if ((tb = libmpq__compression_decompress_pkzip( | |
| 576 | in_buf, in_size, out_buf, out_size | ||
| 577 | )) < 0) { | ||
| 578 | ✗ | return tb; | |
| 579 | } | ||
| 580 |
2/2✓ Branch 0 taken 412 times.
✓ Branch 1 taken 201 times.
|
613 | } else if (in_size < out_size) { |
| 581 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 412 times.
|
412 | if ((tb = libmpq__compression_decompress_multi(in_buf, in_size, out_buf, out_size)) < 0) |
| 582 | ✗ | return tb; | |
| 583 | } else { | ||
| 584 | 201 | memcpy(out_buf, in_buf, out_size); | |
| 585 | 201 | tb = out_size; | |
| 586 | } | ||
| 587 | } | ||
| 588 | 943 | return tb; | |
| 589 | } | ||
| 590 |