/src/libagdb/libfwnt/libfwnt_lzxpress.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * LZXPRESS (de)compression functions |
3 | | * |
4 | | * Copyright (C) 2009-2024, Joachim Metz <joachim.metz@gmail.com> |
5 | | * |
6 | | * Refer to AUTHORS for acknowledgements. |
7 | | * |
8 | | * This program is free software: you can redistribute it and/or modify |
9 | | * it under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation, either version 3 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | #include <common.h> |
23 | | #include <byte_stream.h> |
24 | | #include <memory.h> |
25 | | #include <types.h> |
26 | | |
27 | | #include "libfwnt_bit_stream.h" |
28 | | #include "libfwnt_huffman_tree.h" |
29 | | #include "libfwnt_libcerror.h" |
30 | | #include "libfwnt_libcnotify.h" |
31 | | #include "libfwnt_lzxpress.h" |
32 | | |
33 | | /* Compresses data using LZXPRESS (LZ77 + DIRECT2) compression |
34 | | * Returns 1 on success or -1 on error |
35 | | */ |
36 | | int libfwnt_lzxpress_compress( |
37 | | const uint8_t *uncompressed_data, |
38 | | size_t uncompressed_data_size, |
39 | | uint8_t *compressed_data, |
40 | | size_t *compressed_data_size, |
41 | | libcerror_error_t **error ) |
42 | 0 | { |
43 | 0 | static char *function = "libfwnt_lzxpress_compress"; |
44 | |
|
45 | 0 | if( uncompressed_data == NULL ) |
46 | 0 | { |
47 | 0 | libcerror_error_set( |
48 | 0 | error, |
49 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
50 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
51 | 0 | "%s: invalid uncompressed data.", |
52 | 0 | function ); |
53 | |
|
54 | 0 | return( -1 ); |
55 | 0 | } |
56 | 0 | if( uncompressed_data_size > (size_t) SSIZE_MAX ) |
57 | 0 | { |
58 | 0 | libcerror_error_set( |
59 | 0 | error, |
60 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
61 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM, |
62 | 0 | "%s: invalid uncompressed data size value exceeds maximum.", |
63 | 0 | function ); |
64 | |
|
65 | 0 | return( -1 ); |
66 | 0 | } |
67 | 0 | if( compressed_data == NULL ) |
68 | 0 | { |
69 | 0 | libcerror_error_set( |
70 | 0 | error, |
71 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
72 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
73 | 0 | "%s: invalid compressed data.", |
74 | 0 | function ); |
75 | |
|
76 | 0 | return( -1 ); |
77 | 0 | } |
78 | 0 | if( compressed_data_size == NULL ) |
79 | 0 | { |
80 | 0 | libcerror_error_set( |
81 | 0 | error, |
82 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
83 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
84 | 0 | "%s: invalid compressed data size.", |
85 | 0 | function ); |
86 | |
|
87 | 0 | return( -1 ); |
88 | 0 | } |
89 | | /* TODO implement */ |
90 | 0 | return( -1 ); |
91 | 0 | } |
92 | | |
93 | | /* Decompresses data using LZXPRESS (LZ77 + DIRECT2) compression |
94 | | * Return 1 on success or -1 on error |
95 | | */ |
96 | | int libfwnt_lzxpress_decompress( |
97 | | const uint8_t *compressed_data, |
98 | | size_t compressed_data_size, |
99 | | uint8_t *uncompressed_data, |
100 | | size_t *uncompressed_data_size, |
101 | | libcerror_error_t **error ) |
102 | 0 | { |
103 | 0 | static char *function = "libfwnt_lzxpress_decompress"; |
104 | 0 | size_t compressed_data_offset = 0; |
105 | 0 | size_t compression_index = 0; |
106 | 0 | size_t compression_shared_byte_index = 0; |
107 | 0 | size_t safe_uncompressed_data_size = 0; |
108 | 0 | size_t uncompressed_data_offset = 0; |
109 | 0 | uint32_t compression_indicator = 0; |
110 | 0 | uint32_t compression_indicator_bitmask = 0; |
111 | 0 | uint16_t compression_tuple = 0; |
112 | 0 | uint16_t compression_tuple_size = 0; |
113 | 0 | int16_t compression_tuple_offset = 0; |
114 | |
|
115 | 0 | if( compressed_data == NULL ) |
116 | 0 | { |
117 | 0 | libcerror_error_set( |
118 | 0 | error, |
119 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
120 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
121 | 0 | "%s: invalid compressed data.", |
122 | 0 | function ); |
123 | |
|
124 | 0 | return( -1 ); |
125 | 0 | } |
126 | 0 | if( ( compressed_data_size < 2 ) |
127 | 0 | || ( compressed_data_size > (size_t) SSIZE_MAX ) ) |
128 | 0 | { |
129 | 0 | libcerror_error_set( |
130 | 0 | error, |
131 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
132 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS, |
133 | 0 | "%s: invalid compressed data size value out of bounds.", |
134 | 0 | function ); |
135 | |
|
136 | 0 | return( -1 ); |
137 | 0 | } |
138 | 0 | if( uncompressed_data == NULL ) |
139 | 0 | { |
140 | 0 | libcerror_error_set( |
141 | 0 | error, |
142 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
143 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
144 | 0 | "%s: invalid uncompressed data.", |
145 | 0 | function ); |
146 | |
|
147 | 0 | return( -1 ); |
148 | 0 | } |
149 | 0 | if( uncompressed_data_size == NULL ) |
150 | 0 | { |
151 | 0 | libcerror_error_set( |
152 | 0 | error, |
153 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
154 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
155 | 0 | "%s: invalid uncompressed data size.", |
156 | 0 | function ); |
157 | |
|
158 | 0 | return( -1 ); |
159 | 0 | } |
160 | 0 | if( *uncompressed_data_size > (size_t) SSIZE_MAX ) |
161 | 0 | { |
162 | 0 | libcerror_error_set( |
163 | 0 | error, |
164 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
165 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM, |
166 | 0 | "%s: invalid uncompressed data size value exceeds maximum.", |
167 | 0 | function ); |
168 | |
|
169 | 0 | return( -1 ); |
170 | 0 | } |
171 | 0 | safe_uncompressed_data_size = *uncompressed_data_size; |
172 | |
|
173 | 0 | while( compressed_data_offset < compressed_data_size ) |
174 | 0 | { |
175 | 0 | if( uncompressed_data_offset >= safe_uncompressed_data_size ) |
176 | 0 | { |
177 | 0 | break; |
178 | 0 | } |
179 | 0 | if( ( compressed_data_size < 4 ) |
180 | 0 | || ( compressed_data_offset > ( compressed_data_size - 4 ) ) ) |
181 | 0 | { |
182 | 0 | libcerror_error_set( |
183 | 0 | error, |
184 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
185 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
186 | 0 | "%s: compressed data too small.", |
187 | 0 | function ); |
188 | |
|
189 | 0 | return( -1 ); |
190 | 0 | } |
191 | 0 | byte_stream_copy_to_uint32_little_endian( |
192 | 0 | &( compressed_data[ compressed_data_offset ] ), |
193 | 0 | compression_indicator ); |
194 | |
|
195 | | #if defined( HAVE_DEBUG_OUTPUT ) |
196 | | if( libcnotify_verbose != 0 ) |
197 | | { |
198 | | libcnotify_printf( |
199 | | "%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n", |
200 | | function, |
201 | | compressed_data_offset, |
202 | | compressed_data_offset ); |
203 | | |
204 | | libcnotify_printf( |
205 | | "%s: compression indicator\t\t\t: 0x%08" PRIx32 "\n", |
206 | | function, |
207 | | compression_indicator ); |
208 | | |
209 | | libcnotify_printf( |
210 | | "\n" ); |
211 | | } |
212 | | #endif |
213 | 0 | compressed_data_offset += 4; |
214 | |
|
215 | 0 | for( compression_indicator_bitmask = 0x80000000UL; |
216 | 0 | compression_indicator_bitmask > 0; |
217 | 0 | compression_indicator_bitmask >>= 1 ) |
218 | 0 | { |
219 | 0 | if( uncompressed_data_offset >= safe_uncompressed_data_size ) |
220 | 0 | { |
221 | 0 | break; |
222 | 0 | } |
223 | 0 | if( compressed_data_offset >= compressed_data_size ) |
224 | 0 | { |
225 | 0 | break; |
226 | 0 | } |
227 | | /* If the indicator bit is 0 the data is uncompressed |
228 | | * or 1 if the data is compressed |
229 | | */ |
230 | 0 | if( ( compression_indicator & compression_indicator_bitmask ) != 0 ) |
231 | 0 | { |
232 | 0 | if( compressed_data_offset > ( compressed_data_size - 2 ) ) |
233 | 0 | { |
234 | 0 | libcerror_error_set( |
235 | 0 | error, |
236 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
237 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
238 | 0 | "%s: compressed data too small.", |
239 | 0 | function ); |
240 | |
|
241 | 0 | return( -1 ); |
242 | 0 | } |
243 | 0 | byte_stream_copy_to_uint16_little_endian( |
244 | 0 | &( compressed_data[ compressed_data_offset ] ), |
245 | 0 | compression_tuple ); |
246 | |
|
247 | 0 | compressed_data_offset += 2; |
248 | | |
249 | | /* The compression tuple contains: |
250 | | * 0 - 2 the size |
251 | | * 3 - 15 the offset - 1 |
252 | | */ |
253 | 0 | compression_tuple_size = ( compression_tuple & 0x0007 ); |
254 | 0 | compression_tuple_offset = ( compression_tuple >> 3 ) + 1; |
255 | | |
256 | | /* Check for a first level extended size |
257 | | * stored in the 4-bits of a shared extended compression tuple size byte |
258 | | * the size is added to the previous size |
259 | | */ |
260 | 0 | if( compression_tuple_size == 0x07 ) |
261 | 0 | { |
262 | 0 | if( compression_shared_byte_index == 0 ) |
263 | 0 | { |
264 | 0 | if( compressed_data_offset >= compressed_data_size ) |
265 | 0 | { |
266 | 0 | libcerror_error_set( |
267 | 0 | error, |
268 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
269 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
270 | 0 | "%s: compressed data too small.", |
271 | 0 | function ); |
272 | |
|
273 | 0 | return( -1 ); |
274 | 0 | } |
275 | 0 | compression_tuple_size += compressed_data[ compressed_data_offset ] & 0x0f; |
276 | |
|
277 | 0 | compression_shared_byte_index = compressed_data_offset++; |
278 | 0 | } |
279 | 0 | else |
280 | 0 | { |
281 | 0 | compression_tuple_size += compressed_data[ compression_shared_byte_index ] >> 4; |
282 | |
|
283 | 0 | compression_shared_byte_index = 0; |
284 | 0 | } |
285 | 0 | } |
286 | | /* Check for a second level extended size |
287 | | * stored in the 8-bits of the next byte |
288 | | * the size is added to the previous size |
289 | | */ |
290 | 0 | if( compression_tuple_size == ( 0x07 + 0x0f ) ) |
291 | 0 | { |
292 | 0 | if( compressed_data_offset >= compressed_data_size ) |
293 | 0 | { |
294 | 0 | libcerror_error_set( |
295 | 0 | error, |
296 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
297 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
298 | 0 | "%s: compressed data too small.", |
299 | 0 | function ); |
300 | |
|
301 | 0 | return( -1 ); |
302 | 0 | } |
303 | 0 | compression_tuple_size += compressed_data[ compressed_data_offset++ ]; |
304 | 0 | } |
305 | | /* Check for a third level extended size |
306 | | * stored in the 16-bits of the next two bytes |
307 | | * the previous size is ignored |
308 | | */ |
309 | 0 | if( compression_tuple_size == ( 0x07 + 0x0f + 0xff ) ) |
310 | 0 | { |
311 | 0 | if( compressed_data_offset > ( compressed_data_size - 2 ) ) |
312 | 0 | { |
313 | 0 | libcerror_error_set( |
314 | 0 | error, |
315 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
316 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
317 | 0 | "%s: compressed data too small.", |
318 | 0 | function ); |
319 | |
|
320 | 0 | return( -1 ); |
321 | 0 | } |
322 | 0 | byte_stream_copy_to_uint16_little_endian( |
323 | 0 | &( compressed_data[ compressed_data_offset ] ), |
324 | 0 | compression_tuple_size ); |
325 | | |
326 | 0 | compressed_data_offset += 2; |
327 | 0 | } |
328 | | /* The size value is stored as |
329 | | * size - 3 |
330 | | */ |
331 | 0 | compression_tuple_size += 3; |
332 | |
|
333 | | #if defined( HAVE_DEBUG_OUTPUT ) |
334 | | if( libcnotify_verbose != 0 ) |
335 | | { |
336 | | libcnotify_printf( |
337 | | "%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n", |
338 | | function, |
339 | | compressed_data_offset, |
340 | | compressed_data_offset ); |
341 | | |
342 | | libcnotify_printf( |
343 | | "%s: compression tuple offset\t\t\t: %" PRIi16 "\n", |
344 | | function, |
345 | | compression_tuple_offset ); |
346 | | |
347 | | libcnotify_printf( |
348 | | "%s: compression tuple size\t\t\t: %" PRIu16 "\n", |
349 | | function, |
350 | | compression_tuple_size ); |
351 | | |
352 | | libcnotify_printf( |
353 | | "%s: uncompressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n", |
354 | | function, |
355 | | uncompressed_data_offset, |
356 | | uncompressed_data_offset ); |
357 | | |
358 | | libcnotify_printf( |
359 | | "\n" ); |
360 | | } |
361 | | #endif /* defined( HAVE_DEBUG_OUTPUT ) */ |
362 | |
|
363 | 0 | if( compression_tuple_size > 32771 ) |
364 | 0 | { |
365 | 0 | libcerror_error_set( |
366 | 0 | error, |
367 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
368 | 0 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
369 | 0 | "%s: compression tuple size value out of bounds.", |
370 | 0 | function ); |
371 | |
|
372 | 0 | return( -1 ); |
373 | 0 | } |
374 | 0 | compression_index = uncompressed_data_offset - compression_tuple_offset; |
375 | |
|
376 | 0 | while( compression_tuple_size > 0 ) |
377 | 0 | { |
378 | 0 | if( compression_index >= uncompressed_data_offset ) |
379 | 0 | { |
380 | 0 | libcerror_error_set( |
381 | 0 | error, |
382 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
383 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
384 | 0 | "%s: invalid compressed data at offset: %" PRIzd " - compression index: %" PRIzd " out of range: %" PRIzd ".", |
385 | 0 | function, |
386 | 0 | compressed_data_offset, |
387 | 0 | compression_index, |
388 | 0 | uncompressed_data_offset ); |
389 | |
|
390 | 0 | return( -1 ); |
391 | 0 | } |
392 | 0 | if( uncompressed_data_offset >= safe_uncompressed_data_size ) |
393 | 0 | { |
394 | 0 | libcerror_error_set( |
395 | 0 | error, |
396 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
397 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
398 | 0 | "%s: uncompressed data too small.", |
399 | 0 | function ); |
400 | |
|
401 | 0 | return( -1 ); |
402 | 0 | } |
403 | 0 | uncompressed_data[ uncompressed_data_offset++ ] = uncompressed_data[ compression_index++ ]; |
404 | |
|
405 | 0 | compression_tuple_size--; |
406 | 0 | } |
407 | 0 | } |
408 | 0 | else |
409 | 0 | { |
410 | 0 | if( compressed_data_offset >= compressed_data_size ) |
411 | 0 | { |
412 | 0 | libcerror_error_set( |
413 | 0 | error, |
414 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
415 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
416 | 0 | "%s: compressed data too small.", |
417 | 0 | function ); |
418 | |
|
419 | 0 | return( -1 ); |
420 | 0 | } |
421 | 0 | if( uncompressed_data_offset > safe_uncompressed_data_size ) |
422 | 0 | { |
423 | 0 | libcerror_error_set( |
424 | 0 | error, |
425 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
426 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
427 | 0 | "%s: uncompressed data too small.", |
428 | 0 | function ); |
429 | |
|
430 | 0 | return( -1 ); |
431 | 0 | } |
432 | 0 | uncompressed_data[ uncompressed_data_offset++ ] = compressed_data[ compressed_data_offset++ ]; |
433 | 0 | } |
434 | 0 | } |
435 | 0 | } |
436 | 0 | *uncompressed_data_size = uncompressed_data_offset; |
437 | |
|
438 | 0 | return( 1 ); |
439 | 0 | } |
440 | | |
441 | | /* Decompresses a LZXPRESS Huffman compressed chunk |
442 | | * Return 1 on success or -1 on error |
443 | | */ |
444 | | int libfwnt_lzxpress_huffman_decompress_chunk( |
445 | | libfwnt_bit_stream_t *bit_stream, |
446 | | uint8_t *uncompressed_data, |
447 | | size_t uncompressed_data_size, |
448 | | size_t *uncompressed_data_offset, |
449 | | libcerror_error_t **error ) |
450 | 955 | { |
451 | 955 | uint8_t code_size_array[ 512 ]; |
452 | | |
453 | 955 | libfwnt_huffman_tree_t *huffman_tree = NULL; |
454 | 955 | static char *function = "libfwnt_lzxpress_huffman_decompress_chunk"; |
455 | 955 | size_t next_chunk_uncompressed_data_offset = 0; |
456 | 955 | size_t safe_uncompressed_data_offset = 0; |
457 | 955 | uint32_t compression_offset = 0; |
458 | 955 | uint32_t compression_size = 0; |
459 | 955 | uint32_t symbol = 0; |
460 | 955 | uint8_t byte_value = 0; |
461 | | |
462 | 955 | if( bit_stream == NULL ) |
463 | 0 | { |
464 | 0 | libcerror_error_set( |
465 | 0 | error, |
466 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
467 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
468 | 0 | "%s: invalid bit stream.", |
469 | 0 | function ); |
470 | |
|
471 | 0 | return( -1 ); |
472 | 0 | } |
473 | 955 | if( ( bit_stream->byte_stream_size - bit_stream->byte_stream_offset ) < 260 ) |
474 | 136 | { |
475 | 136 | libcerror_error_set( |
476 | 136 | error, |
477 | 136 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
478 | 136 | LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL, |
479 | 136 | "%s: invalid bit stream - byte stream value too small.", |
480 | 136 | function ); |
481 | | |
482 | 136 | return( -1 ); |
483 | 136 | } |
484 | 819 | if( uncompressed_data == NULL ) |
485 | 0 | { |
486 | 0 | libcerror_error_set( |
487 | 0 | error, |
488 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
489 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
490 | 0 | "%s: invalid uncompressed data.", |
491 | 0 | function ); |
492 | |
|
493 | 0 | return( -1 ); |
494 | 0 | } |
495 | 819 | if( uncompressed_data_size > (size_t) SSIZE_MAX ) |
496 | 0 | { |
497 | 0 | libcerror_error_set( |
498 | 0 | error, |
499 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
500 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM, |
501 | 0 | "%s: invalid uncompressed data size value exceeds maximum.", |
502 | 0 | function ); |
503 | |
|
504 | 0 | return( -1 ); |
505 | 0 | } |
506 | 819 | if( uncompressed_data_offset == NULL ) |
507 | 0 | { |
508 | 0 | libcerror_error_set( |
509 | 0 | error, |
510 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
511 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
512 | 0 | "%s: invalid uncompressed data offset.", |
513 | 0 | function ); |
514 | |
|
515 | 0 | return( -1 ); |
516 | 0 | } |
517 | 819 | safe_uncompressed_data_offset = *uncompressed_data_offset; |
518 | | |
519 | 819 | if( safe_uncompressed_data_offset >= uncompressed_data_size ) |
520 | 0 | { |
521 | 0 | libcerror_error_set( |
522 | 0 | error, |
523 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
524 | 0 | LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS, |
525 | 0 | "%s: uncompressed data offset value out of bounds.", |
526 | 0 | function ); |
527 | |
|
528 | 0 | return( -1 ); |
529 | 0 | } |
530 | | /* The table contains 4-bits code size per symbol |
531 | | */ |
532 | 210k | while( symbol < 512 ) |
533 | 209k | { |
534 | 209k | byte_value = bit_stream->byte_stream[ bit_stream->byte_stream_offset ]; |
535 | | |
536 | 209k | code_size_array[ symbol++ ] = byte_value & 0x0f; |
537 | | |
538 | 209k | byte_value >>= 4; |
539 | | |
540 | 209k | code_size_array[ symbol++ ] = byte_value & 0x0f; |
541 | | |
542 | 209k | bit_stream->byte_stream_offset += 1; |
543 | 209k | } |
544 | 819 | if( libfwnt_huffman_tree_initialize( |
545 | 819 | &huffman_tree, |
546 | 819 | 512, |
547 | 819 | 15, |
548 | 819 | error ) != 1 ) |
549 | 0 | { |
550 | 0 | libcerror_error_set( |
551 | 0 | error, |
552 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
553 | 0 | LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED, |
554 | 0 | "%s: unable to create Huffman tree.", |
555 | 0 | function ); |
556 | |
|
557 | 0 | goto on_error; |
558 | 0 | } |
559 | 819 | if( libfwnt_huffman_tree_build( |
560 | 819 | huffman_tree, |
561 | 819 | code_size_array, |
562 | 819 | 512, |
563 | 819 | error ) != 1 ) |
564 | 25 | { |
565 | 25 | libcerror_error_set( |
566 | 25 | error, |
567 | 25 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
568 | 25 | LIBCERROR_RUNTIME_ERROR_SET_FAILED, |
569 | 25 | "%s: unable to build Huffman tree.", |
570 | 25 | function ); |
571 | | |
572 | 25 | goto on_error; |
573 | 25 | } |
574 | 794 | if( libfwnt_bit_stream_read( |
575 | 794 | bit_stream, |
576 | 794 | 32, |
577 | 794 | error ) == -1 ) |
578 | 0 | { |
579 | 0 | libcerror_error_set( |
580 | 0 | error, |
581 | 0 | LIBCERROR_ERROR_DOMAIN_IO, |
582 | 0 | LIBCERROR_IO_ERROR_READ_FAILED, |
583 | 0 | "%s: unable to read 32-bit from bit stream.", |
584 | 0 | function ); |
585 | |
|
586 | 0 | goto on_error; |
587 | 0 | } |
588 | 794 | next_chunk_uncompressed_data_offset = safe_uncompressed_data_offset + 65536; |
589 | | |
590 | 794 | if( next_chunk_uncompressed_data_offset > uncompressed_data_size ) |
591 | 67 | { |
592 | 67 | next_chunk_uncompressed_data_offset = uncompressed_data_size; |
593 | 67 | } |
594 | 11.1M | while( ( bit_stream->byte_stream_offset < bit_stream->byte_stream_size ) |
595 | 11.1M | || ( bit_stream->bit_buffer_size > 0 ) ) |
596 | 11.1M | { |
597 | 11.1M | if( safe_uncompressed_data_offset >= next_chunk_uncompressed_data_offset ) |
598 | 544 | { |
599 | | #if defined( HAVE_DEBUG_OUTPUT ) |
600 | | if( libcnotify_verbose != 0 ) |
601 | | { |
602 | | libcnotify_printf( |
603 | | "%s: end of chunk at compressed data offset: %" PRIzd " (0x%08" PRIzx "), uncompressed data offset: %" PRIzd " (0x%08" PRIzx ")\n", |
604 | | function, |
605 | | bit_stream->byte_stream_offset, |
606 | | bit_stream->byte_stream_offset, |
607 | | safe_uncompressed_data_offset, |
608 | | safe_uncompressed_data_offset ); |
609 | | |
610 | | libcnotify_printf( |
611 | | "\n" ); |
612 | | } |
613 | | #endif |
614 | 544 | bit_stream->bit_buffer_size = 0; |
615 | | |
616 | 544 | break; |
617 | 544 | } |
618 | | #if defined( HAVE_DEBUG_OUTPUT ) |
619 | | if( libcnotify_verbose != 0 ) |
620 | | { |
621 | | libcnotify_printf( |
622 | | "%s: compressed data offset\t: %" PRIzd " (0x%08" PRIzx ")\n", |
623 | | function, |
624 | | bit_stream->byte_stream_offset, |
625 | | bit_stream->byte_stream_offset ); |
626 | | } |
627 | | #endif |
628 | 11.1M | if( libfwnt_huffman_tree_get_symbol_from_bit_stream( |
629 | 11.1M | huffman_tree, |
630 | 11.1M | bit_stream, |
631 | 11.1M | &symbol, |
632 | 11.1M | error ) != 1 ) |
633 | 28 | { |
634 | 28 | libcerror_error_set( |
635 | 28 | error, |
636 | 28 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
637 | 28 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
638 | 28 | "%s: unable to read symbol.", |
639 | 28 | function ); |
640 | | |
641 | 28 | goto on_error; |
642 | 28 | } |
643 | | #if defined( HAVE_DEBUG_OUTPUT ) |
644 | | if( libcnotify_verbose != 0 ) |
645 | | { |
646 | | libcnotify_printf( |
647 | | "%s: huffman symbol\t\t: 0x%04" PRIx16 "\n", |
648 | | function, |
649 | | symbol ); |
650 | | } |
651 | | #endif |
652 | 11.1M | if( symbol < 256 ) |
653 | 5.70M | { |
654 | 5.70M | uncompressed_data[ safe_uncompressed_data_offset++ ] = (uint8_t) symbol; |
655 | 5.70M | } |
656 | | /* Make sure the bit buffer contains at least 16-bit to ensure end-of-block marker is read correctly |
657 | | */ |
658 | 11.1M | if( bit_stream->bit_buffer_size < 16 ) |
659 | 4.28M | { |
660 | 4.28M | if( libfwnt_bit_stream_read( |
661 | 4.28M | bit_stream, |
662 | 4.28M | 16, |
663 | 4.28M | error ) == -1 ) |
664 | 0 | { |
665 | 0 | libcerror_error_set( |
666 | 0 | error, |
667 | 0 | LIBCERROR_ERROR_DOMAIN_IO, |
668 | 0 | LIBCERROR_IO_ERROR_READ_FAILED, |
669 | 0 | "%s: unable to read 16-bit from bit stream.", |
670 | 0 | function ); |
671 | |
|
672 | 0 | goto on_error; |
673 | 0 | } |
674 | 4.28M | } |
675 | | #if defined( HAVE_DEBUG_OUTPUT ) |
676 | | if( libcnotify_verbose != 0 ) |
677 | | { |
678 | | libcnotify_printf( |
679 | | "%s: number of bits\t\t: %" PRId8 "\n", |
680 | | function, |
681 | | bit_stream->bit_buffer_size ); |
682 | | } |
683 | | #endif |
684 | | /* Check if we have an end-of-block marker (symbol 256) and the number of remaining bits are 0 |
685 | | */ |
686 | | /* TODO add ( symbol == 256 ) */ |
687 | 11.1M | if( ( bit_stream->bit_buffer == 0 ) |
688 | 11.1M | && ( safe_uncompressed_data_offset >= uncompressed_data_size ) ) |
689 | 99 | { |
690 | 99 | break; |
691 | 99 | } |
692 | 11.1M | if( symbol >= 256 ) |
693 | 5.42M | { |
694 | 5.42M | symbol -= 256; |
695 | 5.42M | compression_offset = 0; |
696 | 5.42M | compression_size = symbol & 0x000f; |
697 | 5.42M | symbol >>= 4; |
698 | | |
699 | 5.42M | if( symbol != 0 ) |
700 | 5.10M | { |
701 | 5.10M | if( libfwnt_bit_stream_get_value( |
702 | 5.10M | bit_stream, |
703 | 5.10M | (uint8_t) symbol, |
704 | 5.10M | &compression_offset, |
705 | 5.10M | error ) != 1 ) |
706 | 0 | { |
707 | 0 | libcerror_error_set( |
708 | 0 | error, |
709 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
710 | 0 | LIBCERROR_RUNTIME_ERROR_GET_FAILED, |
711 | 0 | "%s: unable to retrieve compression offset from bit stream.", |
712 | 0 | function ); |
713 | |
|
714 | 0 | goto on_error; |
715 | 0 | } |
716 | | #if defined( HAVE_DEBUG_OUTPUT ) |
717 | | if( libcnotify_verbose != 0 ) |
718 | | { |
719 | | libcnotify_printf( |
720 | | "%s: compression offset\t\t: %" PRIu32 "\n", |
721 | | function, |
722 | | compression_offset ); |
723 | | } |
724 | | #endif |
725 | 5.10M | } |
726 | 5.42M | compression_offset = (uint32_t) ( ( 1 << symbol ) | compression_offset ); |
727 | | |
728 | | #if defined( HAVE_DEBUG_OUTPUT ) |
729 | | if( libcnotify_verbose != 0 ) |
730 | | { |
731 | | libcnotify_printf( |
732 | | "%s: compression size\t\t: %" PRIu32 "\n", |
733 | | function, |
734 | | compression_size ); |
735 | | } |
736 | | #endif |
737 | | /* Ignore any data beyond the uncompressed block size |
738 | | */ |
739 | 5.42M | if( compression_size == 15 ) |
740 | 209k | { |
741 | 209k | if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 1 ) ) |
742 | 21 | { |
743 | 21 | libcerror_error_set( |
744 | 21 | error, |
745 | 21 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
746 | 21 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
747 | 21 | "%s: compressed data size value too small.", |
748 | 21 | function ); |
749 | | |
750 | 21 | goto on_error; |
751 | 21 | } |
752 | 209k | compression_size = bit_stream->byte_stream[ bit_stream->byte_stream_offset ] + 15; |
753 | | |
754 | | #if defined( HAVE_DEBUG_OUTPUT ) |
755 | | if( libcnotify_verbose != 0 ) |
756 | | { |
757 | | libcnotify_printf( |
758 | | "%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n", |
759 | | function, |
760 | | bit_stream->byte_stream_offset, |
761 | | bit_stream->byte_stream_offset ); |
762 | | |
763 | | libcnotify_printf( |
764 | | "%s: extended compression size\t: %" PRIu32 "\n", |
765 | | function, |
766 | | compression_size ); |
767 | | } |
768 | | #endif |
769 | 209k | bit_stream->byte_stream_offset += 1; |
770 | | |
771 | 209k | if( compression_size == 270 ) |
772 | 833 | { |
773 | 833 | if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 2 ) ) |
774 | 4 | { |
775 | 4 | libcerror_error_set( |
776 | 4 | error, |
777 | 4 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
778 | 4 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
779 | 4 | "%s: compressed data size value too small.", |
780 | 4 | function ); |
781 | | |
782 | 4 | goto on_error; |
783 | 4 | } |
784 | 829 | byte_stream_copy_to_uint16_little_endian( |
785 | 829 | &( bit_stream->byte_stream[ bit_stream->byte_stream_offset ] ), |
786 | 829 | compression_size ); |
787 | | |
788 | | #if defined( HAVE_DEBUG_OUTPUT ) |
789 | | if( libcnotify_verbose != 0 ) |
790 | | { |
791 | | libcnotify_printf( |
792 | | "%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n", |
793 | | function, |
794 | | bit_stream->byte_stream_offset, |
795 | | bit_stream->byte_stream_offset ); |
796 | | |
797 | | libcnotify_printf( |
798 | | "%s: extended compression size\t: %" PRIu32 "\n", |
799 | | function, |
800 | | compression_size ); |
801 | | } |
802 | | #endif |
803 | 829 | bit_stream->byte_stream_offset += 2; |
804 | | |
805 | 829 | if( compression_size == 0 ) |
806 | 247 | { |
807 | 247 | if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 4 ) ) |
808 | 4 | { |
809 | 4 | libcerror_error_set( |
810 | 4 | error, |
811 | 4 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
812 | 4 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
813 | 4 | "%s: compressed data size value too small.", |
814 | 4 | function ); |
815 | | |
816 | 4 | goto on_error; |
817 | 4 | } |
818 | 243 | byte_stream_copy_to_uint32_little_endian( |
819 | 243 | &( bit_stream->byte_stream[ bit_stream->byte_stream_offset ] ), |
820 | 243 | compression_size ); |
821 | | |
822 | | #if defined( HAVE_DEBUG_OUTPUT ) |
823 | | if( libcnotify_verbose != 0 ) |
824 | | { |
825 | | libcnotify_printf( |
826 | | "%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n", |
827 | | function, |
828 | | bit_stream->byte_stream_offset, |
829 | | bit_stream->byte_stream_offset ); |
830 | | |
831 | | libcnotify_printf( |
832 | | "%s: extended compression size\t: %" PRIu32 "\n", |
833 | | function, |
834 | | compression_size ); |
835 | | } |
836 | | #endif |
837 | 243 | bit_stream->byte_stream_offset += 4; |
838 | | |
839 | 243 | } |
840 | 829 | } |
841 | 209k | } |
842 | 5.42M | compression_size += 3; |
843 | | |
844 | | #if defined( HAVE_DEBUG_OUTPUT ) |
845 | | if( libcnotify_verbose != 0 ) |
846 | | { |
847 | | libcnotify_printf( |
848 | | "%s: compression offset\t\t: %" PRIu32 "\n", |
849 | | function, |
850 | | compression_offset ); |
851 | | |
852 | | libcnotify_printf( |
853 | | "%s: compression size\t\t: %" PRIu32 "\n", |
854 | | function, |
855 | | compression_size ); |
856 | | |
857 | | libcnotify_printf( |
858 | | "%s: uncompressed data offset\t: %" PRIzd " (0x%08" PRIzx ")\n", |
859 | | function, |
860 | | safe_uncompressed_data_offset, |
861 | | safe_uncompressed_data_offset ); |
862 | | } |
863 | | #endif |
864 | 5.42M | if( compression_offset > safe_uncompressed_data_offset ) |
865 | 34 | { |
866 | 34 | libcerror_error_set( |
867 | 34 | error, |
868 | 34 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
869 | 34 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
870 | 34 | "%s: compression offset value out of bounds.", |
871 | 34 | function ); |
872 | | |
873 | 34 | goto on_error; |
874 | 34 | } |
875 | 5.42M | if( compression_size > ( uncompressed_data_size - safe_uncompressed_data_offset ) ) |
876 | 60 | { |
877 | 60 | libcerror_error_set( |
878 | 60 | error, |
879 | 60 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
880 | 60 | LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, |
881 | 60 | "%s: compression size value out of bounds.", |
882 | 60 | function ); |
883 | | |
884 | 60 | goto on_error; |
885 | 60 | } |
886 | 5.42M | compression_offset = safe_uncompressed_data_offset - compression_offset; |
887 | | |
888 | 44.1M | while( compression_size > 0 ) |
889 | 38.7M | { |
890 | 38.7M | uncompressed_data[ safe_uncompressed_data_offset++ ] = uncompressed_data[ compression_offset++ ]; |
891 | | |
892 | 38.7M | compression_size--; |
893 | 38.7M | } |
894 | | /* Make sure the bit buffer contains at least 16-bit to ensure successive chunks in a stream are read correctly |
895 | | */ |
896 | 5.42M | if( bit_stream->bit_buffer_size < 16 ) |
897 | 1.64M | { |
898 | 1.64M | if( libfwnt_bit_stream_read( |
899 | 1.64M | bit_stream, |
900 | 1.64M | 16, |
901 | 1.64M | error ) == -1 ) |
902 | 0 | { |
903 | 0 | libcerror_error_set( |
904 | 0 | error, |
905 | 0 | LIBCERROR_ERROR_DOMAIN_IO, |
906 | 0 | LIBCERROR_IO_ERROR_READ_FAILED, |
907 | 0 | "%s: unable to read 16-bit from bit stream.", |
908 | 0 | function ); |
909 | |
|
910 | 0 | goto on_error; |
911 | 0 | } |
912 | 1.64M | } |
913 | 5.42M | } |
914 | | #if defined( HAVE_DEBUG_OUTPUT ) |
915 | | if( libcnotify_verbose != 0 ) |
916 | | { |
917 | | libcnotify_printf( |
918 | | "\n" ); |
919 | | } |
920 | | #endif |
921 | 11.1M | } |
922 | 643 | if( libfwnt_huffman_tree_free( |
923 | 643 | &huffman_tree, |
924 | 643 | error ) != 1 ) |
925 | 0 | { |
926 | 0 | libcerror_error_set( |
927 | 0 | error, |
928 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
929 | 0 | LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED, |
930 | 0 | "%s: unable to free Huffman tree.", |
931 | 0 | function ); |
932 | |
|
933 | 0 | goto on_error; |
934 | 0 | } |
935 | 643 | *uncompressed_data_offset = safe_uncompressed_data_offset; |
936 | | |
937 | 643 | return( 1 ); |
938 | | |
939 | 176 | on_error: |
940 | 176 | if( huffman_tree != NULL ) |
941 | 176 | { |
942 | 176 | libfwnt_huffman_tree_free( |
943 | 176 | &huffman_tree, |
944 | 176 | NULL ); |
945 | 176 | } |
946 | 176 | return( -1 ); |
947 | 643 | } |
948 | | |
949 | | /* Decompresses data using LZXPRESS Huffman compression |
950 | | * Return 1 on success or -1 on error |
951 | | */ |
952 | | int libfwnt_lzxpress_huffman_decompress( |
953 | | const uint8_t *compressed_data, |
954 | | size_t compressed_data_size, |
955 | | uint8_t *uncompressed_data, |
956 | | size_t *uncompressed_data_size, |
957 | | libcerror_error_t **error ) |
958 | 955 | { |
959 | 955 | libfwnt_bit_stream_t *bit_stream = NULL; |
960 | 955 | static char *function = "libfwnt_lzxpress_huffman_decompress"; |
961 | 955 | size_t safe_uncompressed_data_size = 0; |
962 | 955 | size_t uncompressed_data_offset = 0; |
963 | | |
964 | 955 | if( uncompressed_data_size == NULL ) |
965 | 0 | { |
966 | 0 | libcerror_error_set( |
967 | 0 | error, |
968 | 0 | LIBCERROR_ERROR_DOMAIN_ARGUMENTS, |
969 | 0 | LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, |
970 | 0 | "%s: invalid uncompressed data size.", |
971 | 0 | function ); |
972 | |
|
973 | 0 | return( -1 ); |
974 | 0 | } |
975 | 955 | if( libfwnt_bit_stream_initialize( |
976 | 955 | &bit_stream, |
977 | 955 | compressed_data, |
978 | 955 | compressed_data_size, |
979 | 955 | error ) != 1 ) |
980 | 0 | { |
981 | 0 | libcerror_error_set( |
982 | 0 | error, |
983 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
984 | 0 | LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED, |
985 | 0 | "%s: unable to create bit stream.", |
986 | 0 | function ); |
987 | |
|
988 | 0 | goto on_error; |
989 | 0 | } |
990 | 955 | safe_uncompressed_data_size = *uncompressed_data_size; |
991 | | |
992 | 1.59k | while( bit_stream->byte_stream_offset < bit_stream->byte_stream_size ) |
993 | 1.32k | { |
994 | 1.32k | if( uncompressed_data_offset >= safe_uncompressed_data_size ) |
995 | 369 | { |
996 | 369 | break; |
997 | 369 | } |
998 | 955 | if( libfwnt_lzxpress_huffman_decompress_chunk( |
999 | 955 | bit_stream, |
1000 | 955 | uncompressed_data, |
1001 | 955 | safe_uncompressed_data_size, |
1002 | 955 | &uncompressed_data_offset, |
1003 | 955 | error ) != 1 ) |
1004 | 312 | { |
1005 | 312 | libcerror_error_set( |
1006 | 312 | error, |
1007 | 312 | LIBCERROR_ERROR_DOMAIN_COMPRESSION, |
1008 | 312 | LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED, |
1009 | 312 | "%s: unable to decompress chunk.", |
1010 | 312 | function ); |
1011 | | |
1012 | 312 | goto on_error; |
1013 | 312 | } |
1014 | 955 | } |
1015 | 643 | if( libfwnt_bit_stream_free( |
1016 | 643 | &bit_stream, |
1017 | 643 | error ) != 1 ) |
1018 | 0 | { |
1019 | 0 | libcerror_error_set( |
1020 | 0 | error, |
1021 | 0 | LIBCERROR_ERROR_DOMAIN_RUNTIME, |
1022 | 0 | LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED, |
1023 | 0 | "%s: unable to free bit stream.", |
1024 | 0 | function ); |
1025 | |
|
1026 | 0 | goto on_error; |
1027 | 0 | } |
1028 | 643 | *uncompressed_data_size = uncompressed_data_offset; |
1029 | | |
1030 | 643 | return( 1 ); |
1031 | | |
1032 | 312 | on_error: |
1033 | 312 | if( bit_stream != NULL ) |
1034 | 312 | { |
1035 | 312 | libfwnt_bit_stream_free( |
1036 | 312 | &bit_stream, |
1037 | 312 | NULL ); |
1038 | 312 | } |
1039 | 312 | return( -1 ); |
1040 | 643 | } |
1041 | | |