1 // Rar5Decoder.cpp
2 // According to unRAR license, this code may not be used to develop
3 // a program that creates RAR archives
4 
5 #include "StdAfx.h"
6 
7 // #include <stdio.h>
8 
9 #include "../Common/StreamUtils.h"
10 
11 #include "Rar5Decoder.h"
12 
13 namespace NCompress {
14 namespace NRar5 {
15 
16 static const size_t kInputBufSize = 1 << 20;
17 
Prepare2()18 void CBitDecoder::Prepare2() throw()
19 {
20   const unsigned kSize = 16;
21   if (_buf > _bufLim)
22     return;
23 
24   size_t rem = _bufLim - _buf;
25   if (rem != 0)
26     memmove(_bufBase, _buf, rem);
27 
28   _bufLim = _bufBase + rem;
29   _processedSize += (_buf - _bufBase);
30   _buf = _bufBase;
31 
32   if (!_wasFinished)
33   {
34     UInt32 processed = (UInt32)(kInputBufSize - rem);
35     _hres = _stream->Read(_bufLim, (UInt32)processed, &processed);
36     _bufLim += processed;
37     _wasFinished = (processed == 0);
38     if (_hres != S_OK)
39     {
40       _wasFinished = true;
41       // throw CInBufferException(result);
42     }
43   }
44 
45   rem = _bufLim - _buf;
46   _bufCheck = _buf;
47   if (rem < kSize)
48     memset(_bufLim, 0xFF, kSize - rem);
49   else
50     _bufCheck = _bufLim - kSize;
51 
52   SetCheck2();
53 }
54 
55 
56 enum FilterType
57 {
58   FILTER_DELTA = 0,
59   FILTER_E8,
60   FILTER_E8E9,
61   FILTER_ARM
62 };
63 
64 static const size_t kWriteStep = (size_t)1 << 22;
65 
CDecoder()66 CDecoder::CDecoder():
67     _isSolid(false),
68     _solidAllowed(false),
69     _wasInit(false),
70     _dictSizeLog(0),
71     _window(NULL),
72     _winPos(0),
73     _lzSize(0),
74     _lzEnd(0),
75     _writtenFileSize(0),
76     _winSizeAllocated(0),
77     _inputBuf(NULL)
78 {
79 }
80 
~CDecoder()81 CDecoder::~CDecoder()
82 {
83   ::MidFree(_window);
84   ::MidFree(_inputBuf);
85 }
86 
WriteData(const Byte * data,size_t size)87 HRESULT CDecoder::WriteData(const Byte *data, size_t size)
88 {
89   HRESULT res = S_OK;
90   if (!_unpackSize_Defined || _writtenFileSize < _unpackSize)
91   {
92     size_t cur = size;
93     if (_unpackSize_Defined)
94     {
95       UInt64 rem = _unpackSize - _writtenFileSize;
96       if (cur > rem)
97         cur = (size_t)rem;
98     }
99     res = WriteStream(_outStream, data, cur);
100     if (res != S_OK)
101       _writeError = true;
102   }
103   _writtenFileSize += size;
104   return res;
105 }
106 
ExecuteFilter(const CFilter & f)107 HRESULT CDecoder::ExecuteFilter(const CFilter &f)
108 {
109   bool useDest = false;
110 
111   Byte *data = _filterSrc;
112   UInt32 dataSize = f.Size;
113 
114   // printf("\nType = %d offset = %9d  size = %5d", f.Type, (unsigned)(f.Start - _lzFileStart), dataSize);
115 
116   switch (f.Type)
117   {
118     case FILTER_E8:
119     case FILTER_E8E9:
120     {
121       // printf("  FILTER_E8");
122       if (dataSize > 4)
123       {
124         dataSize -= 4;
125         UInt32 fileOffset = (UInt32)(f.Start - _lzFileStart);
126 
127         const UInt32 kFileSize = (UInt32)1 << 24;
128         Byte cmpMask = (Byte)(f.Type == FILTER_E8 ? 0xFF : 0xFE);
129 
130         for (UInt32 curPos = 0; curPos < dataSize;)
131         {
132           curPos++;
133           if (((*data++) & cmpMask) == 0xE8)
134           {
135             UInt32 offset = (curPos + fileOffset) & (kFileSize - 1);
136             UInt32 addr = GetUi32(data);
137 
138             if (addr < kFileSize)
139             {
140               SetUi32(data, addr - offset);
141             }
142             else if (addr > ((UInt32)0xFFFFFFFF - offset)) // (addr > ~(offset))
143             {
144               SetUi32(data, addr + kFileSize);
145             }
146 
147             data += 4;
148             curPos += 4;
149           }
150         }
151       }
152       break;
153     }
154 
155     case FILTER_ARM:
156     {
157       if (dataSize >= 4)
158       {
159         dataSize -= 4;
160         UInt32 fileOffset = (UInt32)(f.Start - _lzFileStart);
161 
162         for (UInt32 curPos = 0; curPos <= dataSize; curPos += 4)
163         {
164           Byte *d = data + curPos;
165           if (d[3] == 0xEB)
166           {
167             UInt32 offset = d[0] | ((UInt32)d[1] << 8) | ((UInt32)d[2] << 16);
168             offset -= (fileOffset + curPos) >> 2;
169             d[0] = (Byte)offset;
170             d[1] = (Byte)(offset >> 8);
171             d[2] = (Byte)(offset >> 16);
172           }
173         }
174       }
175       break;
176     }
177 
178     case FILTER_DELTA:
179     {
180       // printf("  channels = %d", f.Channels);
181       _filterDst.AllocAtLeast(dataSize);
182       if (!_filterDst.IsAllocated())
183         return E_OUTOFMEMORY;
184 
185       Byte *dest = _filterDst;
186       UInt32 numChannels = f.Channels;
187 
188       for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)
189       {
190         Byte prevByte = 0;
191         for (UInt32 destPos = curChannel; destPos < dataSize; destPos += numChannels)
192           dest[destPos] = (prevByte = (Byte)(prevByte - *data++));
193       }
194       useDest = true;
195       break;
196     }
197 
198     default:
199       _unsupportedFilter = true;
200       memset(_filterSrc, 0, f.Size);
201       // return S_OK;  // unrar
202   }
203 
204   return WriteData(useDest ?
205       (const Byte *)_filterDst :
206       (const Byte *)_filterSrc,
207       f.Size);
208 }
209 
210 
WriteBuf()211 HRESULT CDecoder::WriteBuf()
212 {
213   DeleteUnusedFilters();
214 
215   for (unsigned i = 0; i < _filters.Size();)
216   {
217     const CFilter &f = _filters[i];
218 
219     UInt64 blockStart = f.Start;
220 
221     size_t lzAvail = (size_t)(_lzSize - _lzWritten);
222     if (lzAvail == 0)
223       break;
224 
225     if (blockStart > _lzWritten)
226     {
227       UInt64 rem = blockStart - _lzWritten;
228       size_t size = lzAvail;
229       if (size > rem)
230         size = (size_t)rem;
231       if (size != 0)
232       {
233         RINOK(WriteData(_window + _winPos - lzAvail, size));
234         _lzWritten += size;
235       }
236       continue;
237     }
238 
239     UInt32 blockSize = f.Size;
240     size_t offset = (size_t)(_lzWritten - blockStart);
241     if (offset == 0)
242     {
243       _filterSrc.AllocAtLeast(blockSize);
244       if (!_filterSrc.IsAllocated())
245         return E_OUTOFMEMORY;
246     }
247 
248     size_t blockRem = (size_t)blockSize - offset;
249     size_t size = lzAvail;
250     if (size > blockRem)
251       size = blockRem;
252     memcpy(_filterSrc + offset, _window + _winPos - lzAvail, size);
253     _lzWritten += size;
254     offset += size;
255     if (offset != blockSize)
256       return S_OK;
257 
258     _numUnusedFilters = ++i;
259     RINOK(ExecuteFilter(f));
260   }
261 
262   DeleteUnusedFilters();
263 
264   if (!_filters.IsEmpty())
265     return S_OK;
266 
267   size_t lzAvail = (size_t)(_lzSize - _lzWritten);
268   RINOK(WriteData(_window + _winPos - lzAvail, lzAvail));
269   _lzWritten += lzAvail;
270   return S_OK;
271 }
272 
273 
ReadUInt32(CBitDecoder & bi)274 static UInt32 ReadUInt32(CBitDecoder &bi)
275 {
276   unsigned numBytes = bi.ReadBits9fix(2) + 1;
277   UInt32 v = 0;
278   for (unsigned i = 0; i < numBytes; i++)
279     v += ((UInt32)bi.ReadBits9fix(8) << (i * 8));
280   return v;
281 }
282 
283 
284 static const unsigned MAX_UNPACK_FILTERS = 8192;
285 
AddFilter(CBitDecoder & _bitStream)286 HRESULT CDecoder::AddFilter(CBitDecoder &_bitStream)
287 {
288   DeleteUnusedFilters();
289 
290   if (_filters.Size() >= MAX_UNPACK_FILTERS)
291   {
292     RINOK(WriteBuf());
293     DeleteUnusedFilters();
294     if (_filters.Size() >= MAX_UNPACK_FILTERS)
295     {
296       _unsupportedFilter = true;
297       InitFilters();
298     }
299   }
300 
301   _bitStream.Prepare();
302 
303   CFilter f;
304   UInt32 blockStart = ReadUInt32(_bitStream);
305   f.Size = ReadUInt32(_bitStream);
306 
307   if (f.Size > ((UInt32)1 << 22))
308   {
309     _unsupportedFilter = true;
310     f.Size = 0;  // unrar 5.5.5
311   }
312 
313   f.Type = (Byte)_bitStream.ReadBits9fix(3);
314   f.Channels = 0;
315   if (f.Type == FILTER_DELTA)
316     f.Channels = (Byte)(_bitStream.ReadBits9fix(5) + 1);
317   f.Start = _lzSize + blockStart;
318 
319   if (f.Start < _filterEnd)
320     _unsupportedFilter = true;
321   else
322   {
323     _filterEnd = f.Start + f.Size;
324     if (f.Size != 0)
325       _filters.Add(f);
326   }
327 
328   return S_OK;
329 }
330 
331 
332 #define RIF(x) { if (!(x)) return S_FALSE; }
333 
ReadTables(CBitDecoder & _bitStream)334 HRESULT CDecoder::ReadTables(CBitDecoder &_bitStream)
335 {
336   if (_progress)
337   {
338     const UInt64 packSize = _bitStream.GetProcessedSize();
339     RINOK(_progress->SetRatioInfo(&packSize, &_writtenFileSize));
340   }
341 
342   _bitStream.AlignToByte();
343   _bitStream.Prepare();
344 
345   {
346     unsigned flags = _bitStream.ReadByteInAligned();
347     unsigned checkSum = _bitStream.ReadByteInAligned();
348     checkSum ^= flags;
349     unsigned num = (flags >> 3) & 3;
350     if (num == 3)
351       return S_FALSE;
352     UInt32 blockSize = _bitStream.ReadByteInAligned();
353     checkSum ^= blockSize;
354 
355     if (num != 0)
356     {
357       unsigned b = _bitStream.ReadByteInAligned();
358       checkSum ^= b;
359       blockSize += (UInt32)b << 8;
360       if (num > 1)
361       {
362         b = _bitStream.ReadByteInAligned();
363         checkSum ^= b;
364         blockSize += (UInt32)b << 16;
365       }
366     }
367 
368     if (checkSum != 0x5A)
369       return S_FALSE;
370 
371     unsigned blockSizeBits7 = (flags & 7) + 1;
372     blockSize += (blockSizeBits7 >> 3);
373     if (blockSize == 0)
374       return S_FALSE;
375     blockSize--;
376     blockSizeBits7 &= 7;
377 
378     _bitStream._blockEndBits7 = (Byte)blockSizeBits7;
379     _bitStream._blockEnd = _bitStream.GetProcessedSize_Round() + blockSize;
380 
381     _bitStream.SetCheck2();
382 
383     _isLastBlock = ((flags & 0x40) != 0);
384 
385     if ((flags & 0x80) == 0)
386     {
387       if (!_tableWasFilled)
388         if (blockSize != 0 || blockSizeBits7 != 0)
389           return S_FALSE;
390       return S_OK;
391     }
392 
393     _tableWasFilled = false;
394   }
395 
396   {
397     Byte lens2[kLevelTableSize];
398 
399     for (unsigned i = 0; i < kLevelTableSize;)
400     {
401       _bitStream.Prepare();
402       unsigned len = (unsigned)_bitStream.ReadBits9fix(4);
403       if (len == 15)
404       {
405         unsigned num = (unsigned)_bitStream.ReadBits9fix(4);
406         if (num != 0)
407         {
408           num += 2;
409           num += i;
410           if (num > kLevelTableSize)
411             num = kLevelTableSize;
412           do
413             lens2[i++] = 0;
414           while (i < num);
415           continue;
416         }
417       }
418       lens2[i++] = (Byte)len;
419     }
420 
421     if (_bitStream.IsBlockOverRead())
422       return S_FALSE;
423 
424     RIF(m_LevelDecoder.Build(lens2));
425   }
426 
427   Byte lens[kTablesSizesSum];
428   unsigned i = 0;
429 
430   do
431   {
432     if (_bitStream._buf >= _bitStream._bufCheck2)
433     {
434       if (_bitStream._buf >= _bitStream._bufCheck)
435         _bitStream.Prepare();
436       if (_bitStream.IsBlockOverRead())
437         return S_FALSE;
438     }
439 
440     UInt32 sym = m_LevelDecoder.Decode(&_bitStream);
441 
442     if (sym < 16)
443       lens[i++] = (Byte)sym;
444     else if (sym > kLevelTableSize)
445       return S_FALSE;
446     else
447     {
448       unsigned num = ((sym - 16) & 1) * 4;
449       num += num + 3 + (unsigned)_bitStream.ReadBits9(num + 3);
450       num += i;
451       if (num > kTablesSizesSum)
452         num = kTablesSizesSum;
453       Byte v = 0;
454       if (sym < 16 + 2)
455       {
456         if (i == 0)
457           return S_FALSE;
458         v = lens[(size_t)i - 1];
459       }
460       do
461         lens[i++] = v;
462       while (i < num);
463     }
464   }
465   while (i < kTablesSizesSum);
466 
467   if (_bitStream.IsBlockOverRead())
468     return S_FALSE;
469   if (_bitStream.InputEofError())
470     return S_FALSE;
471 
472   RIF(m_MainDecoder.Build(&lens[0]));
473   RIF(m_DistDecoder.Build(&lens[kMainTableSize]));
474   RIF(m_AlignDecoder.Build(&lens[kMainTableSize + kDistTableSize]));
475   RIF(m_LenDecoder.Build(&lens[kMainTableSize + kDistTableSize + kAlignTableSize]));
476 
477   _useAlignBits = false;
478   // _useAlignBits = true;
479   for (i = 0; i < kAlignTableSize; i++)
480     if (lens[kMainTableSize + kDistTableSize + (size_t)i] != kNumAlignBits)
481     {
482       _useAlignBits = true;
483       break;
484     }
485 
486   _tableWasFilled = true;
487   return S_OK;
488 }
489 
490 
SlotToLen(CBitDecoder & _bitStream,unsigned slot)491 static inline unsigned SlotToLen(CBitDecoder &_bitStream, unsigned slot)
492 {
493   if (slot < 8)
494     return slot + 2;
495   unsigned numBits = (slot >> 2) - 1;
496   return 2 + ((4 | (slot & 3)) << numBits) + _bitStream.ReadBits9(numBits);
497 }
498 
499 
500 static const UInt32 kSymbolRep = 258;
501 // static const unsigned kMaxMatchLen = 0x1001 + 3;
502 
DecodeLZ()503 HRESULT CDecoder::DecodeLZ()
504 {
505   CBitDecoder _bitStream;
506   _bitStream._stream = _inStream;
507   _bitStream._bufBase = _inputBuf;
508   _bitStream.Init();
509 
510   UInt32 rep0 = _reps[0];
511 
512   UInt32 remLen = 0;
513 
514   size_t limit;
515   {
516     size_t rem = _winSize - _winPos;
517     if (rem > kWriteStep)
518       rem = kWriteStep;
519     limit = _winPos + rem;
520   }
521 
522   for (;;)
523   {
524     if (_winPos >= limit)
525     {
526       RINOK(WriteBuf());
527       if (_unpackSize_Defined && _writtenFileSize > _unpackSize)
528         break; // return S_FALSE;
529 
530       {
531         size_t rem = _winSize - _winPos;
532 
533         if (rem == 0)
534         {
535           _winPos = 0;
536           rem = _winSize;
537         }
538         if (rem > kWriteStep)
539           rem = kWriteStep;
540         limit = _winPos + rem;
541       }
542 
543       if (remLen != 0)
544       {
545         size_t winPos = _winPos;
546         size_t winMask = _winMask;
547         size_t pos = (winPos - (size_t)rep0 - 1) & winMask;
548 
549         Byte *win = _window;
550         do
551         {
552           if (winPos >= limit)
553             break;
554           win[winPos] = win[pos];
555           winPos++;
556           pos = (pos + 1) & winMask;
557         }
558         while (--remLen != 0);
559 
560         _lzSize += winPos - _winPos;
561         _winPos = winPos;
562         continue;
563       }
564     }
565 
566     if (_bitStream._buf >= _bitStream._bufCheck2)
567     {
568       if (_bitStream.InputEofError())
569         break; // return S_FALSE;
570       if (_bitStream._buf >= _bitStream._bufCheck)
571         _bitStream.Prepare2();
572 
573       UInt64 processed = _bitStream.GetProcessedSize_Round();
574       if (processed >= _bitStream._blockEnd)
575       {
576         if (processed > _bitStream._blockEnd)
577           break; // return S_FALSE;
578         {
579           unsigned bits7 = _bitStream.GetProcessedBits7();
580           if (bits7 > _bitStream._blockEndBits7)
581             break; // return S_FALSE;
582           if (bits7 == _bitStream._blockEndBits7)
583           {
584             if (_isLastBlock)
585             {
586               _reps[0] = rep0;
587 
588               if (_bitStream.InputEofError())
589                 break;
590 
591               /*
592               // packSize can be 15 bytes larger for encrypted archive
593               if (_packSize_Defined && _packSize < _bitStream.GetProcessedSize())
594                 break;
595               */
596 
597               return _bitStream._hres;
598               // break;
599             }
600             RINOK(ReadTables(_bitStream));
601             continue;
602           }
603         }
604       }
605 
606       // that check is not required, but it can help, if there is BUG in another code
607       if (!_tableWasFilled)
608         break; // return S_FALSE;
609     }
610 
611     UInt32 sym = m_MainDecoder.Decode(&_bitStream);
612 
613     if (sym < 256)
614     {
615       size_t winPos = _winPos;
616       _window[winPos] = (Byte)sym;
617       _winPos = winPos + 1;
618       _lzSize++;
619       continue;
620     }
621 
622     UInt32 len;
623 
624     if (sym < kSymbolRep + kNumReps)
625     {
626       if (sym >= kSymbolRep)
627       {
628         if (sym != kSymbolRep)
629         {
630           UInt32 dist;
631           if (sym == kSymbolRep + 1)
632             dist = _reps[1];
633           else
634           {
635             if (sym == kSymbolRep + 2)
636               dist = _reps[2];
637             else
638             {
639               dist = _reps[3];
640               _reps[3] = _reps[2];
641             }
642             _reps[2] = _reps[1];
643           }
644           _reps[1] = rep0;
645           rep0 = dist;
646         }
647 
648         const UInt32 sym2 = m_LenDecoder.Decode(&_bitStream);
649         if (sym2 >= kLenTableSize)
650           break; // return S_FALSE;
651         len = SlotToLen(_bitStream, sym2);
652       }
653       else
654       {
655         if (sym == 256)
656         {
657           RINOK(AddFilter(_bitStream));
658           continue;
659         }
660         else // if (sym == 257)
661         {
662           len = _lastLen;
663           // if (len = 0), we ignore that symbol, like original unRAR code, but it can mean error in stream.
664           // if (len == 0) return S_FALSE;
665           if (len == 0)
666             continue;
667         }
668       }
669     }
670     else if (sym >= kMainTableSize)
671       break; // return S_FALSE;
672     else
673     {
674       _reps[3] = _reps[2];
675       _reps[2] = _reps[1];
676       _reps[1] = rep0;
677       len = SlotToLen(_bitStream, sym - (kSymbolRep + kNumReps));
678 
679       rep0 = m_DistDecoder.Decode(&_bitStream);
680 
681       if (rep0 >= 4)
682       {
683         if (rep0 >= _numCorrectDistSymbols)
684           break; // return S_FALSE;
685         unsigned numBits = (rep0 >> 1) - 1;
686         rep0 = (2 | (rep0 & 1)) << numBits;
687 
688         if (numBits < kNumAlignBits)
689           rep0 += _bitStream.ReadBits9(numBits);
690         else
691         {
692           len += (numBits >= 7);
693           len += (numBits >= 12);
694           len += (numBits >= 17);
695 
696           if (_useAlignBits)
697           {
698             // if (numBits > kNumAlignBits)
699               rep0 += (_bitStream.ReadBits32(numBits - kNumAlignBits) << kNumAlignBits);
700             UInt32 a = m_AlignDecoder.Decode(&_bitStream);
701             if (a >= kAlignTableSize)
702               break; // return S_FALSE;
703             rep0 += a;
704           }
705           else
706             rep0 += _bitStream.ReadBits32(numBits);
707         }
708       }
709     }
710 
711     _lastLen = len;
712 
713     if (rep0 >= _lzSize)
714       _lzError = true;
715 
716     {
717       UInt32 lenCur = len;
718       size_t winPos = _winPos;
719       size_t pos = (winPos - (size_t)rep0 - 1) & _winMask;
720       {
721         size_t rem = limit - winPos;
722         // size_t rem = _winSize - winPos;
723 
724         if (lenCur > rem)
725         {
726           lenCur = (UInt32)rem;
727           remLen = len - lenCur;
728         }
729       }
730 
731       Byte *win = _window;
732       _lzSize += lenCur;
733       _winPos = winPos + lenCur;
734       if (_winSize - pos >= lenCur)
735       {
736         const Byte *src = win + pos;
737         Byte *dest = win + winPos;
738         do
739           *dest++ = *src++;
740         while (--lenCur != 0);
741       }
742       else
743       {
744         do
745         {
746           win[winPos] = win[pos];
747           winPos++;
748           pos = (pos + 1) & _winMask;
749         }
750         while (--lenCur != 0);
751       }
752     }
753   }
754 
755   if (_bitStream._hres != S_OK)
756     return _bitStream._hres;
757 
758   return S_FALSE;
759 }
760 
761 
CodeReal()762 HRESULT CDecoder::CodeReal()
763 {
764   _unsupportedFilter = false;
765   _lzError = false;
766   _writeError = false;
767 
768   if (!_isSolid || !_wasInit)
769   {
770     size_t clearSize = _winSize;
771     if (_lzSize < _winSize)
772       clearSize = (size_t)_lzSize;
773     memset(_window, 0, clearSize);
774 
775     _wasInit = true;
776     _lzSize = 0;
777     _lzWritten = 0;
778     _winPos = 0;
779 
780     for (unsigned i = 0; i < kNumReps; i++)
781       _reps[i] = (UInt32)0 - 1;
782 
783     _lastLen = 0;
784     _tableWasFilled = false;
785   }
786 
787   _isLastBlock = false;
788 
789   InitFilters();
790 
791   _filterEnd = 0;
792   _writtenFileSize = 0;
793 
794   _lzFileStart = _lzSize;
795   _lzWritten = _lzSize;
796 
797   HRESULT res = DecodeLZ();
798 
799   HRESULT res2 = S_OK;
800   if (!_writeError && res != E_OUTOFMEMORY)
801     res2 = WriteBuf();
802 
803   /*
804   if (res == S_OK)
805     if (InputEofError())
806       res = S_FALSE;
807   */
808 
809   if (res == S_OK)
810   {
811     _solidAllowed = true;
812     res = res2;
813   }
814 
815   if (res == S_OK && _unpackSize_Defined && _writtenFileSize != _unpackSize)
816     return S_FALSE;
817   return res;
818 }
819 
820 
821 // Original unRAR claims that maximum possible filter block size is (1 << 16) now,
822 // and (1 << 17) is minimum win size required to support filter.
823 // Original unRAR uses (1 << 18) for "extra safety and possible filter area size expansion"
824 // We can use any win size.
825 
826 static const unsigned kWinSize_Log_Min = 17;
827 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)828 STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
829     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
830 {
831   try
832   {
833     if (_isSolid && !_solidAllowed)
834       return S_FALSE;
835     _solidAllowed = false;
836 
837     if (_dictSizeLog >= sizeof(size_t) * 8)
838       return E_NOTIMPL;
839 
840     if (!_isSolid)
841       _lzEnd = 0;
842     else
843     {
844       if (_lzSize < _lzEnd)
845       {
846         if (_window)
847         {
848           UInt64 rem = _lzEnd - _lzSize;
849           if (rem >= _winSize)
850             memset(_window, 0, _winSize);
851           else
852           {
853             size_t pos = (size_t)_lzSize & _winSize;
854             size_t rem2 = _winSize - pos;
855             if (rem2 > rem)
856               rem2 = (size_t)rem;
857             memset(_window + pos, 0, rem2);
858             rem -= rem2;
859             memset(_window, 0, (size_t)rem);
860           }
861         }
862         _lzEnd &= ((((UInt64)1) << 33) - 1);
863         _lzSize = _lzEnd;
864         _winPos = (size_t)(_lzSize & _winSize);
865       }
866       _lzEnd = _lzSize;
867     }
868 
869     size_t newSize;
870     {
871       unsigned newSizeLog = _dictSizeLog;
872       if (newSizeLog < kWinSize_Log_Min)
873         newSizeLog = kWinSize_Log_Min;
874       newSize = (size_t)1 << newSizeLog;
875       _numCorrectDistSymbols = newSizeLog * 2;
876     }
877 
878     // If dictionary was reduced, we use allocated dictionary block
879     // for compatibility with original unRAR decoder.
880 
881     if (_window && newSize < _winSizeAllocated)
882       _winSize = _winSizeAllocated;
883     else if (!_window || _winSize != newSize)
884     {
885       if (!_isSolid)
886       {
887         ::MidFree(_window);
888         _window = NULL;
889         _winSizeAllocated = 0;
890       }
891 
892       Byte *win;
893 
894       {
895         win = (Byte *)::MidAlloc(newSize);
896         if (!win)
897           return E_OUTOFMEMORY;
898         memset(win, 0, newSize);
899       }
900 
901       if (_isSolid && _window)
902       {
903         // original unRAR claims:
904         // "Archiving code guarantees that win size does not grow in the same solid stream",
905         // but the original unRAR decoder still supports such grow case.
906 
907         Byte *winOld = _window;
908         size_t oldSize = _winSize;
909         size_t newMask = newSize - 1;
910         size_t oldMask = _winSize - 1;
911         size_t winPos = _winPos;
912         for (size_t i = 1; i <= oldSize; i++)
913           win[(winPos - i) & newMask] = winOld[(winPos - i) & oldMask];
914         ::MidFree(_window);
915       }
916 
917       _window = win;
918       _winSizeAllocated = newSize;
919       _winSize = newSize;
920     }
921 
922     _winMask = _winSize - 1;
923     _winPos &= _winMask;
924 
925     if (!_inputBuf)
926     {
927       _inputBuf = (Byte *)::MidAlloc(kInputBufSize);
928       if (!_inputBuf)
929         return E_OUTOFMEMORY;
930     }
931 
932     _inStream = inStream;
933     _outStream = outStream;
934 
935     /*
936     _packSize = 0;
937     _packSize_Defined = (inSize != NULL);
938     if (_packSize_Defined)
939       _packSize = *inSize;
940     */
941 
942     _unpackSize = 0;
943     _unpackSize_Defined = (outSize != NULL);
944     if (_unpackSize_Defined)
945       _unpackSize = *outSize;
946 
947     if ((Int64)_unpackSize >= 0)
948       _lzEnd += _unpackSize;
949     else
950       _lzEnd = 0;
951 
952     _progress = progress;
953 
954     HRESULT res = CodeReal();
955 
956     if (res != S_OK)
957       return res;
958     if (_lzError)
959       return S_FALSE;
960     if (_unsupportedFilter)
961       return E_NOTIMPL;
962     return S_OK;
963   }
964   // catch(const CInBufferException &e)  { return e.ErrorCode; }
965   // catch(...) { return S_FALSE; }
966   catch(...) { return E_OUTOFMEMORY; }
967   // CNewException is possible here. But probably CNewException is caused
968   // by error in data stream.
969 }
970 
SetDecoderProperties2(const Byte * data,UInt32 size)971 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
972 {
973   if (size != 2)
974     return E_NOTIMPL;
975   _dictSizeLog = (Byte)((data[0] & 0xF) + 17);
976   _isSolid = ((data[1] & 1) != 0);
977   return S_OK;
978 }
979 
980 }}
981