1 /*
2  * LZFu (un)compression functions
3  *
4  * Copyright (C) 2009-2018, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This software 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 software 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 software.  If not, see <http://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 "libfmapi_checksum.h"
28 #include "libfmapi_libcerror.h"
29 #include "libfmapi_libcnotify.h"
30 #include "libfmapi_lzfu.h"
31 
32 #define LIBFMAPI_LZFU_SIGNATURE_COMPRESSED        0x75465a4c
33 #define LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED      0x414c454d
34 
35 const char *libfmapi_lzfu_rtf_dictionary = \
36 	"{\\rtf1\\ansi\\mac\\deff0\\deftab720"
37 	"{\\fonttbl;}"
38 	"{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier"
39 	"{\\colortbl\\red0\\green0\\blue0\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
40 
41 /* Determines the uncompressed data size from the LZFu header in the compressed data
42  * Returns 1 on success or -1 on error
43  */
libfmapi_lzfu_get_uncompressed_data_size(uint8_t * compressed_data,size_t compressed_data_size,size_t * uncompressed_data_size,libcerror_error_t ** error)44 int libfmapi_lzfu_get_uncompressed_data_size(
45      uint8_t *compressed_data,
46      size_t compressed_data_size,
47      size_t *uncompressed_data_size,
48      libcerror_error_t **error )
49 {
50 	libfmapi_lzfu_header_t lzfu_header;
51 
52 	uint8_t *lzfu_data                = 0;
53 	static char *function             = "libfmapi_lzfu_get_uncompressed_data_size";
54 
55 #if defined( HAVE_DEBUG_OUTPUT )
56 	uint8_t lz_buffer[ 4096 ];
57 
58 	uint8_t *lzfu_reference_data      = 0;
59 	size_t compressed_data_iterator   = 0;
60 	size_t uncompressed_data_iterator = 0;
61 	uint16_t lz_buffer_iterator       = 0;
62 	uint16_t reference_offset         = 0;
63 	uint16_t reference_size           = 0;
64 	uint16_t reference_iterator       = 0;
65 	uint8_t flag_byte_bit_mask        = 0;
66 	uint8_t flag_byte                 = 0;
67 #endif
68 
69 	if( compressed_data == NULL )
70 	{
71 		libcerror_error_set(
72 		 error,
73 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
74 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
75 		 "%s: invalid compressed data.",
76 		 function );
77 
78 		return( -1 );
79 	}
80 	if( compressed_data_size > (size_t) SSIZE_MAX )
81 	{
82 		libcerror_error_set(
83 		 error,
84 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
85 		 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
86 		 "%s: invalid compressed data size value exceeds maximum.",
87 		 function );
88 
89 		return( -1 );
90 	}
91 	if( uncompressed_data_size == NULL )
92 	{
93 		libcerror_error_set(
94 		 error,
95 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
96 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
97 		 "%s: invalid uncompressed data size.",
98 		 function );
99 
100 		return( -1 );
101 	}
102 	lzfu_data = compressed_data;
103 
104 	byte_stream_copy_to_uint32_little_endian(
105 	 lzfu_data,
106 	 lzfu_header.compressed_data_size );
107 
108 	lzfu_data += 4;
109 
110 	byte_stream_copy_to_uint32_little_endian(
111 	 lzfu_data,
112 	 lzfu_header.uncompressed_data_size );
113 
114 	lzfu_data += 4;
115 
116 	byte_stream_copy_to_uint32_little_endian(
117 	 lzfu_data,
118 	 lzfu_header.signature );
119 
120 	lzfu_data += 8;
121 
122 	if( ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_COMPRESSED )
123 	 && ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED ) )
124 	{
125 		libcerror_error_set(
126 		 error,
127 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
128 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
129 		 "%s: unsupported compression signature: 0x%08" PRIx32 ".",
130 		 function,
131 		 lzfu_header.signature );
132 
133 		return( -1 );
134 	}
135 	compressed_data_size -= sizeof( libfmapi_lzfu_header_t );
136 
137 	/* The compressed data size includes 12 bytes of the header
138 	 */
139 	lzfu_header.compressed_data_size -= 12;
140 
141 	if( (size_t) lzfu_header.compressed_data_size != compressed_data_size )
142 	{
143 		libcerror_error_set(
144 		 error,
145 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
146 		 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
147 		 "%s: mismatch in compressed data size (%" PRIu32 " != %" PRIzd ").",
148 		 function,
149 		 lzfu_header.compressed_data_size,
150 		 compressed_data_size );
151 
152 		return( -1 );
153 	}
154 #if defined( HAVE_DEBUG_OUTPUT )
155 	if( libcnotify_verbose != 0 )
156 	{
157 		if( memory_copy(
158 		     lz_buffer,
159 		     libfmapi_lzfu_rtf_dictionary,
160 		     207 ) == NULL )
161 		{
162 			libcerror_error_set(
163 			 error,
164 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
165 			 LIBCERROR_MEMORY_ERROR_COPY_FAILED,
166 			 "%s: unable to initialize lz buffer.",
167 			 function );
168 
169 			return( -1 );
170 		}
171 		lz_buffer_iterator = 207;
172 
173 		if( memory_set(
174 		     &( lz_buffer[ lz_buffer_iterator ] ),
175 		     0,
176 		     4096 - lz_buffer_iterator ) == NULL )
177 		{
178 			libcerror_error_set(
179 			 error,
180 			 LIBCERROR_ERROR_DOMAIN_MEMORY,
181 			 LIBCERROR_MEMORY_ERROR_SET_FAILED,
182 			 "%s: unable to clear lz buffer.",
183 			 function );
184 
185 			return( -1 );
186 		}
187 		while( compressed_data_iterator < (size_t) lzfu_header.compressed_data_size )
188 		{
189 			flag_byte = lzfu_data[ compressed_data_iterator++ ];
190 
191 			/* Check every bit in the chunk flag byte from LSB to MSB
192 			 */
193 			for( flag_byte_bit_mask = 0x01;
194 			     flag_byte_bit_mask != 0x00;
195 			     flag_byte_bit_mask <<= 1 )
196 			{
197 				if( compressed_data_iterator == (size_t) lzfu_header.compressed_data_size )
198 				{
199 					break;
200 				}
201 				/* Check if the byte value is a literal or a reference
202 				 */
203 				if( ( flag_byte & flag_byte_bit_mask ) == 0 )
204 				{
205 					lz_buffer[ lz_buffer_iterator++ ] = lzfu_data[ compressed_data_iterator ];
206 
207 					uncompressed_data_iterator++;
208 					compressed_data_iterator++;
209 
210 					/* Make sure the lz buffer iterator wraps around
211 					 */
212 					lz_buffer_iterator %= 4096;
213 
214 					lz_buffer[ lz_buffer_iterator ] = 0;
215 				}
216 				else
217 				{
218 					lzfu_reference_data = &( lzfu_data[ compressed_data_iterator ] );
219 
220 					compressed_data_iterator += 2;
221 
222 					byte_stream_copy_to_uint16_big_endian(
223 					 lzfu_reference_data,
224 					 reference_offset );
225 
226 					reference_size     = ( reference_offset & 0x000f ) + 2;
227 					reference_offset >>= 4;
228 
229 					for( reference_iterator = 0; reference_iterator < reference_size; reference_iterator++ )
230 					{
231 						lz_buffer[ lz_buffer_iterator++ ] = lz_buffer[ reference_offset ];
232 
233 						uncompressed_data_iterator++;
234 						reference_offset++;
235 
236 						/* Make sure the lz buffer iterator and reference offset wrap around
237 						 */
238 						lz_buffer_iterator %= 4096;
239 						reference_offset   %= 4096;
240 
241 						lz_buffer[ lz_buffer_iterator ] = 0;
242 					}
243 				}
244 			}
245 		}
246 		if( (size_t) ( lzfu_header.uncompressed_data_size + 2 ) != uncompressed_data_iterator )
247 		{
248 			libcnotify_printf(
249 			 "%s: mismatch in uncompressed data size (in header: %" PRIu32 " != required: %" PRIzd ").\n",
250 			 function,
251 			 lzfu_header.uncompressed_data_size + 2,
252 			 uncompressed_data_iterator );
253 		}
254 	}
255 #endif
256 	/* Compensate for the 2 trailing zero bytes
257 	 */
258 	*uncompressed_data_size = lzfu_header.uncompressed_data_size + 2;
259 
260 	return( 1 );
261 }
262 
263 /* Compresses data using LZFu compression
264  * Returns 1 on success or -1 on error
265  */
libfmapi_lzfu_compress(uint8_t * compressed_data,size_t * compressed_data_size,uint8_t * uncompressed_data,size_t uncompressed_data_size,libcerror_error_t ** error)266 int libfmapi_lzfu_compress(
267      uint8_t *compressed_data,
268      size_t *compressed_data_size,
269      uint8_t *uncompressed_data,
270      size_t uncompressed_data_size,
271      libcerror_error_t **error )
272 {
273 	static char *function = "libfmapi_lzfu_compress";
274 
275 	if( compressed_data == NULL )
276 	{
277 		libcerror_error_set(
278 		 error,
279 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
280 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
281 		 "%s: invalid compressed data.",
282 		 function );
283 
284 		return( -1 );
285 	}
286 	if( compressed_data_size == NULL )
287 	{
288 		libcerror_error_set(
289 		 error,
290 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
291 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
292 		 "%s: invalid compressed data size.",
293 		 function );
294 
295 		return( -1 );
296 	}
297 	if( uncompressed_data == NULL )
298 	{
299 		libcerror_error_set(
300 		 error,
301 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
302 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
303 		 "%s: invalid uncompressed data.",
304 		 function );
305 
306 		return( -1 );
307 	}
308 	if( uncompressed_data_size > (size_t) SSIZE_MAX )
309 	{
310 		libcerror_error_set(
311 		 error,
312 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
313 		 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
314 		 "%s: invalid uncompressed data size value exceeds maximum.",
315 		 function );
316 
317 		return( -1 );
318 	}
319 	/* TODO implement */
320 	libcerror_error_set(
321 	 error,
322 	 LIBCERROR_ERROR_DOMAIN_RUNTIME,
323 	 LIBCERROR_RUNTIME_ERROR_GENERIC,
324 	 "%s: NOT IMPLEMENTED YET",
325 	 function );
326 
327 	return( -1 );
328 }
329 
330 /* Decompresses data using LZFu compression
331  * Returns 1 on success or -1 on error
332  */
libfmapi_lzfu_decompress(uint8_t * uncompressed_data,size_t * uncompressed_data_size,uint8_t * compressed_data,size_t compressed_data_size,libcerror_error_t ** error)333 int libfmapi_lzfu_decompress(
334      uint8_t *uncompressed_data,
335      size_t *uncompressed_data_size,
336      uint8_t *compressed_data,
337      size_t compressed_data_size,
338      libcerror_error_t **error )
339 {
340 	libfmapi_lzfu_header_t lzfu_header;
341 	uint8_t lz_buffer[ 4096 ];
342 
343 	uint8_t *lzfu_data                = 0;
344 	uint8_t *lzfu_reference_data      = 0;
345 	static char *function             = "libfmapi_lzfu_decompress";
346 	size_t compressed_data_iterator   = 0;
347 	size_t uncompressed_data_iterator = 0;
348 	uint32_t calculated_checksum      = 0;
349 	uint16_t lz_buffer_iterator       = 0;
350 	uint16_t reference_offset         = 0;
351 	uint16_t reference_size           = 0;
352 	uint16_t reference_iterator       = 0;
353 	uint8_t flag_byte_bit_mask        = 0;
354 	uint8_t flag_byte                 = 0;
355 
356 	if( uncompressed_data == NULL )
357 	{
358 		libcerror_error_set(
359 		 error,
360 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
361 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
362 		 "%s: invalid uncompressed data.",
363 		 function );
364 
365 		return( -1 );
366 	}
367 	if( uncompressed_data_size == NULL )
368 	{
369 		libcerror_error_set(
370 		 error,
371 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
372 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
373 		 "%s: invalid uncompressed data size.",
374 		 function );
375 
376 		return( -1 );
377 	}
378 	if( compressed_data == NULL )
379 	{
380 		libcerror_error_set(
381 		 error,
382 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
383 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
384 		 "%s: invalid compressed data.",
385 		 function );
386 
387 		return( -1 );
388 	}
389 	if( compressed_data_size > (size_t) SSIZE_MAX )
390 	{
391 		libcerror_error_set(
392 		 error,
393 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
394 		 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
395 		 "%s: invalid compressed data size value exceeds maximum.",
396 		 function );
397 
398 		return( -1 );
399 	}
400 	if( memory_copy(
401 	     lz_buffer,
402 	     libfmapi_lzfu_rtf_dictionary,
403 	     207 ) == NULL )
404 	{
405 		libcerror_error_set(
406 		 error,
407 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
408 		 LIBCERROR_MEMORY_ERROR_COPY_FAILED,
409 		 "%s: unable to initialize lz buffer.",
410 		 function );
411 
412 		return( -1 );
413 	}
414 	lz_buffer_iterator = 207;
415 
416 	if( memory_set(
417 	     &( lz_buffer[ lz_buffer_iterator ] ),
418 	     0,
419 	     4096 - lz_buffer_iterator ) == NULL )
420 	{
421 		libcerror_error_set(
422 		 error,
423 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
424 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
425 		 "%s: unable to clear lz buffer.",
426 		 function );
427 
428 		return( -1 );
429 	}
430 	lzfu_data = compressed_data;
431 
432 	byte_stream_copy_to_uint32_little_endian(
433 	 lzfu_data,
434 	 lzfu_header.compressed_data_size );
435 
436 	lzfu_data += 4;
437 
438 	byte_stream_copy_to_uint32_little_endian(
439 	 lzfu_data,
440 	 lzfu_header.uncompressed_data_size );
441 
442 	lzfu_data += 4;
443 
444 	byte_stream_copy_to_uint32_little_endian(
445 	 lzfu_data,
446 	 lzfu_header.signature );
447 
448 	lzfu_data += 4;
449 
450 	byte_stream_copy_to_uint32_little_endian(
451 	 lzfu_data,
452 	 lzfu_header.checksum );
453 
454 	lzfu_data += 4;
455 
456 #if defined( HAVE_DEBUG_OUTPUT )
457 	if( libcnotify_verbose != 0 )
458 	{
459 		libcnotify_printf(
460 		 "%s: lzfu header compressed data size\t: %" PRIu32 "\n",
461 		 function,
462 		 lzfu_header.compressed_data_size );
463 		libcnotify_printf(
464 		 "%s: lzfu header uncompressed data size\t: %" PRIu32 "\n",
465 		 function,
466 		 lzfu_header.uncompressed_data_size );
467 		libcnotify_printf(
468 		 "%s: lzfu header signature\t\t\t: 0x08%" PRIx32 "\n",
469 		 function,
470 		 lzfu_header.signature );
471 		libcnotify_printf(
472 		 "%s: lzfu header checksum\t\t: %" PRIu32 "\n",
473 		 function,
474 		 lzfu_header.checksum );
475 	}
476 #endif
477 
478 	if( ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_COMPRESSED )
479 	 && ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED ) )
480 	{
481 		libcerror_error_set(
482 		 error,
483 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
484 		 LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
485 		 "%s: unsupported compression signature: 0x%08" PRIx32 ".",
486 		 function,
487 		 lzfu_header.signature );
488 
489 		return( -1 );
490 	}
491 	compressed_data_size -= sizeof( libfmapi_lzfu_header_t );
492 
493 	/* The compressed data size includes 12 bytes of the header
494 	 */
495 	lzfu_header.compressed_data_size -= 12;
496 
497 	if( lzfu_header.compressed_data_size != compressed_data_size )
498 	{
499 		libcerror_error_set(
500 		 error,
501 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
502 		 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
503 		 "%s: mismatch in compressed data size (%" PRIu32 " != %" PRIzd ").",
504 		 function,
505 		 lzfu_header.compressed_data_size,
506 		 compressed_data_size );
507 
508 		return( -1 );
509 	}
510 	/* Make sure the uncompressed buffer is large enough
511 	 */
512 	if( *uncompressed_data_size < lzfu_header.uncompressed_data_size )
513 	{
514 		libcerror_error_set(
515 		 error,
516 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
517 		 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
518 		 "%s: uncompressed data too small.",
519 		 function );
520 
521 		*uncompressed_data_size = lzfu_header.uncompressed_data_size;
522 
523 		return( -1 );
524 	}
525 	if( libfmapi_checksum_calculate_weak_crc32(
526 	     &calculated_checksum,
527 	     lzfu_data,
528 	     (size_t) lzfu_header.compressed_data_size,
529 	     0,
530 	     error ) != 1 )
531 	{
532 		libcerror_error_set(
533 		 error,
534 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
535 		 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
536 		 "%s: unable to calculate weak CRC-32.",
537 		 function );
538 
539 		return( -1 );
540 	}
541 	if( lzfu_header.checksum != calculated_checksum )
542 	{
543 		libcerror_error_set(
544 		 error,
545 		 LIBCERROR_ERROR_DOMAIN_INPUT,
546 		 LIBCERROR_INPUT_ERROR_CHECKSUM_MISMATCH,
547 		 "%s: mismatch in checksum ( %" PRIu32 " != %" PRIu32 " ).",
548 		 function,
549 		 lzfu_header.checksum,
550 		 calculated_checksum );
551 
552 		return( -1 );
553 	}
554 	while( compressed_data_iterator < (size_t) lzfu_header.compressed_data_size )
555 	{
556 		flag_byte = lzfu_data[ compressed_data_iterator++ ];
557 
558 		/* Check every bit in the chunk flag byte from LSB to MSB
559 		 */
560 		for( flag_byte_bit_mask = 0x01; flag_byte_bit_mask != 0x00; flag_byte_bit_mask <<= 1 )
561 		{
562 			if( compressed_data_iterator == (size_t) lzfu_header.compressed_data_size )
563 			{
564 				break;
565 			}
566 			/* Check if the byte value is a literal or a reference
567 			 */
568 			if( ( flag_byte & flag_byte_bit_mask ) == 0 )
569 			{
570 				if( compressed_data_iterator >= (size_t) lzfu_header.compressed_data_size )
571 				{
572 					libcerror_error_set(
573 					 error,
574 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
575 					 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
576 					 "%s: missing compressed data.",
577 					 function );
578 
579 					*uncompressed_data_size = 0;
580 
581 					return( -1 );
582 				}
583 				if( uncompressed_data_iterator >= *uncompressed_data_size )
584 				{
585 					libcerror_error_set(
586 					 error,
587 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
588 					 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
589 					 "%s: uncompressed data too small.",
590 					 function );
591 
592 					*uncompressed_data_size = uncompressed_data_iterator;
593 
594 					return( -1 );
595 				}
596 				lz_buffer[ lz_buffer_iterator++ ]                 = lzfu_data[ compressed_data_iterator ];
597 				uncompressed_data[ uncompressed_data_iterator++ ] = lzfu_data[ compressed_data_iterator ];
598 
599 				compressed_data_iterator++;
600 
601 				/* Make sure the lz buffer iterator wraps around
602 				 */
603 				lz_buffer_iterator %= 4096;
604 
605 				lz_buffer[ lz_buffer_iterator ] = 0;
606 			}
607 			else
608 			{
609 				if( ( compressed_data_iterator + 1 ) >= (size_t) lzfu_header.compressed_data_size )
610 				{
611 					libcerror_error_set(
612 					 error,
613 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
614 					 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
615 					 "%s: missing compressed data.",
616 					 function );
617 
618 					*uncompressed_data_size = 0;
619 
620 					return( -1 );
621 				}
622 				lzfu_reference_data = &( lzfu_data[ compressed_data_iterator ] );
623 
624 				compressed_data_iterator += 2;
625 
626 				byte_stream_copy_to_uint16_big_endian(
627 				 lzfu_reference_data,
628 				 reference_offset );
629 
630 				reference_size     = ( reference_offset & 0x000f ) + 2;
631 				reference_offset >>= 4;
632 
633 				if( ( uncompressed_data_iterator + reference_size - 1 ) >= *uncompressed_data_size )
634 				{
635 					libcerror_error_set(
636 					 error,
637 					 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
638 					 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
639 					 "%s: uncompressed data too small.",
640 					 function );
641 
642 					*uncompressed_data_size = uncompressed_data_iterator + reference_size;
643 
644 					return( -1 );
645 				}
646 				for( reference_iterator = 0; reference_iterator < reference_size; reference_iterator++ )
647 				{
648 					lz_buffer[ lz_buffer_iterator++ ]                 = lz_buffer[ reference_offset ];
649 					uncompressed_data[ uncompressed_data_iterator++ ] = lz_buffer[ reference_offset ];
650 
651 					reference_offset++;
652 
653 					/* Make sure the lz buffer iterator and reference offset wrap around
654 					 */
655 					lz_buffer_iterator %= 4096;
656 					reference_offset   %= 4096;
657 
658 					lz_buffer[ lz_buffer_iterator ] = 0;
659 				}
660 			}
661 		}
662 	}
663 	*uncompressed_data_size = uncompressed_data_iterator;
664 
665 	return( 1 );
666 }
667 
668