GCC Code Coverage Report


Directory: src/
File: src/mpq-pkware.c
Date: 2026-09-02 16:40:44
Exec Total Coverage
Lines: 156 229 68.1%
Functions: 10 11 90.9%
Branches: 70 130 53.8%

Line Branch Exec Source
1 /*
2 * mpq-pkware.c -- PKWARE Data Compression Library implementation.
3 *
4 * Copyright (c) 2003-2026 Maik Broemme <mbroemme@libmpq.org>
5 *
6 * This source was adapted from the C++ version of pkware.cpp included
7 * in stormlib. The C++ version belongs to the following authors:
8 *
9 * Ladislav Zezula <ladik@zezula.net>
10 *
11 * This file is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
15 *
16 * This file is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this file; if not, see <https://www.gnu.org/licenses/>.
23 */
24
25 #include "mpq-pkware.h"
26 #include "mpq-internal.h"
27 #include <libmpq/mpq.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 /* Distance bit lengths used by the PKWARE explode distance decoder. */
33 static const uint8_t pkzip_dist_bits[] = {
34 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
35 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
36 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
37 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
38 };
39
40 /* Distance code lookup table used by the PKWARE explode decoder. */
41 static const uint8_t pkzip_dist_code[] = {
42 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A,
43 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C,
44 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
45 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00
46 };
47
48 /* Extra bit counts for decoded copy lengths. */
49 static const uint8_t pkzip_clen_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
51
52 /* Base values for decoded copy lengths. */
53 static const uint16_t pkzip_len_base[] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005,
54 0x0006, 0x0007, 0x0008, 0x000A, 0x000E, 0x0016,
55 0x0026, 0x0046, 0x0086, 0x0106 };
56
57 /* Bit lengths for the static copy-length Huffman table. */
58 static const uint8_t pkzip_slen_bits[] = { 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05,
59 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 };
60
61 /* Codes for the static copy-length Huffman table. */
62 static const uint8_t pkzip_len_code[] = { 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14,
63 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 };
64
65 /* Append a low-bit-first PKWARE code to the output bitstream.
66 * The caller provides a zeroed output buffer and a bit cursor, and this helper
67 * sets only the bits represented by the requested code. */
68 static void
69 3476834 pkzip_put_bits(uint8_t *out, size_t *bit_pos, uint32_t value, unsigned bits)
70 {
71 unsigned i;
72
2/2
✓ Branch 0 taken 15649850 times.
✓ Branch 1 taken 3476834 times.
19126684 for (i = 0; i < bits; i++, (*bit_pos)++)
73
2/2
✓ Branch 0 taken 6445940 times.
✓ Branch 1 taken 9203910 times.
15649850 if (value & (1u << i))
74 6445940 out[*bit_pos >> 3] |= (uint8_t)(1u << (*bit_pos & 7));
75 3476834 }
76
77 /* Bit lengths for the ASCII literal table. */
78 static const uint8_t pkzip_bits_asc[] = {
79 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C,
80 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
81 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08,
82 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B,
83 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06,
84 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08,
85 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05,
86 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C,
87 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
88 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
89 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
90 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
91 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
92 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
93 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D,
94 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D
95 };
96
97 /* Codes for the ASCII literal table. */
98 static const uint16_t pkzip_code_asc[] = {
99 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, 0x01E0, 0x00B8, 0x0062, 0x0EE0,
100 0x06E0, 0x0022, 0x0AE0, 0x02E0, 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360,
101 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, 0x000F, 0x0250, 0x0038, 0x0260,
102 0x0050, 0x0C60, 0x0390, 0x00D8, 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098,
103 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, 0x0074, 0x00E8, 0x0068, 0x0460,
104 0x0090, 0x0034, 0x00B0, 0x0710, 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8,
105 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, 0x0024, 0x0510, 0x000E, 0x0036,
106 0x0016, 0x0044, 0x0030, 0x00C8, 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088,
107 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, 0x000A, 0x000B, 0x0210, 0x0004,
108 0x0013, 0x0032, 0x0003, 0x001D, 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078,
109 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, 0x0240, 0x1C40, 0x0C40, 0x1440,
110 0x0440, 0x1840, 0x0840, 0x1040, 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380,
111 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, 0x0180, 0x1E80, 0x0E80, 0x1680,
112 0x0680, 0x1A80, 0x0A80, 0x1280, 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080,
113 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, 0x0DA0, 0x05A0, 0x09A0, 0x01A0,
114 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320,
115 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, 0x0C20, 0x0420, 0x0820, 0x0020,
116 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0,
117 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, 0x0300, 0x0D40, 0x1D00, 0x0D00,
118 0x1500, 0x0540, 0x0500, 0x1900, 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600,
119 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, 0x1C00, 0x0C00, 0x1400, 0x0400,
120 0x1800, 0x0800, 0x1000, 0x0000
121 };
122
123 /* Consume bits from the PKWARE input accumulator.
124 * It refills the eight-bit staging window through the configured callback and
125 * returns a nonzero value when the compressed input ends prematurely. */
126 static int32_t
127 1243 skip_bit(pkzip_cmp_s *mpq_pkzip, uint32_t bits)
128 {
129
2/2
✓ Branch 0 taken 546 times.
✓ Branch 1 taken 697 times.
1243 if (bits <= mpq_pkzip->extra_bits) {
130 546 mpq_pkzip->extra_bits -= bits;
131 546 mpq_pkzip->bit_buf >>= bits;
132 546 return 0;
133 }
134
135 /* Refill the bit buffer from the input callback when the staging buffer is empty. */
136 697 mpq_pkzip->bit_buf >>= mpq_pkzip->extra_bits;
137
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 694 times.
697 if (mpq_pkzip->in_pos == mpq_pkzip->in_bytes) {
138 3 uint32_t in_size = sizeof(mpq_pkzip->in_buf);
139
140
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if ((mpq_pkzip->in_bytes =
141 3 mpq_pkzip->read_buf((char *)mpq_pkzip->in_buf, &in_size, mpq_pkzip->param)) == 0) {
142 3 return 1;
143 }
144 mpq_pkzip->in_pos = 0;
145 }
146
147 694 mpq_pkzip->bit_buf |= (mpq_pkzip->in_buf[mpq_pkzip->in_pos++] << 8);
148 694 mpq_pkzip->bit_buf >>= (bits - mpq_pkzip->extra_bits);
149 694 mpq_pkzip->extra_bits = (mpq_pkzip->extra_bits - bits) + 8;
150
151 694 return 0;
152 }
153
154 /* Build a decode lookup table from PKWARE canonical bit codes.
155 * Each short code is expanded across all matching low-byte prefixes so the
156 * decoder can resolve literals and lengths without walking a tree at runtime. */
157 static void
158 6 generate_tables_decode(int32_t count, uint8_t *bits, const uint8_t *code, uint8_t *buf2)
159 {
160
161 /* Walk backwards because the original table order assigns higher codes first. */
162 int32_t i;
163
164
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 6 times.
246 for (i = count - 1; i >= 0; i--) {
165
166 /* Fill every lookup slot reachable by this code prefix. */
167 240 uint32_t idx1 = code[i];
168 240 uint32_t idx2 = 1 << bits[i];
169
170 do {
171 1536 buf2[idx1] = (uint8_t)i;
172 1536 idx1 += idx2;
173
2/2
✓ Branch 0 taken 1296 times.
✓ Branch 1 taken 240 times.
1536 } while (idx1 < 0x100);
174 }
175 6 }
176
177 /* Build the ASCII literal lookup tables used by the PKWARE decoder.
178 * The static canonical tables are expanded into prefix tables that support
179 * the variable-length ASCII mode used by DCL streams. */
180 static void
181 generate_tables_ascii(pkzip_cmp_s *mpq_pkzip)
182 {
183
184 /* ASCII table cursor and bit expansion state. */
185 const uint16_t *code_asc = &pkzip_code_asc[0xFF];
186 uint32_t acc;
187 uint32_t add;
188 uint16_t count;
189
190 /* Expand the static ASCII literal codes into the decoder lookup tables. */
191 for (count = 0x00FF; code_asc >= pkzip_code_asc; code_asc--, count--) {
192 uint8_t *bits_asc = mpq_pkzip->bits_asc + count;
193 uint8_t bits_tmp = *bits_asc;
194
195 if (bits_tmp <= 8) {
196 add = (1 << bits_tmp);
197 acc = *code_asc;
198 while (acc < 0x100) {
199 mpq_pkzip->offs_2c34[acc] = (uint8_t)count;
200 acc += add;
201 }
202 } else {
203 if ((acc = (*code_asc & 0xFF)) != 0) {
204 mpq_pkzip->offs_2c34[acc] = 0xFF;
205 if (*code_asc & 0x3F) {
206 bits_tmp -= 4;
207 *bits_asc = bits_tmp;
208 add = (1 << bits_tmp);
209 acc = *code_asc >> 4;
210 do {
211 mpq_pkzip->offs_2d34[acc] = (uint8_t)count;
212 acc += add;
213 } while (acc < 0x100);
214 } else {
215 bits_tmp -= 6;
216 *bits_asc = bits_tmp;
217 add = (1 << bits_tmp);
218 acc = *code_asc >> 6;
219 do {
220 mpq_pkzip->offs_2e34[acc] = (uint8_t)count;
221 acc += add;
222 } while (acc < 0x80);
223 }
224 } else {
225 bits_tmp -= 8;
226 *bits_asc = bits_tmp;
227 add = (1 << bits_tmp);
228 acc = *code_asc >> 8;
229 do {
230 mpq_pkzip->offs_2eb4[acc] = (uint8_t)count;
231 acc += add;
232 } while (acc < 0x100);
233 }
234 }
235 }
236 }
237
238 /* Decode one PKWARE literal or copy marker.
239 * The low control bit distinguishes a length/distance match from a literal,
240 * and the selected mode determines whether literals are binary or ASCII-coded. */
241 static uint32_t
242 581 decode_literal(pkzip_cmp_s *mpq_pkzip)
243 {
244 uint32_t bits;
245 uint32_t value;
246
247 /* A set low bit marks a length code; an unset bit marks a literal code. */
248
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 551 times.
581 if (mpq_pkzip->bit_buf & 1) {
249
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (skip_bit(mpq_pkzip, 1)) {
250 return 0x306;
251 }
252
253 /* The next prefix selects a length-code table entry. */
254 30 value = mpq_pkzip->pos2[(mpq_pkzip->bit_buf & 0xFF)];
255
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
30 if (skip_bit(mpq_pkzip, mpq_pkzip->slen_bits[value])) {
257 return 0x306;
258 }
259
260
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 3 times.
30 if ((bits = mpq_pkzip->clen_bits[value]) != 0) {
261
262 /* Decoded literal symbol and bit length for this table entry. */
263 27 uint32_t val2 = mpq_pkzip->bit_buf & ((1 << bits) - 1);
264
265
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 24 times.
27 if (skip_bit(mpq_pkzip, bits)) {
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if ((value + val2) != 0x10E) {
267 return 0x306;
268 }
269 }
270
271 27 value = mpq_pkzip->len_base[value] + val2;
272 }
273
274 30 return value + 0x100;
275 }
276
277
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 551 times.
551 if (skip_bit(mpq_pkzip, 1)) {
278 return 0x306;
279 }
280
281 /* Binary mode carries literal bytes directly after the control bit. */
282
1/2
✓ Branch 0 taken 551 times.
✗ Branch 1 not taken.
551 if (mpq_pkzip->cmp_type == LIBMPQ_PKZIP_CMP_BINARY) {
283 551 value = mpq_pkzip->bit_buf & 0xFF;
284
285
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 551 times.
551 if (skip_bit(mpq_pkzip, 8)) {
286 return 0x306;
287 }
288
289 551 return value;
290 }
291
292 /* ASCII mode resolves short prefixes through progressively wider tables. */
293 if (mpq_pkzip->bit_buf & 0xFF) {
294 value = mpq_pkzip->offs_2c34[mpq_pkzip->bit_buf & 0xFF];
295
296 if (value == 0xFF) {
297 if (mpq_pkzip->bit_buf & 0x3F) {
298 if (skip_bit(mpq_pkzip, 4)) {
299 return 0x306;
300 }
301
302 value = mpq_pkzip->offs_2d34[mpq_pkzip->bit_buf & 0xFF];
303 } else {
304 if (skip_bit(mpq_pkzip, 6)) {
305 return 0x306;
306 }
307
308 value = mpq_pkzip->offs_2e34[mpq_pkzip->bit_buf & 0x7F];
309 }
310 }
311 } else {
312
313 /* Ensure eight bits are available before advancing the bit buffer. */
314 if (skip_bit(mpq_pkzip, 8)) {
315 return 0x306;
316 }
317
318 value = mpq_pkzip->offs_2eb4[mpq_pkzip->bit_buf & 0xFF];
319 }
320
321 return skip_bit(mpq_pkzip, mpq_pkzip->bits_asc[value]) ? 0x306 : value;
322 }
323
324 /* Decode the backward distance for a PKWARE copy operation.
325 * Two-byte matches use a fixed suffix width, while longer matches use the
326 * dictionary-size-dependent distance width configured in the stream header. */
327 static uint32_t
328 27 decode_distance(pkzip_cmp_s *mpq_pkzip, uint32_t length)
329 {
330
331 /* Distance prefix, extra bits and final byte distance. */
332 27 uint32_t pos = mpq_pkzip->pos1[(mpq_pkzip->bit_buf & 0xFF)];
333
334 27 uint32_t skip = mpq_pkzip->dist_bits[pos];
335
336
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if (skip_bit(mpq_pkzip, skip) == 1) {
337 return 0;
338 }
339
340 /* Two-byte matches use a fixed two-bit distance suffix. */
341
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
27 if (length == 2) {
342 1 pos = (pos << 2) | (mpq_pkzip->bit_buf & 0x03);
343
344
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (skip_bit(mpq_pkzip, 2) == 1) {
345 return 0;
346 }
347 } else {
348 26 pos = (pos << mpq_pkzip->dsize_bits) | (mpq_pkzip->bit_buf & mpq_pkzip->dsize_mask);
349
350
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if (skip_bit(mpq_pkzip, mpq_pkzip->dsize_bits) == 1) {
351 return 0;
352 }
353 }
354
355 27 return pos + 1;
356 }
357
358 /* Supply compressed bytes to the PKWARE decoder callback.
359 * The callback clamps each request to the remaining input and advances the
360 * wrapper-owned cursor so the codec never reads beyond the MPQ block. */
361 static uint32_t
362 6 data_read_input(char *buf, uint32_t *size, void *param)
363 {
364
365 /* Source state passed by the public decompression wrapper. */
366 6 pkzip_data_s *info = (pkzip_data_s *)param;
367 6 uint32_t max_avail = (info->in_bytes - info->in_pos);
368 6 uint32_t to_read = *size;
369
370
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (to_read > max_avail) {
371 6 to_read = max_avail;
372 }
373
374 6 memcpy(buf, info->in_buf + info->in_pos, to_read);
375 6 info->in_pos += to_read;
376
377 6 return to_read;
378 }
379
380 /* Receive expanded bytes from the PKWARE decoder callback.
381 * Output is clamped to the caller's capacity and the accepted byte count is
382 * recorded for the extraction wrapper after each decoder flush. */
383 static void
384 6 data_write_output(char *buf, uint32_t *size, void *param)
385 {
386
387 /* Destination state passed by the public decompression wrapper. */
388 6 pkzip_data_s *info = (pkzip_data_s *)param;
389 6 uint32_t max_write = (info->max_out - info->out_pos);
390 6 uint32_t to_write = *size;
391
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (to_write > max_write) {
393 to_write = max_write;
394 }
395
396 6 memcpy(info->out_buf + info->out_pos, buf, to_write);
397 6 info->out_pos += to_write;
398 6 }
399
400 /* Expand one complete PKWARE stream using its configured callbacks.
401 * The sliding 4 KiB window supplies LZ history while completed upper-half
402 * data is flushed incrementally through the output callback. */
403 static uint32_t
404 3 expand(pkzip_cmp_s *mpq_pkzip)
405 {
406 uint32_t copy_bytes;
407 uint32_t one_byte;
408 uint32_t result;
409
410 /* The lower half preserves history while the upper half is flushed to the caller. */
411 3 mpq_pkzip->out_pos = 0x1000;
412
413
2/2
✓ Branch 1 taken 578 times.
✓ Branch 2 taken 3 times.
581 while ((result = one_byte = decode_literal(mpq_pkzip)) < 0x305) {
414
415 /* Values above 0x100 are LZ matches; lower values are literal bytes. */
416
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 551 times.
578 if (one_byte >= 0x100) {
417 uint8_t *source;
418 uint8_t *target;
419
420 /* Decoded match length and backward copy distance. */
421 27 uint32_t copy_length = one_byte - 0xFE;
422 uint32_t move_back;
423
424
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 if ((move_back = decode_distance(mpq_pkzip, copy_length)) == 0) {
425 result = 0x306;
426 break;
427 }
428
429 /* Copy from the sliding history window, allowing overlapping matches. */
430 27 target = &mpq_pkzip->out_buf[mpq_pkzip->out_pos];
431 27 source = target - move_back;
432 27 mpq_pkzip->out_pos += copy_length;
433
434
2/2
✓ Branch 0 taken 12277 times.
✓ Branch 1 taken 27 times.
12304 while (copy_length-- > 0) {
435 12277 *target++ = *source++;
436 }
437 } else {
438 551 mpq_pkzip->out_buf[mpq_pkzip->out_pos++] = (uint8_t)one_byte;
439 }
440
441 /* Flush half the window while retaining the other half as match history. */
442
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 575 times.
578 if (mpq_pkzip->out_pos >= 0x2000) {
443 3 copy_bytes = 0x1000;
444 3 mpq_pkzip->write_buf(
445 3 (char *)&mpq_pkzip->out_buf[0x1000], &copy_bytes, mpq_pkzip->param
446 );
447
448 3 memmove(mpq_pkzip->out_buf, &mpq_pkzip->out_buf[0x1000], mpq_pkzip->out_pos - 0x1000);
449 3 mpq_pkzip->out_pos -= 0x1000;
450 }
451 }
452
453 3 copy_bytes = mpq_pkzip->out_pos - 0x1000;
454 3 mpq_pkzip->write_buf((char *)&mpq_pkzip->out_buf[0x1000], &copy_bytes, mpq_pkzip->param);
455
456 3 return result;
457 }
458
459 /* Encode binary input as a PKWARE DCL stream for MPQ implode storage.
460 * Repeated bytes become distance-one matches, while other bytes are emitted
461 * literally; the output ends with the canonical DCL terminator. */
462 int32_t
463 351 libmpq__pkzip_compress(
464 const uint8_t *in_buf, uint32_t in_size, uint8_t **out_buf, uint32_t *out_size
465 )
466 {
467 351 size_t bit_count = (size_t)in_size * 9 + 16;
468 351 size_t bytes = 2 + (bit_count + 7) / 8;
469 uint8_t *out;
470 uint32_t i;
471
472
4/8
✓ Branch 0 taken 351 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 351 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 351 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 351 times.
351 if (out_buf == NULL || out_size == NULL || (in_size != 0 && in_buf == NULL))
473 return LIBMPQ_ERROR_FORMAT;
474
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 351 times.
351 if (bytes < 2)
475 return LIBMPQ_ERROR_FORMAT;
476
1/2
✓ Branch 0 taken 351 times.
✗ Branch 1 not taken.
351 out = calloc(1, bytes ? bytes : 1);
477
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 351 times.
351 if (out == NULL)
478 return LIBMPQ_ERROR_MALLOC;
479 351 out[0] = LIBMPQ_PKZIP_CMP_BINARY;
480 351 out[1] = 4;
481
482 /* The first two bytes identify binary mode and the four-bit dictionary. */
483 351 bit_count = 0;
484
485 /* Encode runs as matches and leave non-repeating bytes as literals. */
486
2/2
✓ Branch 0 taken 1738165 times.
✓ Branch 1 taken 351 times.
1738516 for (i = 0; i < in_size;) {
487 1738165 uint32_t run_length = 1;
488
489 1738165 pkzip_put_bits(out + 2, &bit_count, 0, 1);
490 1738165 pkzip_put_bits(out + 2, &bit_count, in_buf[i], 8);
491
4/4
✓ Branch 0 taken 1754050 times.
✓ Branch 1 taken 351 times.
✓ Branch 2 taken 1754027 times.
✓ Branch 3 taken 23 times.
1754401 while (i + run_length < in_size && run_length < 0x207 &&
492
2/2
✓ Branch 0 taken 16236 times.
✓ Branch 1 taken 1737791 times.
1754027 in_buf[i + run_length] == in_buf[i]) {
493 16236 run_length++;
494 }
495
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 1738114 times.
1738165 if (run_length >= 3) {
496 51 uint32_t match_length = run_length - 1;
497 uint32_t value;
498 uint32_t entry;
499 uint32_t extra;
500 uint32_t code;
501 51 value = match_length - 2;
502
1/2
✓ Branch 0 taken 418 times.
✗ Branch 1 not taken.
418 for (entry = 0; entry < 16; entry++) {
503 418 extra = pkzip_clen_bits[entry];
504
3/4
✓ Branch 0 taken 418 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 367 times.
418 if (value >= pkzip_len_base[entry] && value < pkzip_len_base[entry] + (1u << extra))
505 51 break;
506 }
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (entry == 16)
508 entry = 15;
509 51 code = ((value - pkzip_len_base[entry]) << (pkzip_slen_bits[entry] + 1)) |
510 51 (pkzip_len_code[entry] * 2u) | 1u;
511 51 pkzip_put_bits(
512 51 out + 2, &bit_count, code, pkzip_slen_bits[entry] + pkzip_clen_bits[entry] + 1
513 );
514
515 /* Distance one: short matches use a two-bit suffix; longer matches
516 * use the dictionary-width suffix selected in the stream header. */
517 51 pkzip_put_bits(out + 2, &bit_count, pkzip_dist_code[0], pkzip_dist_bits[0]);
518
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 26 times.
51 pkzip_put_bits(out + 2, &bit_count, 0, match_length == 2 ? 2 : 4);
519 51 i += run_length;
520 } else {
521 1738114 i++;
522 }
523 }
524
525 /* Length table 15, extra value 255: the canonical 0x305 terminator. */
526 351 pkzip_put_bits(out + 2, &bit_count, 0xFF01u, 16);
527 351 *out_buf = out;
528 351 *out_size = (uint32_t)(2 + (bit_count + 7) / 8);
529 351 return LIBMPQ_SUCCESS;
530 }
531
532 /* PKWARE copyright banner kept for parity with the original implementation. */
533 char pkware_copyright[] = "PKWARE Data Compression Library for Win32\r\n"
534 "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n"
535 "Patent No. 5,051,745\r\n"
536 "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n"
537 "Version 1.11\r\n";
538
539 /* Initialize PKWARE decoder state and explode the compressed data stream.
540 * The caller owns the work buffer and callback parameter; this routine fills
541 * the codec state, validates the stream header, and delegates expansion. */
542 uint32_t
543 3 libmpq__pkzip_decompress(uint8_t *work_buf, void *param)
544 {
545
546 /* Caller-provided work buffer interpreted as PKWARE decoder state. */
547 3 pkzip_cmp_s *mpq_pkzip = (pkzip_cmp_s *)work_buf;
548
549 3 memset(mpq_pkzip, 0, sizeof(pkzip_cmp_s));
550
551 3 mpq_pkzip->read_buf = data_read_input;
552 3 mpq_pkzip->write_buf = data_write_output;
553 3 mpq_pkzip->param = param;
554 3 mpq_pkzip->in_pos = 0;
555
556 3 uint32_t in_size = sizeof(mpq_pkzip->in_buf);
557 3 mpq_pkzip->in_bytes =
558 3 mpq_pkzip->read_buf((char *)mpq_pkzip->in_buf, &in_size, mpq_pkzip->param);
559
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (mpq_pkzip->in_bytes <= 4) {
561 return LIBMPQ_PKZIP_CMP_BAD_DATA;
562 }
563
564 /* The two-byte header selects literal mode and dictionary width. */
565 3 mpq_pkzip->cmp_type = mpq_pkzip->in_buf[0];
566 3 mpq_pkzip->dsize_bits = mpq_pkzip->in_buf[1];
567 3 mpq_pkzip->bit_buf = mpq_pkzip->in_buf[2];
568 3 mpq_pkzip->extra_bits = 0;
569 3 mpq_pkzip->in_pos = 3;
570
571
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (4 > mpq_pkzip->dsize_bits || mpq_pkzip->dsize_bits > 6) {
572 return LIBMPQ_PKZIP_CMP_INV_DICTSIZE;
573 }
574
575 /* Mask the variable-width distance suffix for the selected dictionary size. */
576 3 mpq_pkzip->dsize_mask = 0xFFFF >> (0x10 - mpq_pkzip->dsize_bits);
577
578
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (mpq_pkzip->cmp_type != LIBMPQ_PKZIP_CMP_BINARY) {
579 if (mpq_pkzip->cmp_type != LIBMPQ_PKZIP_CMP_ASCII) {
580 return LIBMPQ_PKZIP_CMP_INV_MODE;
581 }
582
583 memcpy(mpq_pkzip->bits_asc, pkzip_bits_asc, sizeof(mpq_pkzip->bits_asc));
584 generate_tables_ascii(mpq_pkzip);
585 }
586
587 /* Build lookup tables for copy lengths, distances and optional ASCII literals. */
588 3 memcpy(mpq_pkzip->slen_bits, pkzip_slen_bits, sizeof(mpq_pkzip->slen_bits));
589 3 generate_tables_decode(0x10, mpq_pkzip->slen_bits, pkzip_len_code, mpq_pkzip->pos2);
590
591 3 memcpy(mpq_pkzip->clen_bits, pkzip_clen_bits, sizeof(mpq_pkzip->clen_bits));
592 3 memcpy(mpq_pkzip->len_base, pkzip_len_base, sizeof(mpq_pkzip->len_base));
593 3 memcpy(mpq_pkzip->dist_bits, pkzip_dist_bits, sizeof(mpq_pkzip->dist_bits));
594 3 generate_tables_decode(0x40, mpq_pkzip->dist_bits, pkzip_dist_code, mpq_pkzip->pos1);
595
596
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (expand(mpq_pkzip) != 0x306) {
597 3 return LIBMPQ_PKZIP_CMP_NO_ERROR;
598 }
599
600 return LIBMPQ_PKZIP_CMP_ABORT;
601 }
602