mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-08 08:07:06 -06:00
311 lines
8.8 KiB
C++
Executable File
311 lines
8.8 KiB
C++
Executable File
// Archive/Cab/LZXDecoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "LZXDecoder.h"
|
|
|
|
#include "Common/Defs.h"
|
|
#include "Windows/Defs.h"
|
|
|
|
namespace NArchive {
|
|
namespace NCab {
|
|
namespace NLZX {
|
|
|
|
static const UInt32 kHistorySize = (1 << 21);
|
|
|
|
CDecoder::CDecoder()
|
|
{
|
|
m_i86TranslationOutStreamSpec = new Ci86TranslationOutStream;
|
|
m_i86TranslationOutStream = m_i86TranslationOutStreamSpec;
|
|
}
|
|
|
|
void CDecoder::ReleaseStreams()
|
|
{
|
|
m_OutWindowStream.ReleaseStream();
|
|
m_InBitStream.ReleaseStream();
|
|
m_i86TranslationOutStreamSpec->ReleaseStream();
|
|
}
|
|
|
|
STDMETHODIMP CDecoder::Flush()
|
|
{
|
|
RINOK(m_OutWindowStream.Flush());
|
|
return m_i86TranslationOutStreamSpec->Flush();
|
|
}
|
|
|
|
void CDecoder::ReadTable(Byte *lastLevels, Byte *newLevels, UInt32 numSymbols)
|
|
{
|
|
Byte levelLevels[kLevelTableSize];
|
|
UInt32 i;
|
|
for (i = 0; i < kLevelTableSize; i++)
|
|
levelLevels[i] = Byte(m_InBitStream.ReadBits(kNumBitsForPreTreeLevel));
|
|
m_LevelDecoder.SetCodeLengths(levelLevels);
|
|
for (i = 0; i < numSymbols;)
|
|
{
|
|
UInt32 number = m_LevelDecoder.DecodeSymbol(&m_InBitStream);
|
|
if (number <= kNumHuffmanBits)
|
|
newLevels[i++] = Byte((17 + lastLevels[i] - number) % (kNumHuffmanBits + 1));
|
|
else if (number == kLevelSymbolZeros || number == kLevelSymbolZerosBig)
|
|
{
|
|
int num;
|
|
if (number == kLevelSymbolZeros)
|
|
num = kLevelSymbolZerosStartValue +
|
|
m_InBitStream.ReadBits(kLevelSymbolZerosNumBits);
|
|
else
|
|
num = kLevelSymbolZerosBigStartValue +
|
|
m_InBitStream.ReadBits(kLevelSymbolZerosBigNumBits);
|
|
for (;num > 0 && i < numSymbols; num--, i++)
|
|
newLevels[i] = 0;
|
|
}
|
|
else if (number == kLevelSymbolSame)
|
|
{
|
|
int num = kLevelSymbolSameStartValue + m_InBitStream.ReadBits(kLevelSymbolSameNumBits);
|
|
UInt32 number = m_LevelDecoder.DecodeSymbol(&m_InBitStream);
|
|
if (number > kNumHuffmanBits)
|
|
throw "bad data";
|
|
Byte symbol = Byte((17 + lastLevels[i] - number) % (kNumHuffmanBits + 1));
|
|
for (; num > 0 && i < numSymbols; num--, i++)
|
|
newLevels[i] = symbol;
|
|
}
|
|
else
|
|
throw "bad data";
|
|
}
|
|
|
|
memmove(lastLevels, newLevels, numSymbols);
|
|
}
|
|
|
|
void CDecoder::ReadTables(void)
|
|
{
|
|
int blockType = m_InBitStream.ReadBits(NBlockType::kNumBits);
|
|
|
|
if (blockType != NBlockType::kVerbatim && blockType != NBlockType::kAligned &&
|
|
blockType != NBlockType::kUncompressed)
|
|
throw "bad data";
|
|
|
|
m_UnCompressedBlockSize = m_InBitStream.ReadBitsBig(kUncompressedBlockSizeNumBits);
|
|
|
|
if (blockType == NBlockType::kUncompressed)
|
|
{
|
|
m_UncompressedBlock = true;
|
|
UInt32 bitPos = m_InBitStream.GetBitPosition() % 16;
|
|
m_InBitStream.ReadBits(16 - bitPos);
|
|
for (int i = 0; i < kNumRepDistances; i++)
|
|
{
|
|
m_RepDistances[i] = 0;
|
|
for (int j = 0; j < 4; j++)
|
|
m_RepDistances[i] |= (m_InBitStream.DirectReadByte()) << (8 * j);
|
|
m_RepDistances[i]--;
|
|
}
|
|
return;
|
|
}
|
|
|
|
m_UncompressedBlock = false;
|
|
|
|
m_AlignIsUsed = (blockType == NBlockType::kAligned);
|
|
|
|
Byte newLevels[kMaxTableSize];
|
|
|
|
if (m_AlignIsUsed)
|
|
{
|
|
for(int i = 0; i < kAlignTableSize; i++)
|
|
newLevels[i] = m_InBitStream.ReadBits(kNumBitsForAlignLevel);
|
|
m_AlignDecoder.SetCodeLengths(newLevels);
|
|
}
|
|
|
|
ReadTable(m_LastByteLevels, newLevels, 256);
|
|
ReadTable(m_LastPosLenLevels, newLevels + 256, m_NumPosLenSlots);
|
|
for (int i = m_NumPosLenSlots; i < kNumPosSlotLenSlotSymbols; i++)
|
|
newLevels[256 + i] = 0;
|
|
m_MainDecoder.SetCodeLengths(newLevels);
|
|
|
|
ReadTable(m_LastLenLevels, newLevels, kNumLenSymbols);
|
|
m_LenDecoder.SetCodeLengths(newLevels);
|
|
|
|
}
|
|
|
|
class CDecoderFlusher
|
|
{
|
|
CDecoder *m_Decoder;
|
|
public:
|
|
CDecoderFlusher(CDecoder *decoder): m_Decoder(decoder) {}
|
|
~CDecoderFlusher()
|
|
{
|
|
m_Decoder->Flush();
|
|
m_Decoder->ReleaseStreams();
|
|
}
|
|
};
|
|
|
|
|
|
void CDecoder::ClearPrevLeveles()
|
|
{
|
|
memset(m_LastByteLevels, 0, 256);
|
|
memset(m_LastPosLenLevels, 0, kNumPosSlotLenSlotSymbols);
|
|
memset(m_LastLenLevels, 0, kNumLenSymbols);
|
|
};
|
|
|
|
|
|
STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream,
|
|
ISequentialOutStream *outStream,
|
|
const UInt64 *inSize, const UInt64 *outSize,
|
|
ICompressProgressInfo *progress)
|
|
{
|
|
if (outSize == NULL)
|
|
return E_INVALIDARG;
|
|
UInt64 size = *outSize;
|
|
|
|
if (!m_OutWindowStream.Create(kHistorySize))
|
|
return E_OUTOFMEMORY;
|
|
if (!m_InBitStream.Create(1 << 20))
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_OutWindowStream.SetStream(m_i86TranslationOutStream);
|
|
m_OutWindowStream.Init();
|
|
|
|
m_InBitStream.SetStream(inStream);
|
|
m_InBitStream.Init(m_ReservedSize, m_NumInDataBlocks);
|
|
|
|
CDecoderFlusher flusher(this);
|
|
|
|
UInt32 uncompressedCFDataBlockSize;
|
|
bool dataAreCorrect;
|
|
RINOK(m_InBitStream.ReadBlock(uncompressedCFDataBlockSize, dataAreCorrect));
|
|
if (!dataAreCorrect)
|
|
{
|
|
throw "Data Error";
|
|
}
|
|
UInt32 uncompressedCFDataCurrentValue = 0;
|
|
m_InBitStream.Init();
|
|
|
|
ClearPrevLeveles();
|
|
|
|
if (m_InBitStream.ReadBits(1) == 0)
|
|
m_i86TranslationOutStreamSpec->Init(outStream, false, 0);
|
|
else
|
|
{
|
|
UInt32 i86TranslationSize = m_InBitStream.ReadBits(16) << 16;
|
|
i86TranslationSize |= m_InBitStream.ReadBits(16);
|
|
m_i86TranslationOutStreamSpec->Init(outStream, true , i86TranslationSize);
|
|
}
|
|
|
|
for(int i = 0 ; i < kNumRepDistances; i++)
|
|
m_RepDistances[i] = 0;
|
|
|
|
UInt64 nowPos64 = 0;
|
|
while(nowPos64 < size)
|
|
{
|
|
if (uncompressedCFDataCurrentValue == uncompressedCFDataBlockSize)
|
|
{
|
|
bool dataAreCorrect;
|
|
RINOK(m_InBitStream.ReadBlock(uncompressedCFDataBlockSize, dataAreCorrect));
|
|
if (!dataAreCorrect)
|
|
{
|
|
throw "Data Error";
|
|
}
|
|
m_InBitStream.Init();
|
|
uncompressedCFDataCurrentValue = 0;
|
|
}
|
|
ReadTables();
|
|
UInt32 nowPos = 0;
|
|
UInt32 next = (UInt32)MyMin((UInt64)m_UnCompressedBlockSize, size - nowPos64);
|
|
if (m_UncompressedBlock)
|
|
{
|
|
while(nowPos < next)
|
|
{
|
|
m_OutWindowStream.PutByte(m_InBitStream.DirectReadByte());
|
|
nowPos++;
|
|
uncompressedCFDataCurrentValue++;
|
|
if (uncompressedCFDataCurrentValue == uncompressedCFDataBlockSize)
|
|
{
|
|
bool dataAreCorrect;
|
|
RINOK(m_InBitStream.ReadBlock(uncompressedCFDataBlockSize, dataAreCorrect));
|
|
if (!dataAreCorrect)
|
|
{
|
|
throw "Data Error";
|
|
}
|
|
// m_InBitStream.Init();
|
|
uncompressedCFDataCurrentValue = 0;
|
|
continue;
|
|
}
|
|
}
|
|
int bitPos = m_InBitStream.GetBitPosition() % 16;
|
|
if (bitPos == 8)
|
|
m_InBitStream.DirectReadByte();
|
|
m_InBitStream.Normalize();
|
|
}
|
|
else for (;nowPos < next;)
|
|
{
|
|
if (uncompressedCFDataCurrentValue == uncompressedCFDataBlockSize)
|
|
{
|
|
bool dataAreCorrect;
|
|
RINOK(m_InBitStream.ReadBlock(uncompressedCFDataBlockSize, dataAreCorrect));
|
|
if (!dataAreCorrect)
|
|
{
|
|
throw "Data Error";
|
|
}
|
|
m_InBitStream.Init();
|
|
uncompressedCFDataCurrentValue = 0;
|
|
}
|
|
UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);
|
|
if (number < 256)
|
|
{
|
|
m_OutWindowStream.PutByte(Byte(number));
|
|
nowPos++;
|
|
uncompressedCFDataCurrentValue++;
|
|
// continue;
|
|
}
|
|
else if (number < 256 + m_NumPosLenSlots)
|
|
{
|
|
UInt32 posLenSlot = number - 256;
|
|
UInt32 posSlot = posLenSlot / kNumLenSlots;
|
|
UInt32 lenSlot = posLenSlot % kNumLenSlots;
|
|
UInt32 length = 2 + lenSlot;
|
|
if (lenSlot == kNumLenSlots - 1)
|
|
length += m_LenDecoder.DecodeSymbol(&m_InBitStream);
|
|
|
|
if (posSlot < kNumRepDistances)
|
|
{
|
|
UInt32 distance = m_RepDistances[posSlot];
|
|
m_OutWindowStream.CopyBlock(distance, length);
|
|
if (posSlot != 0)
|
|
{
|
|
m_RepDistances[posSlot] = m_RepDistances[0];
|
|
m_RepDistances[0] = distance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UInt32 pos = kDistStart[posSlot];
|
|
UInt32 posDirectBits = kDistDirectBits[posSlot];
|
|
if (m_AlignIsUsed && posDirectBits >= kNumAlignBits)
|
|
{
|
|
pos += (m_InBitStream.ReadBits(posDirectBits - kNumAlignBits) << kNumAlignBits);
|
|
pos += m_AlignDecoder.DecodeSymbol(&m_InBitStream);
|
|
}
|
|
else
|
|
pos += m_InBitStream.ReadBits(posDirectBits);
|
|
UInt32 distance = pos - kNumRepDistances;
|
|
if (distance >= nowPos64 + nowPos)
|
|
throw 777123;
|
|
m_OutWindowStream.CopyBlock(distance, length);
|
|
m_RepDistances[2] = m_RepDistances[1];
|
|
m_RepDistances[1] = m_RepDistances[0];
|
|
m_RepDistances[0] = distance;
|
|
}
|
|
nowPos += length;
|
|
uncompressedCFDataCurrentValue += length;
|
|
}
|
|
else
|
|
throw 98112823;
|
|
}
|
|
if (progress != NULL)
|
|
{
|
|
UInt64 inSize = m_InBitStream.GetProcessedSize();
|
|
UInt64 outSize = nowPos64 + nowPos;
|
|
RINOK(progress->SetRatioInfo(&inSize, &outSize));
|
|
}
|
|
nowPos64 += nowPos;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
}}}
|