source-engine/utils/lzma/lzma.cpp

333 lines
7.9 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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;
}