530 lines
12 KiB
C++
530 lines
12 KiB
C++
// LzxDecoder.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include <string.h>
|
|
|
|
// #define SHOW_DEBUG_INFO
|
|
|
|
#ifdef SHOW_DEBUG_INFO
|
|
#include <stdio.h>
|
|
#define PRF(x) x
|
|
#else
|
|
#define PRF(x)
|
|
#endif
|
|
|
|
#include "../../../C/Alloc.h"
|
|
|
|
#include "LzxDecoder.h"
|
|
|
|
namespace NCompress {
|
|
namespace NLzx {
|
|
|
|
static void x86_Filter(Byte *data, UInt32 size, UInt32 processedSize, UInt32 translationSize)
|
|
{
|
|
const UInt32 kResidue = 10;
|
|
if (size <= kResidue)
|
|
return;
|
|
size -= kResidue;
|
|
|
|
Byte save = data[size + 4];
|
|
data[size + 4] = 0xE8;
|
|
|
|
for (UInt32 i = 0;;)
|
|
{
|
|
const Byte *p = data + i;
|
|
for (;;)
|
|
{
|
|
if (*p++ == 0xE8) break;
|
|
if (*p++ == 0xE8) break;
|
|
if (*p++ == 0xE8) break;
|
|
if (*p++ == 0xE8) break;
|
|
}
|
|
|
|
i = (UInt32)(p - data);
|
|
|
|
if (i > size)
|
|
break;
|
|
{
|
|
Int32 v = GetUi32(p);
|
|
Int32 pos = (Int32)((Int32)1 - (Int32)(processedSize + i));
|
|
i += 4;
|
|
if (v >= pos && v < (Int32)translationSize)
|
|
{
|
|
v += (v >= 0 ? pos : translationSize);
|
|
SetUi32(p, v);
|
|
}
|
|
}
|
|
}
|
|
|
|
data[size + 4] = save;
|
|
}
|
|
|
|
|
|
CDecoder::CDecoder(bool wimMode):
|
|
_win(NULL),
|
|
_keepHistory(false),
|
|
_skipByte(false),
|
|
_wimMode(wimMode),
|
|
_numDictBits(15),
|
|
_unpackBlockSize(0),
|
|
_x86_buf(NULL),
|
|
_x86_translationSize(0),
|
|
KeepHistoryForNext(true),
|
|
NeedAlloc(true),
|
|
_unpackedData(NULL)
|
|
{
|
|
}
|
|
|
|
CDecoder::~CDecoder()
|
|
{
|
|
if (NeedAlloc)
|
|
::MidFree(_win);
|
|
::MidFree(_x86_buf);
|
|
}
|
|
|
|
HRESULT CDecoder::Flush()
|
|
{
|
|
if (_x86_translationSize != 0)
|
|
{
|
|
Byte *destData = _win + _writePos;
|
|
UInt32 curSize = _pos - _writePos;
|
|
if (KeepHistoryForNext)
|
|
{
|
|
if (!_x86_buf)
|
|
{
|
|
// we must change it to support another chunk sizes
|
|
const size_t kChunkSize = (size_t)1 << 15;
|
|
if (curSize > kChunkSize)
|
|
return E_NOTIMPL;
|
|
_x86_buf = (Byte *)::MidAlloc(kChunkSize);
|
|
if (!_x86_buf)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
memcpy(_x86_buf, destData, curSize);
|
|
_unpackedData = _x86_buf;
|
|
destData = _x86_buf;
|
|
}
|
|
x86_Filter(destData, (UInt32)curSize, _x86_processedSize, _x86_translationSize);
|
|
_x86_processedSize += (UInt32)curSize;
|
|
if (_x86_processedSize >= ((UInt32)1 << 30))
|
|
_x86_translationSize = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
UInt32 CDecoder::ReadBits(unsigned numBits) { return _bitStream.ReadBitsSmall(numBits); }
|
|
|
|
#define RIF(x) { if (!(x)) return false; }
|
|
|
|
bool CDecoder::ReadTable(Byte *levels, unsigned numSymbols)
|
|
{
|
|
{
|
|
Byte levels2[kLevelTableSize];
|
|
for (unsigned i = 0; i < kLevelTableSize; i++)
|
|
levels2[i] = (Byte)ReadBits(kNumLevelBits);
|
|
RIF(_levelDecoder.Build(levels2));
|
|
}
|
|
|
|
unsigned i = 0;
|
|
do
|
|
{
|
|
UInt32 sym = _levelDecoder.Decode(&_bitStream);
|
|
if (sym <= kNumHuffmanBits)
|
|
{
|
|
int delta = (int)levels[i] - (int)sym;
|
|
delta += (delta < 0) ? (kNumHuffmanBits + 1) : 0;
|
|
levels[i++] = (Byte)delta;
|
|
continue;
|
|
}
|
|
|
|
unsigned num;
|
|
Byte symbol;
|
|
|
|
if (sym < kLevelSym_Same)
|
|
{
|
|
sym -= kLevelSym_Zero1;
|
|
num = kLevelSym_Zero1_Start + ((unsigned)sym << kLevelSym_Zero1_NumBits) +
|
|
(unsigned)ReadBits(kLevelSym_Zero1_NumBits + sym);
|
|
symbol = 0;
|
|
}
|
|
else if (sym == kLevelSym_Same)
|
|
{
|
|
num = kLevelSym_Same_Start + (unsigned)ReadBits(kLevelSym_Same_NumBits);
|
|
sym = _levelDecoder.Decode(&_bitStream);
|
|
if (sym > kNumHuffmanBits)
|
|
return false;
|
|
int delta = (int)levels[i] - (int)sym;
|
|
delta += (delta < 0) ? (kNumHuffmanBits + 1) : 0;
|
|
symbol = (Byte)delta;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
unsigned limit = i + num;
|
|
if (limit > numSymbols)
|
|
return false;
|
|
|
|
do
|
|
levels[i++] = symbol;
|
|
while (i < limit);
|
|
}
|
|
while (i < numSymbols);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CDecoder::ReadTables(void)
|
|
{
|
|
{
|
|
if (_skipByte)
|
|
{
|
|
if (_bitStream.DirectReadByte() != 0)
|
|
return false;
|
|
}
|
|
|
|
_bitStream.NormalizeBig();
|
|
|
|
unsigned blockType = (unsigned)ReadBits(kBlockType_NumBits);
|
|
if (blockType > kBlockType_Uncompressed)
|
|
return false;
|
|
|
|
_unpackBlockSize = (1 << 15);
|
|
if (!_wimMode || ReadBits(1) == 0)
|
|
{
|
|
_unpackBlockSize = ReadBits(16);
|
|
// wimlib supports chunks larger than 32KB (unsupported my MS wim).
|
|
if (!_wimMode || _numDictBits >= 16)
|
|
{
|
|
_unpackBlockSize <<= 8;
|
|
_unpackBlockSize |= ReadBits(8);
|
|
}
|
|
}
|
|
|
|
PRF(printf("\nBlockSize = %6d %s ", _unpackBlockSize, (_pos & 1) ? "@@@" : " "));
|
|
|
|
_isUncompressedBlock = (blockType == kBlockType_Uncompressed);
|
|
|
|
_skipByte = false;
|
|
|
|
if (_isUncompressedBlock)
|
|
{
|
|
_skipByte = ((_unpackBlockSize & 1) != 0);
|
|
|
|
PRF(printf(" UncompressedBlock "));
|
|
if (_unpackBlockSize & 1)
|
|
{
|
|
PRF(printf(" ######### "));
|
|
}
|
|
|
|
if (!_bitStream.PrepareUncompressed())
|
|
return false;
|
|
if (_bitStream.GetRem() < kNumReps * 4)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < kNumReps; i++)
|
|
{
|
|
UInt32 rep = _bitStream.ReadUInt32();
|
|
if (rep > _winSize)
|
|
return false;
|
|
_reps[i] = rep;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
_numAlignBits = 64;
|
|
|
|
if (blockType == kBlockType_Aligned)
|
|
{
|
|
Byte levels[kAlignTableSize];
|
|
_numAlignBits = kNumAlignBits;
|
|
for (unsigned i = 0; i < kAlignTableSize; i++)
|
|
levels[i] = (Byte)ReadBits(kNumAlignLevelBits);
|
|
RIF(_alignDecoder.Build(levels));
|
|
}
|
|
}
|
|
|
|
RIF(ReadTable(_mainLevels, 256));
|
|
RIF(ReadTable(_mainLevels + 256, _numPosLenSlots));
|
|
unsigned end = 256 + _numPosLenSlots;
|
|
memset(_mainLevels + end, 0, kMainTableSize - end);
|
|
RIF(_mainDecoder.Build(_mainLevels));
|
|
RIF(ReadTable(_lenLevels, kNumLenSymbols));
|
|
return _lenDecoder.Build(_lenLevels);
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::CodeSpec(UInt32 curSize)
|
|
{
|
|
if (!_keepHistory || !_isUncompressedBlock)
|
|
_bitStream.NormalizeBig();
|
|
|
|
if (!_keepHistory)
|
|
{
|
|
_skipByte = false;
|
|
_unpackBlockSize = 0;
|
|
|
|
memset(_mainLevels, 0, kMainTableSize);
|
|
memset(_lenLevels, 0, kNumLenSymbols);
|
|
|
|
{
|
|
_x86_translationSize = 12000000;
|
|
if (!_wimMode)
|
|
{
|
|
_x86_translationSize = 0;
|
|
if (ReadBits(1) != 0)
|
|
{
|
|
UInt32 v = ReadBits(16) << 16;
|
|
v |= ReadBits(16);
|
|
_x86_translationSize = v;
|
|
}
|
|
}
|
|
|
|
_x86_processedSize = 0;
|
|
}
|
|
|
|
_reps[0] = 1;
|
|
_reps[1] = 1;
|
|
_reps[2] = 1;
|
|
}
|
|
|
|
while (curSize > 0)
|
|
{
|
|
if (_bitStream.WasExtraReadError_Fast())
|
|
return S_FALSE;
|
|
|
|
if (_unpackBlockSize == 0)
|
|
{
|
|
if (!ReadTables())
|
|
return S_FALSE;
|
|
continue;
|
|
}
|
|
|
|
UInt32 next = _unpackBlockSize;
|
|
if (next > curSize)
|
|
next = curSize;
|
|
|
|
if (_isUncompressedBlock)
|
|
{
|
|
size_t rem = _bitStream.GetRem();
|
|
if (rem == 0)
|
|
return S_FALSE;
|
|
if (next > rem)
|
|
next = (UInt32)rem;
|
|
_bitStream.CopyTo(_win + _pos, next);
|
|
_pos += next;
|
|
curSize -= next;
|
|
_unpackBlockSize -= next;
|
|
|
|
/* we don't know where skipByte can be placed, if it's end of chunk:
|
|
1) in current chunk - there are such cab archives, if chunk is last
|
|
2) in next chunk - are there such archives ? */
|
|
|
|
if (_skipByte
|
|
&& _unpackBlockSize == 0
|
|
&& curSize == 0
|
|
&& _bitStream.IsOneDirectByteLeft())
|
|
{
|
|
_skipByte = false;
|
|
if (_bitStream.DirectReadByte() != 0)
|
|
return S_FALSE;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
curSize -= next;
|
|
_unpackBlockSize -= next;
|
|
|
|
Byte *win = _win;
|
|
|
|
while (next > 0)
|
|
{
|
|
if (_bitStream.WasExtraReadError_Fast())
|
|
return S_FALSE;
|
|
|
|
UInt32 sym = _mainDecoder.Decode(&_bitStream);
|
|
|
|
if (sym < 256)
|
|
{
|
|
win[_pos++] = (Byte)sym;
|
|
next--;
|
|
continue;
|
|
}
|
|
{
|
|
sym -= 256;
|
|
if (sym >= _numPosLenSlots)
|
|
return S_FALSE;
|
|
UInt32 posSlot = sym / kNumLenSlots;
|
|
UInt32 lenSlot = sym % kNumLenSlots;
|
|
UInt32 len = kMatchMinLen + lenSlot;
|
|
|
|
if (lenSlot == kNumLenSlots - 1)
|
|
{
|
|
UInt32 lenTemp = _lenDecoder.Decode(&_bitStream);
|
|
if (lenTemp >= kNumLenSymbols)
|
|
return S_FALSE;
|
|
len = kMatchMinLen + kNumLenSlots - 1 + lenTemp;
|
|
}
|
|
|
|
UInt32 dist;
|
|
|
|
if (posSlot < kNumReps)
|
|
{
|
|
dist = _reps[posSlot];
|
|
_reps[posSlot] = _reps[0];
|
|
_reps[0] = dist;
|
|
}
|
|
else
|
|
{
|
|
unsigned numDirectBits;
|
|
|
|
if (posSlot < kNumPowerPosSlots)
|
|
{
|
|
numDirectBits = (unsigned)(posSlot >> 1) - 1;
|
|
dist = ((2 | (posSlot & 1)) << numDirectBits);
|
|
}
|
|
else
|
|
{
|
|
numDirectBits = kNumLinearPosSlotBits;
|
|
dist = ((posSlot - 0x22) << kNumLinearPosSlotBits);
|
|
}
|
|
|
|
if (numDirectBits >= _numAlignBits)
|
|
{
|
|
dist += (_bitStream.ReadBitsSmall(numDirectBits - kNumAlignBits) << kNumAlignBits);
|
|
UInt32 alignTemp = _alignDecoder.Decode(&_bitStream);
|
|
if (alignTemp >= kAlignTableSize)
|
|
return S_FALSE;
|
|
dist += alignTemp;
|
|
}
|
|
else
|
|
dist += _bitStream.ReadBitsBig(numDirectBits);
|
|
|
|
dist -= kNumReps - 1;
|
|
_reps[2] = _reps[1];
|
|
_reps[1] = _reps[0];
|
|
_reps[0] = dist;
|
|
}
|
|
|
|
if (len > next)
|
|
return S_FALSE;
|
|
|
|
if (dist > _pos && !_overDict)
|
|
return S_FALSE;
|
|
|
|
Byte *dest = win + _pos;
|
|
const UInt32 mask = (_winSize - 1);
|
|
UInt32 srcPos = (_pos - dist) & mask;
|
|
|
|
next -= len;
|
|
|
|
if (len > _winSize - srcPos)
|
|
{
|
|
_pos += len;
|
|
do
|
|
{
|
|
*dest++ = win[srcPos++];
|
|
srcPos &= mask;
|
|
}
|
|
while (--len);
|
|
}
|
|
else
|
|
{
|
|
ptrdiff_t src = (ptrdiff_t)srcPos - (ptrdiff_t)_pos;
|
|
_pos += len;
|
|
const Byte *lim = dest + len;
|
|
*(dest) = *(dest + src);
|
|
dest++;
|
|
do
|
|
*(dest) = *(dest + src);
|
|
while (++dest != lim);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!_bitStream.WasFinishedOK())
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::Code(const Byte *inData, size_t inSize, UInt32 outSize)
|
|
{
|
|
if (!_keepHistory)
|
|
{
|
|
_pos = 0;
|
|
_overDict = false;
|
|
}
|
|
else if (_pos == _winSize)
|
|
{
|
|
_pos = 0;
|
|
_overDict = true;
|
|
}
|
|
|
|
_writePos = _pos;
|
|
_unpackedData = _win + _pos;
|
|
|
|
if (outSize > _winSize - _pos)
|
|
return S_FALSE;
|
|
|
|
PRF(printf("\ninSize = %d", inSize));
|
|
if ((inSize & 1) != 0)
|
|
{
|
|
PRF(printf(" ---------"));
|
|
}
|
|
|
|
if (inSize < 1)
|
|
return S_FALSE;
|
|
|
|
_bitStream.Init(inData, inSize);
|
|
|
|
HRESULT res = CodeSpec(outSize);
|
|
HRESULT res2 = Flush();
|
|
return (res == S_OK ? res2 : res);
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::SetParams2(unsigned numDictBits)
|
|
{
|
|
_numDictBits = numDictBits;
|
|
if (numDictBits < kNumDictBits_Min || numDictBits > kNumDictBits_Max)
|
|
return E_INVALIDARG;
|
|
unsigned numPosSlots = (numDictBits < 20) ?
|
|
numDictBits * 2 :
|
|
34 + ((unsigned)1 << (numDictBits - 17));
|
|
_numPosLenSlots = numPosSlots * kNumLenSlots;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::SetParams_and_Alloc(unsigned numDictBits)
|
|
{
|
|
RINOK(SetParams2(numDictBits));
|
|
|
|
UInt32 newWinSize = (UInt32)1 << numDictBits;
|
|
|
|
if (NeedAlloc)
|
|
{
|
|
if (!_win || newWinSize != _winSize)
|
|
{
|
|
::MidFree(_win);
|
|
_winSize = 0;
|
|
_win = (Byte *)::MidAlloc(newWinSize);
|
|
if (!_win)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
_winSize = (UInt32)newWinSize;
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|