| 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 |