You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
7.9 KiB
332 lines
7.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// LZMA Codec interface for engine. Based largely on LzmaUtil.c in SDK |
|
// |
|
// LZMA SDK 9.38 beta |
|
// 2015-01-03 : Igor Pavlov : Public domain |
|
// http://www.7-zip.org/ |
|
// |
|
//========================================================================// |
|
|
|
#ifdef POSIX |
|
#include <stdlib.h> |
|
#endif |
|
#include "tier0/memdbgon.h" |
|
#include "../../public/tier1/lzmaDecoder.h" |
|
#include "C/7zTypes.h" |
|
#include "C/LzmaEnc.h" |
|
#include "C/LzmaDec.h" |
|
#include "tier0/dbg.h" |
|
|
|
// Allocator to pass to LZMA functions |
|
static void *SzAlloc(void *p, size_t size) { return malloc(size); } |
|
static void SzFree(void *p, void *address) { free(address); } |
|
static ISzAlloc g_Alloc = { SzAlloc, SzFree }; |
|
|
|
// lzma buffers will have a 13 byte trivial header |
|
// [0] reserved |
|
// [1..4] dictionary size, little endian |
|
// [5..8] uncompressed size, little endian low word |
|
// [9..12] uncompressed size, little endian high word |
|
// [13..] lzma compressed data |
|
#define LZMA_ORIGINAL_HEADER_SIZE 13 |
|
|
|
SRes CInStreamRam_StaticRead(void *p, void *buf, size_t *size ); |
|
size_t COutStreamRam_StaticWrite(void *p, const void *buf, size_t size); |
|
|
|
class CInStreamRam : public ISeqInStream |
|
{ |
|
const Byte *Data; |
|
size_t Size; |
|
size_t Pos; |
|
|
|
public: |
|
void Init(const Byte *data, size_t size) |
|
{ |
|
Data = data; |
|
Size = size; |
|
Pos = 0; |
|
Read = CInStreamRam_StaticRead; |
|
} |
|
|
|
SRes DoRead( void *buf, size_t *size ) |
|
{ |
|
size_t inSize = *size; |
|
UInt32 remain = Size - Pos; |
|
if (inSize > remain) |
|
inSize = remain; |
|
|
|
for (UInt32 i = 0; i < inSize; i++) |
|
((Byte *)buf)[i] = Data[Pos + i]; |
|
|
|
Pos += inSize; |
|
|
|
*size = inSize; |
|
|
|
return SZ_OK; |
|
} |
|
}; |
|
|
|
class COutStreamRam: public ISeqOutStream |
|
{ |
|
size_t Size; |
|
|
|
public: |
|
Byte *Data; |
|
size_t Pos; |
|
bool Overflow; |
|
|
|
void Init(Byte *data, size_t size) |
|
{ |
|
Data = data; |
|
Size = size; |
|
Pos = 0; |
|
Overflow = false; |
|
Write = COutStreamRam_StaticWrite; |
|
} |
|
|
|
size_t DoWrite( const void *buf, size_t size ) |
|
{ |
|
UInt32 i; |
|
for (i = 0; i < size && Pos < Size; i++) |
|
Data[Pos++] = ((const Byte *)buf)[i]; |
|
if (i != size) |
|
{ |
|
Overflow = true; |
|
} |
|
return i; |
|
} |
|
}; |
|
|
|
SRes CInStreamRam_StaticRead(void *p, void *buf, size_t *size ) |
|
{ |
|
return reinterpret_cast<CInStreamRam *>(p)->DoRead( buf, size ); |
|
} |
|
|
|
size_t COutStreamRam_StaticWrite(void *p, const void *buf, size_t size) |
|
{ |
|
return reinterpret_cast<COutStreamRam *>(p)->DoWrite( buf, size ); |
|
} |
|
|
|
SRes |
|
LzmaEncode( const Byte *inBuffer, |
|
size_t inSize, |
|
Byte *outBuffer, |
|
size_t outSize, |
|
size_t *outSizeProcessed ) |
|
{ |
|
// Based on Encode helper in SDK/LzmaUtil |
|
*outSizeProcessed = 0; |
|
|
|
const size_t kMinDestSize = LZMA_ORIGINAL_HEADER_SIZE; |
|
if ( outSize < kMinDestSize ) |
|
{ |
|
return SZ_ERROR_FAIL; |
|
} |
|
|
|
CLzmaEncHandle enc; |
|
SRes res; |
|
CLzmaEncProps props; |
|
|
|
enc = LzmaEnc_Create( &g_Alloc ); |
|
if ( !enc ) |
|
{ |
|
return SZ_ERROR_FAIL; |
|
} |
|
|
|
LzmaEncProps_Init( &props ); |
|
res = LzmaEnc_SetProps( enc, &props ); |
|
|
|
if ( res != SZ_OK ) |
|
{ |
|
return res; |
|
} |
|
|
|
COutStreamRam outStream; |
|
|
|
outStream.Init( outBuffer, outSize ); |
|
|
|
Byte header[LZMA_PROPS_SIZE + 8]; |
|
size_t headerSize = LZMA_PROPS_SIZE; |
|
int i; |
|
|
|
res = LzmaEnc_WriteProperties( enc, header, &headerSize ); |
|
if ( res != SZ_OK ) |
|
{ |
|
return res; |
|
} |
|
|
|
// Uncompressed size after properties in header |
|
for (i = 0; i < 8; i++) |
|
{ |
|
header[headerSize++] = (Byte)(inSize >> (8 * i)); |
|
} |
|
|
|
if ( outStream.DoWrite( header, headerSize ) != headerSize ) |
|
{ |
|
res = SZ_ERROR_WRITE; |
|
} |
|
else if ( res == SZ_OK ) |
|
{ |
|
CInStreamRam inStream; |
|
inStream.Init( inBuffer, inSize ); |
|
res = LzmaEnc_Encode( enc, &outStream, &inStream, NULL, &g_Alloc, &g_Alloc ); |
|
|
|
if ( outStream.Overflow ) |
|
{ |
|
res = SZ_ERROR_FAIL; |
|
} |
|
else |
|
{ |
|
*outSizeProcessed = outStream.Pos; |
|
} |
|
} |
|
|
|
LzmaEnc_Destroy( enc, &g_Alloc, &g_Alloc ); |
|
|
|
return res; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Encoding glue. Returns non-null Compressed buffer if successful. |
|
// Caller must free. |
|
//----------------------------------------------------------------------------- |
|
unsigned char *LZMA_Compress( unsigned char *pInput, |
|
unsigned int inputSize, |
|
unsigned int *pOutputSize ) |
|
{ |
|
*pOutputSize = 0; |
|
|
|
// using same work buffer calcs as the SDK 105% + 64K |
|
unsigned outSize = inputSize/20 * 21 + (1<<16); |
|
unsigned char *pOutputBuffer = (unsigned char*)malloc( outSize ); |
|
if ( !pOutputBuffer ) |
|
{ |
|
return NULL; |
|
} |
|
|
|
// compress, skipping past our header |
|
size_t compressedSize; |
|
int result = LzmaEncode( pInput, inputSize, pOutputBuffer + sizeof( lzma_header_t ), outSize - sizeof( lzma_header_t ), &compressedSize ); |
|
if ( result != SZ_OK ) |
|
{ |
|
Warning( "LZMA encode failed (%i)\n", result ); |
|
Assert( result == SZ_OK ); |
|
free( pOutputBuffer ); |
|
return NULL; |
|
} |
|
|
|
// construct our header, strip theirs |
|
lzma_header_t *pHeader = (lzma_header_t *)pOutputBuffer; |
|
pHeader->id = LZMA_ID; |
|
pHeader->actualSize = inputSize; |
|
pHeader->lzmaSize = compressedSize - LZMA_ORIGINAL_HEADER_SIZE; |
|
memcpy( pHeader->properties, pOutputBuffer + sizeof( lzma_header_t ), LZMA_PROPS_SIZE ); |
|
|
|
// shift the compressed data into place |
|
memmove( pOutputBuffer + sizeof( lzma_header_t ), |
|
pOutputBuffer + sizeof( lzma_header_t ) + LZMA_ORIGINAL_HEADER_SIZE, |
|
compressedSize - LZMA_ORIGINAL_HEADER_SIZE ); |
|
|
|
// final output size is our header plus compressed bits |
|
*pOutputSize = sizeof( lzma_header_t ) + compressedSize - LZMA_ORIGINAL_HEADER_SIZE; |
|
|
|
return pOutputBuffer; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Above, but returns null if compression would not yield a size improvement |
|
//----------------------------------------------------------------------------- |
|
unsigned char *LZMA_OpportunisticCompress( unsigned char *pInput, |
|
unsigned int inputSize, |
|
unsigned int *pOutputSize ) |
|
{ |
|
unsigned char *pRet = LZMA_Compress( pInput, inputSize, pOutputSize ); |
|
if ( *pOutputSize <= inputSize ) |
|
{ |
|
// compression got worse or stayed the same |
|
free( pRet ); |
|
return NULL; |
|
} |
|
|
|
return pRet; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Decoding glue. Returns TRUE if succesful. |
|
//----------------------------------------------------------------------------- |
|
bool LZMA_Uncompress( unsigned char *pInBuffer, |
|
unsigned char **ppOutBuffer, |
|
unsigned int *pOutSize ) |
|
{ |
|
*ppOutBuffer = NULL; |
|
*pOutSize = 0; |
|
|
|
lzma_header_t *pHeader = (lzma_header_t *)pInBuffer; |
|
if ( pHeader->id != LZMA_ID ) |
|
{ |
|
// not ours |
|
return false; |
|
} |
|
|
|
CLzmaDec state; |
|
|
|
LzmaDec_Construct(&state); |
|
|
|
if ( LzmaDec_Allocate(&state, pHeader->properties, LZMA_PROPS_SIZE, &g_Alloc) != SZ_OK ) |
|
{ |
|
return false; |
|
} |
|
|
|
unsigned char *pOutBuffer = (unsigned char *)malloc( pHeader->actualSize ); |
|
if ( !pOutBuffer ) |
|
{ |
|
LzmaDec_Free(&state, &g_Alloc); |
|
return false; |
|
} |
|
|
|
// These are in/out variables |
|
SizeT outProcessed = pHeader->actualSize; |
|
SizeT inProcessed = pHeader->lzmaSize; |
|
ELzmaStatus status; |
|
SRes result = LzmaDecode( (Byte *)pOutBuffer, &outProcessed, (Byte *)(pInBuffer + sizeof( lzma_header_t ) ), |
|
&inProcessed, (Byte *)pHeader->properties, LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &g_Alloc ); |
|
|
|
|
|
LzmaDec_Free(&state, &g_Alloc); |
|
|
|
if ( result != SZ_OK || pHeader->actualSize != outProcessed ) |
|
{ |
|
free( pOutBuffer ); |
|
return false; |
|
} |
|
|
|
*ppOutBuffer = pOutBuffer; |
|
*pOutSize = pHeader->actualSize; |
|
|
|
return true; |
|
} |
|
|
|
bool LZMA_IsCompressed( unsigned char *pInput ) |
|
{ |
|
lzma_header_t *pHeader = (lzma_header_t *)pInput; |
|
if ( pHeader && pHeader->id == LZMA_ID ) |
|
{ |
|
return true; |
|
} |
|
|
|
// unrecognized |
|
return false; |
|
} |
|
|
|
unsigned int LZMA_GetActualSize( unsigned char *pInput ) |
|
{ |
|
lzma_header_t *pHeader = (lzma_header_t *)pInput; |
|
if ( pHeader && pHeader->id == LZMA_ID ) |
|
{ |
|
return pHeader->actualSize; |
|
} |
|
|
|
// unrecognized |
|
return 0; |
|
}
|
|
|