summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp')
-rw-r--r--src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp391
1 files changed, 391 insertions, 0 deletions
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp b/src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp
new file mode 100644
index 000000000..4e669bd64
--- /dev/null
+++ b/src/libs/7zip/unix/CPP/7zip/Compress/Rar2Decoder.cpp
@@ -0,0 +1,391 @@
+// Rar2Decoder.cpp
+// According to unRAR license, this code may not be used to develop
+// a program that creates RAR archives
+
+#include "StdAfx.h"
+
+#include "Rar2Decoder.h"
+
+namespace NCompress {
+namespace NRar2 {
+
+namespace NMultimedia {
+
+Byte CFilter::Decode(int &channelDelta, Byte deltaByte)
+{
+ D4 = D3;
+ D3 = D2;
+ D2 = LastDelta - D1;
+ D1 = LastDelta;
+ int predictedValue = ((8 * LastChar + K1 * D1 + K2 * D2 + K3 * D3 + K4 * D4 + K5 * channelDelta) >> 3);
+
+ Byte realValue = (Byte)(predictedValue - deltaByte);
+ int i = ((int)(signed char)deltaByte) << 3;
+
+ Dif[0] += abs(i);
+ Dif[1] += abs(i - D1);
+ Dif[2] += abs(i + D1);
+ Dif[3] += abs(i - D2);
+ Dif[4] += abs(i + D2);
+ Dif[5] += abs(i - D3);
+ Dif[6] += abs(i + D3);
+ Dif[7] += abs(i - D4);
+ Dif[8] += abs(i + D4);
+ Dif[9] += abs(i - channelDelta);
+ Dif[10] += abs(i + channelDelta);
+
+ channelDelta = LastDelta = (signed char)(realValue - LastChar);
+ LastChar = realValue;
+
+ if (((++ByteCount) & 0x1F) == 0)
+ {
+ UInt32 minDif = Dif[0];
+ UInt32 numMinDif = 0;
+ Dif[0] = 0;
+ for (i = 1; i < sizeof(Dif) / sizeof(Dif[0]); i++)
+ {
+ if (Dif[i] < minDif)
+ {
+ minDif = Dif[i];
+ numMinDif = i;
+ }
+ Dif[i] = 0;
+ }
+ switch(numMinDif)
+ {
+ case 1: if (K1 >= -16) K1--; break;
+ case 2: if (K1 < 16) K1++; break;
+ case 3: if (K2 >= -16) K2--; break;
+ case 4: if (K2 < 16) K2++; break;
+ case 5: if (K3 >= -16) K3--; break;
+ case 6: if (K3 < 16) K3++; break;
+ case 7: if (K4 >= -16) K4--; break;
+ case 8: if (K4 < 16) K4++; break;
+ case 9: if (K5 >= -16) K5--; break;
+ case 10:if (K5 < 16) K5++; break;
+ }
+ }
+ return realValue;
+}
+}
+
+static const char *kNumberErrorMessage = "Number error";
+
+static const UInt32 kHistorySize = 1 << 20;
+
+static const int kNumStats = 11;
+
+static const UInt32 kWindowReservSize = (1 << 22) + 256;
+
+CDecoder::CDecoder():
+ m_IsSolid(false)
+{
+}
+
+void CDecoder::InitStructures()
+{
+ m_MmFilter.Init();
+ for(int i = 0; i < kNumRepDists; i++)
+ m_RepDists[i] = 0;
+ m_RepDistPtr = 0;
+ m_LastLength = 0;
+ memset(m_LastLevels, 0, kMaxTableSize);
+}
+
+UInt32 CDecoder::ReadBits(int numBits) { return m_InBitStream.ReadBits(numBits); }
+
+#define RIF(x) { if (!(x)) return false; }
+
+bool CDecoder::ReadTables(void)
+{
+ Byte levelLevels[kLevelTableSize];
+ Byte newLevels[kMaxTableSize];
+ m_AudioMode = (ReadBits(1) == 1);
+
+ if (ReadBits(1) == 0)
+ memset(m_LastLevels, 0, kMaxTableSize);
+ int numLevels;
+ if (m_AudioMode)
+ {
+ m_NumChannels = ReadBits(2) + 1;
+ if (m_MmFilter.CurrentChannel >= m_NumChannels)
+ m_MmFilter.CurrentChannel = 0;
+ numLevels = m_NumChannels * kMMTableSize;
+ }
+ else
+ numLevels = kHeapTablesSizesSum;
+
+ int i;
+ for (i = 0; i < kLevelTableSize; i++)
+ levelLevels[i] = (Byte)ReadBits(4);
+ RIF(m_LevelDecoder.SetCodeLengths(levelLevels));
+ i = 0;
+ while (i < numLevels)
+ {
+ UInt32 number = m_LevelDecoder.DecodeSymbol(&m_InBitStream);
+ if (number < kTableDirectLevels)
+ {
+ newLevels[i] = (Byte)((number + m_LastLevels[i]) & kLevelMask);
+ i++;
+ }
+ else
+ {
+ if (number == kTableLevelRepNumber)
+ {
+ int t = ReadBits(2) + 3;
+ for (int reps = t; reps > 0 && i < numLevels ; reps--, i++)
+ newLevels[i] = newLevels[i - 1];
+ }
+ else
+ {
+ int num;
+ if (number == kTableLevel0Number)
+ num = ReadBits(3) + 3;
+ else if (number == kTableLevel0Number2)
+ num = ReadBits(7) + 11;
+ else
+ return false;
+ for (;num > 0 && i < numLevels; num--)
+ newLevels[i++] = 0;
+ }
+ }
+ }
+ if (m_AudioMode)
+ for (i = 0; i < m_NumChannels; i++)
+ {
+ RIF(m_MMDecoders[i].SetCodeLengths(&newLevels[i * kMMTableSize]));
+ }
+ else
+ {
+ RIF(m_MainDecoder.SetCodeLengths(&newLevels[0]));
+ RIF(m_DistDecoder.SetCodeLengths(&newLevels[kMainTableSize]));
+ RIF(m_LenDecoder.SetCodeLengths(&newLevels[kMainTableSize + kDistTableSize]));
+ }
+ memcpy(m_LastLevels, newLevels, kMaxTableSize);
+ return true;
+}
+
+bool CDecoder::ReadLastTables()
+{
+ // it differs a little from pure RAR sources;
+ // UInt64 ttt = m_InBitStream.GetProcessedSize() + 2;
+ // + 2 works for: return 0xFF; in CInBuffer::ReadByte.
+ if (m_InBitStream.GetProcessedSize() + 7 <= m_PackSize) // test it: probably incorrect;
+ // if (m_InBitStream.GetProcessedSize() + 2 <= m_PackSize) // test it: probably incorrect;
+ if (m_AudioMode)
+ {
+ UInt32 symbol = m_MMDecoders[m_MmFilter.CurrentChannel].DecodeSymbol(&m_InBitStream);
+ if (symbol == 256)
+ return ReadTables();
+ if (symbol >= kMMTableSize)
+ return false;
+ }
+ else
+ {
+ UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);
+ if (number == kReadTableNumber)
+ return ReadTables();
+ if (number >= kMainTableSize)
+ return false;
+ }
+ return true;
+}
+
+class CCoderReleaser
+{
+ CDecoder *m_Coder;
+public:
+ CCoderReleaser(CDecoder *coder): m_Coder(coder) {}
+ ~CCoderReleaser()
+ {
+ m_Coder->ReleaseStreams();
+ }
+};
+
+bool CDecoder::DecodeMm(UInt32 pos)
+{
+ while (pos-- > 0)
+ {
+ UInt32 symbol = m_MMDecoders[m_MmFilter.CurrentChannel].DecodeSymbol(&m_InBitStream);
+ if (symbol == 256)
+ return true;
+ if (symbol >= kMMTableSize)
+ return false;
+ /*
+ Byte byPredict = m_Predictor.Predict();
+ Byte byReal = (Byte)(byPredict - (Byte)symbol);
+ m_Predictor.Update(byReal, byPredict);
+ */
+ Byte byReal = m_MmFilter.Decode((Byte)symbol);
+ m_OutWindowStream.PutByte(byReal);
+ if (++m_MmFilter.CurrentChannel == m_NumChannels)
+ m_MmFilter.CurrentChannel = 0;
+ }
+ return true;
+}
+
+bool CDecoder::DecodeLz(Int32 pos)
+{
+ while (pos > 0)
+ {
+ UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);
+ UInt32 length, distance;
+ if (number < 256)
+ {
+ m_OutWindowStream.PutByte(Byte(number));
+ pos--;
+ continue;
+ }
+ else if (number >= kMatchNumber)
+ {
+ number -= kMatchNumber;
+ length = kNormalMatchMinLen + UInt32(kLenStart[number]) +
+ m_InBitStream.ReadBits(kLenDirectBits[number]);
+ number = m_DistDecoder.DecodeSymbol(&m_InBitStream);
+ if (number >= kDistTableSize)
+ return false;
+ distance = kDistStart[number] + m_InBitStream.ReadBits(kDistDirectBits[number]);
+ if (distance >= kDistLimit3)
+ {
+ length += 2 - ((distance - kDistLimit4) >> 31);
+ // length++;
+ // if (distance >= kDistLimit4)
+ // length++;
+ }
+ }
+ else if (number == kRepBothNumber)
+ {
+ length = m_LastLength;
+ if (length == 0)
+ return false;
+ distance = m_RepDists[(m_RepDistPtr + 4 - 1) & 3];
+ }
+ else if (number < kLen2Number)
+ {
+ distance = m_RepDists[(m_RepDistPtr - (number - kRepNumber + 1)) & 3];
+ number = m_LenDecoder.DecodeSymbol(&m_InBitStream);
+ if (number >= kLenTableSize)
+ return false;
+ length = 2 + kLenStart[number] + m_InBitStream.ReadBits(kLenDirectBits[number]);
+ if (distance >= kDistLimit2)
+ {
+ length++;
+ if (distance >= kDistLimit3)
+ {
+ length += 2 - ((distance - kDistLimit4) >> 31);
+ // length++;
+ // if (distance >= kDistLimit4)
+ // length++;
+ }
+ }
+ }
+ else if (number < kReadTableNumber)
+ {
+ number -= kLen2Number;
+ distance = kLen2DistStarts[number] +
+ m_InBitStream.ReadBits(kLen2DistDirectBits[number]);
+ length = 2;
+ }
+ else if (number == kReadTableNumber)
+ return true;
+ else
+ return false;
+ m_RepDists[m_RepDistPtr++ & 3] = distance;
+ m_LastLength = length;
+ if (!m_OutWindowStream.CopyBlock(distance, length))
+ return false;
+ pos -= length;
+ }
+ return true;
+}
+
+HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+ const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)
+{
+ if (inSize == NULL || outSize == NULL)
+ return E_INVALIDARG;
+
+ if (!m_OutWindowStream.Create(kHistorySize))
+ return E_OUTOFMEMORY;
+ if (!m_InBitStream.Create(1 << 20))
+ return E_OUTOFMEMORY;
+
+ m_PackSize = *inSize;
+
+ UInt64 pos = 0, unPackSize = *outSize;
+
+ m_OutWindowStream.SetStream(outStream);
+ m_OutWindowStream.Init(m_IsSolid);
+ m_InBitStream.SetStream(inStream);
+ m_InBitStream.Init();
+
+ CCoderReleaser coderReleaser(this);
+ if (!m_IsSolid)
+ {
+ InitStructures();
+ if (unPackSize == 0)
+ {
+ if (m_InBitStream.GetProcessedSize() + 2 <= m_PackSize) // test it: probably incorrect;
+ if (!ReadTables())
+ return S_FALSE;
+ return S_OK;
+ }
+ if (!ReadTables())
+ return S_FALSE;
+ }
+
+ UInt64 startPos = m_OutWindowStream.GetProcessedSize();
+ while(pos < unPackSize)
+ {
+ UInt32 blockSize = 1 << 20;
+ if (blockSize > unPackSize - pos)
+ blockSize = (UInt32)(unPackSize - pos);
+ UInt64 blockStartPos = m_OutWindowStream.GetProcessedSize();
+ if (m_AudioMode)
+ {
+ if (!DecodeMm(blockSize))
+ return S_FALSE;
+ }
+ else
+ {
+ if (!DecodeLz((Int32)blockSize))
+ return S_FALSE;
+ }
+ UInt64 globalPos = m_OutWindowStream.GetProcessedSize();
+ pos = globalPos - blockStartPos;
+ if (pos < blockSize)
+ if (!ReadTables())
+ return S_FALSE;
+ pos = globalPos - startPos;
+ if (progress != 0)
+ {
+ UInt64 packSize = m_InBitStream.GetProcessedSize();
+ RINOK(progress->SetRatioInfo(&packSize, &pos));
+ }
+ }
+ if (pos > unPackSize)
+ return S_FALSE;
+
+ if (!ReadLastTables())
+ return S_FALSE;
+ return m_OutWindowStream.Flush();
+}
+
+STDMETHODIMP 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; }
+}
+
+STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
+{
+ if (size < 1)
+ return E_INVALIDARG;
+ m_IsSolid = (data[0] != 0);
+ return S_OK;
+}
+
+}}