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