Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Compress / Rar2Decoder.cpp
1 // Rar2Decoder.cpp\r
2 // According to unRAR license, this code may not be used to develop\r
3 // a program that creates RAR archives\r
4  \r
5 #include "StdAfx.h"\r
6 \r
7 #include "Rar2Decoder.h"\r
8 \r
9 namespace NCompress {\r
10 namespace NRar2 {\r
11 \r
12 namespace NMultimedia {\r
13 \r
14 Byte CFilter::Decode(int &channelDelta, Byte deltaByte)\r
15 {\r
16   D4 = D3;\r
17   D3 = D2;\r
18   D2 = LastDelta - D1;\r
19   D1 = LastDelta;\r
20   int predictedValue = ((8 * LastChar + K1 * D1 + K2 * D2 + K3 * D3 + K4 * D4 + K5 * channelDelta) >> 3);\r
21 \r
22   Byte realValue = (Byte)(predictedValue - deltaByte);\r
23   int i = ((int)(signed char)deltaByte) << 3;\r
24 \r
25   Dif[0] += abs(i);\r
26   Dif[1] += abs(i - D1);\r
27   Dif[2] += abs(i + D1);\r
28   Dif[3] += abs(i - D2);\r
29   Dif[4] += abs(i + D2);\r
30   Dif[5] += abs(i - D3);\r
31   Dif[6] += abs(i + D3);\r
32   Dif[7] += abs(i - D4);\r
33   Dif[8] += abs(i + D4);\r
34   Dif[9] += abs(i - channelDelta);\r
35   Dif[10] += abs(i + channelDelta);\r
36 \r
37   channelDelta = LastDelta = (signed char)(realValue - LastChar);\r
38   LastChar = realValue;\r
39 \r
40   if (((++ByteCount) & 0x1F) == 0)\r
41   {\r
42     UInt32 minDif = Dif[0];\r
43     UInt32 numMinDif = 0;\r
44     Dif[0] = 0;\r
45     for (i = 1; i < sizeof(Dif) / sizeof(Dif[0]); i++)\r
46     {\r
47       if (Dif[i] < minDif)\r
48       {\r
49         minDif = Dif[i];\r
50         numMinDif = i;\r
51       }\r
52       Dif[i] = 0;\r
53     }\r
54     switch(numMinDif)\r
55     {\r
56       case 1: if (K1 >= -16) K1--; break;\r
57       case 2: if (K1 <   16) K1++; break;\r
58       case 3: if (K2 >= -16) K2--; break;\r
59       case 4: if (K2 <   16) K2++; break;\r
60       case 5: if (K3 >= -16) K3--; break;\r
61       case 6: if (K3 <   16) K3++; break;\r
62       case 7: if (K4 >= -16) K4--; break;\r
63       case 8: if (K4 <   16) K4++; break;\r
64       case 9: if (K5 >= -16) K5--; break;\r
65       case 10:if (K5 <   16) K5++; break;\r
66     }\r
67   }\r
68   return realValue;\r
69 }\r
70 }\r
71 \r
72 static const char *kNumberErrorMessage = "Number error";\r
73 \r
74 static const UInt32 kHistorySize = 1 << 20;\r
75 \r
76 static const int kNumStats = 11;\r
77 \r
78 static const UInt32 kWindowReservSize = (1 << 22) + 256;\r
79 \r
80 CDecoder::CDecoder():\r
81   m_IsSolid(false)\r
82 {\r
83 }\r
84 \r
85 void CDecoder::InitStructures()\r
86 {\r
87   m_MmFilter.Init();\r
88   for(int i = 0; i < kNumRepDists; i++)\r
89     m_RepDists[i] = 0;\r
90   m_RepDistPtr = 0;\r
91   m_LastLength = 0;\r
92   memset(m_LastLevels, 0, kMaxTableSize);\r
93 }\r
94 \r
95 UInt32 CDecoder::ReadBits(int numBits) { return m_InBitStream.ReadBits(numBits); }\r
96 \r
97 #define RIF(x) { if (!(x)) return false; }\r
98 \r
99 bool CDecoder::ReadTables(void)\r
100 {\r
101   Byte levelLevels[kLevelTableSize];\r
102   Byte newLevels[kMaxTableSize];\r
103   m_AudioMode = (ReadBits(1) == 1);\r
104 \r
105   if (ReadBits(1) == 0)\r
106     memset(m_LastLevels, 0, kMaxTableSize);\r
107   int numLevels;\r
108   if (m_AudioMode)\r
109   {\r
110     m_NumChannels = ReadBits(2) + 1;\r
111     if (m_MmFilter.CurrentChannel >= m_NumChannels)\r
112       m_MmFilter.CurrentChannel = 0;\r
113     numLevels = m_NumChannels * kMMTableSize;\r
114   }\r
115   else\r
116     numLevels = kHeapTablesSizesSum;\r
117  \r
118   int i;\r
119   for (i = 0; i < kLevelTableSize; i++)\r
120     levelLevels[i] = (Byte)ReadBits(4);\r
121   RIF(m_LevelDecoder.SetCodeLengths(levelLevels));\r
122   i = 0;\r
123   while (i < numLevels)\r
124   {\r
125     UInt32 number = m_LevelDecoder.DecodeSymbol(&m_InBitStream);\r
126     if (number < kTableDirectLevels)\r
127     {\r
128       newLevels[i] = (Byte)((number + m_LastLevels[i]) & kLevelMask);\r
129       i++;\r
130     }\r
131     else\r
132     {\r
133       if (number == kTableLevelRepNumber)\r
134       {\r
135         int t = ReadBits(2) + 3;\r
136         for (int reps = t; reps > 0 && i < numLevels ; reps--, i++)\r
137           newLevels[i] = newLevels[i - 1];\r
138       }\r
139       else\r
140       {\r
141         int num;\r
142         if (number == kTableLevel0Number)\r
143           num = ReadBits(3) + 3;\r
144         else if (number == kTableLevel0Number2)\r
145           num = ReadBits(7) + 11;\r
146         else\r
147           return false;\r
148         for (;num > 0 && i < numLevels; num--)\r
149           newLevels[i++] = 0;\r
150       }\r
151     }\r
152   }\r
153   if (m_AudioMode)\r
154     for (i = 0; i < m_NumChannels; i++)\r
155     {\r
156       RIF(m_MMDecoders[i].SetCodeLengths(&newLevels[i * kMMTableSize]));\r
157     }\r
158   else\r
159   {\r
160     RIF(m_MainDecoder.SetCodeLengths(&newLevels[0]));\r
161     RIF(m_DistDecoder.SetCodeLengths(&newLevels[kMainTableSize]));\r
162     RIF(m_LenDecoder.SetCodeLengths(&newLevels[kMainTableSize + kDistTableSize]));\r
163   }\r
164   memcpy(m_LastLevels, newLevels, kMaxTableSize);\r
165   return true;\r
166 }\r
167 \r
168 bool CDecoder::ReadLastTables()\r
169 {\r
170   // it differs a little from pure RAR sources;\r
171   // UInt64 ttt = m_InBitStream.GetProcessedSize() + 2;\r
172   // + 2 works for: return 0xFF; in CInBuffer::ReadByte.\r
173   if (m_InBitStream.GetProcessedSize() + 7 <= m_PackSize) // test it: probably incorrect;\r
174   // if (m_InBitStream.GetProcessedSize() + 2 <= m_PackSize) // test it: probably incorrect;\r
175     if (m_AudioMode)\r
176     {\r
177       UInt32 symbol = m_MMDecoders[m_MmFilter.CurrentChannel].DecodeSymbol(&m_InBitStream);\r
178       if (symbol == 256)\r
179         return ReadTables();\r
180       if (symbol >= kMMTableSize)\r
181         return false;\r
182     }\r
183     else\r
184     {\r
185       UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);\r
186       if (number == kReadTableNumber)\r
187         return ReadTables();\r
188       if (number >= kMainTableSize)\r
189         return false;\r
190     }\r
191   return true;\r
192 }\r
193 \r
194 class CCoderReleaser\r
195 {\r
196   CDecoder *m_Coder;\r
197 public:\r
198   CCoderReleaser(CDecoder *coder): m_Coder(coder) {}\r
199   ~CCoderReleaser()\r
200   {\r
201     m_Coder->ReleaseStreams();\r
202   }\r
203 };\r
204 \r
205 bool CDecoder::DecodeMm(UInt32 pos)\r
206 {\r
207   while (pos-- > 0)\r
208   {\r
209     UInt32 symbol = m_MMDecoders[m_MmFilter.CurrentChannel].DecodeSymbol(&m_InBitStream);\r
210     if (symbol == 256)\r
211       return true;\r
212     if (symbol >= kMMTableSize)\r
213       return false;\r
214     /*\r
215     Byte byPredict = m_Predictor.Predict();\r
216     Byte byReal = (Byte)(byPredict - (Byte)symbol);\r
217     m_Predictor.Update(byReal, byPredict);\r
218     */\r
219     Byte byReal = m_MmFilter.Decode((Byte)symbol);\r
220     m_OutWindowStream.PutByte(byReal);\r
221     if (++m_MmFilter.CurrentChannel == m_NumChannels)\r
222       m_MmFilter.CurrentChannel = 0;\r
223   }\r
224   return true;\r
225 }\r
226 \r
227 bool CDecoder::DecodeLz(Int32 pos)\r
228 {\r
229   while (pos > 0)\r
230   {\r
231     UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream);\r
232     UInt32 length, distance;\r
233     if (number < 256)\r
234     {\r
235       m_OutWindowStream.PutByte(Byte(number));\r
236       pos--;\r
237       continue;\r
238     }\r
239     else if (number >= kMatchNumber)\r
240     {\r
241       number -= kMatchNumber;\r
242       length = kNormalMatchMinLen + UInt32(kLenStart[number]) +\r
243         m_InBitStream.ReadBits(kLenDirectBits[number]);\r
244       number = m_DistDecoder.DecodeSymbol(&m_InBitStream);\r
245       if (number >= kDistTableSize)\r
246         return false;\r
247       distance = kDistStart[number] + m_InBitStream.ReadBits(kDistDirectBits[number]);\r
248       if (distance >= kDistLimit3)\r
249       {\r
250         length += 2 - ((distance - kDistLimit4) >> 31);\r
251         // length++;\r
252         // if (distance >= kDistLimit4)\r
253         //  length++;\r
254       }\r
255     }\r
256     else if (number == kRepBothNumber)\r
257     {\r
258       length = m_LastLength;\r
259       if (length == 0)\r
260         return false;\r
261       distance = m_RepDists[(m_RepDistPtr + 4 - 1) & 3];\r
262     }\r
263     else if (number < kLen2Number)\r
264     {\r
265       distance = m_RepDists[(m_RepDistPtr - (number - kRepNumber + 1)) & 3];\r
266       number = m_LenDecoder.DecodeSymbol(&m_InBitStream);\r
267       if (number >= kLenTableSize)\r
268         return false;\r
269       length = 2 + kLenStart[number] + m_InBitStream.ReadBits(kLenDirectBits[number]);\r
270       if (distance >= kDistLimit2)\r
271       {\r
272         length++;\r
273         if (distance >= kDistLimit3)\r
274         {\r
275           length += 2 - ((distance - kDistLimit4) >> 31);\r
276           // length++;\r
277           // if (distance >= kDistLimit4)\r
278           //   length++;\r
279         }\r
280       }\r
281     }\r
282     else if (number < kReadTableNumber)\r
283     {\r
284       number -= kLen2Number;\r
285       distance = kLen2DistStarts[number] +\r
286         m_InBitStream.ReadBits(kLen2DistDirectBits[number]);\r
287       length = 2;\r
288     }\r
289     else if (number == kReadTableNumber)\r
290       return true;\r
291     else\r
292       return false;\r
293     m_RepDists[m_RepDistPtr++ & 3] = distance;\r
294     m_LastLength = length;\r
295     if (!m_OutWindowStream.CopyBlock(distance, length))\r
296       return false;\r
297     pos -= length;\r
298   }\r
299   return true;\r
300 }\r
301 \r
302 HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
303     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)\r
304 {\r
305   if (inSize == NULL || outSize == NULL)\r
306     return E_INVALIDARG;\r
307 \r
308   if (!m_OutWindowStream.Create(kHistorySize))\r
309     return E_OUTOFMEMORY;\r
310   if (!m_InBitStream.Create(1 << 20))\r
311     return E_OUTOFMEMORY;\r
312 \r
313   m_PackSize = *inSize;\r
314 \r
315   UInt64 pos = 0, unPackSize = *outSize;\r
316   \r
317   m_OutWindowStream.SetStream(outStream);\r
318   m_OutWindowStream.Init(m_IsSolid);\r
319   m_InBitStream.SetStream(inStream);\r
320   m_InBitStream.Init();\r
321 \r
322   CCoderReleaser coderReleaser(this);\r
323   if (!m_IsSolid)\r
324   {\r
325     InitStructures();\r
326     if (unPackSize == 0)\r
327     {\r
328       if (m_InBitStream.GetProcessedSize() + 2 <= m_PackSize) // test it: probably incorrect;\r
329         if (!ReadTables())\r
330           return S_FALSE;\r
331       return S_OK;\r
332     }\r
333     if (!ReadTables())\r
334       return S_FALSE;\r
335   }\r
336 \r
337   UInt64 startPos = m_OutWindowStream.GetProcessedSize();\r
338   while(pos < unPackSize)\r
339   {\r
340     UInt32 blockSize = 1 << 20;\r
341     if (blockSize > unPackSize - pos)\r
342       blockSize = (UInt32)(unPackSize - pos);\r
343     UInt64 blockStartPos = m_OutWindowStream.GetProcessedSize();\r
344     if (m_AudioMode)\r
345     {\r
346       if (!DecodeMm(blockSize))\r
347         return S_FALSE;\r
348     }\r
349     else\r
350     {\r
351       if (!DecodeLz((Int32)blockSize))\r
352         return S_FALSE;\r
353     }\r
354     UInt64 globalPos = m_OutWindowStream.GetProcessedSize();\r
355     pos = globalPos - blockStartPos;\r
356     if (pos < blockSize)\r
357       if (!ReadTables())\r
358         return S_FALSE;\r
359     pos = globalPos - startPos;\r
360     if (progress != 0)\r
361     {\r
362       UInt64 packSize = m_InBitStream.GetProcessedSize();\r
363       RINOK(progress->SetRatioInfo(&packSize, &pos));\r
364     }\r
365   }\r
366   if (pos > unPackSize)\r
367     return S_FALSE;\r
368 \r
369   if (!ReadLastTables())\r
370     return S_FALSE;\r
371   return m_OutWindowStream.Flush();\r
372 }\r
373 \r
374 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
375     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)\r
376 {\r
377   try { return CodeReal(inStream, outStream, inSize, outSize, progress); }\r
378   catch(const CInBufferException &e) { return e.ErrorCode; }\r
379   catch(const CLzOutWindowException &e) { return e.ErrorCode; }\r
380   catch(...) { return S_FALSE; }\r
381 }\r
382 \r
383 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)\r
384 {\r
385   if (size < 1)\r
386     return E_INVALIDARG;\r
387   m_IsSolid = (data[0] != 0);\r
388   return S_OK;\r
389 }\r
390 \r
391 }}\r