2
0
mirror of https://github.com/FWGS/xash3d-fwgs synced 2024-11-22 01:45:19 +01:00

engine: common: add lzss fixes by nillerusr

This commit is contained in:
Alibek Omarov 2024-11-05 23:39:18 +03:00
parent 41b78c26ef
commit c3b6422ad0
4 changed files with 134 additions and 25 deletions

View File

@ -115,6 +115,16 @@ typedef uint64_t longtime_t;
#define WARN_UNUSED_RESULT
#endif
#if defined( __has_feature )
#if __has_feature( address_sanitizer )
#define USE_ASAN 1
#endif // __has_feature
#endif // defined( __has_feature )
#if !defined( USE_ASAN ) && defined( __SANITIZE_ADDRESS__ )
#define USE_ASAN 1
#endif
#if __GNUC__ >= 3
#define unlikely( x ) __builtin_expect( x, 0 )
#define likely( x ) __builtin_expect( x, 1 )

View File

@ -218,21 +218,32 @@ typedef struct
int window_size;
} lzss_state_t;
qboolean LZSS_IsCompressed( const byte *source )
qboolean LZSS_IsCompressed( const byte *source, size_t input_len )
{
lzss_header_t *phdr = (lzss_header_t *)source;
const lzss_header_t *phdr;
if( input_len <= sizeof( lzss_header_t ))
return 0;
phdr = (const lzss_header_t *)source;
if( phdr && phdr->id == LZSS_ID )
return true;
return false;
}
uint LZSS_GetActualSize( const byte *source )
uint LZSS_GetActualSize( const byte *source, size_t input_len )
{
lzss_header_t *phdr = (lzss_header_t *)source;
const lzss_header_t *phdr;
if( input_len <= sizeof( lzss_header_t ))
return 0;
phdr = (const lzss_header_t *)source;
if( phdr && phdr->id == LZSS_ID )
return phdr->size;
return 0;
}
@ -356,6 +367,8 @@ static byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_
if( pOutput >= pEnd )
{
// compression is worse, abandon
state->hash_table = NULL;
state->hash_node = NULL;
return NULL;
}
}
@ -364,6 +377,8 @@ static byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_
{
// unexpected failure
Assert( 0 );
state->hash_table = NULL;
state->hash_node = NULL;
return NULL;
}
@ -389,16 +404,13 @@ static byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_
byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize )
{
byte *pStart = (byte *)malloc( inputLength );
byte *pFinal = NULL;
lzss_state_t state;
byte *pStart = (byte *)malloc( inputLength );
byte *pFinal = NULL;
lzss_state_t state = { .window_size = LZSS_WINDOW_SIZE };
if( !pStart )
return NULL;
memset( &state, 0, sizeof( state ));
state.window_size = LZSS_WINDOW_SIZE;
pFinal = LZSS_CompressNoAlloc( &state, pInput, inputLength, pStart, pOutputSize );
if( !pFinal )
@ -410,14 +422,21 @@ byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize )
return pStart;
}
uint LZSS_Decompress( const byte *pInput, byte *pOutput )
uint LZSS_Decompress( const byte *pInput, byte *pOutput, size_t input_len, size_t output_len )
{
uint totalBytes = 0;
int getCmdByte = 0;
int cmdByte = 0;
uint actualSize = LZSS_GetActualSize( pInput );
uint actualSize;
const byte *pInputEnd = pInput + input_len - 1; // thanks to nillerusr for the fix!
byte *pOrigOutput = pOutput;
if( !actualSize )
if( input_len <= sizeof( lzss_header_t ))
return 0;
actualSize = LZSS_GetActualSize( pInput, input_len );
if( !actualSize || actualSize > output_len )
return 0;
pInput += sizeof( lzss_header_t );
@ -425,15 +444,24 @@ uint LZSS_Decompress( const byte *pInput, byte *pOutput )
while( 1 )
{
if( !getCmdByte )
{
if( pInput > pInputEnd )
return 0;
cmdByte = *pInput++;
}
getCmdByte = ( getCmdByte + 1 ) & 0x07;
if( cmdByte & 0x01 )
{
int position = *pInput++ << LZSS_LOOKSHIFT;
int position;
int i, count;
byte *pSource;
if( pInput > pInputEnd )
return 0;
position = *pInput++ << LZSS_LOOKSHIFT;
position |= ( *pInput >> LZSS_LOOKSHIFT );
count = ( *pInput++ & 0x0F ) + 1;
@ -441,12 +469,19 @@ uint LZSS_Decompress( const byte *pInput, byte *pOutput )
break;
pSource = pOutput - position - 1;
if( totalBytes + count > output_len || pSource < pOrigOutput )
return 0;
for( i = 0; i < count; i++ )
*pOutput++ = *pSource++;
totalBytes += count;
}
else
{
if( totalBytes + 1 > output_len || pInput > pInputEnd )
return 0;
*pOutput++ = *pInput++;
totalBytes++;
}
@ -1043,6 +1078,67 @@ void GAME_EXPORT pfnResetTutorMessageDecayData( void )
#include "tests.h"
#ifdef USE_ASAN
#include <sanitizer/asan_interface.h>
#endif
static void Test_LZSS( void )
{
char poison1[8192];
byte in[256];
char poison2[8192];
byte out[256];
char poison3[8192];
lzss_header_t *hdr = (lzss_header_t *)in;
uint result;
const byte compressed[] =
{
0x4c, 0x5a, 0x53, 0x53, 0x1a, 0x00, 0x00, 0x00, 0x00,
0x44, 0x6f, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6c, 0x00,
0x69, 0x6b, 0x65, 0x20, 0x77, 0x68, 0x61, 0x74, 0x41,
0x00, 0xd4, 0x73, 0x65, 0x65, 0x3f, 0x00, 0x00, 0x00,
};
const char decompressed[] = "Do you like what you see?";
#ifdef USING_ASAN
ASAN_POISON_MEMORY_REGION( poison1, sizeof( poison1 ));
ASAN_POISON_MEMORY_REGION( poison2, sizeof( poison2 ));
ASAN_POISON_MEMORY_REGION( poison3, sizeof( poison3 ));
#endif
hdr->size = sizeof( in ) - sizeof( *hdr );
hdr->id = LZSS_ID;
memset( in + sizeof( *hdr ), 0xff, sizeof( in ) - sizeof( *hdr ));
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
memset( in + sizeof( *hdr ), 0x00, sizeof( in ) - sizeof( *hdr ));
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = 1;
hdr->id = LZSS_ID;
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = 999;
hdr->id = LZSS_ID;
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = sizeof( in ) - sizeof( *hdr );
hdr->id = 0xa1ba;
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
result = LZSS_Decompress( compressed, out, sizeof( compressed ), sizeof( out ));
TASSERT_EQi( result, 26 );
TASSERT_STR( out, decompressed );
}
void Test_RunCommon( void )
{
Msg( "Checking COM_IsSafeFileToDownload...\n" );
@ -1053,5 +1149,8 @@ void Test_RunCommon( void )
TASSERT_EQi( COM_IsSafeFileToDownload( "!MD5/../../valve/resource/GameMenu.res" ), false );
TASSERT_EQi( COM_IsSafeFileToDownload( "not-a-virus-trust-me.bat" ), false );
TASSERT_EQi( COM_IsSafeFileToDownload( "a-texture.png" ), true );
Msg( "Checking LZSS_Decompress...\n" );
Test_LZSS();
}
#endif

View File

@ -759,10 +759,10 @@ int Cmd_CheckMapsList( int fRefresh );
void COM_SetRandomSeed( int lSeed );
int COM_RandomLong( int lMin, int lMax );
float COM_RandomFloat( float fMin, float fMax );
qboolean LZSS_IsCompressed( const byte *source );
uint LZSS_GetActualSize( const byte *source );
qboolean LZSS_IsCompressed( const byte *source, size_t input_len );
uint LZSS_GetActualSize( const byte *source, size_t input_len );
byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize );
uint LZSS_Decompress( const byte *pInput, byte *pOutput );
uint LZSS_Decompress( const byte *pInput, byte *pOutput, size_t input_len, size_t output_len );
void GL_FreeImage( const char *name );
void VID_InitDefaultResolution( void );
void VID_Init( void );

View File

@ -768,7 +768,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg )
Host_Error( "%s: BZ2 compression is not supported for server", __func__ );
#endif
}
else if( !chan->use_bz2 && !LZSS_IsCompressed( MSG_GetData( msg )))
else if( !chan->use_bz2 && !LZSS_IsCompressed( MSG_GetData( msg ), MSG_GetMaxBytes( msg )))
{
uint uCompressedSize = 0;
uint uSourceSize = MSG_GetNumBytesWritten( msg );
@ -925,7 +925,7 @@ void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filenam
chunksize = chan->pfnBlockSize( chan->client, FRAGSIZE_FRAG );
if( !LZSS_IsCompressed( pbuf ))
if( !LZSS_IsCompressed( pbuf, size ))
{
uint uCompressedSize = 0;
byte *pbOut = LZSS_Compress( pbuf, size, &uCompressedSize );
@ -1188,14 +1188,14 @@ qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg, size_t *l
Host_Error( "%s: BZ2 compression is not supported for server", __func__ );
#endif
}
else if( !chan->use_bz2 && LZSS_IsCompressed( MSG_GetData( msg )))
else if( !chan->use_bz2 && LZSS_IsCompressed( MSG_GetData( msg ), size ))
{
uint uDecompressedLen = LZSS_GetActualSize( MSG_GetData( msg ));
uint uDecompressedLen = LZSS_GetActualSize( MSG_GetData( msg ), size );
byte buf[NET_MAX_MESSAGE];
if( uDecompressedLen <= sizeof( buf ))
{
size = LZSS_Decompress( MSG_GetData( msg ), buf );
size = LZSS_Decompress( MSG_GetData( msg ), buf, size, sizeof( buf ));
memcpy( msg->pData, buf, size );
}
else
@ -1339,14 +1339,14 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
Host_Error( "%s: BZ2 compression is not supported for server", __func__ );
#endif
}
else if( LZSS_IsCompressed( buffer ))
else if( LZSS_IsCompressed( buffer, nsize + 1 ))
{
byte *uncompressedBuffer;
uncompressedSize = LZSS_GetActualSize( buffer ) + 1;
uncompressedSize = LZSS_GetActualSize( buffer, nsize + 1 ) + 1;
uncompressedBuffer = Mem_Calloc( net_mempool, uncompressedSize );
nsize = LZSS_Decompress( buffer, uncompressedBuffer );
nsize = LZSS_Decompress( buffer, uncompressedBuffer, nsize + 1, uncompressedSize );
Mem_Free( buffer );
buffer = uncompressedBuffer;
}