962 lines
21 KiB
C++
962 lines
21 KiB
C++
// Rar5Decoder.cpp
|
|
// According to unRAR license, this code may not be used to develop
|
|
// a program that creates RAR archives
|
|
|
|
#include "StdAfx.h"
|
|
|
|
// #include <stdio.h>
|
|
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "Rar5Decoder.h"
|
|
|
|
namespace NCompress {
|
|
namespace NRar5 {
|
|
|
|
static const size_t kInputBufSize = 1 << 20;
|
|
|
|
void CBitDecoder::Prepare2() throw()
|
|
{
|
|
const unsigned kSize = 16;
|
|
if (_buf > _bufLim)
|
|
return;
|
|
|
|
size_t rem = _bufLim - _buf;
|
|
if (rem != 0)
|
|
memmove(_bufBase, _buf, rem);
|
|
|
|
_bufLim = _bufBase + rem;
|
|
_processedSize += (_buf - _bufBase);
|
|
_buf = _bufBase;
|
|
|
|
if (!_wasFinished)
|
|
{
|
|
UInt32 processed = (UInt32)(kInputBufSize - rem);
|
|
_hres = _stream->Read(_bufLim, (UInt32)processed, &processed);
|
|
_bufLim += processed;
|
|
_wasFinished = (processed == 0);
|
|
if (_hres != S_OK)
|
|
{
|
|
_wasFinished = true;
|
|
// throw CInBufferException(result);
|
|
}
|
|
}
|
|
|
|
rem = _bufLim - _buf;
|
|
_bufCheck = _buf;
|
|
if (rem < kSize)
|
|
memset(_bufLim, 0xFF, kSize - rem);
|
|
else
|
|
_bufCheck = _bufLim - kSize;
|
|
|
|
SetCheck2();
|
|
}
|
|
|
|
|
|
enum FilterType
|
|
{
|
|
FILTER_DELTA = 0,
|
|
FILTER_E8,
|
|
FILTER_E8E9,
|
|
FILTER_ARM
|
|
};
|
|
|
|
static const size_t kWriteStep = (size_t)1 << 22;
|
|
|
|
CDecoder::CDecoder():
|
|
_window(NULL),
|
|
_winPos(0),
|
|
_winSizeAllocated(0),
|
|
_lzSize(0),
|
|
_lzEnd(0),
|
|
_writtenFileSize(0),
|
|
_dictSizeLog(0),
|
|
_isSolid(false),
|
|
_wasInit(false),
|
|
_inputBuf(NULL)
|
|
{
|
|
}
|
|
|
|
CDecoder::~CDecoder()
|
|
{
|
|
::MidFree(_window);
|
|
::MidFree(_inputBuf);
|
|
}
|
|
|
|
HRESULT CDecoder::WriteData(const Byte *data, size_t size)
|
|
{
|
|
HRESULT res = S_OK;
|
|
if (!_unpackSize_Defined || _writtenFileSize < _unpackSize)
|
|
{
|
|
size_t cur = size;
|
|
if (_unpackSize_Defined)
|
|
{
|
|
UInt64 rem = _unpackSize - _writtenFileSize;
|
|
if (cur > rem)
|
|
cur = (size_t)rem;
|
|
}
|
|
res = WriteStream(_outStream, data, cur);
|
|
if (res != S_OK)
|
|
_writeError = true;
|
|
}
|
|
_writtenFileSize += size;
|
|
return res;
|
|
}
|
|
|
|
HRESULT CDecoder::ExecuteFilter(const CFilter &f)
|
|
{
|
|
bool useDest = false;
|
|
|
|
Byte *data = _filterSrc;
|
|
UInt32 dataSize = f.Size;
|
|
|
|
// printf("\nType = %d offset = %9d size = %5d", f.Type, (unsigned)(f.Start - _lzFileStart), dataSize);
|
|
|
|
switch (f.Type)
|
|
{
|
|
case FILTER_E8:
|
|
case FILTER_E8E9:
|
|
{
|
|
// printf(" FILTER_E8");
|
|
if (dataSize > 4)
|
|
{
|
|
dataSize -= 4;
|
|
UInt32 fileOffset = (UInt32)(f.Start - _lzFileStart);
|
|
|
|
const UInt32 kFileSize = (UInt32)1 << 24;
|
|
Byte cmpMask = (Byte)(f.Type == FILTER_E8 ? 0xFF : 0xFE);
|
|
|
|
for (UInt32 curPos = 0; curPos < dataSize;)
|
|
{
|
|
curPos++;
|
|
if (((*data++) & cmpMask) == 0xE8)
|
|
{
|
|
UInt32 offset = (curPos + fileOffset) & (kFileSize - 1);
|
|
UInt32 addr = GetUi32(data);
|
|
|
|
if (addr < kFileSize)
|
|
{
|
|
SetUi32(data, addr - offset);
|
|
}
|
|
else if (addr > ((UInt32)0xFFFFFFFF - offset)) // (addr > ~(offset))
|
|
{
|
|
SetUi32(data, addr + kFileSize);
|
|
}
|
|
|
|
data += 4;
|
|
curPos += 4;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FILTER_ARM:
|
|
{
|
|
if (dataSize >= 4)
|
|
{
|
|
dataSize -= 4;
|
|
UInt32 fileOffset = (UInt32)(f.Start - _lzFileStart);
|
|
|
|
for (UInt32 curPos = 0; curPos <= dataSize; curPos += 4)
|
|
{
|
|
Byte *d = data + curPos;
|
|
if (d[3] == 0xEB)
|
|
{
|
|
UInt32 offset = d[0] | ((UInt32)d[1] << 8) | ((UInt32)d[2] << 16);
|
|
offset -= (fileOffset + curPos) >> 2;
|
|
d[0] = (Byte)offset;
|
|
d[1] = (Byte)(offset >> 8);
|
|
d[2] = (Byte)(offset >> 16);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FILTER_DELTA:
|
|
{
|
|
// printf(" channels = %d", f.Channels);
|
|
_filterDst.AllocAtLeast(dataSize);
|
|
if (!_filterDst.IsAllocated())
|
|
return E_OUTOFMEMORY;
|
|
|
|
Byte *dest = _filterDst;
|
|
UInt32 numChannels = f.Channels;
|
|
|
|
for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)
|
|
{
|
|
Byte prevByte = 0;
|
|
for (UInt32 destPos = curChannel; destPos < dataSize; destPos += numChannels)
|
|
dest[destPos] = (prevByte = (Byte)(prevByte - *data++));
|
|
}
|
|
useDest = true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
_unsupportedFilter = true;
|
|
}
|
|
|
|
return WriteData(useDest ?
|
|
(const Byte *)_filterDst :
|
|
(const Byte *)_filterSrc,
|
|
f.Size);
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::WriteBuf()
|
|
{
|
|
DeleteUnusedFilters();
|
|
|
|
for (unsigned i = 0; i < _filters.Size();)
|
|
{
|
|
const CFilter &f = _filters[i];
|
|
|
|
UInt64 blockStart = f.Start;
|
|
|
|
size_t lzAvail = (size_t)(_lzSize - _lzWritten);
|
|
if (lzAvail == 0)
|
|
break;
|
|
|
|
if (blockStart > _lzWritten)
|
|
{
|
|
UInt64 rem = blockStart - _lzWritten;
|
|
size_t size = lzAvail;
|
|
if (size > rem)
|
|
size = (size_t)rem;
|
|
if (size != 0)
|
|
{
|
|
RINOK(WriteData(_window + _winPos - lzAvail, size));
|
|
_lzWritten += size;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
UInt32 blockSize = f.Size;
|
|
size_t offset = (size_t)(_lzWritten - blockStart);
|
|
if (offset == 0)
|
|
{
|
|
_filterSrc.AllocAtLeast(blockSize);
|
|
if (!_filterSrc.IsAllocated())
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
size_t blockRem = (size_t)blockSize - offset;
|
|
size_t size = lzAvail;
|
|
if (size > blockRem)
|
|
size = blockRem;
|
|
memcpy(_filterSrc + offset, _window + _winPos - lzAvail, size);
|
|
_lzWritten += size;
|
|
offset += size;
|
|
if (offset != blockSize)
|
|
return S_OK;
|
|
|
|
_numUnusedFilters = ++i;
|
|
RINOK(ExecuteFilter(f));
|
|
}
|
|
|
|
DeleteUnusedFilters();
|
|
|
|
if (!_filters.IsEmpty())
|
|
return S_OK;
|
|
|
|
size_t lzAvail = (size_t)(_lzSize - _lzWritten);
|
|
RINOK(WriteData(_window + _winPos - lzAvail, lzAvail));
|
|
_lzWritten += lzAvail;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
static UInt32 ReadUInt32(CBitDecoder &bi)
|
|
{
|
|
unsigned numBytes = bi.ReadBits9fix(2) + 1;
|
|
UInt32 v = 0;
|
|
for (unsigned i = 0; i < numBytes; i++)
|
|
v += ((UInt32)bi.ReadBits9fix(8) << (i * 8));
|
|
return v;
|
|
}
|
|
|
|
|
|
static const unsigned MAX_UNPACK_FILTERS = 8192;
|
|
|
|
HRESULT CDecoder::AddFilter(CBitDecoder &_bitStream)
|
|
{
|
|
DeleteUnusedFilters();
|
|
|
|
if (_filters.Size() >= MAX_UNPACK_FILTERS)
|
|
{
|
|
RINOK(WriteBuf());
|
|
DeleteUnusedFilters();
|
|
if (_filters.Size() >= MAX_UNPACK_FILTERS)
|
|
{
|
|
_unsupportedFilter = true;
|
|
InitFilters();
|
|
}
|
|
}
|
|
|
|
_bitStream.Prepare();
|
|
|
|
CFilter f;
|
|
UInt32 blockStart = ReadUInt32(_bitStream);
|
|
f.Size = ReadUInt32(_bitStream);
|
|
|
|
// if (f.Size > ((UInt32)1 << 16)) _unsupportedFilter = true;
|
|
|
|
f.Type = (Byte)_bitStream.ReadBits9fix(3);
|
|
f.Channels = 0;
|
|
if (f.Type == FILTER_DELTA)
|
|
f.Channels = (Byte)(_bitStream.ReadBits9fix(5) + 1);
|
|
f.Start = _lzSize + blockStart;
|
|
|
|
if (f.Start < _filterEnd)
|
|
_unsupportedFilter = true;
|
|
else
|
|
{
|
|
_filterEnd = f.Start + f.Size;
|
|
if (f.Size != 0)
|
|
_filters.Add(f);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#define RIF(x) { if (!(x)) return S_FALSE; }
|
|
|
|
HRESULT CDecoder::ReadTables(CBitDecoder &_bitStream)
|
|
{
|
|
if (_progress)
|
|
{
|
|
UInt64 packSize = _bitStream.GetProcessedSize();
|
|
RINOK(_progress->SetRatioInfo(&packSize, &_writtenFileSize));
|
|
}
|
|
|
|
_bitStream.AlignToByte();
|
|
_bitStream.Prepare();
|
|
|
|
unsigned flags = _bitStream.ReadByteInAligned();
|
|
unsigned checkSum = _bitStream.ReadByteInAligned();
|
|
checkSum ^= flags;
|
|
|
|
UInt32 blockSize;
|
|
{
|
|
unsigned num = (flags >> 3) & 3;
|
|
if (num == 3)
|
|
return S_FALSE;
|
|
blockSize = _bitStream.ReadByteInAligned();
|
|
if (num > 0)
|
|
{
|
|
blockSize += (UInt32)_bitStream.ReadByteInAligned() << 8;
|
|
if (num > 1)
|
|
blockSize += (UInt32)_bitStream.ReadByteInAligned() << 16;
|
|
}
|
|
}
|
|
|
|
checkSum ^= blockSize ^ (blockSize >> 8) ^ (blockSize >> 16);
|
|
if ((Byte)checkSum != 0x5A)
|
|
return S_FALSE;
|
|
|
|
unsigned blockSizeBits7 = (flags & 7) + 1;
|
|
|
|
if (blockSize == 0 && blockSizeBits7 != 8)
|
|
return S_FALSE;
|
|
|
|
blockSize += (blockSizeBits7 >> 3);
|
|
blockSize--;
|
|
|
|
_bitStream._blockEndBits7 = (Byte)(blockSizeBits7 & 7);
|
|
_bitStream._blockEnd = _bitStream.GetProcessedSize_Round() + blockSize;
|
|
|
|
_bitStream.SetCheck2();
|
|
|
|
_isLastBlock = ((flags & 0x40) != 0);
|
|
|
|
if ((flags & 0x80) == 0)
|
|
{
|
|
if (!_tableWasFilled && blockSize != 0)
|
|
return S_FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
_tableWasFilled = false;
|
|
|
|
{
|
|
Byte lens2[kLevelTableSize];
|
|
|
|
for (unsigned i = 0; i < kLevelTableSize;)
|
|
{
|
|
_bitStream.Prepare();
|
|
unsigned len = (unsigned)_bitStream.ReadBits9fix(4);
|
|
if (len == 15)
|
|
{
|
|
unsigned num = (unsigned)_bitStream.ReadBits9fix(4);
|
|
if (num != 0)
|
|
{
|
|
num += 2;
|
|
num += i;
|
|
if (num > kLevelTableSize)
|
|
num = kLevelTableSize;
|
|
do
|
|
lens2[i++] = 0;
|
|
while (i < num);
|
|
continue;
|
|
}
|
|
}
|
|
lens2[i++] = (Byte)len;
|
|
}
|
|
|
|
if (_bitStream.IsBlockOverRead())
|
|
return S_FALSE;
|
|
|
|
RIF(m_LevelDecoder.Build(lens2));
|
|
}
|
|
|
|
Byte lens[kTablesSizesSum];
|
|
unsigned i = 0;
|
|
|
|
while (i < kTablesSizesSum)
|
|
{
|
|
if (_bitStream._buf >= _bitStream._bufCheck2)
|
|
{
|
|
if (_bitStream._buf >= _bitStream._bufCheck)
|
|
_bitStream.Prepare();
|
|
if (_bitStream.IsBlockOverRead())
|
|
return S_FALSE;
|
|
}
|
|
|
|
UInt32 sym = m_LevelDecoder.Decode(&_bitStream);
|
|
|
|
if (sym < 16)
|
|
lens[i++] = (Byte)sym;
|
|
else if (sym > kLevelTableSize)
|
|
return S_FALSE;
|
|
else
|
|
{
|
|
sym -= 16;
|
|
unsigned sh = ((sym & 1) << 2);
|
|
unsigned num = (unsigned)_bitStream.ReadBits9(3 + sh) + 3 + (sh << 1);
|
|
|
|
num += i;
|
|
if (num > kTablesSizesSum)
|
|
num = kTablesSizesSum;
|
|
|
|
if (sym < 2)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
// return S_FALSE;
|
|
continue; // original unRAR
|
|
}
|
|
Byte v = lens[i - 1];
|
|
do
|
|
lens[i++] = v;
|
|
while (i < num);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
lens[i++] = 0;
|
|
while (i < num);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_bitStream.IsBlockOverRead())
|
|
return S_FALSE;
|
|
if (_bitStream.InputEofError())
|
|
return S_FALSE;
|
|
|
|
RIF(m_MainDecoder.Build(&lens[0]));
|
|
RIF(m_DistDecoder.Build(&lens[kMainTableSize]));
|
|
RIF(m_AlignDecoder.Build(&lens[kMainTableSize + kDistTableSize]));
|
|
RIF(m_LenDecoder.Build(&lens[kMainTableSize + kDistTableSize + kAlignTableSize]));
|
|
|
|
_useAlignBits = false;
|
|
// _useAlignBits = true;
|
|
for (i = 0; i < kAlignTableSize; i++)
|
|
if (lens[kMainTableSize + kDistTableSize + i] != kNumAlignBits)
|
|
{
|
|
_useAlignBits = true;
|
|
break;
|
|
}
|
|
|
|
_tableWasFilled = true;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
static inline unsigned SlotToLen(CBitDecoder &_bitStream, unsigned slot)
|
|
{
|
|
if (slot < 8)
|
|
return slot + 2;
|
|
unsigned numBits = (slot >> 2) - 1;
|
|
return 2 + ((4 | (slot & 3)) << numBits) + _bitStream.ReadBits9(numBits);
|
|
}
|
|
|
|
|
|
static const UInt32 kSymbolRep = 258;
|
|
// static const unsigned kMaxMatchLen = 0x1001 + 3;
|
|
|
|
HRESULT CDecoder::DecodeLZ()
|
|
{
|
|
CBitDecoder _bitStream;
|
|
_bitStream._stream = _inStream;
|
|
_bitStream._bufBase = _inputBuf;
|
|
_bitStream.Init();
|
|
|
|
UInt32 rep0 = _reps[0];
|
|
|
|
UInt32 remLen = 0;
|
|
|
|
size_t limit;
|
|
{
|
|
size_t rem = _winSize - _winPos;
|
|
if (rem > kWriteStep)
|
|
rem = kWriteStep;
|
|
limit = _winPos + rem;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (_winPos >= limit)
|
|
{
|
|
RINOK(WriteBuf());
|
|
if (_unpackSize_Defined && _writtenFileSize > _unpackSize)
|
|
break; // return S_FALSE;
|
|
|
|
{
|
|
size_t rem = _winSize - _winPos;
|
|
|
|
if (rem == 0)
|
|
{
|
|
_winPos = 0;
|
|
rem = _winSize;
|
|
}
|
|
if (rem > kWriteStep)
|
|
rem = kWriteStep;
|
|
limit = _winPos + rem;
|
|
}
|
|
|
|
if (remLen != 0)
|
|
{
|
|
size_t winPos = _winPos;
|
|
size_t winMask = _winMask;
|
|
size_t pos = (winPos - (size_t)rep0 - 1) & winMask;
|
|
|
|
Byte *win = _window;
|
|
do
|
|
{
|
|
if (winPos >= limit)
|
|
break;
|
|
win[winPos] = win[pos];
|
|
winPos++;
|
|
pos = (pos + 1) & winMask;
|
|
}
|
|
while (--remLen != 0);
|
|
|
|
_lzSize += winPos - _winPos;
|
|
_winPos = winPos;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_bitStream._buf >= _bitStream._bufCheck2)
|
|
{
|
|
if (_bitStream.InputEofError())
|
|
break; // return S_FALSE;
|
|
if (_bitStream._buf >= _bitStream._bufCheck)
|
|
_bitStream.Prepare2();
|
|
|
|
UInt64 processed = _bitStream.GetProcessedSize_Round();
|
|
if (processed >= _bitStream._blockEnd)
|
|
{
|
|
if (processed > _bitStream._blockEnd)
|
|
break; // return S_FALSE;
|
|
{
|
|
unsigned bits7 = _bitStream.GetProcessedBits7();
|
|
if (bits7 > _bitStream._blockEndBits7)
|
|
break; // return S_FALSE;
|
|
if (bits7 == _bitStream._blockEndBits7)
|
|
{
|
|
if (_isLastBlock)
|
|
{
|
|
_reps[0] = rep0;
|
|
|
|
if (_bitStream.InputEofError())
|
|
break;
|
|
|
|
/*
|
|
// packSize can be 15 bytes larger for encrypted archive
|
|
if (_packSize_Defined && _packSize < _bitStream.GetProcessedSize())
|
|
break;
|
|
*/
|
|
|
|
return _bitStream._hres;
|
|
// break;
|
|
}
|
|
RINOK(ReadTables(_bitStream));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UInt32 sym = m_MainDecoder.Decode(&_bitStream);
|
|
|
|
if (sym < 256)
|
|
{
|
|
size_t winPos = _winPos;
|
|
_window[winPos] = (Byte)sym;
|
|
_winPos = winPos + 1;
|
|
_lzSize++;
|
|
continue;
|
|
}
|
|
|
|
UInt32 len;
|
|
|
|
if (sym < kSymbolRep + kNumReps)
|
|
{
|
|
if (sym >= kSymbolRep)
|
|
{
|
|
if (sym != kSymbolRep)
|
|
{
|
|
UInt32 dist;
|
|
if (sym == kSymbolRep + 1)
|
|
dist = _reps[1];
|
|
else
|
|
{
|
|
if (sym == kSymbolRep + 2)
|
|
dist = _reps[2];
|
|
else
|
|
{
|
|
dist = _reps[3];
|
|
_reps[3] = _reps[2];
|
|
}
|
|
_reps[2] = _reps[1];
|
|
}
|
|
_reps[1] = rep0;
|
|
rep0 = dist;
|
|
}
|
|
|
|
const UInt32 sym2 = m_LenDecoder.Decode(&_bitStream);
|
|
if (sym2 >= kLenTableSize)
|
|
break; // return S_FALSE;
|
|
len = SlotToLen(_bitStream, sym2);
|
|
}
|
|
else
|
|
{
|
|
if (sym == 256)
|
|
{
|
|
RINOK(AddFilter(_bitStream));
|
|
continue;
|
|
}
|
|
else // if (sym == 257)
|
|
{
|
|
len = _lastLen;
|
|
// if (len = 0), we ignore that symbol, like original unRAR code, but it can mean error in stream.
|
|
// if (len == 0) return S_FALSE;
|
|
if (len == 0)
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else if (sym >= kMainTableSize)
|
|
break; // return S_FALSE;
|
|
else
|
|
{
|
|
_reps[3] = _reps[2];
|
|
_reps[2] = _reps[1];
|
|
_reps[1] = rep0;
|
|
len = SlotToLen(_bitStream, sym - (kSymbolRep + kNumReps));
|
|
|
|
rep0 = m_DistDecoder.Decode(&_bitStream);
|
|
|
|
if (rep0 >= 4)
|
|
{
|
|
if (rep0 >= _numCorrectDistSymbols)
|
|
break; // return S_FALSE;
|
|
unsigned numBits = (rep0 >> 1) - 1;
|
|
rep0 = (2 | (rep0 & 1)) << numBits;
|
|
|
|
if (numBits < kNumAlignBits)
|
|
rep0 += _bitStream.ReadBits9(numBits);
|
|
else
|
|
{
|
|
len += (numBits >= 7);
|
|
len += (numBits >= 12);
|
|
len += (numBits >= 17);
|
|
|
|
if (_useAlignBits)
|
|
{
|
|
// if (numBits > kNumAlignBits)
|
|
rep0 += (_bitStream.ReadBits32(numBits - kNumAlignBits) << kNumAlignBits);
|
|
UInt32 a = m_AlignDecoder.Decode(&_bitStream);
|
|
if (a >= kAlignTableSize)
|
|
break; // return S_FALSE;
|
|
rep0 += a;
|
|
}
|
|
else
|
|
rep0 += _bitStream.ReadBits32(numBits);
|
|
}
|
|
}
|
|
}
|
|
|
|
_lastLen = len;
|
|
|
|
if (rep0 >= _lzSize)
|
|
_lzError = true;
|
|
|
|
{
|
|
UInt32 lenCur = len;
|
|
size_t winPos = _winPos;
|
|
size_t pos = (winPos - (size_t)rep0 - 1) & _winMask;
|
|
{
|
|
size_t rem = limit - winPos;
|
|
// size_t rem = _winSize - winPos;
|
|
|
|
if (lenCur > rem)
|
|
{
|
|
lenCur = (UInt32)rem;
|
|
remLen = len - lenCur;
|
|
}
|
|
}
|
|
|
|
Byte *win = _window;
|
|
_lzSize += lenCur;
|
|
_winPos = winPos + lenCur;
|
|
if (_winSize - pos >= lenCur)
|
|
{
|
|
const Byte *src = win + pos;
|
|
Byte *dest = win + winPos;
|
|
do
|
|
*dest++ = *src++;
|
|
while (--lenCur != 0);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
win[winPos] = win[pos];
|
|
winPos++;
|
|
pos = (pos + 1) & _winMask;
|
|
}
|
|
while (--lenCur != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_bitStream._hres != S_OK)
|
|
return _bitStream._hres;
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT CDecoder::CodeReal()
|
|
{
|
|
_unsupportedFilter = false;
|
|
_lzError = false;
|
|
_writeError = false;
|
|
|
|
if (!_isSolid || !_wasInit)
|
|
{
|
|
size_t clearSize = _winSize;
|
|
if (_lzSize < _winSize)
|
|
clearSize = (size_t)_lzSize;
|
|
memset(_window, 0, clearSize);
|
|
|
|
_wasInit = true;
|
|
_lzSize = 0;
|
|
_lzWritten = 0;
|
|
_winPos = 0;
|
|
|
|
for (unsigned i = 0; i < kNumReps; i++)
|
|
_reps[i] = (UInt32)0 - 1;
|
|
|
|
_lastLen = 0;
|
|
_tableWasFilled = false;
|
|
}
|
|
|
|
_isLastBlock = false;
|
|
|
|
InitFilters();
|
|
|
|
_filterEnd = 0;
|
|
_writtenFileSize = 0;
|
|
|
|
_lzFileStart = _lzSize;
|
|
_lzWritten = _lzSize;
|
|
|
|
HRESULT res = DecodeLZ();
|
|
|
|
HRESULT res2 = S_OK;
|
|
if (!_writeError && res != E_OUTOFMEMORY)
|
|
res2 = WriteBuf();
|
|
|
|
/*
|
|
if (res == S_OK)
|
|
if (InputEofError())
|
|
res = S_FALSE;
|
|
*/
|
|
|
|
if (res == S_OK)
|
|
res = res2;
|
|
|
|
if (res == S_OK && _unpackSize_Defined && _writtenFileSize != _unpackSize)
|
|
return S_FALSE;
|
|
return res;
|
|
}
|
|
|
|
|
|
// Original unRAR claims that maximum possible filter block size is (1 << 16) now,
|
|
// and (1 << 17) is minimum win size required to support filter.
|
|
// Original unRAR uses (1 << 18) for "extra safety and possible filter area size expansion"
|
|
// We can use any win size.
|
|
|
|
static const unsigned kWinSize_Log_Min = 17;
|
|
|
|
STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
|
|
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
|
|
{
|
|
try
|
|
{
|
|
if (_dictSizeLog >= sizeof(size_t) * 8)
|
|
return E_NOTIMPL;
|
|
|
|
if (!_isSolid)
|
|
_lzEnd = 0;
|
|
else
|
|
{
|
|
if (_lzSize < _lzEnd)
|
|
{
|
|
if (_window)
|
|
{
|
|
UInt64 rem = _lzEnd - _lzSize;
|
|
if (rem >= _winSize)
|
|
memset(_window, 0, _winSize);
|
|
else
|
|
{
|
|
size_t pos = (size_t)_lzSize & _winSize;
|
|
size_t rem2 = _winSize - pos;
|
|
if (rem2 > rem)
|
|
rem2 = (size_t)rem;
|
|
memset(_window + pos, 0, rem2);
|
|
rem -= rem2;
|
|
memset(_window, 0, (size_t)rem);
|
|
}
|
|
}
|
|
_lzEnd &= ((((UInt64)1) << 33) - 1);
|
|
_lzSize = _lzEnd;
|
|
_winPos = (size_t)(_lzSize & _winSize);
|
|
}
|
|
_lzEnd = _lzSize;
|
|
}
|
|
|
|
size_t newSize;
|
|
{
|
|
unsigned newSizeLog = _dictSizeLog;
|
|
if (newSizeLog < kWinSize_Log_Min)
|
|
newSizeLog = kWinSize_Log_Min;
|
|
newSize = (size_t)1 << newSizeLog;
|
|
_numCorrectDistSymbols = newSizeLog * 2;
|
|
}
|
|
|
|
if (!_window || _winSize != newSize)
|
|
{
|
|
if (!_isSolid && newSize > _winSizeAllocated)
|
|
{
|
|
::MidFree(_window);
|
|
_window = NULL;
|
|
_winSizeAllocated = 0;
|
|
}
|
|
|
|
Byte *win = _window;
|
|
if (!_window || newSize > _winSizeAllocated)
|
|
{
|
|
win = (Byte *)::MidAlloc(newSize);
|
|
if (!win)
|
|
return E_OUTOFMEMORY;
|
|
_winSizeAllocated = newSize;
|
|
memset(win, 0, newSize);
|
|
}
|
|
|
|
if (_isSolid && _window)
|
|
{
|
|
// original unRAR claims:
|
|
// "Archiving code guarantees that win size does not grow in the same solid stream",
|
|
// but the original unRAR decoder still supports such grow case.
|
|
|
|
Byte *winOld = _window;
|
|
size_t oldSize = _winSize;
|
|
size_t newMask = newSize - 1;
|
|
size_t oldMask = _winSize - 1;
|
|
size_t winPos = _winPos;
|
|
for (size_t i = 1; i < oldSize; i++) // i < oldSize) ?
|
|
win[(winPos - i) & newMask] = winOld[(winPos - i) & oldMask];
|
|
::MidFree(_window);
|
|
}
|
|
|
|
_window = win;
|
|
_winSize = newSize;
|
|
}
|
|
|
|
_winMask = _winSize - 1;
|
|
|
|
if (!_inputBuf)
|
|
{
|
|
_inputBuf = (Byte *)::MidAlloc(kInputBufSize);
|
|
if (!_inputBuf)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
_inStream = inStream;
|
|
_outStream = outStream;
|
|
|
|
/*
|
|
_packSize = 0;
|
|
_packSize_Defined = (inSize != NULL);
|
|
if (_packSize_Defined)
|
|
_packSize = *inSize;
|
|
*/
|
|
|
|
_unpackSize = 0;
|
|
_unpackSize_Defined = (outSize != NULL);
|
|
if (_unpackSize_Defined)
|
|
_unpackSize = *outSize;
|
|
|
|
if ((Int64)_unpackSize >= 0)
|
|
_lzEnd += _unpackSize;
|
|
else
|
|
_lzEnd = 0;
|
|
|
|
_progress = progress;
|
|
|
|
HRESULT res = CodeReal();
|
|
|
|
if (res != S_OK)
|
|
return res;
|
|
if (_lzError)
|
|
return S_FALSE;
|
|
if (_unsupportedFilter)
|
|
return E_NOTIMPL;
|
|
return S_OK;
|
|
}
|
|
// catch(const CInBufferException &e) { return e.ErrorCode; }
|
|
// catch(...) { return S_FALSE; }
|
|
catch(...) { return E_OUTOFMEMORY; }
|
|
// CNewException is possible here. But probably CNewException is caused
|
|
// by error in data stream.
|
|
}
|
|
|
|
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
|
|
{
|
|
if (size != 2)
|
|
return E_NOTIMPL;
|
|
_dictSizeLog = (Byte)((data[0] & 0xF) + 17);
|
|
_isSolid = ((data[1] & 1) != 0);
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|