p7zip-rar/CPP/7zip/Compress/Rar5Decoder.cpp

962 lines
21 KiB
C++
Raw Normal View History

2017-10-11 12:40:22 +02:00
// 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;
}
}}