blob: 050cde21cf9893051b7032a038e4a94b68107930 [file] [log] [blame]
// Rar1Decoder.cpp
// According to unRAR license, this code may not be used to develop
// a program that creates RAR archives
#include "StdAfx.h"
#include "Rar1Decoder.h"
namespace NCompress {
namespace NRar1 {
static const unsigned kNumBits = 12;
static const Byte kShortLen1[16 * 3] =
{
0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0,0,
1,3,4,4,5,6,7,8,8,4,4,5,6,6,0,0,
1,4,4,4,5,6,7,8,8,4,4,5,6,6,4,0
};
static const Byte kShortLen2[16 * 3] =
{
0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0,0,
2,3,3,3,4,4,5,6,6,4,4,5,6,6,0,0,
2,3,3,4,4,4,5,6,6,4,4,5,6,6,4,0
};
static const Byte PosL1[kNumBits + 1] = { 0,0,2,1,2,2,4,5,4,4,8,0,224 };
static const Byte PosL2[kNumBits + 1] = { 0,0,0,5,2,2,4,5,4,4,8,2,220 };
static const Byte PosHf0[kNumBits + 1] = { 0,0,0,0,8,8,8,9,0,0,0,0,224 };
static const Byte PosHf1[kNumBits + 1] = { 0,0,0,0,0,4,40,16,16,4,0,47,130 };
static const Byte PosHf2[kNumBits + 1] = { 0,0,0,0,0,2,5,46,64,116,24,0,0 };
static const Byte PosHf3[kNumBits + 1] = { 0,0,0,0,0,0,2,14,202,33,6,0,0 };
static const Byte PosHf4[kNumBits + 1] = { 0,0,0,0,0,0,0,0,255,2,0,0,0 };
static const UInt32 kHistorySize = (1 << 16);
CDecoder::CDecoder():
_isSolid(false),
_solidAllowed(false)
{}
UInt32 CDecoder::ReadBits(unsigned numBits) { return m_InBitStream.ReadBits(numBits); }
HRESULT CDecoder::CopyBlock(UInt32 distance, UInt32 len)
{
if (len == 0)
return S_FALSE;
if (m_UnpackSize < len)
return S_FALSE;
m_UnpackSize -= len;
return m_OutWindowStream.CopyBlock(distance, len) ? S_OK : S_FALSE;
}
UInt32 CDecoder::DecodeNum(const Byte *numTab)
{
/*
{
// we can check that tables are correct
UInt32 sum = 0;
for (unsigned i = 0; i <= kNumBits; i++)
sum += ((UInt32)numTab[i] << (kNumBits - i));
if (sum != (1 << kNumBits))
throw 111;
}
*/
UInt32 val = m_InBitStream.GetValue(kNumBits);
UInt32 sum = 0;
unsigned i = 2;
for (;;)
{
const UInt32 num = numTab[i];
const UInt32 cur = num << (kNumBits - i);
if (val < cur)
break;
i++;
val -= cur;
sum += num;
}
m_InBitStream.MovePos(i);
return ((val >> (kNumBits - i)) + sum);
}
HRESULT CDecoder::ShortLZ()
{
NumHuf = 0;
if (LCount == 2)
{
if (ReadBits(1))
return CopyBlock(LastDist, LastLength);
LCount = 0;
}
UInt32 bitField = m_InBitStream.GetValue(8);
UInt32 len, dist;
{
const Byte *xors = (AvrLn1 < 37) ? kShortLen1 : kShortLen2;
const Byte *lens = xors + 16 + Buf60;
for (len = 0; ((bitField ^ xors[len]) >> (8 - lens[len])) != 0; len++);
m_InBitStream.MovePos(lens[len]);
}
if (len >= 9)
{
if (len == 9)
{
LCount++;
return CopyBlock(LastDist, LastLength);
}
LCount = 0;
if (len == 14)
{
len = DecodeNum(PosL2) + 5;
dist = 0x8000 + ReadBits(15) - 1;
LastLength = len;
LastDist = dist;
return CopyBlock(dist, len);
}
const UInt32 saveLen = len;
dist = m_RepDists[(m_RepDistPtr - (len - 9)) & 3];
len = DecodeNum(PosL1);
if (len == 0xff && saveLen == 10)
{
Buf60 ^= 16;
return S_OK;
}
if (dist >= 256)
{
len++;
if (dist >= MaxDist3 - 1)
len++;
}
}
else
{
LCount = 0;
AvrLn1 += len;
AvrLn1 -= AvrLn1 >> 4;
unsigned distancePlace = DecodeNum(PosHf2) & 0xff;
dist = ChSetA[distancePlace];
if (distancePlace != 0)
{
PlaceA[dist]--;
UInt32 lastDistance = ChSetA[(size_t)distancePlace - 1];
PlaceA[lastDistance]++;
ChSetA[distancePlace] = lastDistance;
ChSetA[(size_t)distancePlace - 1] = dist;
}
}
m_RepDists[m_RepDistPtr++] = dist;
m_RepDistPtr &= 3;
len += 2;
LastLength = len;
LastDist = dist;
return CopyBlock(dist, len);
}
HRESULT CDecoder::LongLZ()
{
UInt32 len;
UInt32 dist;
UInt32 distancePlace, newDistancePlace;
UInt32 oldAvr2, oldAvr3;
NumHuf = 0;
Nlzb += 16;
if (Nlzb > 0xff)
{
Nlzb = 0x90;
Nhfb >>= 1;
}
oldAvr2 = AvrLn2;
if (AvrLn2 >= 64)
len = DecodeNum(AvrLn2 < 122 ? PosL1 : PosL2);
else
{
UInt32 bitField = m_InBitStream.GetValue(16);
if (bitField < 0x100)
{
len = bitField;
m_InBitStream.MovePos(16);
}
else
{
for (len = 0; ((bitField << len) & 0x8000) == 0; len++);
m_InBitStream.MovePos(len + 1);
}
}
AvrLn2 += len;
AvrLn2 -= AvrLn2 >> 5;
{
const Byte *tab;
if (AvrPlcB >= 0x2900) tab = PosHf2;
else if (AvrPlcB >= 0x0700) tab = PosHf1;
else tab = PosHf0;
distancePlace = DecodeNum(tab); // [0, 256]
}
AvrPlcB += distancePlace;
AvrPlcB -= AvrPlcB >> 8;
distancePlace &= 0xff;
for (;;)
{
dist = ChSetB[distancePlace];
newDistancePlace = NToPlB[dist++ & 0xff]++;
if (dist & 0xff)
break;
CorrHuff(ChSetB,NToPlB);
}
ChSetB[distancePlace] = ChSetB[newDistancePlace];
ChSetB[newDistancePlace] = dist;
dist = ((dist & 0xff00) >> 1) | ReadBits(7);
oldAvr3 = AvrLn3;
if (len != 1 && len != 4)
{
if (len == 0 && dist <= MaxDist3)
{
AvrLn3++;
AvrLn3 -= AvrLn3 >> 8;
}
else if (AvrLn3 > 0)
AvrLn3--;
}
len += 3;
if (dist >= MaxDist3)
len++;
if (dist <= 256)
len += 8;
if (oldAvr3 > 0xb0 || (AvrPlc >= 0x2a00 && oldAvr2 < 0x40))
MaxDist3 = 0x7f00;
else
MaxDist3 = 0x2001;
m_RepDists[m_RepDistPtr++] = --dist;
m_RepDistPtr &= 3;
LastLength = len;
LastDist = dist;
return CopyBlock(dist, len);
}
HRESULT CDecoder::HuffDecode()
{
UInt32 curByte, newBytePlace;
UInt32 len;
UInt32 dist;
unsigned bytePlace;
{
const Byte *tab;
if (AvrPlc >= 0x7600) tab = PosHf4;
else if (AvrPlc >= 0x5e00) tab = PosHf3;
else if (AvrPlc >= 0x3600) tab = PosHf2;
else if (AvrPlc >= 0x0e00) tab = PosHf1;
else tab = PosHf0;
bytePlace = DecodeNum(tab); // [0, 256]
}
if (StMode)
{
if (bytePlace == 0)
{
if (ReadBits(1))
{
NumHuf = 0;
StMode = false;
return S_OK;
}
len = ReadBits(1) + 3;
dist = DecodeNum(PosHf2);
dist = (dist << 5) | ReadBits(5);
if (dist == 0)
return S_FALSE;
return CopyBlock(dist - 1, len);
}
bytePlace--; // bytePlace is [0, 255]
}
else if (NumHuf++ >= 16 && FlagsCnt == 0)
StMode = true;
bytePlace &= 0xff;
AvrPlc += bytePlace;
AvrPlc -= AvrPlc >> 8;
Nhfb += 16;
if (Nhfb > 0xff)
{
Nhfb = 0x90;
Nlzb >>= 1;
}
m_UnpackSize--;
m_OutWindowStream.PutByte((Byte)(ChSet[bytePlace] >> 8));
for (;;)
{
curByte = ChSet[bytePlace];
newBytePlace = NToPl[curByte++ & 0xff]++;
if ((curByte & 0xff) <= 0xa1)
break;
CorrHuff(ChSet, NToPl);
}
ChSet[bytePlace] = ChSet[newBytePlace];
ChSet[newBytePlace] = curByte;
return S_OK;
}
void CDecoder::GetFlagsBuf()
{
UInt32 flags, newFlagsPlace;
const UInt32 flagsPlace = DecodeNum(PosHf2); // [0, 256]
if (flagsPlace >= Z7_ARRAY_SIZE(ChSetC))
return;
for (;;)
{
flags = ChSetC[flagsPlace];
FlagBuf = flags >> 8;
newFlagsPlace = NToPlC[flags++ & 0xff]++;
if ((flags & 0xff) != 0)
break;
CorrHuff(ChSetC, NToPlC);
}
ChSetC[flagsPlace] = ChSetC[newFlagsPlace];
ChSetC[newFlagsPlace] = flags;
}
void CDecoder::CorrHuff(UInt32 *CharSet, UInt32 *NumToPlace)
{
int i;
for (i = 7; i >= 0; i--)
for (unsigned j = 0; j < 32; j++, CharSet++)
*CharSet = (*CharSet & ~(UInt32)0xff) | (unsigned)i;
memset(NumToPlace, 0, sizeof(NToPl));
for (i = 6; i >= 0; i--)
NumToPlace[i] = (7 - (unsigned)i) * 32;
}
HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo * /* progress */)
{
if (!inSize || !outSize)
return E_INVALIDARG;
if (_isSolid && !_solidAllowed)
return S_FALSE;
_solidAllowed = false;
if (!m_OutWindowStream.Create(kHistorySize))
return E_OUTOFMEMORY;
if (!m_InBitStream.Create(1 << 20))
return E_OUTOFMEMORY;
m_UnpackSize = *outSize;
m_OutWindowStream.SetStream(outStream);
m_OutWindowStream.Init(_isSolid);
m_InBitStream.SetStream(inStream);
m_InBitStream.Init();
// InitData
FlagsCnt = 0;
FlagBuf = 0;
StMode = false;
LCount = 0;
if (!_isSolid)
{
AvrPlcB = AvrLn1 = AvrLn2 = AvrLn3 = NumHuf = Buf60 = 0;
AvrPlc = 0x3500;
MaxDist3 = 0x2001;
Nhfb = Nlzb = 0x80;
{
// InitStructures
for (unsigned i = 0; i < kNumRepDists; i++)
m_RepDists[i] = 0;
m_RepDistPtr = 0;
LastLength = 0;
LastDist = 0;
}
// InitHuff
for (UInt32 i = 0; i < 256; i++)
{
Place[i] = PlaceA[i] = PlaceB[i] = i;
UInt32 c = (~i + 1) & 0xff;
PlaceC[i] = c;
ChSet[i] = ChSetB[i] = i << 8;
ChSetA[i] = i;
ChSetC[i] = c << 8;
}
memset(NToPl, 0, sizeof(NToPl));
memset(NToPlB, 0, sizeof(NToPlB));
memset(NToPlC, 0, sizeof(NToPlC));
CorrHuff(ChSetB, NToPlB);
}
if (m_UnpackSize > 0)
{
GetFlagsBuf();
FlagsCnt = 8;
}
while (m_UnpackSize != 0)
{
if (!StMode)
{
if (--FlagsCnt < 0)
{
GetFlagsBuf();
FlagsCnt = 7;
}
if (FlagBuf & 0x80)
{
FlagBuf <<= 1;
if (Nlzb > Nhfb)
{
RINOK(LongLZ())
continue;
}
}
else
{
FlagBuf <<= 1;
if (--FlagsCnt < 0)
{
GetFlagsBuf();
FlagsCnt = 7;
}
if ((FlagBuf & 0x80) == 0)
{
FlagBuf <<= 1;
RINOK(ShortLZ())
continue;
}
FlagBuf <<= 1;
if (Nlzb <= Nhfb)
{
RINOK(LongLZ())
continue;
}
}
}
RINOK(HuffDecode())
}
_solidAllowed = true;
return m_OutWindowStream.Flush();
}
Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress))
{
try { return CodeReal(inStream, outStream, inSize, outSize, progress); }
catch(const CInBufferException &e) { return e.ErrorCode; }
catch(const CLzOutWindowException &e) { return e.ErrorCode; }
catch(...) { return S_FALSE; }
}
Z7_COM7F_IMF(CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size))
{
if (size < 1)
return E_INVALIDARG;
_isSolid = ((data[0] & 1) != 0);
return S_OK;
}
}}