GCC Code Coverage Report


Directory: src/
File: src/mpq-compression.c
Date: 2026-10-06 13:47:41
Exec Total Coverage
Lines: 360 425 84.7%
Functions: 15 15 100.0%
Branches: 211 286 73.8%

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-block.h"
22 #include "mpq-endian.h"
23 #include "mpq-huffman.h"
24 #include "mpq-pkware.h"
25 #include "mpq-sparse.h"
26 #include "mpq-wave.h"
27 #include <libmpq/mpq.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <bzlib.h>
33 #include <limits.h>
34 #include <lzma.h>
35 #include <zlib.h>
36
37 /* Map MPQ compression flags to the backend that can decode that payload. */
38 static decompress_table_s dcmp_table[] = {
39
40 /* Reverse of the canonical writer order. */
41 { LIBMPQ_COMPRESSION_BZIP2, libmpq__compression_decompress_bzip2 },
42 { LIBMPQ_COMPRESSION_PKZIP, libmpq__compression_decompress_pkzip },
43 { LIBMPQ_COMPRESSION_ZLIB, libmpq__compression_decompress_zlib },
44 { LIBMPQ_COMPRESSION_HUFFMAN, libmpq__compression_decompress_huffman },
45 { LIBMPQ_COMPRESSION_WAVE_STEREO, libmpq__compression_decompress_wave_stereo },
46 { LIBMPQ_COMPRESSION_WAVE_MONO, libmpq__compression_decompress_wave_mono },
47 { LIBMPQ_COMPRESSION_SPARSE, libmpq__compression_decompress_sparse }
48 };
49
50 /* Return a raw-sector copy when a requested codec cannot beat raw storage. */
51 static int32_t
52 19 copy_raw(
53 const uint8_t *input, size_t input_size, uint8_t **output, size_t *output_size,
54 uint8_t *emitted_mask
55 )
56 {
57
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 uint8_t *data = malloc(input_size ? input_size : 1);
58
59
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 if (data == NULL)
60 ✗ return LIBMPQ_ERROR_MALLOC;
61 19 memcpy(data, input, input_size);
62 19 *output = data;
63 19 *output_size = input_size;
64 19 *emitted_mask = 0;
65 19 return 0;
66 }
67
68 /* Decode the MPQ v2+ LZMA framing using the caller-known unpacked sector size. */
69 static int32_t
70 88 decompress_lzma(uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size)
71 {
72 88 lzma_filter filters[2] = { { LZMA_FILTER_LZMA1, NULL }, { LZMA_VLI_UNKNOWN, NULL } };
73 88 lzma_stream strm = LZMA_STREAM_INIT;
74 uint64_t memusage;
75 lzma_ret ret;
76 88 int decoder_initialized = 0;
77 88 int32_t result = LIBMPQ_ERROR_UNPACK;
78
79
4/6
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 85 times.
88 if (in_buf == NULL || out_buf == NULL || in_size <= LIBMPQ_LZMA_HEADER_SIZE)
80 3 return LIBMPQ_ERROR_UNPACK;
81
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 84 times.
85 if (in_buf[0] != LIBMPQ_LZMA_USE_FILTER)
82 1 return LIBMPQ_ERROR_UNPACK;
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 if (out_size > INT32_MAX)
84 ✗ return LIBMPQ_ERROR_SIZE;
85
86 84 ret = lzma_properties_decode(&filters[0], NULL, in_buf + 1, LIBMPQ_LZMA_PROPERTIES_SIZE);
87
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 if (ret == LZMA_MEM_ERROR)
88 ✗ return LIBMPQ_ERROR_MALLOC;
89
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 if (ret != LZMA_OK)
90 ✗ return LIBMPQ_ERROR_UNPACK;
91
92 84 memusage = lzma_raw_decoder_memusage(filters);
93
3/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 83 times.
84 if (memusage == UINT64_MAX || memusage > LIBMPQ_LZMA_DECODER_MEMORY_MAX)
94 1 goto cleanup;
95
96 83 ret = lzma_raw_decoder(&strm, filters);
97
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (ret == LZMA_MEM_ERROR) {
98 ✗ result = LIBMPQ_ERROR_MALLOC;
99 ✗ goto cleanup;
100 }
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83 times.
83 if (ret != LZMA_OK)
102 ✗ goto cleanup;
103 83 decoder_initialized = 1;
104 83 strm.next_in = in_buf + LIBMPQ_LZMA_HEADER_SIZE;
105 83 strm.avail_in = in_size - LIBMPQ_LZMA_HEADER_SIZE;
106 83 strm.next_out = out_buf;
107 83 strm.avail_out = out_size;
108
109 do {
110 85 ret = lzma_code(&strm, LZMA_FINISH);
111
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 83 times.
85 } while (ret == LZMA_OK);
112
113 /*
114 * Raw LZMA1 with an EOPM returns STREAM_END. StormLib streams omit the
115 * marker and finish with BUF_ERROR. Accept that only when all supplied
116 * input was consumed and the expected output size was produced.
117 */
118
5/6
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 81 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 82 times.
83 if ((ret == LZMA_STREAM_END || ret == LZMA_BUF_ERROR) && strm.total_out == out_size &&
119
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 82 times.
82 strm.avail_out == 0 && strm.avail_in == 0) {
120 82 result = (int32_t)out_size;
121 }
122
123 1 cleanup:
124
2/2
✓ Branch 0 taken 83 times.
✓ Branch 1 taken 1 times.
84 if (decoder_initialized)
125 83 lzma_end(&strm);
126 84 free(filters[0].options);
127 84 return result;
128 }
129
130 /* Encode one MPQ v2+ LZMA sector with an EOPM or return raw data when it cannot win. */
131 static int32_t
132 90 encode_lzma(
133 const uint8_t *input, size_t input_size, uint8_t **output, size_t *output_size,
134 uint8_t *emitted_mask
135 )
136 {
137 lzma_options_lzma options;
138 90 lzma_filter filters[2] = { { LZMA_FILTER_LZMA1, &options }, { LZMA_VLI_UNKNOWN, NULL } };
139 uint8_t *packed;
140 90 size_t encoded_size = 0;
141 size_t candidate_capacity;
142 uint32_t properties_size;
143 lzma_ret ret;
144
145
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 71 times.
90 if (input_size <= LIBMPQ_LZMA_TOTAL_OVERHEAD)
146 19 return copy_raw(input, input_size, output, output_size, emitted_mask);
147
148 /* 15 + raw_lzma_size < input_size, so only input_size - 16 can win. */
149 71 candidate_capacity = input_size - LIBMPQ_LZMA_TOTAL_OVERHEAD - 1;
150 71 packed = malloc(LIBMPQ_LZMA_TOTAL_OVERHEAD + candidate_capacity);
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71 times.
71 if (packed == NULL)
152 ✗ return LIBMPQ_ERROR_MALLOC;
153
154 71 memset(&options, 0, sizeof(options));
155
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 71 times.
71 if (lzma_lzma_preset(&options, 6)) {
156 ✗ free(packed);
157 ✗ return LIBMPQ_ERROR_UNPACK;
158 }
159 71 options.dict_size = 1U << 20;
160 71 options.lc = 3;
161 71 options.lp = 0;
162 71 options.pb = 2;
163 71 ret = lzma_properties_size(&properties_size, &filters[0]);
164
2/4
✓ Branch 0 taken 71 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 71 times.
71 if (ret != LZMA_OK || properties_size != LIBMPQ_LZMA_PROPERTIES_SIZE) {
165 ✗ free(packed);
166 ✗ return LIBMPQ_ERROR_UNPACK;
167 }
168
169 71 packed[0] = LIBMPQ_COMPRESSION_LZMA_METHOD;
170 71 packed[1] = LIBMPQ_LZMA_USE_FILTER;
171
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 71 times.
71 if (lzma_properties_encode(&filters[0], packed + 2) != LZMA_OK) {
172 ✗ free(packed);
173 ✗ return LIBMPQ_ERROR_UNPACK;
174 }
175 71 libmpq__store_le64(packed + 7, input_size);
176 71 ret = lzma_raw_buffer_encode(
177 filters, NULL, input, input_size, packed + LIBMPQ_LZMA_TOTAL_OVERHEAD, &encoded_size,
178 candidate_capacity
179 );
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71 times.
71 if (ret == LZMA_BUF_ERROR) {
181 ✗ free(packed);
182 ✗ return copy_raw(input, input_size, output, output_size, emitted_mask);
183 }
184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71 times.
71 if (ret == LZMA_MEM_ERROR) {
185 ✗ free(packed);
186 ✗ return LIBMPQ_ERROR_MALLOC;
187 }
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 71 times.
71 if (ret != LZMA_OK) {
189 ✗ free(packed);
190 ✗ return LIBMPQ_ERROR_UNPACK;
191 }
192 71 *output = packed;
193 71 *output_size = LIBMPQ_LZMA_TOTAL_OVERHEAD + encoded_size;
194 71 *emitted_mask = LIBMPQ_COMPRESSION_LZMA_METHOD;
195 71 return 0;
196 }
197
198 /*
199 * Decompress an MPQ Huffman-compressed stream into the caller-provided buffer.
200 * The stream owns adaptive tree state, so the function initializes and frees
201 * a separate tree and bit reader for each archive block.
202 */
203 int32_t
204 371 libmpq__compression_decompress_huffman(
205 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
206 )
207 {
208
209 /* Huffman state and transferred-byte count for this stream. */
210 371 int32_t tb = 0;
211 struct huffman_tree_s *ht;
212 struct huffman_input_stream_s *is;
213
214
3/6
✓ Branch 0 taken 371 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 371 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 371 times.
371 if (in_buf == NULL || out_buf == NULL || in_size < sizeof(uint32_t)) {
215 ✗ return LIBMPQ_ERROR_FORMAT;
216 }
217
218
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 371 times.
371 if ((ht = malloc(sizeof(struct huffman_tree_s))) == NULL) {
219 ✗ return LIBMPQ_ERROR_MALLOC;
220 }
221
222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 371 times.
371 if ((is = malloc(sizeof(struct huffman_input_stream_s))) == NULL) {
223 ✗ free(ht);
224 ✗ return LIBMPQ_ERROR_MALLOC;
225 }
226
227 /* Start from a clean tree and input stream because both keep adaptive state. */
228 371 memset(ht, 0, sizeof(struct huffman_tree_s));
229 371 memset(is, 0, sizeof(struct huffman_input_stream_s));
230
231 /* The first four bytes seed the bit buffer; remaining bytes form the stream. */
232 371 is->bit_buf = libmpq__load_le32(in_buf);
233 371 in_buf += sizeof(int32_t);
234 371 is->in_buf = in_buf;
235 371 is->in_end = in_buf + in_size - sizeof(uint32_t);
236 371 is->bits = 32;
237
238 371 libmpq__huffman_tree_init(ht, LIBMPQ_HUFF_DECOMPRESS);
239
240 371 tb = libmpq__huffman_decode(ht, is, out_buf, out_size);
241
242 371 free(is);
243 371 free(ht);
244
245 371 return tb;
246 }
247
248 /*
249 * Decompress an MPQ zlib-compressed stream into the caller-provided buffer.
250 * The output count returned by zlib is converted to the libmpq block API's
251 * signed transfer convention, while zlib failures are propagated unchanged.
252 */
253 int32_t
254 14644 libmpq__compression_decompress_zlib(
255 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
256 )
257 {
258
259 /* Zlib stream state and transferred-byte count for this stream. */
260 14644 int32_t result = 0;
261 14644 int32_t tb = 0;
262 z_stream z;
263
264 /* Zlib consumes the complete MPQ block and writes directly to the caller buffer. */
265 14644 memset(&z, 0, sizeof(z));
266 14644 z.next_in = (Bytef *)in_buf;
267 14644 z.avail_in = (uInt)in_size;
268 14644 z.total_in = in_size;
269 14644 z.next_out = (Bytef *)out_buf;
270 14644 z.avail_out = (uInt)out_size;
271 14644 z.total_out = 0;
272 14644 z.zalloc = NULL;
273 14644 z.zfree = NULL;
274
275 /* Use zlib's default window handling; MPQ streams are standard zlib payloads. */
276
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14644 times.
14644 if ((result = inflateInit(&z)) != Z_OK) {
277 ✗ return result;
278 }
279
280
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14644 times.
14644 if ((result = inflate(&z, Z_FINISH)) != Z_STREAM_END) {
281 ✗ inflateEnd(&z);
282 ✗ return result;
283 }
284
285 14644 tb = z.total_out;
286
287
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14644 times.
14644 if ((result = inflateEnd(&z)) != Z_OK) {
288 ✗ return result;
289 }
290
291 14644 return tb;
292 }
293
294 /*
295 * Decompress an MPQ PKWARE Data Compression Library stream.
296 * A scratch codec object and callback state isolate the decoder from the
297 * caller's buffers while preserving the exact number of produced bytes.
298 */
299 int32_t
300 1106 libmpq__compression_decompress_pkzip(
301 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
302 )
303 {
304
305 /* PKZIP work buffer, callback state and transferred-byte count. */
306 1106 int32_t tb = 0;
307 uint32_t status;
308 pkzip_cmp_s *work_buf;
309 pkzip_data_s info;
310
311
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1106 times.
1106 if ((work_buf = malloc(sizeof(*work_buf))) == NULL) {
312 ✗ return LIBMPQ_ERROR_MALLOC;
313 }
314
315 /* The PKWARE decoder uses caller-provided scratch memory as its full state. */
316 1106 memset(work_buf, 0, sizeof(*work_buf));
317
318 /* Callback state tracks input and output positions for the decoder. */
319 1106 info.in_buf = in_buf;
320 1106 info.in_pos = 0;
321 1106 info.in_bytes = in_size;
322 1106 info.out_buf = out_buf;
323 1106 info.out_pos = 0;
324 1106 info.max_out = out_size;
325
326 1106 status = libmpq__pkzip_decompress((uint8_t *)work_buf, &info);
327
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1098 times.
1106 if (status != LIBMPQ_PKZIP_CMP_NO_ERROR) {
328 8 free(work_buf);
329 8 return LIBMPQ_ERROR_UNPACK;
330 }
331
332 1098 tb = info.out_pos;
333
334 1098 free(work_buf);
335
336 1098 return tb;
337 }
338
339 /*
340 * Decompress an MPQ bzip2-compressed stream into the caller-provided buffer.
341 * The bzip2 state consumes the compressed block and writes decoded bytes
342 * directly to the destination supplied by the archive reader.
343 */
344 int32_t
345 1025 libmpq__compression_decompress_bzip2(
346 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
347 )
348 {
349
350 /* Bzip2 stream state and transferred-byte count for this stream. */
351 1025 int32_t result = 0;
352 1025 int32_t tb = 0;
353 bz_stream strm;
354
355 /* Default bzip2 allocators are sufficient for MPQ block decompression. */
356 1025 strm.bzalloc = NULL;
357 1025 strm.bzfree = NULL;
358
359
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1025 times.
1025 if ((result = BZ2_bzDecompressInit(&strm, 0, 0)) != BZ_OK) {
360 ✗ return result;
361 }
362
363 /* Bzip2 consumes the complete MPQ block and writes directly to the caller buffer. */
364 1025 strm.next_in = (char *)in_buf;
365 1025 strm.avail_in = in_size;
366 1025 strm.next_out = (char *)out_buf;
367 1025 strm.avail_out = out_size;
368
369 ✗ for (;;) {
370 1025 uint32_t available_in = strm.avail_in;
371 1025 uint32_t available_out = strm.avail_out;
372
373 1025 result = BZ2_bzDecompress(&strm);
374
2/2
✓ Branch 0 taken 1024 times.
✓ Branch 1 taken 1 times.
1025 if (result == BZ_STREAM_END) {
375 1024 break;
376 }
377
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 if (result != BZ_OK || (strm.avail_in == available_in && strm.avail_out == available_out)) {
378 1 BZ2_bzDecompressEnd(&strm);
379 1 return LIBMPQ_ERROR_UNPACK;
380 }
381 ✗ if (strm.avail_out == 0) {
382 ✗ BZ2_bzDecompressEnd(&strm);
383 ✗ return LIBMPQ_ERROR_SIZE;
384 }
385 ✗ if (strm.avail_in == 0) {
386 ✗ BZ2_bzDecompressEnd(&strm);
387 ✗ return LIBMPQ_ERROR_UNPACK;
388 }
389 }
390
391 1024 tb = strm.total_out_lo32;
392
393 1024 BZ2_bzDecompressEnd(&strm);
394
395 1024 return tb;
396 }
397
398 /*
399 * Decompress an MPQ mono WAVE-compressed stream.
400 * The channel count is fixed to one so the shared WAVE decoder can validate
401 * the payload format and reconstruct the original PCM byte stream.
402 */
403 int32_t
404 72 libmpq__compression_decompress_wave_mono(
405 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
406 )
407 {
408
409 /* Transferred-byte count reported by the shared WAVE decoder. */
410 72 int32_t tb = 0;
411
412
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
72 if ((tb = libmpq__wave_decompress(out_buf, out_size, in_buf, in_size, 1)) < 0) {
413 ✗ return tb;
414 }
415
416 72 return tb;
417 }
418
419 /*
420 * Decompress an MPQ stereo WAVE-compressed stream.
421 * The channel count is fixed to two, matching the stereo ADPCM framing and
422 * predictor state expected by the shared WAVE decoder.
423 */
424 int32_t
425 132 libmpq__compression_decompress_wave_stereo(
426 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
427 )
428 {
429
430 /* Transferred-byte count reported by the shared WAVE decoder. */
431 132 int32_t tb = 0;
432
433
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 132 times.
132 if ((tb = libmpq__wave_decompress(out_buf, out_size, in_buf, in_size, 2)) < 0) {
434 ✗ return tb;
435 }
436
437 132 return tb;
438 }
439
440 /* Adapt the private SPARSE codec to the multi-compression callback signature. */
441 int32_t
442 2714 libmpq__compression_decompress_sparse(
443 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size
444 )
445 {
446 2714 return libmpq__sparse_decompress(in_buf, in_size, out_buf, out_size);
447 }
448
449 /*
450 * Decode a Blizzard multi-compression stream by applying each flagged backend in order.
451 * The leading mask selects supported codecs, and intermediate buffers preserve
452 * each stage's output while the next stage consumes it.
453 */
454 int32_t
455 17085 libmpq__compression_decompress_multi(
456 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size, uint32_t format_version
457 )
458 {
459
460 /* Compression mask state, temporary buffers and transferred-byte count. */
461 17085 int32_t tb = 0;
462 17085 uint32_t count = 0;
463 17085 uint32_t entries = (sizeof(dcmp_table) / sizeof(decompress_table_s));
464 17085 uint8_t *temp_buf = NULL;
465 17085 uint8_t *work_buf = 0;
466 uint8_t decompress_flag;
467 uint8_t decompress_unsupp;
468 uint32_t i;
469
470
3/6
✓ Branch 0 taken 17085 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17085 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17085 times.
17085 if (in_buf == NULL || out_buf == NULL || in_size == 0) {
471 ✗ return LIBMPQ_ERROR_UNPACK;
472 }
473
474 /* First byte selects the chained decompression backends for this block. */
475 17085 decompress_flag = decompress_unsupp = *in_buf++;
476
477 17085 in_size--;
478
479 /* MPQ v2+ serializes LZMA as special method 0x12, not a bitmask chain. */
480
4/4
✓ Branch 0 taken 8664 times.
✓ Branch 1 taken 8421 times.
✓ Branch 2 taken 88 times.
✓ Branch 3 taken 8576 times.
17085 if (format_version >= LIBMPQ_ARCHIVE_VERSION_TWO &&
481 decompress_flag == LIBMPQ_COMPRESSION_LZMA_METHOD) {
482 88 return decompress_lzma(in_buf, in_size, out_buf, out_size);
483 }
484
485 /* Count supported algorithms and remember flags that have no local backend. */
486
2/2
✓ Branch 0 taken 118979 times.
✓ Branch 1 taken 16997 times.
135976 for (i = 0; i < entries; i++) {
487
2/2
✓ Branch 0 taken 19048 times.
✓ Branch 1 taken 99931 times.
118979 if (decompress_flag & dcmp_table[i].mask) {
488 19048 count++;
489 19048 decompress_unsupp &= ~dcmp_table[i].mask;
490 }
491 }
492
493 /* Refuse streams that use a compression method from a newer unsupported format. */
494
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 16964 times.
16997 if (decompress_unsupp) {
495 33 return LIBMPQ_ERROR_UNPACK;
496 }
497
498 /* Multiple backends need a temporary buffer between decompression stages. */
499
2/2
✓ Branch 0 taken 2084 times.
✓ Branch 1 taken 14880 times.
16964 if (count > 1) {
500
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2084 times.
2084 if ((temp_buf = malloc(out_size)) == NULL) {
501 ✗ return LIBMPQ_ERROR_MALLOC;
502 }
503
504 2084 memset(temp_buf, 0, out_size);
505 }
506
507 /* Apply selected backends in table order, alternating buffers between stages. */
508
2/2
✓ Branch 0 taken 118742 times.
✓ Branch 1 taken 16963 times.
135705 for (i = 0, count = 0; i < entries; i++) {
509
510 /* Apply this decompressor if its bit is present in the stream header. */
511
2/2
✓ Branch 0 taken 19047 times.
✓ Branch 1 taken 99695 times.
118742 if (decompress_flag & dcmp_table[i].mask) {
512
513 /* Chained stages ping-pong between output and temporary storage. */
514
2/2
✓ Branch 0 taken 16964 times.
✓ Branch 1 taken 2083 times.
19047 if (count == 0)
515
2/2
✓ Branch 0 taken 2084 times.
✓ Branch 1 taken 14880 times.
16964 work_buf = temp_buf != NULL ? temp_buf : out_buf;
516 else
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2083 times.
2083 work_buf = (in_buf == out_buf) ? temp_buf : out_buf;
518
519 /* Decompress the current stage with the mapped backend. */
520
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 19046 times.
19047 if ((tb = dcmp_table[i].decompress(in_buf, in_size, work_buf, out_size)) < 0) {
521 1 free(temp_buf);
522 1 return tb;
523 }
524
525 /* Feed this stage's output into the next decompression stage. */
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19046 times.
19046 if (tb < 0) {
527 ✗ free(temp_buf);
528 ✗ return tb;
529 }
530 19046 in_size = (uint32_t)tb;
531 19046 in_buf = work_buf;
532
533 19046 count++;
534 }
535 }
536
537 /* Copy the final stage back if it ended in the temporary buffer. */
538
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16963 times.
16963 if (work_buf != out_buf) {
539 ✗ memcpy(out_buf, in_buf, (size_t)tb);
540 }
541
542 16963 free(temp_buf);
543
544 16963 return tb;
545 }
546
547 /*
548 * Return whether the compression mask is allowed for this archive version
549 * and writer policy.
550 */
551 int32_t
552 22118 libmpq__compression_allowed(
553 uint32_t format_version, uint32_t mask, libmpq_compression_policy_t policy
554 )
555 {
556
4/4
✓ Branch 0 taken 22116 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11913 times.
✓ Branch 3 taken 10203 times.
22118 if (format_version > LIBMPQ_ARCHIVE_VERSION_TWO ||
557
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 11909 times.
11913 (policy != LIBMPQ_COMPRESSION_POLICY_STANDARD &&
558 policy != LIBMPQ_COMPRESSION_POLICY_EXTENDED))
559 6 return 0;
560
2/2
✓ Branch 0 taken 701 times.
✓ Branch 1 taken 21411 times.
22112 if (mask == LIBMPQ_COMPRESSION_LZMA)
561 701 return format_version >= LIBMPQ_ARCHIVE_VERSION_TWO;
562
2/2
✓ Branch 0 taken 1037 times.
✓ Branch 1 taken 20374 times.
21411 if (mask & LIBMPQ_COMPRESSION_LZMA)
563 1037 return 0;
564
565 /* Lossy ADPCM would corrupt the preceding SPARSE control/data stream. */
566
2/2
✓ Branch 0 taken 10065 times.
✓ Branch 1 taken 10309 times.
20374 if ((mask & LIBMPQ_COMPRESSION_SPARSE) &&
567
2/2
✓ Branch 0 taken 448 times.
✓ Branch 1 taken 9617 times.
10065 (mask & (LIBMPQ_COMPRESSION_WAVE_MONO | LIBMPQ_COMPRESSION_WAVE_STEREO)))
568 448 return 0;
569
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 19798 times.
19926 if ((mask & (LIBMPQ_COMPRESSION_WAVE_MONO | LIBMPQ_COMPRESSION_WAVE_STEREO)) ==
570 (LIBMPQ_COMPRESSION_WAVE_MONO | LIBMPQ_COMPRESSION_WAVE_STEREO))
571 128 return 0;
572
2/2
✓ Branch 0 taken 9551 times.
✓ Branch 1 taken 10247 times.
19798 if (format_version >= LIBMPQ_ARCHIVE_VERSION_TWO &&
573
2/2
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 9366 times.
9551 (mask & (LIBMPQ_COMPRESSION_ZLIB | LIBMPQ_COMPRESSION_BZIP2)) ==
574 (LIBMPQ_COMPRESSION_ZLIB | LIBMPQ_COMPRESSION_BZIP2)) {
575 185 return 0;
576 }
577
578 /* STANDARD v2 uses a fixed method list instead of arbitrary chains. */
579
4/4
✓ Branch 0 taken 9366 times.
✓ Branch 1 taken 10247 times.
✓ Branch 2 taken 4528 times.
✓ Branch 3 taken 4838 times.
19613 if (format_version >= LIBMPQ_ARCHIVE_VERSION_TWO &&
580 policy == LIBMPQ_COMPRESSION_POLICY_STANDARD)
581
6/6
✓ Branch 0 taken 2635 times.
✓ Branch 1 taken 1363 times.
✓ Branch 2 taken 2625 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 2590 times.
✓ Branch 5 taken 35 times.
3998 return mask == 0 || mask == LIBMPQ_COMPRESSION_ZLIB || mask == LIBMPQ_COMPRESSION_PKZIP ||
582
4/4
✓ Branch 0 taken 1689 times.
✓ Branch 1 taken 901 times.
✓ Branch 2 taken 917 times.
✓ Branch 3 taken 772 times.
2590 mask == LIBMPQ_COMPRESSION_BZIP2 || mask == LIBMPQ_COMPRESSION_SPARSE ||
583
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 772 times.
917 mask == (LIBMPQ_COMPRESSION_SPARSE | LIBMPQ_COMPRESSION_ZLIB) ||
584
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 11 times.
145 mask == (LIBMPQ_COMPRESSION_SPARSE | LIBMPQ_COMPRESSION_BZIP2) ||
585
4/4
✓ Branch 0 taken 3998 times.
✓ Branch 1 taken 530 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 117 times.
8526 mask == (LIBMPQ_COMPRESSION_HUFFMAN | LIBMPQ_COMPRESSION_WAVE_MONO) ||
586 mask == (LIBMPQ_COMPRESSION_HUFFMAN | LIBMPQ_COMPRESSION_WAVE_STEREO);
587 15085 return (mask &
588 ~(LIBMPQ_COMPRESSION_HUFFMAN | LIBMPQ_COMPRESSION_ZLIB | LIBMPQ_COMPRESSION_PKZIP |
589 LIBMPQ_COMPRESSION_BZIP2 | LIBMPQ_COMPRESSION_SPARSE | LIBMPQ_COMPRESSION_WAVE_MONO |
590 15085 LIBMPQ_COMPRESSION_WAVE_STEREO)) == 0;
591 }
592
593 /*
594 * Apply one selected compression backend and replace the current buffer.
595 * Each backend receives the current stage output and returns a newly owned
596 * buffer, allowing the caller to retain the previous stage on fallback.
597 */
598 static int32_t
599 7141 compression_stage(uint8_t **data, size_t *size, uint32_t mask)
600 {
601 uint8_t *out;
602 7141 size_t out_size = *size + (*size / 100) + 1024;
603 z_stream z;
604 bz_stream b;
605 int result;
606
607
2/2
✓ Branch 0 taken 3323 times.
✓ Branch 1 taken 3818 times.
7141 if (mask == LIBMPQ_COMPRESSION_SPARSE) {
608
3/4
✓ Branch 0 taken 2884 times.
✓ Branch 1 taken 439 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2884 times.
3323 if (*size < 7 || *size > INT32_MAX)
609 439 return LIBMPQ_ERROR_UNPACK;
610
611 /* Keep only a candidate that can save the required two stage bytes. */
612 2884 out_size = *size - 2;
613 }
614
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 6509 times.
6702 if (mask == LIBMPQ_COMPRESSION_HUFFMAN)
615 193 out_size = *size * 2 + 64;
616
1/2
✓ Branch 0 taken 6702 times.
✗ Branch 1 not taken.
6702 out = malloc(out_size ? out_size : 1);
617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6702 times.
6702 if (out == NULL)
618 ✗ return LIBMPQ_ERROR_MALLOC;
619
2/2
✓ Branch 0 taken 2884 times.
✓ Branch 1 taken 3818 times.
6702 if (mask == LIBMPQ_COMPRESSION_SPARSE) {
620 2884 int32_t encoded = libmpq__sparse_compress(*data, (uint32_t)*size, out, (uint32_t)out_size);
621
2/2
✓ Branch 0 taken 218 times.
✓ Branch 1 taken 2666 times.
2884 if (encoded < 0) {
622 218 free(out);
623 218 return encoded;
624 }
625 2666 out_size = (size_t)encoded;
626
2/2
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 3655 times.
3818 } else if (mask == LIBMPQ_COMPRESSION_PKZIP) {
627 163 uint8_t *candidate = NULL;
628 163 uint32_t candidate_size = 0;
629 int32_t status;
630 163 free(out);
631 163 status = libmpq__pkzip_compress(*data, (uint32_t)*size, &candidate, &candidate_size);
632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 163 times.
163 if (status < 0)
633 ✗ return status;
634 163 free(*data);
635 163 *data = candidate;
636 163 *size = candidate_size;
637 163 return 0;
638
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 3462 times.
3655 } else if (mask == LIBMPQ_COMPRESSION_HUFFMAN) {
639 193 struct huffman_tree_s *tree = calloc(1, sizeof(*tree));
640 struct huffman_output_stream_s stream;
641 int32_t encoded;
642
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
193 if (tree == NULL) {
643 ✗ free(out);
644 ✗ return LIBMPQ_ERROR_MALLOC;
645 }
646 193 stream.out_buf = out;
647 193 stream.capacity = (uint32_t)out_size;
648 193 encoded = libmpq__huffman_encode(tree, &stream, *data, (uint32_t)*size);
649 193 free(tree);
650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 193 times.
193 if (encoded < 0) {
651 ✗ free(out);
652 ✗ return encoded;
653 }
654 193 free(*data);
655 193 *data = out;
656 193 *size = (size_t)encoded;
657 193 return 0;
658
4/4
✓ Branch 0 taken 3452 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 3436 times.
3462 } else if (mask == LIBMPQ_COMPRESSION_WAVE_MONO || mask == LIBMPQ_COMPRESSION_WAVE_STEREO) {
659 26 uint8_t *candidate = NULL;
660 26 uint32_t candidate_size = 0;
661
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 16 times.
26 int32_t status = libmpq__wave_compress(
662 26 *data, (uint32_t)*size, &candidate, &candidate_size,
663 mask == LIBMPQ_COMPRESSION_WAVE_MONO ? 1 : 2
664 );
665 26 free(out);
666
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 22 times.
26 if (status < 0)
667 4 return status;
668 22 free(*data);
669 22 *data = candidate;
670 22 *size = candidate_size;
671 22 return 0;
672
2/2
✓ Branch 0 taken 2166 times.
✓ Branch 1 taken 1270 times.
3436 } else if (mask == LIBMPQ_COMPRESSION_ZLIB) {
673 2166 memset(&z, 0, sizeof(z));
674 2166 z.next_in = *data;
675 2166 z.avail_in = (uInt)*size;
676 2166 z.next_out = out;
677 2166 z.avail_out = (uInt)out_size;
678 2166 result = deflateInit(&z, Z_DEFAULT_COMPRESSION);
679
1/2
✓ Branch 0 taken 2166 times.
✗ Branch 1 not taken.
2166 if (result == Z_OK)
680 2166 result = deflate(&z, Z_FINISH);
681
1/2
✓ Branch 0 taken 2166 times.
✗ Branch 1 not taken.
2166 if (result == Z_STREAM_END)
682 2166 result = deflateEnd(&z);
683 else
684 ✗ deflateEnd(&z);
685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2166 times.
2166 if (result != Z_OK) {
686 ✗ free(out);
687 ✗ return LIBMPQ_ERROR_UNPACK;
688 }
689 2166 out_size = z.total_out;
690 } else {
691 1270 memset(&b, 0, sizeof(b));
692 1270 b.next_in = (char *)*data;
693 1270 b.avail_in = (unsigned int)*size;
694 1270 b.next_out = (char *)out;
695 1270 b.avail_out = (unsigned int)out_size;
696 1270 result = BZ2_bzCompressInit(&b, 9, 0, 30);
697
1/2
✓ Branch 0 taken 1270 times.
✗ Branch 1 not taken.
1270 if (result == BZ_OK) {
698 do {
699 1270 result = BZ2_bzCompress(&b, BZ_FINISH);
700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1270 times.
1270 } while (result == BZ_FINISH_OK);
701 }
702
1/2
✓ Branch 0 taken 1270 times.
✗ Branch 1 not taken.
1270 if (result == BZ_STREAM_END)
703 1270 result = BZ2_bzCompressEnd(&b);
704 else
705 ✗ BZ2_bzCompressEnd(&b);
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1270 times.
1270 if (result != BZ_OK) {
707 ✗ free(out);
708 ✗ return LIBMPQ_ERROR_UNPACK;
709 }
710 1270 out_size = b.total_out_lo32;
711 }
712 6102 free(*data);
713 6102 *data = out;
714 6102 *size = out_size;
715 6102 return 0;
716 }
717
718 /*
719 * Apply the selected compression chain and return its actual successful mask.
720 * Stages run in canonical Storm order, and a stage is kept only when it saves
721 * at least two bytes; the emitted mask therefore describes actual reductions.
722 */
723 int32_t
724 4835 libmpq__compression_encode_sector(
725 const uint8_t *input, size_t input_size, uint32_t requested, uint32_t format_version,
726 libmpq_compression_policy_t policy, uint8_t **output, size_t *output_size, uint8_t *emitted_mask
727 )
728 {
729 uint8_t *data;
730 size_t size;
731 4835 uint32_t masks[] = { LIBMPQ_COMPRESSION_SPARSE, LIBMPQ_COMPRESSION_WAVE_MONO,
732 LIBMPQ_COMPRESSION_WAVE_STEREO, LIBMPQ_COMPRESSION_HUFFMAN,
733 LIBMPQ_COMPRESSION_ZLIB, LIBMPQ_COMPRESSION_PKZIP,
734 LIBMPQ_COMPRESSION_BZIP2 };
735 size_t i;
736
737
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4835 times.
4835 if (!libmpq__compression_allowed(format_version, requested, policy))
738 ✗ return LIBMPQ_ERROR_FORMAT;
739
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 4745 times.
4835 if (requested == LIBMPQ_COMPRESSION_LZMA)
740 90 return encode_lzma(input, input_size, output, output_size, emitted_mask);
741
2/2
✓ Branch 0 taken 4738 times.
✓ Branch 1 taken 7 times.
4745 data = malloc(input_size ? input_size : 1);
742
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4745 times.
4745 if (data == NULL)
743 ✗ return LIBMPQ_ERROR_MALLOC;
744 4745 memcpy(data, input, input_size);
745 4745 size = input_size;
746 4745 *emitted_mask = 0;
747
748 /* Try each requested codec independently so expansion does not poison later stages. */
749
2/2
✓ Branch 0 taken 33215 times.
✓ Branch 1 taken 4745 times.
37960 for (i = 0; i < sizeof(masks) / sizeof(masks[0]); i++) {
750
2/2
✓ Branch 0 taken 7141 times.
✓ Branch 1 taken 26074 times.
33215 if (requested & masks[i]) {
751 7141 size_t before = size;
752
2/2
✓ Branch 0 taken 7134 times.
✓ Branch 1 taken 7 times.
7141 uint8_t *saved = malloc(before ? before : 1);
753 int32_t result;
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7141 times.
7141 if (saved == NULL) {
755 ✗ free(data);
756 ✗ return LIBMPQ_ERROR_MALLOC;
757 }
758 7141 memcpy(saved, data, before);
759 7141 result = compression_stage(&data, &size, masks[i]);
760
2/2
✓ Branch 0 taken 661 times.
✓ Branch 1 taken 6480 times.
7141 if (result < 0) {
761 661 free(saved);
762 661 continue;
763 }
764
2/2
✓ Branch 0 taken 5684 times.
✓ Branch 1 taken 796 times.
6480 if (size <= before - (before >= 2 ? 2 : before))
765 5684 *emitted_mask |= (uint8_t)masks[i];
766 else {
767 796 free(data);
768 796 data = saved;
769 796 size = before;
770 796 saved = NULL;
771 }
772 6480 free(saved);
773 }
774 }
775
776 /* Skipped stages must not leave a disallowed method; restore raw input. */
777
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4741 times.
4745 if (!libmpq__compression_allowed(format_version, *emitted_mask, policy)) {
778 4 free(data);
779
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 data = malloc(input_size ? input_size : 1);
780
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (data == NULL)
781 ✗ return LIBMPQ_ERROR_MALLOC;
782 4 memcpy(data, input, input_size);
783 4 size = input_size;
784 4 *emitted_mask = 0;
785 }
786
3/4
✓ Branch 0 taken 3832 times.
✓ Branch 1 taken 913 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3832 times.
4745 if (*emitted_mask == 0 || size + 1 >= input_size) {
787 913 *emitted_mask = 0;
788 913 *output = data;
789 913 *output_size = size;
790 } else {
791 3832 uint8_t *packed = realloc(data, size + 1);
792
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3832 times.
3832 if (packed == NULL) {
793 ✗ free(data);
794 ✗ return LIBMPQ_ERROR_MALLOC;
795 }
796 3832 memmove(packed + 1, packed, size);
797 3832 packed[0] = *emitted_mask;
798 3832 *output = packed;
799 3832 *output_size = size + 1;
800 }
801 4745 return 0;
802 }
803
804 /*
805 * Decompress one archive block according to its MPQ compression flags.
806 * Raw data is copied directly, while PKWARE and multi-compression payloads
807 * are dispatched to the codec layer with MPQ-compatible expansion semantics.
808 */
809 int32_t
810 22433 libmpq__compression_decompress_block(
811 uint8_t *in_buf, uint32_t in_size, uint8_t *out_buf, uint32_t out_size,
812 uint32_t compression_type, uint32_t format_version
813 )
814 {
815 22433 int32_t tb = 0;
816 22433 int pkzip = compression_type == LIBMPQ_FLAG_COMPRESS_PKZIP;
817
818
2/2
✓ Branch 0 taken 1652 times.
✓ Branch 1 taken 20781 times.
22433 if (compression_type == LIBMPQ_FLAG_COMPRESS_NONE) {
819
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1652 times.
1652 if (in_size < out_size)
820 ✗ return LIBMPQ_ERROR_SIZE;
821
2/2
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 1513 times.
1652 if (out_buf != in_buf)
822 139 memcpy(out_buf, in_buf, out_size);
823 1652 tb = out_size;
824
3/4
✓ Branch 0 taken 19900 times.
✓ Branch 1 taken 881 times.
✓ Branch 2 taken 19900 times.
✗ Branch 3 not taken.
20781 } else if (compression_type == LIBMPQ_FLAG_COMPRESS_PKZIP ||
825 compression_type == LIBMPQ_FLAG_COMPRESS_MULTI) {
826
2/2
✓ Branch 0 taken 881 times.
✓ Branch 1 taken 19900 times.
20781 if (compression_type == LIBMPQ_FLAG_COMPRESS_PKZIP) {
827
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 760 times.
881 if (in_size >= out_size) {
828 121 memcpy(out_buf, in_buf, out_size);
829 121 tb = out_size;
830
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 760 times.
760 } else if ((tb = libmpq__compression_decompress_pkzip(
831 in_buf, in_size, out_buf, out_size
832 )) < 0) {
833 ✗ return tb;
834 }
835
2/2
✓ Branch 0 taken 17067 times.
✓ Branch 1 taken 2833 times.
19900 } else if (in_size < out_size) {
836
2/4
✓ Branch 0 taken 17067 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17067 times.
✗ Branch 3 not taken.
17067 if (in_buf != NULL && in_size > 0)
837 17067 pkzip = (in_buf[0] & LIBMPQ_COMPRESSION_PKZIP) != 0;
838
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 17033 times.
17067 if ((tb = libmpq__compression_decompress_multi(
839 in_buf, in_size, out_buf, out_size, format_version
840 )) < 0)
841 34 return tb;
842 } else {
843 2833 memcpy(out_buf, in_buf, out_size);
844 2833 tb = out_size;
845 }
846 }
847
848 /*
849 * A PKWARE stage may produce a shorter intermediate stream, but a complete
850 * MPQ block must produce exactly its declared unpacked size.
851 */
852
5/6
✓ Branch 0 taken 968 times.
✓ Branch 1 taken 21431 times.
✓ Branch 2 taken 968 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 966 times.
22399 if (pkzip && tb >= 0 && (uint32_t)tb != out_size)
853 2 return LIBMPQ_ERROR_UNPACK;
854 22397 return tb;
855 }
856