source2006/utils/lzma/lzma.cpp
2020-02-25 05:28:57 +02:00

367 lines
No EOL
8.8 KiB
C++

//========= Copyright © 1996-2007, Valve Corporation, All rights reserved. ============//
//
// LZMA Codec.
//
// LZMA SDK 4.43 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01)
// http://www.7-zip.org/
//
//=====================================================================================//
#include "C/Common/MyWindows.h"
#include "C/Common/MyInitGuid.h"
#include "C/Common/Types.h"
#include "C/7zip/Compress/LZMA/LZMADecoder.h"
#include "C/7zip/Compress/LZMA/LZMAEncoder.h"
#include "C/7zip/Compress/LZMA_C/LzmaDecode.h"
#include "../../public/tier1/lzmaDecoder.h"
#include "../../public/tier0/memdbgon.h"
// 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
class CInStreamRam: public ISequentialInStream, public CMyUnknownImp
{
const Byte *Data;
size_t Size;
size_t Pos;
public:
MY_UNKNOWN_IMP
void Init(const Byte *data, size_t size)
{
Data = data;
Size = size;
Pos = 0;
}
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
};
STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize)
{
UInt32 remain = Size - Pos;
if (size > remain)
size = remain;
for (UInt32 i = 0; i < size; i++)
((Byte *)data)[i] = Data[Pos + i];
Pos += size;
if (processedSize != NULL)
*processedSize = size;
return S_OK;
}
class COutStreamRam: public ISequentialOutStream, public CMyUnknownImp
{
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;
}
void SetPos(size_t pos)
{
Overflow = false;
Pos = pos;
}
MY_UNKNOWN_IMP
HRESULT WriteByte(Byte b)
{
if (Pos >= Size)
{
Overflow = true;
return E_FAIL;
}
Data[Pos++] = b;
return S_OK;
}
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
};
STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
UInt32 i;
for (i = 0; i < size && Pos < Size; i++)
Data[Pos++] = ((const Byte *)data)[i];
if (processedSize != NULL)
*processedSize = i;
if (i != size)
{
Overflow = true;
return E_FAIL;
}
return S_OK;
}
#define SZE_OK 0
#define SZE_FAIL 1
#define SZE_OUTOFMEMORY 2
#define SZE_OUT_OVERFLOW 3
int LzmaEncode(
const Byte *inBuffer,
size_t inSize,
Byte *outBuffer,
size_t outSize,
size_t *outSizeProcessed,
UInt32 dictionarySize )
{
*outSizeProcessed = 0;
const size_t kLzmaPropsSize = 5;
const size_t kMinDestSize = LZMA_ORIGINAL_HEADER_SIZE;
if ( outSize < kMinDestSize )
return SZE_OUT_OVERFLOW;
NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
CMyComPtr<ICompressCoder> encoder = encoderSpec;
// defaults
UInt32 posStateBits = 2;
UInt32 litContextBits = 3;
UInt32 litPosBits = 0;
UInt32 algorithm = 2;
UInt32 numFastBytes = 64;
wchar_t *mf = L"BT4";
PROPID propIDs[] =
{
NCoderPropID::kDictionarySize,
NCoderPropID::kPosStateBits,
NCoderPropID::kLitContextBits,
NCoderPropID::kLitPosBits,
NCoderPropID::kAlgorithm,
NCoderPropID::kNumFastBytes,
NCoderPropID::kMatchFinder,
NCoderPropID::kEndMarker,
};
const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
PROPVARIANT properties[kNumProps];
// set all properties
properties[0].vt = VT_UI4;
properties[0].ulVal = UInt32(dictionarySize);
properties[1].vt = VT_UI4;
properties[1].ulVal = UInt32(posStateBits);
properties[2].vt = VT_UI4;
properties[2].ulVal = UInt32(litContextBits);
properties[3].vt = VT_UI4;
properties[3].ulVal = UInt32(litPosBits);
properties[4].vt = VT_UI4;
properties[4].ulVal = UInt32(algorithm);
properties[5].vt = VT_UI4;
properties[5].ulVal = UInt32(numFastBytes);
properties[6].vt = VT_BSTR;
properties[6].bstrVal = (BSTR)(const wchar_t *)mf;
properties[7].vt = VT_BOOL;
properties[7].boolVal = VARIANT_FALSE;
if ( encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK )
return 1;
COutStreamRam *outStreamSpec = new COutStreamRam;
if ( outStreamSpec == 0 )
return SZE_OUTOFMEMORY;
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
CInStreamRam *inStreamSpec = new CInStreamRam;
if ( inStreamSpec == 0 )
return SZE_OUTOFMEMORY;
CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
outStreamSpec->Init( outBuffer, outSize );
if ( encoderSpec->WriteCoderProperties(outStream) != S_OK )
return SZE_OUT_OVERFLOW;
if ( outStreamSpec->Pos != kLzmaPropsSize )
return 1;
int i;
for ( i = 0; i < 8; i++ )
{
UInt64 t = (UInt64)(inSize);
if ( outStreamSpec->WriteByte((Byte)((t) >> (8 * i))) != S_OK )
return SZE_OUT_OVERFLOW;
}
UInt32 minSize = 0;
int mainResult = SZE_OK;
size_t startPos = outStreamSpec->Pos;
outStreamSpec->SetPos( startPos );
inStreamSpec->Init( inBuffer, inSize );
HRESULT lzmaResult = encoder->Code( inStream, outStream, 0, 0, 0 );
if ( lzmaResult == E_OUTOFMEMORY )
{
mainResult = SZE_OUTOFMEMORY;
}
else if ( outStreamSpec->Pos <= minSize )
{
minSize = outStreamSpec->Pos;
}
if ( outStreamSpec->Overflow )
{
mainResult = SZE_OUT_OVERFLOW;
}
else if ( lzmaResult != S_OK )
{
mainResult = SZE_FAIL;
}
*outSizeProcessed = outStreamSpec->Pos;
return mainResult;
}
//-----------------------------------------------------------------------------
// 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,
unsigned int dictionarySize )
{
*pOutputSize = 0;
if ( inputSize <= sizeof( lzma_header_t ) )
{
// pointless
return NULL;
}
dictionarySize = ( 1 << dictionarySize );
// 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
unsigned int compressedSize;
int result = LzmaEncode( pInput, inputSize, pOutputBuffer + sizeof( lzma_header_t ), outSize - sizeof( lzma_header_t ), &compressedSize, dictionarySize );
if ( result != SZE_OK )
{
free( pOutputBuffer );
return NULL;
}
if ( compressedSize - LZMA_ORIGINAL_HEADER_SIZE + sizeof( lzma_header_t ) >= inputSize )
{
// compression got worse or stayed the same
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_PROPERTIES_SIZE );
// shift the compressed data into place
memcpy( 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;
}
//-----------------------------------------------------------------------------
// 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;
}
CLzmaDecoderState state;
if ( LzmaDecodeProperties( &state.Properties, pHeader->properties, LZMA_PROPERTIES_SIZE ) != LZMA_RESULT_OK )
{
return false;
}
state.Probs = (CProb *)malloc( LzmaGetNumProbs( &state.Properties ) * sizeof( CProb ) );
if ( !state.Probs )
{
return false;
}
unsigned char *pOutBuffer = (unsigned char *)malloc( pHeader->actualSize );
if ( !pOutBuffer )
{
return false;
}
SizeT outProcessed;
SizeT inProcessed;
int result = LzmaDecode( &state, pInBuffer + sizeof( lzma_header_t ), pHeader->lzmaSize, &inProcessed, pOutBuffer, pHeader->actualSize, &outProcessed );
free ( state.Probs );
if ( result != LZMA_RESULT_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;
}