//========= 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 #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(p)->DoRead( buf, size ); } size_t COutStreamRam_StaticWrite(void *p, const void *buf, size_t size) { return reinterpret_cast(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; }