File: /home/django/libpff/libfwnt/libfwnt_lzxpress.c
/*
* LZXPRESS (de)compression functions
*
* Copyright (C) 2009-2024, Joachim Metz <joachim.metz@gmail.com>
*
* Refer to AUTHORS for acknowledgements.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <byte_stream.h>
#include <memory.h>
#include <types.h>
#include "libfwnt_bit_stream.h"
#include "libfwnt_huffman_tree.h"
#include "libfwnt_libcerror.h"
#include "libfwnt_libcnotify.h"
#include "libfwnt_lzxpress.h"
/* Compresses data using LZXPRESS (LZ77 + DIRECT2) compression
* Returns 1 on success or -1 on error
*/
int libfwnt_lzxpress_compress(
const uint8_t *uncompressed_data,
size_t uncompressed_data_size,
uint8_t *compressed_data,
size_t *compressed_data_size,
libcerror_error_t **error )
{
static char *function = "libfwnt_lzxpress_compress";
if( uncompressed_data == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data.",
function );
return( -1 );
}
if( uncompressed_data_size > (size_t) SSIZE_MAX )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
"%s: invalid uncompressed data size value exceeds maximum.",
function );
return( -1 );
}
if( compressed_data == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid compressed data.",
function );
return( -1 );
}
if( compressed_data_size == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid compressed data size.",
function );
return( -1 );
}
/* TODO implement */
return( -1 );
}
/* Decompresses data using LZXPRESS (LZ77 + DIRECT2) compression
* Return 1 on success or -1 on error
*/
int libfwnt_lzxpress_decompress(
const uint8_t *compressed_data,
size_t compressed_data_size,
uint8_t *uncompressed_data,
size_t *uncompressed_data_size,
libcerror_error_t **error )
{
static char *function = "libfwnt_lzxpress_decompress";
size_t compressed_data_offset = 0;
size_t compression_index = 0;
size_t compression_shared_byte_index = 0;
size_t safe_uncompressed_data_size = 0;
size_t uncompressed_data_offset = 0;
uint32_t compression_indicator = 0;
uint32_t compression_indicator_bitmask = 0;
uint16_t compression_tuple = 0;
uint16_t compression_tuple_size = 0;
int16_t compression_tuple_offset = 0;
if( compressed_data == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid compressed data.",
function );
return( -1 );
}
if( ( compressed_data_size < 2 )
|| ( compressed_data_size > (size_t) SSIZE_MAX ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: invalid compressed data size value out of bounds.",
function );
return( -1 );
}
if( uncompressed_data == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data.",
function );
return( -1 );
}
if( uncompressed_data_size == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data size.",
function );
return( -1 );
}
if( *uncompressed_data_size > (size_t) SSIZE_MAX )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
"%s: invalid uncompressed data size value exceeds maximum.",
function );
return( -1 );
}
safe_uncompressed_data_size = *uncompressed_data_size;
while( compressed_data_offset < compressed_data_size )
{
if( uncompressed_data_offset >= safe_uncompressed_data_size )
{
break;
}
if( ( compressed_data_size < 4 )
|| ( compressed_data_offset > ( compressed_data_size - 4 ) ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
byte_stream_copy_to_uint32_little_endian(
&( compressed_data[ compressed_data_offset ] ),
compression_indicator );
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
compressed_data_offset,
compressed_data_offset );
libcnotify_printf(
"%s: compression indicator\t\t\t: 0x%08" PRIx32 "\n",
function,
compression_indicator );
libcnotify_printf(
"\n" );
}
#endif
compressed_data_offset += 4;
for( compression_indicator_bitmask = 0x80000000UL;
compression_indicator_bitmask > 0;
compression_indicator_bitmask >>= 1 )
{
if( uncompressed_data_offset >= safe_uncompressed_data_size )
{
break;
}
if( compressed_data_offset >= compressed_data_size )
{
break;
}
/* If the indicator bit is 0 the data is uncompressed
* or 1 if the data is compressed
*/
if( ( compression_indicator & compression_indicator_bitmask ) != 0 )
{
if( compressed_data_offset > ( compressed_data_size - 2 ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
byte_stream_copy_to_uint16_little_endian(
&( compressed_data[ compressed_data_offset ] ),
compression_tuple );
compressed_data_offset += 2;
/* The compression tuple contains:
* 0 - 2 the size
* 3 - 15 the offset - 1
*/
compression_tuple_size = ( compression_tuple & 0x0007 );
compression_tuple_offset = ( compression_tuple >> 3 ) + 1;
/* Check for a first level extended size
* stored in the 4-bits of a shared extended compression tuple size byte
* the size is added to the previous size
*/
if( compression_tuple_size == 0x07 )
{
if( compression_shared_byte_index == 0 )
{
if( compressed_data_offset >= compressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
compression_tuple_size += compressed_data[ compressed_data_offset ] & 0x0f;
compression_shared_byte_index = compressed_data_offset++;
}
else
{
compression_tuple_size += compressed_data[ compression_shared_byte_index ] >> 4;
compression_shared_byte_index = 0;
}
}
/* Check for a second level extended size
* stored in the 8-bits of the next byte
* the size is added to the previous size
*/
if( compression_tuple_size == ( 0x07 + 0x0f ) )
{
if( compressed_data_offset >= compressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
compression_tuple_size += compressed_data[ compressed_data_offset++ ];
}
/* Check for a third level extended size
* stored in the 16-bits of the next two bytes
* the previous size is ignored
*/
if( compression_tuple_size == ( 0x07 + 0x0f + 0xff ) )
{
if( compressed_data_offset > ( compressed_data_size - 2 ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
byte_stream_copy_to_uint16_little_endian(
&( compressed_data[ compressed_data_offset ] ),
compression_tuple_size );
compressed_data_offset += 2;
}
/* The size value is stored as
* size - 3
*/
compression_tuple_size += 3;
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
compressed_data_offset,
compressed_data_offset );
libcnotify_printf(
"%s: compression tuple offset\t\t\t: %" PRIi16 "\n",
function,
compression_tuple_offset );
libcnotify_printf(
"%s: compression tuple size\t\t\t: %" PRIu16 "\n",
function,
compression_tuple_size );
libcnotify_printf(
"%s: uncompressed data offset\t\t\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
uncompressed_data_offset,
uncompressed_data_offset );
libcnotify_printf(
"\n" );
}
#endif /* defined( HAVE_DEBUG_OUTPUT ) */
if( compression_tuple_size > 32771 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compression tuple size value out of bounds.",
function );
return( -1 );
}
compression_index = uncompressed_data_offset - compression_tuple_offset;
while( compression_tuple_size > 0 )
{
if( compression_index >= uncompressed_data_offset )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: invalid compressed data at offset: %" PRIzd " - compression index: %" PRIzd " out of range: %" PRIzd ".",
function,
compressed_data_offset,
compression_index,
uncompressed_data_offset );
return( -1 );
}
if( uncompressed_data_offset >= safe_uncompressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: uncompressed data too small.",
function );
return( -1 );
}
uncompressed_data[ uncompressed_data_offset++ ] = uncompressed_data[ compression_index++ ];
compression_tuple_size--;
}
}
else
{
if( compressed_data_offset >= compressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: compressed data too small.",
function );
return( -1 );
}
if( uncompressed_data_offset > safe_uncompressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: uncompressed data too small.",
function );
return( -1 );
}
uncompressed_data[ uncompressed_data_offset++ ] = compressed_data[ compressed_data_offset++ ];
}
}
}
*uncompressed_data_size = uncompressed_data_offset;
return( 1 );
}
/* Decompresses a LZXPRESS Huffman compressed chunk
* Return 1 on success or -1 on error
*/
int libfwnt_lzxpress_huffman_decompress_chunk(
libfwnt_bit_stream_t *bit_stream,
uint8_t *uncompressed_data,
size_t uncompressed_data_size,
size_t *uncompressed_data_offset,
libcerror_error_t **error )
{
uint8_t code_size_array[ 512 ];
libfwnt_huffman_tree_t *huffman_tree = NULL;
static char *function = "libfwnt_lzxpress_huffman_decompress_chunk";
size_t next_chunk_uncompressed_data_offset = 0;
size_t safe_uncompressed_data_offset = 0;
uint32_t compression_offset = 0;
uint32_t compression_size = 0;
uint32_t symbol = 0;
uint8_t byte_value = 0;
if( bit_stream == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid bit stream.",
function );
return( -1 );
}
if( ( bit_stream->byte_stream_size - bit_stream->byte_stream_offset ) < 260 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
"%s: invalid bit stream - byte stream value too small.",
function );
return( -1 );
}
if( uncompressed_data == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data.",
function );
return( -1 );
}
if( uncompressed_data_size > (size_t) SSIZE_MAX )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
"%s: invalid uncompressed data size value exceeds maximum.",
function );
return( -1 );
}
if( uncompressed_data_offset == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data offset.",
function );
return( -1 );
}
safe_uncompressed_data_offset = *uncompressed_data_offset;
if( safe_uncompressed_data_offset >= uncompressed_data_size )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: uncompressed data offset value out of bounds.",
function );
return( -1 );
}
/* The table contains 4-bits code size per symbol
*/
while( symbol < 512 )
{
byte_value = bit_stream->byte_stream[ bit_stream->byte_stream_offset ];
code_size_array[ symbol++ ] = byte_value & 0x0f;
byte_value >>= 4;
code_size_array[ symbol++ ] = byte_value & 0x0f;
bit_stream->byte_stream_offset += 1;
}
if( libfwnt_huffman_tree_initialize(
&huffman_tree,
512,
15,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
"%s: unable to create Huffman tree.",
function );
goto on_error;
}
if( libfwnt_huffman_tree_build(
huffman_tree,
code_size_array,
512,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_SET_FAILED,
"%s: unable to build Huffman tree.",
function );
goto on_error;
}
if( libfwnt_bit_stream_read(
bit_stream,
32,
error ) == -1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_IO,
LIBCERROR_IO_ERROR_READ_FAILED,
"%s: unable to read 32-bit from bit stream.",
function );
goto on_error;
}
next_chunk_uncompressed_data_offset = safe_uncompressed_data_offset + 65536;
if( next_chunk_uncompressed_data_offset > uncompressed_data_size )
{
next_chunk_uncompressed_data_offset = uncompressed_data_size;
}
while( ( bit_stream->byte_stream_offset < bit_stream->byte_stream_size )
|| ( bit_stream->bit_buffer_size > 0 ) )
{
if( safe_uncompressed_data_offset >= next_chunk_uncompressed_data_offset )
{
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: end of chunk at compressed data offset: %" PRIzd " (0x%08" PRIzx "), uncompressed data offset: %" PRIzd " (0x%08" PRIzx ")\n",
function,
bit_stream->byte_stream_offset,
bit_stream->byte_stream_offset,
safe_uncompressed_data_offset,
safe_uncompressed_data_offset );
libcnotify_printf(
"\n" );
}
#endif
bit_stream->bit_buffer_size = 0;
break;
}
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compressed data offset\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
bit_stream->byte_stream_offset,
bit_stream->byte_stream_offset );
}
#endif
if( libfwnt_huffman_tree_get_symbol_from_bit_stream(
huffman_tree,
bit_stream,
&symbol,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_GET_FAILED,
"%s: unable to read symbol.",
function );
goto on_error;
}
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: huffman symbol\t\t: 0x%04" PRIx16 "\n",
function,
symbol );
}
#endif
if( symbol < 256 )
{
uncompressed_data[ safe_uncompressed_data_offset++ ] = (uint8_t) symbol;
}
/* Make sure the bit buffer contains at least 16-bit to ensure end-of-block marker is read correctly
*/
if( bit_stream->bit_buffer_size < 16 )
{
if( libfwnt_bit_stream_read(
bit_stream,
16,
error ) == -1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_IO,
LIBCERROR_IO_ERROR_READ_FAILED,
"%s: unable to read 16-bit from bit stream.",
function );
goto on_error;
}
}
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: number of bits\t\t: %" PRId8 "\n",
function,
bit_stream->bit_buffer_size );
}
#endif
/* Check if we have an end-of-block marker (symbol 256) and the number of remaining bits are 0
*/
/* TODO add ( symbol == 256 ) */
if( ( bit_stream->bit_buffer == 0 )
&& ( safe_uncompressed_data_offset >= uncompressed_data_size ) )
{
break;
}
if( symbol >= 256 )
{
symbol -= 256;
compression_offset = 0;
compression_size = symbol & 0x000f;
symbol >>= 4;
if( symbol != 0 )
{
if( libfwnt_bit_stream_get_value(
bit_stream,
(uint8_t) symbol,
&compression_offset,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_GET_FAILED,
"%s: unable to retrieve compression offset from bit stream.",
function );
goto on_error;
}
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compression offset\t\t: %" PRIu32 "\n",
function,
compression_offset );
}
#endif
}
compression_offset = (uint32_t) ( ( 1 << symbol ) | compression_offset );
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compression size\t\t: %" PRIu32 "\n",
function,
compression_size );
}
#endif
/* Ignore any data beyond the uncompressed block size
*/
if( compression_size == 15 )
{
if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 1 ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compressed data size value too small.",
function );
goto on_error;
}
compression_size = bit_stream->byte_stream[ bit_stream->byte_stream_offset ] + 15;
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
bit_stream->byte_stream_offset,
bit_stream->byte_stream_offset );
libcnotify_printf(
"%s: extended compression size\t: %" PRIu32 "\n",
function,
compression_size );
}
#endif
bit_stream->byte_stream_offset += 1;
if( compression_size == 270 )
{
if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 2 ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compressed data size value too small.",
function );
goto on_error;
}
byte_stream_copy_to_uint16_little_endian(
&( bit_stream->byte_stream[ bit_stream->byte_stream_offset ] ),
compression_size );
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
bit_stream->byte_stream_offset,
bit_stream->byte_stream_offset );
libcnotify_printf(
"%s: extended compression size\t: %" PRIu32 "\n",
function,
compression_size );
}
#endif
bit_stream->byte_stream_offset += 2;
if( compression_size == 0 )
{
if( bit_stream->byte_stream_offset > ( bit_stream->byte_stream_size - 4 ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compressed data size value too small.",
function );
goto on_error;
}
byte_stream_copy_to_uint32_little_endian(
&( bit_stream->byte_stream[ bit_stream->byte_stream_offset ] ),
compression_size );
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: extended compression offset\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
bit_stream->byte_stream_offset,
bit_stream->byte_stream_offset );
libcnotify_printf(
"%s: extended compression size\t: %" PRIu32 "\n",
function,
compression_size );
}
#endif
bit_stream->byte_stream_offset += 4;
}
}
}
compression_size += 3;
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"%s: compression offset\t\t: %" PRIu32 "\n",
function,
compression_offset );
libcnotify_printf(
"%s: compression size\t\t: %" PRIu32 "\n",
function,
compression_size );
libcnotify_printf(
"%s: uncompressed data offset\t: %" PRIzd " (0x%08" PRIzx ")\n",
function,
safe_uncompressed_data_offset,
safe_uncompressed_data_offset );
}
#endif
if( compression_offset > safe_uncompressed_data_offset )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compression offset value out of bounds.",
function );
goto on_error;
}
if( compression_size > ( uncompressed_data_size - safe_uncompressed_data_offset ) )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
"%s: compression size value out of bounds.",
function );
goto on_error;
}
compression_offset = safe_uncompressed_data_offset - compression_offset;
while( compression_size > 0 )
{
uncompressed_data[ safe_uncompressed_data_offset++ ] = uncompressed_data[ compression_offset++ ];
compression_size--;
}
/* Make sure the bit buffer contains at least 16-bit to ensure successive chunks in a stream are read correctly
*/
if( bit_stream->bit_buffer_size < 16 )
{
if( libfwnt_bit_stream_read(
bit_stream,
16,
error ) == -1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_IO,
LIBCERROR_IO_ERROR_READ_FAILED,
"%s: unable to read 16-bit from bit stream.",
function );
goto on_error;
}
}
}
#if defined( HAVE_DEBUG_OUTPUT )
if( libcnotify_verbose != 0 )
{
libcnotify_printf(
"\n" );
}
#endif
}
if( libfwnt_huffman_tree_free(
&huffman_tree,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
"%s: unable to free Huffman tree.",
function );
goto on_error;
}
*uncompressed_data_offset = safe_uncompressed_data_offset;
return( 1 );
on_error:
if( huffman_tree != NULL )
{
libfwnt_huffman_tree_free(
&huffman_tree,
NULL );
}
return( -1 );
}
/* Decompresses data using LZXPRESS Huffman compression
* Return 1 on success or -1 on error
*/
int libfwnt_lzxpress_huffman_decompress(
const uint8_t *compressed_data,
size_t compressed_data_size,
uint8_t *uncompressed_data,
size_t *uncompressed_data_size,
libcerror_error_t **error )
{
libfwnt_bit_stream_t *bit_stream = NULL;
static char *function = "libfwnt_lzxpress_huffman_decompress";
size_t safe_uncompressed_data_size = 0;
size_t uncompressed_data_offset = 0;
if( uncompressed_data_size == NULL )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
"%s: invalid uncompressed data size.",
function );
return( -1 );
}
if( libfwnt_bit_stream_initialize(
&bit_stream,
compressed_data,
compressed_data_size,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
"%s: unable to create bit stream.",
function );
goto on_error;
}
safe_uncompressed_data_size = *uncompressed_data_size;
while( bit_stream->byte_stream_offset < bit_stream->byte_stream_size )
{
if( uncompressed_data_offset >= safe_uncompressed_data_size )
{
break;
}
if( libfwnt_lzxpress_huffman_decompress_chunk(
bit_stream,
uncompressed_data,
safe_uncompressed_data_size,
&uncompressed_data_offset,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_COMPRESSION,
LIBCERROR_COMPRESSION_ERROR_DECOMPRESS_FAILED,
"%s: unable to decompress chunk.",
function );
goto on_error;
}
}
if( libfwnt_bit_stream_free(
&bit_stream,
error ) != 1 )
{
libcerror_error_set(
error,
LIBCERROR_ERROR_DOMAIN_RUNTIME,
LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
"%s: unable to free bit stream.",
function );
goto on_error;
}
*uncompressed_data_size = uncompressed_data_offset;
return( 1 );
on_error:
if( bit_stream != NULL )
{
libfwnt_bit_stream_free(
&bit_stream,
NULL );
}
return( -1 );
}