1 // ArjHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/StringConvert.h"
9 
10 #include "../../Windows/PropVariant.h"
11 #include "../../Windows/PropVariantUtils.h"
12 #include "../../Windows/TimeUtils.h"
13 
14 #include "../Common/LimitedStreams.h"
15 #include "../Common/ProgressUtils.h"
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamObjects.h"
18 #include "../Common/StreamUtils.h"
19 
20 #include "../Compress/CopyCoder.h"
21 #include "../Compress/LzhDecoder.h"
22 
23 #include "Common/ItemNameUtils.h"
24 #include "Common/OutStreamWithCRC.h"
25 
26 namespace NCompress {
27 namespace NArj {
28 namespace NDecoder {
29 
30 static const unsigned kMatchMinLen = 3;
31 
32 static const UInt32 kWindowSize = 1 << 15; // must be >= (1 << 14)
33 
34 class CCoder:
35   public ICompressCoder,
36   public CMyUnknownImp
37 {
38   CLzOutWindow _outWindow;
39   NBitm::CDecoder<CInBuffer> _inBitStream;
40 
41   class CCoderReleaser
42   {
43     CCoder *_coder;
44   public:
CCoderReleaser(CCoder * coder)45     CCoderReleaser(CCoder *coder): _coder(coder) {}
Disable()46     void Disable() { _coder = NULL; }
~CCoderReleaser()47     ~CCoderReleaser() { if (_coder) _coder->_outWindow.Flush(); }
48   };
49   friend class CCoderReleaser;
50 
51   HRESULT CodeReal(UInt64 outSize, ICompressProgressInfo *progress);
52 public:
53   MY_UNKNOWN_IMP
54 
55   bool FinishMode;
CCoder()56   CCoder(): FinishMode(false) {}
57 
58   STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
59       const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
GetInputProcessedSize() const60   UInt64 GetInputProcessedSize() const { return _inBitStream.GetProcessedSize(); }
61 };
62 
CodeReal(UInt64 rem,ICompressProgressInfo * progress)63 HRESULT CCoder::CodeReal(UInt64 rem, ICompressProgressInfo *progress)
64 {
65   const UInt32 kStep = 1 << 20;
66   UInt64 next = 0;
67   if (rem > kStep && progress)
68     next = rem - kStep;
69 
70   while (rem != 0)
71   {
72     if (rem <= next)
73     {
74       if (_inBitStream.ExtraBitsWereRead())
75         return S_FALSE;
76 
77       UInt64 packSize = _inBitStream.GetProcessedSize();
78       UInt64 pos = _outWindow.GetProcessedSize();
79       RINOK(progress->SetRatioInfo(&packSize, &pos));
80       next = 0;
81       if (rem > kStep)
82         next = rem - kStep;
83     }
84 
85     UInt32 len;
86 
87     {
88       const unsigned kNumBits = 7 + 7;
89       UInt32 val = _inBitStream.GetValue(kNumBits);
90 
91       if ((val & (1 << (kNumBits - 1))) == 0)
92       {
93         _outWindow.PutByte((Byte)(val >> 5));
94         _inBitStream.MovePos(1 + 8);
95         rem--;
96         continue;
97       }
98 
99       UInt32 mask = 1 << (kNumBits - 2);
100       unsigned w;
101 
102       for (w = 1; w < 7; w++, mask >>= 1)
103         if ((val & mask) == 0)
104           break;
105 
106       unsigned readBits = (w != 7 ? 1 : 0);
107       readBits += w + w;
108       len = (1 << w) - 1 + kMatchMinLen - 1 +
109           (((val >> (kNumBits - readBits)) & ((1 << w) - 1)));
110       _inBitStream.MovePos(readBits);
111     }
112 
113     {
114       const unsigned kNumBits = 4 + 13;
115       UInt32 val = _inBitStream.GetValue(kNumBits);
116 
117       unsigned readBits = 1;
118       unsigned w;
119 
120            if ((val & ((UInt32)1 << 16)) == 0) w = 9;
121       else if ((val & ((UInt32)1 << 15)) == 0) w = 10;
122       else if ((val & ((UInt32)1 << 14)) == 0) w = 11;
123       else if ((val & ((UInt32)1 << 13)) == 0) w = 12;
124       else { w = 13; readBits = 0; }
125 
126       readBits += w + w - 9;
127 
128       UInt32 dist = ((UInt32)1 << w) - (1 << 9) +
129           (((val >> (kNumBits - readBits)) & ((1 << w) - 1)));
130       _inBitStream.MovePos(readBits);
131 
132       if (len > rem)
133         len = (UInt32)rem;
134 
135       if (!_outWindow.CopyBlock(dist, len))
136         return S_FALSE;
137       rem -= len;
138     }
139   }
140 
141   if (FinishMode)
142   {
143     if (_inBitStream.ReadAlignBits() != 0)
144       return S_FALSE;
145   }
146 
147   if (_inBitStream.ExtraBitsWereRead())
148     return S_FALSE;
149 
150   return S_OK;
151 }
152 
153 
154 
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const UInt64 *,const UInt64 * outSize,ICompressProgressInfo * progress)155 STDMETHODIMP CCoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
156     const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
157 {
158   try
159   {
160     if (!outSize)
161       return E_INVALIDARG;
162 
163     if (!_outWindow.Create(kWindowSize))
164       return E_OUTOFMEMORY;
165     if (!_inBitStream.Create(1 << 17))
166       return E_OUTOFMEMORY;
167 
168     _outWindow.SetStream(outStream);
169     _outWindow.Init(false);
170     _inBitStream.SetStream(inStream);
171     _inBitStream.Init();
172 
173     CCoderReleaser coderReleaser(this);
174     HRESULT res;
175     {
176       res = CodeReal(*outSize, progress);
177       if (res != S_OK)
178         return res;
179     }
180 
181     coderReleaser.Disable();
182     return _outWindow.Flush();
183   }
184   catch(const CInBufferException &e) { return e.ErrorCode; }
185   catch(const CLzOutWindowException &e) { return e.ErrorCode; }
186   catch(...) { return S_FALSE; }
187 }
188 
189 }}}
190 
191 
192 
193 
194 using namespace NWindows;
195 
196 #define Get16(p) GetUi16(p)
197 #define Get32(p) GetUi32(p)
198 
199 namespace NArchive {
200 namespace NArj {
201 
202 static const unsigned kBlockSizeMin = 30;
203 static const unsigned kBlockSizeMax = 2600;
204 
205 static const Byte kSig0 = 0x60;
206 static const Byte kSig1 = 0xEA;
207 
208 namespace NCompressionMethod
209 {
210   enum
211   {
212     kStored = 0,
213     kCompressed1a = 1,
214     kCompressed1b = 2,
215     kCompressed1c = 3,
216     kCompressed2 = 4,
217     kNoDataNoCRC = 8,
218     kNoData = 9
219   };
220 }
221 
222 namespace NFileType
223 {
224   enum
225   {
226     kBinary = 0,
227     k7BitText,
228     kArchiveHeader,
229     kDirectory,
230     kVolumeLablel,
231     kChapterLabel
232   };
233 }
234 
235 namespace NFlags
236 {
237   const Byte kGarbled  = 1 << 0;
238   const Byte kAnsiPage = 1 << 1; // or (OLD_SECURED_FLAG) obsolete
239   const Byte kVolume   = 1 << 2;
240   const Byte kExtFile  = 1 << 3;
241   const Byte kPathSym  = 1 << 4;
242   const Byte kBackup   = 1 << 5; // obsolete
243   const Byte kSecured  = 1 << 6;
244   const Byte kDualName = 1 << 7;
245 }
246 
247 namespace NHostOS
248 {
249   enum EEnum
250   {
251     kMSDOS = 0,  // MS-DOS, OS/2, Win32, pkarj 2.50 (FAT / VFAT / FAT32)
252     kPRIMOS,
253     kUnix,
254     kAMIGA,
255     kMac,
256     kOS_2,
257     kAPPLE_GS,
258     kAtari_ST,
259     kNext,
260     kVAX_VMS,
261     kWIN95
262   };
263 }
264 
265 static const char * const kHostOS[] =
266 {
267     "MSDOS"
268   , "PRIMOS"
269   , "UNIX"
270   , "AMIGA"
271   , "MAC"
272   , "OS/2"
273   , "APPLE GS"
274   , "ATARI ST"
275   , "NEXT"
276   , "VAX VMS"
277   , "WIN95"
278 };
279 
280 struct CArcHeader
281 {
282   // Byte ArchiverVersion;
283   // Byte ExtractVersion;
284   Byte HostOS;
285   // Byte Flags;
286   // Byte SecuryVersion;
287   // Byte FileType;
288   // Byte Reserved;
289   UInt32 CTime;
290   UInt32 MTime;
291   UInt32 ArchiveSize;
292   // UInt32 SecurPos;
293   // UInt16 FilespecPosInFilename;
294   UInt16 SecurSize;
295   // Byte EncryptionVersion;
296   // Byte LastChapter;
297   AString Name;
298   AString Comment;
299 
300   HRESULT Parse(const Byte *p, unsigned size);
301 };
302 
IsArc_Arj(const Byte * p,size_t size)303 API_FUNC_static_IsArc IsArc_Arj(const Byte *p, size_t size)
304 {
305   if (size < kBlockSizeMin + 4)
306     return k_IsArc_Res_NEED_MORE;
307   if (p[0] != kSig0 || p[1] != kSig1)
308     return k_IsArc_Res_NO;
309   UInt32 blockSize = Get16(p + 2);
310   if (blockSize < kBlockSizeMin ||
311       blockSize > kBlockSizeMax)
312     return k_IsArc_Res_NO;
313 
314   p += 4;
315   size -= 4;
316 
317   Byte headerSize = p[0];
318   if (headerSize < kBlockSizeMin ||
319       headerSize > blockSize ||
320       p[6] != NFileType::kArchiveHeader ||
321       p[28] > 8) // EncryptionVersion
322     return k_IsArc_Res_NO;
323 
324   if (blockSize + 4 <= size)
325     if (Get32(p + blockSize) != CrcCalc(p, blockSize))
326       return k_IsArc_Res_NO;
327 
328   return k_IsArc_Res_YES;
329 }
330 }
331 
ReadString(const Byte * p,unsigned & size,AString & res)332 static HRESULT ReadString(const Byte *p, unsigned &size, AString &res)
333 {
334   unsigned num = size;
335   for (unsigned i = 0; i < num;)
336   {
337     if (p[i++] == 0)
338     {
339       size = i;
340       res = (const char *)p;
341       return S_OK;
342     }
343   }
344   return S_FALSE;
345 }
346 
Parse(const Byte * p,unsigned size)347 HRESULT CArcHeader::Parse(const Byte *p, unsigned size)
348 {
349   Byte headerSize = p[0];
350   if (headerSize < kBlockSizeMin || headerSize > size)
351     return S_FALSE;
352   // ArchiverVersion = p[1];
353   // ExtractVersion = p[2];
354   HostOS = p[3];
355   // Flags = p[4];
356   // SecuryVersion = p[5];
357   if (p[6] != NFileType::kArchiveHeader)
358     return S_FALSE;
359   // Reserved = p[7];
360   CTime = Get32(p + 8);
361   MTime = Get32(p + 12);
362   ArchiveSize = Get32(p + 16); // it can be zero. (currently used only for secured archives)
363   // SecurPos = Get32(p + 20);
364   // UInt16 filespecPositionInFilename = Get16(p + 24);
365   SecurSize = Get16(p + 26);
366   // EncryptionVersion = p[28];
367   // LastChapter = p[29];
368   unsigned pos = headerSize;
369   unsigned size1 = size - pos;
370   RINOK(ReadString(p + pos, size1, Name));
371   pos += size1;
372   size1 = size - pos;
373   RINOK(ReadString(p + pos, size1, Comment));
374   pos += size1;
375   return S_OK;
376 }
377 
378 struct CItem
379 {
380   AString Name;
381   AString Comment;
382 
383   UInt32 MTime;
384   UInt32 PackSize;
385   UInt32 Size;
386   UInt32 FileCRC;
387   UInt32 SplitPos;
388 
389   Byte Version;
390   Byte ExtractVersion;
391   Byte HostOS;
392   Byte Flags;
393   Byte Method;
394   Byte FileType;
395 
396   // UInt16 FilespecPosInFilename;
397   UInt16 FileAccessMode;
398   // Byte FirstChapter;
399   // Byte LastChapter;
400 
401   UInt64 DataPosition;
402 
IsEncryptedNArchive::CItem403   bool IsEncrypted() const { return (Flags & NFlags::kGarbled) != 0; }
IsDirNArchive::CItem404   bool IsDir() const { return (FileType == NFileType::kDirectory); }
IsSplitAfterNArchive::CItem405   bool IsSplitAfter() const { return (Flags & NFlags::kVolume) != 0; }
IsSplitBeforeNArchive::CItem406   bool IsSplitBefore() const { return (Flags & NFlags::kExtFile) != 0; }
GetWinAttribNArchive::CItem407   UInt32 GetWinAttrib() const
408   {
409     UInt32 atrrib = 0;
410     switch (HostOS)
411     {
412       case NHostOS::kMSDOS:
413       case NHostOS::kWIN95:
414         atrrib = FileAccessMode;
415         break;
416     }
417     if (IsDir())
418       atrrib |= FILE_ATTRIBUTE_DIRECTORY;
419     return atrrib;
420   }
421 
422   HRESULT Parse(const Byte *p, unsigned size);
423 };
424 
Parse(const Byte * p,unsigned size)425 HRESULT CItem::Parse(const Byte *p, unsigned size)
426 {
427   Byte headerSize = p[0];
428   if (headerSize < kBlockSizeMin || headerSize > size)
429     return S_FALSE;
430   Version = p[1];
431   ExtractVersion = p[2];
432   HostOS = p[3];
433   Flags = p[4];
434   Method = p[5];
435   FileType = p[6];
436   // Reserved = p[7];
437   MTime = Get32(p + 8);
438   PackSize = Get32(p + 12);
439   Size = Get32(p + 16);
440   FileCRC = Get32(p + 20);
441   // FilespecPosInFilename = Get16(p + 24);
442   FileAccessMode = Get16(p + 26);
443   // FirstChapter = p[28];
444   // FirstChapter = p[29];
445 
446   SplitPos = 0;
447   if (IsSplitBefore() && headerSize >= 34)
448     SplitPos = Get32(p + 30);
449 
450   unsigned pos = headerSize;
451   unsigned size1 = size - pos;
452   RINOK(ReadString(p + pos, size1, Name));
453   pos += size1;
454   size1 = size - pos;
455   RINOK(ReadString(p + pos, size1, Comment));
456   pos += size1;
457 
458   return S_OK;
459 }
460 
461 enum EErrorType
462 {
463   k_ErrorType_OK,
464   k_ErrorType_Corrupted,
465   k_ErrorType_UnexpectedEnd,
466 };
467 
468 class CArc
469 {
470 public:
471   UInt64 Processed;
472   EErrorType Error;
473   bool IsArc;
474   IInStream *Stream;
475   IArchiveOpenCallback *Callback;
476   UInt64 NumFiles;
477   CArcHeader Header;
478 
479   HRESULT Open();
480   HRESULT GetNextItem(CItem &item, bool &filled);
Close()481   void Close()
482   {
483     IsArc = false;
484     Error = k_ErrorType_OK;
485   }
486 private:
487   UInt32 _blockSize;
488   Byte _block[kBlockSizeMax + 4];
489 
490   HRESULT ReadBlock(bool &filled, bool readSignature);
491   HRESULT SkipExtendedHeaders();
492   HRESULT Read(void *data, size_t *size);
493 };
494 
Read(void * data,size_t * size)495 HRESULT CArc::Read(void *data, size_t *size)
496 {
497   HRESULT res = ReadStream(Stream, data, size);
498   Processed += *size;
499   return res;
500 }
501 
502 #define READ_STREAM(_dest_, _size_) \
503   { size_t _processed_ = (_size_); RINOK(Read(_dest_, &_processed_)); \
504   if (_processed_ != (_size_)) { Error = k_ErrorType_UnexpectedEnd; return S_OK; } }
505 
ReadBlock(bool & filled,bool readSignature)506 HRESULT CArc::ReadBlock(bool &filled, bool readSignature)
507 {
508   Error = k_ErrorType_OK;
509   filled = false;
510   Byte buf[4];
511   unsigned signSize = readSignature ? 2 : 0;
512   READ_STREAM(buf, signSize + 2)
513   if (readSignature)
514     if (buf[0] != kSig0 || buf[1] != kSig1)
515     {
516       Error = k_ErrorType_Corrupted;
517       return S_OK;
518     }
519   _blockSize = Get16(buf + signSize);
520   if (_blockSize == 0) // end of archive
521     return S_OK;
522   if (_blockSize < kBlockSizeMin ||
523       _blockSize > kBlockSizeMax)
524   {
525     Error = k_ErrorType_Corrupted;
526     return S_OK;
527   }
528   READ_STREAM(_block, _blockSize + 4);
529   if (Get32(_block + _blockSize) != CrcCalc(_block, _blockSize))
530   {
531     Error = k_ErrorType_Corrupted;
532     return S_OK;
533   }
534   filled = true;
535   return S_OK;
536 }
537 
SkipExtendedHeaders()538 HRESULT CArc::SkipExtendedHeaders()
539 {
540   for (UInt32 i = 0;; i++)
541   {
542     bool filled;
543     RINOK(ReadBlock(filled, false));
544     if (!filled)
545       return S_OK;
546     if (Callback && (i & 0xFF) == 0)
547       RINOK(Callback->SetCompleted(&NumFiles, &Processed));
548   }
549 }
550 
Open()551 HRESULT CArc::Open()
552 {
553   bool filled;
554   RINOK(ReadBlock(filled, true));
555   if (!filled)
556     return S_FALSE;
557   RINOK(Header.Parse(_block, _blockSize));
558   IsArc = true;
559   return SkipExtendedHeaders();
560 }
561 
GetNextItem(CItem & item,bool & filled)562 HRESULT CArc::GetNextItem(CItem &item, bool &filled)
563 {
564   RINOK(ReadBlock(filled, true));
565   if (!filled)
566     return S_OK;
567   filled = false;
568   if (item.Parse(_block, _blockSize) != S_OK)
569   {
570     Error = k_ErrorType_Corrupted;
571     return S_OK;
572   }
573   /*
574   UInt32 extraData;
575   if ((header.Flags & NFlags::kExtFile) != 0)
576     extraData = GetUi32(_block + pos);
577   */
578 
579   RINOK(SkipExtendedHeaders());
580   filled = true;
581   return S_OK;
582 }
583 
584 class CHandler:
585   public IInArchive,
586   public CMyUnknownImp
587 {
588   CObjectVector<CItem> _items;
589   CMyComPtr<IInStream> _stream;
590   UInt64 _phySize;
591   CArc _arc;
592 public:
593   MY_UNKNOWN_IMP1(IInArchive)
594 
595   INTERFACE_IInArchive(;)
596 
597   HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
598 };
599 
600 static const Byte kArcProps[] =
601 {
602   kpidName,
603   kpidCTime,
604   kpidMTime,
605   kpidHostOS,
606   kpidComment
607 };
608 
609 static const Byte kProps[] =
610 {
611   kpidPath,
612   kpidIsDir,
613   kpidSize,
614   kpidPosition,
615   kpidPackSize,
616   kpidMTime,
617   kpidAttrib,
618   kpidEncrypted,
619   kpidCRC,
620   kpidMethod,
621   kpidHostOS,
622   kpidComment
623 };
624 
625 IMP_IInArchive_Props
626 IMP_IInArchive_ArcProps
627 
SetTime(UInt32 dosTime,NCOM::CPropVariant & prop)628 static void SetTime(UInt32 dosTime, NCOM::CPropVariant &prop)
629 {
630   if (dosTime == 0)
631     return;
632   FILETIME localFileTime, utc;
633   if (NTime::DosTimeToFileTime(dosTime, localFileTime))
634   {
635     if (!LocalFileTimeToFileTime(&localFileTime, &utc))
636       utc.dwHighDateTime = utc.dwLowDateTime = 0;
637   }
638   else
639     utc.dwHighDateTime = utc.dwLowDateTime = 0;
640   prop = utc;
641 }
642 
SetHostOS(Byte hostOS,NCOM::CPropVariant & prop)643 static void SetHostOS(Byte hostOS, NCOM::CPropVariant &prop)
644 {
645   TYPE_TO_PROP(kHostOS, hostOS, prop);
646 }
647 
SetUnicodeString(const AString & s,NCOM::CPropVariant & prop)648 static void SetUnicodeString(const AString &s, NCOM::CPropVariant &prop)
649 {
650   if (!s.IsEmpty())
651     prop = MultiByteToUnicodeString(s, CP_OEMCP);
652 }
653 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)654 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
655 {
656   COM_TRY_BEGIN
657   NCOM::CPropVariant prop;
658   switch (propID)
659   {
660     case kpidPhySize: prop = _phySize; break;
661     case kpidName: SetUnicodeString(_arc.Header.Name, prop); break;
662     case kpidCTime: SetTime(_arc.Header.CTime, prop); break;
663     case kpidMTime: SetTime(_arc.Header.MTime, prop); break;
664     case kpidHostOS: SetHostOS(_arc.Header.HostOS, prop); break;
665     case kpidComment: SetUnicodeString(_arc.Header.Comment, prop); break;
666     case kpidErrorFlags:
667     {
668       UInt32 v = 0;
669       if (!_arc.IsArc) v |= kpv_ErrorFlags_IsNotArc;
670       switch (_arc.Error)
671       {
672         case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break;
673         case k_ErrorType_Corrupted: v |= kpv_ErrorFlags_HeadersError; break;
674       }
675       prop = v;
676       break;
677     }
678   }
679   prop.Detach(value);
680   return S_OK;
681   COM_TRY_END
682 }
683 
GetNumberOfItems(UInt32 * numItems)684 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
685 {
686   *numItems = _items.Size();
687   return S_OK;
688 }
689 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)690 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
691 {
692   COM_TRY_BEGIN
693   NCOM::CPropVariant prop;
694   const CItem &item = _items[index];
695   switch (propID)
696   {
697     case kpidPath:  prop = NItemName::GetOsPath(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break;
698     case kpidIsDir:  prop = item.IsDir(); break;
699     case kpidSize:  prop = item.Size; break;
700     case kpidPackSize:  prop = item.PackSize; break;
701     case kpidPosition:  if (item.IsSplitBefore() || item.IsSplitAfter()) prop = (UInt64)item.SplitPos; break;
702     case kpidAttrib:  prop = item.GetWinAttrib(); break;
703     case kpidEncrypted:  prop = item.IsEncrypted(); break;
704     case kpidCRC:  prop = item.FileCRC; break;
705     case kpidMethod:  prop = item.Method; break;
706     case kpidHostOS:  SetHostOS(item.HostOS, prop); break;
707     case kpidMTime:  SetTime(item.MTime, prop); break;
708     case kpidComment:  SetUnicodeString(item.Comment, prop); break;
709   }
710   prop.Detach(value);
711   return S_OK;
712   COM_TRY_END
713 }
714 
Open2(IInStream * inStream,IArchiveOpenCallback * callback)715 HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback)
716 {
717   Close();
718 
719   UInt64 endPos = 0;
720   RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos));
721   RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
722 
723   _arc.Stream = inStream;
724   _arc.Callback = callback;
725   _arc.NumFiles = 0;
726   _arc.Processed = 0;
727 
728   RINOK(_arc.Open());
729 
730   _phySize = _arc.Processed;
731   if (_arc.Header.ArchiveSize != 0)
732     _phySize = (UInt64)_arc.Header.ArchiveSize + _arc.Header.SecurSize;
733 
734   for (;;)
735   {
736     CItem item;
737     bool filled;
738 
739     _arc.Error = k_ErrorType_OK;
740     RINOK(_arc.GetNextItem(item, filled));
741 
742     if (_arc.Error != k_ErrorType_OK)
743       break;
744 
745     if (!filled)
746     {
747       if (_arc.Error == k_ErrorType_OK)
748         if (_arc.Header.ArchiveSize == 0)
749           _phySize = _arc.Processed;
750       break;
751     }
752     item.DataPosition = _arc.Processed;
753     _items.Add(item);
754 
755     UInt64 pos = item.DataPosition + item.PackSize;
756     if (_arc.Header.ArchiveSize == 0)
757       _phySize = pos;
758     if (pos > endPos)
759     {
760       _arc.Error = k_ErrorType_UnexpectedEnd;
761       break;
762     }
763 
764     RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL));
765     _arc.NumFiles = _items.Size();
766     _arc.Processed = pos;
767 
768     if (callback && (_items.Size() & 0xFF) == 0)
769     {
770       RINOK(callback->SetCompleted(&_arc.NumFiles, &_arc.Processed));
771     }
772   }
773   return S_OK;
774 }
775 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)776 STDMETHODIMP CHandler::Open(IInStream *inStream,
777     const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback)
778 {
779   COM_TRY_BEGIN
780   HRESULT res;
781   {
782     res = Open2(inStream, callback);
783     if (res == S_OK)
784     {
785       _stream = inStream;
786       return S_OK;
787     }
788   }
789   return res;
790   COM_TRY_END
791 }
792 
Close()793 STDMETHODIMP CHandler::Close()
794 {
795   _arc.Close();
796   _phySize = 0;
797   _items.Clear();
798   _stream.Release();
799   return S_OK;
800 }
801 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)802 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
803     Int32 testMode, IArchiveExtractCallback *extractCallback)
804 {
805   COM_TRY_BEGIN
806   UInt64 totalUnpacked = 0, totalPacked = 0;
807   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
808   if (allFilesMode)
809     numItems = _items.Size();
810   if (numItems == 0)
811     return S_OK;
812   UInt32 i;
813   for (i = 0; i < numItems; i++)
814   {
815     const CItem &item = _items[allFilesMode ? i : indices[i]];
816     totalUnpacked += item.Size;
817     // totalPacked += item.PackSize;
818   }
819   extractCallback->SetTotal(totalUnpacked);
820 
821   totalUnpacked = totalPacked = 0;
822   UInt64 curUnpacked, curPacked;
823 
824   NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = NULL;
825   CMyComPtr<ICompressCoder> lzhDecoder;
826 
827   NCompress::NArj::NDecoder::CCoder *arjDecoderSpec = NULL;
828   CMyComPtr<ICompressCoder> arjDecoder;
829 
830   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
831   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
832 
833   CLocalProgress *lps = new CLocalProgress;
834   CMyComPtr<ICompressProgressInfo> progress = lps;
835   lps->Init(extractCallback, false);
836 
837   CLimitedSequentialInStream *inStreamSpec = new CLimitedSequentialInStream;
838   CMyComPtr<ISequentialInStream> inStream(inStreamSpec);
839   inStreamSpec->SetStream(_stream);
840 
841   for (i = 0; i < numItems; i++, totalUnpacked += curUnpacked, totalPacked += curPacked)
842   {
843     lps->InSize = totalPacked;
844     lps->OutSize = totalUnpacked;
845     RINOK(lps->SetCur());
846 
847     curUnpacked = curPacked = 0;
848 
849     CMyComPtr<ISequentialOutStream> realOutStream;
850     Int32 askMode = testMode ?
851         NExtract::NAskMode::kTest :
852         NExtract::NAskMode::kExtract;
853     Int32 index = allFilesMode ? i : indices[i];
854     const CItem &item = _items[index];
855     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
856 
857     if (item.IsDir())
858     {
859       // if (!testMode)
860       {
861         RINOK(extractCallback->PrepareOperation(askMode));
862         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
863       }
864       continue;
865     }
866 
867     if (!testMode && !realOutStream)
868       continue;
869 
870     RINOK(extractCallback->PrepareOperation(askMode));
871     curUnpacked = item.Size;
872     curPacked = item.PackSize;
873 
874     {
875       COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
876       CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
877       outStreamSpec->SetStream(realOutStream);
878       realOutStream.Release();
879       outStreamSpec->Init();
880 
881       inStreamSpec->Init(item.PackSize);
882 
883       UInt64 pos;
884       _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos);
885 
886       HRESULT result = S_OK;
887       Int32 opRes = NExtract::NOperationResult::kOK;
888 
889       if (item.IsEncrypted())
890         opRes = NExtract::NOperationResult::kUnsupportedMethod;
891       else
892       {
893         switch (item.Method)
894         {
895           case NCompressionMethod::kStored:
896           {
897             result = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
898             if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize)
899               result = S_FALSE;
900             break;
901           }
902           case NCompressionMethod::kCompressed1a:
903           case NCompressionMethod::kCompressed1b:
904           case NCompressionMethod::kCompressed1c:
905           {
906             if (!lzhDecoder)
907             {
908               lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder;
909               lzhDecoder = lzhDecoderSpec;
910             }
911             lzhDecoderSpec->FinishMode = true;
912             const UInt32 kHistorySize = 26624;
913             lzhDecoderSpec->SetDictSize(kHistorySize);
914             result = lzhDecoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
915             if (result == S_OK && lzhDecoderSpec->GetInputProcessedSize() != item.PackSize)
916               result = S_FALSE;
917             break;
918           }
919           case NCompressionMethod::kCompressed2:
920           {
921             if (!arjDecoder)
922             {
923               arjDecoderSpec = new NCompress::NArj::NDecoder::CCoder;
924               arjDecoder = arjDecoderSpec;
925             }
926             arjDecoderSpec->FinishMode = true;
927             result = arjDecoder->Code(inStream, outStream, NULL, &curUnpacked, progress);
928             if (result == S_OK && arjDecoderSpec->GetInputProcessedSize() != item.PackSize)
929               result = S_FALSE;
930             break;
931           }
932           default:
933             opRes = NExtract::NOperationResult::kUnsupportedMethod;
934         }
935       }
936 
937       if (opRes == NExtract::NOperationResult::kOK)
938       {
939         if (result == S_FALSE)
940           opRes = NExtract::NOperationResult::kDataError;
941         else
942         {
943           RINOK(result);
944           opRes = (outStreamSpec->GetCRC() == item.FileCRC) ?
945               NExtract::NOperationResult::kOK:
946               NExtract::NOperationResult::kCRCError;
947         }
948       }
949 
950       outStream.Release();
951       RINOK(extractCallback->SetOperationResult(opRes));
952     }
953   }
954 
955   return S_OK;
956   COM_TRY_END
957 }
958 
959 static const Byte k_Signature[] = { kSig0, kSig1 };
960 
961 REGISTER_ARC_I(
962   "Arj", "arj", 0, 4,
963   k_Signature,
964   0,
965   0,
966   IsArc_Arj)
967 
968 }}
969