2014-04-25 18:57:00 +02:00
// Copyright (C) 2014 Hykem <hykem@hotmail.com>
// Licensed under the terms of the GNU GPL, version 3
// http://www.gnu.org/licenses/gpl-3.0.txt
# include "lz.h"
2014-10-01 15:57:44 +02:00
void decode_range ( unsigned int * range , unsigned int * code , unsigned char * * src )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
if ( ! ( ( * range ) > > 24 ) )
2014-04-25 18:57:00 +02:00
{
( * range ) < < = 8 ;
* code = ( ( * code ) < < 8 ) + ( * src ) + + [ 5 ] ;
}
}
int decode_bit ( unsigned int * range , unsigned int * code , int * index , unsigned char * * src , unsigned char * c )
{
2014-10-01 15:57:44 +02:00
decode_range ( range , code , src ) ;
unsigned int val = ( ( * range ) > > 8 ) * ( * c ) ;
2014-04-25 18:57:00 +02:00
* c - = ( ( * c ) > > 3 ) ;
if ( index ) ( * index ) < < = 1 ;
if ( * code < val )
{
* range = val ;
* c + = 31 ;
if ( index ) ( * index ) + + ;
return 1 ;
}
else
{
* code - = val ;
* range - = val ;
return 0 ;
}
}
int decode_number ( unsigned char * ptr , int index , int * bit_flag , unsigned int * range , unsigned int * code , unsigned char * * src )
{
int i = 1 ;
2014-10-01 15:57:44 +02:00
if ( index > = 3 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 0x18 ) ;
if ( index > = 4 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 0x18 ) ;
2014-04-25 18:57:00 +02:00
if ( index > = 5 )
{
decode_range ( range , code , src ) ;
for ( ; index > = 5 ; index - - )
{
i < < = 1 ;
( * range ) > > = 1 ;
2014-10-01 15:57:44 +02:00
if ( * code < * range )
i + + ;
2014-04-25 18:57:00 +02:00
else
( * code ) - = * range ;
}
}
}
}
2014-10-01 15:57:44 +02:00
* bit_flag = decode_bit ( range , code , & i , src , ptr ) ;
2014-04-25 18:57:00 +02:00
2014-10-01 15:57:44 +02:00
if ( index > = 1 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 0x8 ) ;
if ( index > = 2 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 0x10 ) ;
2014-04-25 18:57:00 +02:00
}
2014-10-01 15:57:44 +02:00
}
2014-04-25 18:57:00 +02:00
return i ;
}
int decode_word ( unsigned char * ptr , int index , int * bit_flag , unsigned int * range , unsigned int * code , unsigned char * * src )
{
int i = 1 ;
index / = 8 ;
2014-10-01 15:57:44 +02:00
if ( index > = 3 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 4 ) ;
if ( index > = 4 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 4 ) ;
2014-04-25 18:57:00 +02:00
if ( index > = 5 )
{
decode_range ( range , code , src ) ;
for ( ; index > = 5 ; index - - )
{
i < < = 1 ;
( * range ) > > = 1 ;
2014-10-01 15:57:44 +02:00
if ( * code < * range )
i + + ;
2014-04-25 18:57:00 +02:00
else
( * code ) - = * range ;
}
}
}
}
2014-10-01 15:57:44 +02:00
* bit_flag = decode_bit ( range , code , & i , src , ptr ) ;
2014-04-25 18:57:00 +02:00
2014-10-01 15:57:44 +02:00
if ( index > = 1 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 1 ) ;
if ( index > = 2 )
2014-04-25 18:57:00 +02:00
{
2014-10-01 15:57:44 +02:00
decode_bit ( range , code , & i , src , ptr + 2 ) ;
2014-04-25 18:57:00 +02:00
}
2014-10-01 15:57:44 +02:00
}
2014-04-25 18:57:00 +02:00
return i ;
}
int decompress ( unsigned char * out , unsigned char * in , unsigned int size )
{
int result ;
2014-10-01 15:57:44 +02:00
unsigned char * tmp = new unsigned char [ 0xCC8 ] ;
2014-04-25 18:57:00 +02:00
int offset = 0 ;
int bit_flag = 0 ;
int data_length = 0 ;
int data_offset = 0 ;
unsigned char * tmp_sect1 , * tmp_sect2 , * tmp_sect3 ;
unsigned char * buf_start , * buf_end ;
unsigned char prev = 0 ;
unsigned char * start = out ;
unsigned char * end = ( out + size ) ;
unsigned char head = in [ 0 ] ;
unsigned int range = 0xFFFFFFFF ;
unsigned int code = ( in [ 1 ] < < 24 ) | ( in [ 2 ] < < 16 ) | ( in [ 3 ] < < 8 ) | in [ 4 ] ;
2015-04-10 14:13:25 +02:00
// TODO:: Syphurith: There was a check against the unsigned char head. if (head < 0) would always be false.. I don't know are you tried to if (head > 0x80)?
2014-04-25 18:57:00 +02:00
if ( head < 0 ) // Check if we have a valid starting byte.
{
// The dictionary header is invalid, the data is not compressed.
result = - 1 ;
if ( code < = size )
{
memcpy ( out , ( const void * ) ( in + 5 ) , code ) ;
result = ( start - out ) ;
}
}
else
{
// Set up a temporary buffer (sliding window).
2014-10-01 15:57:44 +02:00
memset ( tmp , 0x80 , 0xCA8 ) ;
2014-04-25 18:57:00 +02:00
while ( 1 )
{
2014-10-01 15:57:44 +02:00
// Start reading at 0xB68.
tmp_sect1 = tmp + offset + 0xB68 ;
2014-04-25 18:57:00 +02:00
if ( ! decode_bit ( & range , & code , 0 , & in , tmp_sect1 ) ) // Raw char.
{
// Adjust offset and check for stream end.
if ( offset > 0 ) offset - - ;
if ( start = = end ) return ( start - out ) ;
// Locate first section.
int sect = ( ( ( ( ( ( ( int ) ( start - out ) ) & 7 ) < < 8 ) + prev ) > > head ) & 7 ) * 0xFF - 1 ;
tmp_sect1 = tmp + sect ;
int index = 1 ;
// Read, decode and write back.
do
{
decode_bit ( & range , & code , & index , & in , tmp_sect1 + index ) ;
} while ( ( index > > 8 ) = = 0 ) ;
// Save index.
* start + + = index ;
}
else // Compressed char stream.
{
int index = - 1 ;
// Identify the data length bit field.
2014-10-01 15:57:44 +02:00
do
{
tmp_sect1 + = 8 ;
2014-04-25 18:57:00 +02:00
bit_flag = decode_bit ( & range , & code , 0 , & in , tmp_sect1 ) ;
index + = bit_flag ;
} while ( ( bit_flag ! = 0 ) & & ( index < 6 ) ) ;
2014-10-01 15:57:44 +02:00
// Default block size is 0x160.
int b_size = 0x160 ;
2014-04-25 18:57:00 +02:00
tmp_sect2 = tmp + index + 0x7F1 ;
// If the data length was found, parse it as a number.
2014-10-01 15:57:44 +02:00
if ( ( index > = 0 ) | | ( bit_flag ! = 0 ) )
2014-04-25 18:57:00 +02:00
{
// Locate next section.
2014-10-01 15:57:44 +02:00
int sect = ( index < < 5 ) | ( ( ( ( ( int ) ( start - out ) ) < < index ) & 3 ) < < 3 ) | ( offset & 7 ) ;
tmp_sect1 = tmp + 0xBA8 + sect ;
2014-04-25 18:57:00 +02:00
// Decode the data length (8 bit fields).
data_length = decode_number ( tmp_sect1 , index , & bit_flag , & range , & code , & in ) ;
2014-10-01 15:57:44 +02:00
if ( data_length = = 0xFF ) return ( start - out ) ; // End of stream.
}
else
{
2014-04-25 18:57:00 +02:00
// Assume one byte of advance.
data_length = 1 ;
}
2014-10-01 15:57:44 +02:00
// If we got valid parameters, seek to find data offset.
if ( ( data_length < = 2 ) )
{
tmp_sect2 + = 0xF8 ;
b_size = 0x40 ; // Block size is now 0x40.
}
2014-04-25 18:57:00 +02:00
int diff = 0 ;
int shift = 1 ;
// Identify the data offset bit field.
2014-10-01 15:57:44 +02:00
do
{
2014-04-25 18:57:00 +02:00
diff = ( shift < < 4 ) - b_size ;
bit_flag = decode_bit ( & range , & code , & shift , & in , tmp_sect2 + ( shift < < 3 ) ) ;
} while ( diff < 0 ) ;
// If the data offset was found, parse it as a number.
2014-10-01 15:57:44 +02:00
if ( ( diff > 0 ) | | ( bit_flag ! = 0 ) )
2014-04-25 18:57:00 +02:00
{
// Adjust diff if needed.
if ( bit_flag = = 0 ) diff - = 8 ;
// Locate section.
2014-10-01 15:57:44 +02:00
tmp_sect3 = tmp + 0x928 + diff ;
2014-04-25 18:57:00 +02:00
// Decode the data offset (1 bit fields).
data_offset = decode_word ( tmp_sect3 , diff , & bit_flag , & range , & code , & in ) ;
2014-10-01 15:57:44 +02:00
}
else
{
2014-04-25 18:57:00 +02:00
// Assume one byte of advance.
data_offset = 1 ;
}
// Set buffer start/end.
buf_start = start - data_offset ;
buf_end = start + data_length + 1 ;
// Underflow.
if ( buf_start < out )
return - 1 ;
// Overflow.
if ( buf_end > end )
return - 1 ;
// Update offset.
offset = ( ( ( ( int ) ( buf_end - out ) ) + 1 ) & 1 ) + 6 ;
// Copy data.
2014-10-01 15:57:44 +02:00
do
{
2014-04-25 18:57:00 +02:00
* start + + = * buf_start + + ;
} while ( start < buf_end ) ;
}
prev = * ( start - 1 ) ;
}
result = ( start - out ) ;
}
delete [ ] tmp ;
return result ;
2014-10-01 15:57:44 +02:00
}