1 // OpenArchive.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #endif
10 
11 #include "../../../../C/CpuArch.h"
12 
13 #include "../../../Common/ComTry.h"
14 #include "../../../Common/IntToString.h"
15 #include "../../../Common/StringConvert.h"
16 #include "../../../Common/StringToInt.h"
17 #include "../../../Common/UTFConvert.h"
18 #include "../../../Common/Wildcard.h"
19 
20 #include "../../../Windows/FileDir.h"
21 
22 #include "../../Common/FileStreams.h"
23 #include "../../Common/LimitedStreams.h"
24 #include "../../Common/ProgressUtils.h"
25 #include "../../Common/StreamUtils.h"
26 
27 #include "../../Compress/CopyCoder.h"
28 
29 #include "DefaultName.h"
30 #include "OpenArchive.h"
31 
32 #ifndef Z7_SFX
33 #include "SetProperties.h"
34 #endif
35 
36 #ifndef Z7_SFX
37 #ifdef SHOW_DEBUG_INFO
38 #define PRF(x) x
39 #else
40 #define PRF(x)
41 #endif
42 #endif
43 
44 // increase it, if you need to support larger SFX stubs
45 static const UInt64 kMaxCheckStartPosition = 1 << 23;
46 
47 /*
48 Open:
49   - formatIndex >= 0 (exact Format)
50        1) Open with main type. Archive handler is allowed to use archive start finder.
51           Warning, if there is tail.
52 
53   - formatIndex = -1 (Parser:0) (default)
54     - same as #1 but doesn't return Parser
55 
56   - formatIndex = -2 (#1)
57     - file has supported extension (like a.7z)
58       Open with that main type (only starting from start of file).
59         - open OK:
60             - if there is no tail - return OK
61             - if there is tail:
62               - archive is not "Self Exe" - return OK with Warning, that there is tail
63               - archive is "Self Exe"
64                 ignore "Self Exe" stub, and tries to open tail
65                   - tail can be open as archive - shows that archive and stub size property.
66                   - tail can't be open as archive - shows Parser ???
67         - open FAIL:
68            Try to open with all other types from offset 0 only.
69            If some open type is OK and physical archive size is uequal or larger
70            than file size, then return that archive with warning that cannot be open as [extension type].
71            If extension was EXE, it will try to open as unknown_extension case
72     - file has unknown extension (like a.hhh)
73        It tries to open via parser code.
74          - if there is full archive or tail archive and unknown block or "Self Exe"
75            at front, it shows tail archive and stub size property.
76          - in another cases, if there is some archive inside file, it returns parser/
77          - in another cases, it retuens S_FALSE
78 
79 
80   - formatIndex = -3 (#2)
81     - same as #1, but
82     - stub (EXE) + archive is open in Parser
83 
84   - formatIndex = -4 (#3)
85     - returns only Parser. skip full file archive. And show other sub-archives
86 
87   - formatIndex = -5 (#4)
88     - returns only Parser. skip full file archive. And show other sub-archives for each byte pos
89 
90 */
91 
92 
93 
94 
95 using namespace NWindows;
96 
97 /*
98 #ifdef Z7_SFX
99 #define OPEN_PROPS_PARAM
100 #else
101 #define OPEN_PROPS_PARAM  , props
102 #endif
103 */
104 
105 /*
106 CArc::~CArc()
107 {
108   GetRawProps.Release();
109   Archive.Release();
110   printf("\nCArc::~CArc()\n");
111 }
112 */
113 
114 #ifndef Z7_SFX
115 
116 namespace NArchive {
117 namespace NParser {
118 
119 struct CParseItem
120 {
121   UInt64 Offset;
122   UInt64 Size;
123   // UInt64 OkSize;
124   UString Name;
125   UString Extension;
126   FILETIME FileTime;
127   UString Comment;
128   UString ArcType;
129 
130   bool FileTime_Defined;
131   bool UnpackSize_Defined;
132   bool NumSubDirs_Defined;
133   bool NumSubFiles_Defined;
134 
135   bool IsSelfExe;
136   bool IsNotArcType;
137 
138   UInt64 UnpackSize;
139   UInt64 NumSubDirs;
140   UInt64 NumSubFiles;
141 
142   int FormatIndex;
143 
144   bool LenIsUnknown;
145 
CParseItemNArchive::NParser::CParseItem146   CParseItem():
147       // OkSize(0),
148       FileTime_Defined(false),
149       UnpackSize_Defined(false),
150       NumSubDirs_Defined(false),
151       NumSubFiles_Defined(false),
152       IsSelfExe(false),
153       IsNotArcType(false),
154       LenIsUnknown(false)
155     {}
156 
157   /*
158   bool IsEqualTo(const CParseItem &item) const
159   {
160     return Offset == item.Offset && Size == item.Size;
161   }
162   */
163 
NormalizeOffsetNArchive::NParser::CParseItem164   void NormalizeOffset()
165   {
166     if ((Int64)Offset < 0)
167     {
168       Size += Offset;
169       // OkSize += Offset;
170       Offset = 0;
171     }
172   }
173 };
174 
175 Z7_CLASS_IMP_CHandler_IInArchive_1(
176   IInArchiveGetStream
177 )
178 public:
179   CObjectVector<CParseItem> _items;
180   UInt64 _maxEndOffset;
181   CMyComPtr<IInStream> _stream;
182 
GetLastEnd() const183   UInt64 GetLastEnd() const
184   {
185     if (_items.IsEmpty())
186       return 0;
187     const CParseItem &back = _items.Back();
188     return back.Offset + back.Size;
189   }
190 
191   void AddUnknownItem(UInt64 next);
192   int FindInsertPos(const CParseItem &item) const;
193   void AddItem(const CParseItem &item);
194 
CHandler()195   CHandler(): _maxEndOffset(0) {}
196 };
197 
FindInsertPos(const CParseItem &item) const198 int CHandler::FindInsertPos(const CParseItem &item) const
199 {
200   unsigned left = 0, right = _items.Size();
201   while (left != right)
202   {
203     const unsigned mid = (unsigned)(((size_t)left + (size_t)right) / 2);
204     const CParseItem &midItem = _items[mid];
205     if (item.Offset < midItem.Offset)
206       right = mid;
207     else if (item.Offset > midItem.Offset)
208       left = mid + 1;
209     else if (item.Size < midItem.Size)
210       right = mid;
211     /*
212     else if (item.Size > midItem.Size)
213       left = mid + 1;
214     */
215     else
216     {
217       left = mid + 1;
218       // return -1;
219     }
220   }
221   return (int)left;
222 }
223 
AddUnknownItem(UInt64 next)224 void CHandler::AddUnknownItem(UInt64 next)
225 {
226   /*
227   UInt64 prevEnd = 0;
228   if (!_items.IsEmpty())
229   {
230     const CParseItem &back = _items.Back();
231     prevEnd = back.Offset + back.Size;
232   }
233   */
234   if (_maxEndOffset < next)
235   {
236     CParseItem item2;
237     item2.Offset = _maxEndOffset;
238     item2.Size = next - _maxEndOffset;
239     _maxEndOffset = next;
240     _items.Add(item2);
241   }
242   else if (_maxEndOffset > next && !_items.IsEmpty())
243   {
244     CParseItem &back = _items.Back();
245     if (back.LenIsUnknown)
246     {
247       back.Size = next - back.Offset;
248       _maxEndOffset = next;
249     }
250   }
251 }
252 
AddItem(const CParseItem &item)253 void CHandler::AddItem(const CParseItem &item)
254 {
255   AddUnknownItem(item.Offset);
256   const int pos = FindInsertPos(item);
257   if (pos != -1)
258   {
259     _items.Insert((unsigned)pos, item);
260     UInt64 next = item.Offset + item.Size;
261     if (_maxEndOffset < next)
262       _maxEndOffset = next;
263   }
264 }
265 
266 /*
267 static const CStatProp kProps[] =
268 {
269   { NULL, kpidPath, VT_BSTR},
270   { NULL, kpidSize, VT_UI8},
271   { NULL, kpidMTime, VT_FILETIME},
272   { NULL, kpidType, VT_BSTR},
273   { NULL, kpidComment, VT_BSTR},
274   { NULL, kpidOffset, VT_UI8},
275   { NULL, kpidUnpackSize, VT_UI8},
276 //   { NULL, kpidNumSubDirs, VT_UI8},
277 };
278 */
279 
280 static const Byte kProps[] =
281 {
282   kpidPath,
283   kpidSize,
284   kpidMTime,
285   kpidType,
286   kpidComment,
287   kpidOffset,
288   kpidUnpackSize
289 };
290 
291 IMP_IInArchive_Props
292 IMP_IInArchive_ArcProps_NO
293 
Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * )294 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */))
295 {
296   COM_TRY_BEGIN
297   {
298     Close();
299     _stream = stream;
300   }
301   return S_OK;
302   COM_TRY_END
303 }
304 
Close()305 Z7_COM7F_IMF(CHandler::Close())
306 {
307   _items.Clear();
308   _stream.Release();
309   return S_OK;
310 }
311 
GetNumberOfItems(UInt32 *numItems)312 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
313 {
314   *numItems = _items.Size();
315   return S_OK;
316 }
317 
GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)318 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
319 {
320   COM_TRY_BEGIN
321   NCOM::CPropVariant prop;
322 
323   const CParseItem &item = _items[index];
324 
325   switch (propID)
326   {
327     case kpidPath:
328     {
329       char sz[32];
330       ConvertUInt32ToString(index + 1, sz);
331       UString s(sz);
332       if (!item.Name.IsEmpty())
333       {
334         s.Add_Dot();
335         s += item.Name;
336       }
337       if (!item.Extension.IsEmpty())
338       {
339         s.Add_Dot();
340         s += item.Extension;
341       }
342       prop = s; break;
343     }
344     case kpidSize:
345     case kpidPackSize: prop = item.Size; break;
346     case kpidOffset: prop = item.Offset; break;
347     case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break;
348     case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break;
349     case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break;
350     case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break;
351     case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break;
352     case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break;
353   }
354   prop.Detach(value);
355   return S_OK;
356   COM_TRY_END
357 }
358 
Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback)359 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
360     Int32 testMode, IArchiveExtractCallback *extractCallback))
361 {
362   COM_TRY_BEGIN
363 
364   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
365   if (allFilesMode)
366     numItems = _items.Size();
367   if (_stream && numItems == 0)
368     return S_OK;
369   UInt64 totalSize = 0;
370   UInt32 i;
371   for (i = 0; i < numItems; i++)
372     totalSize += _items[allFilesMode ? i : indices[i]].Size;
373   extractCallback->SetTotal(totalSize);
374 
375   totalSize = 0;
376 
377   CLocalProgress *lps = new CLocalProgress;
378   CMyComPtr<ICompressProgressInfo> progress = lps;
379   lps->Init(extractCallback, false);
380 
381   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
382   CMyComPtr<ISequentialInStream> inStream(streamSpec);
383   streamSpec->SetStream(_stream);
384 
385   CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
386   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
387 
388   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
389   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
390 
391   for (i = 0; i < numItems; i++)
392   {
393     lps->InSize = totalSize;
394     lps->OutSize = totalSize;
395     RINOK(lps->SetCur())
396     CMyComPtr<ISequentialOutStream> realOutStream;
397     const Int32 askMode = testMode ?
398         NExtract::NAskMode::kTest :
399         NExtract::NAskMode::kExtract;
400     const UInt32 index = allFilesMode ? i : indices[i];
401     const CParseItem &item = _items[index];
402 
403     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
404     UInt64 unpackSize = item.Size;
405     totalSize += unpackSize;
406     bool skipMode = false;
407     if (!testMode && !realOutStream)
408       continue;
409     RINOK(extractCallback->PrepareOperation(askMode))
410 
411     outStreamSpec->SetStream(realOutStream);
412     realOutStream.Release();
413     outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
414 
415     Int32 opRes = NExtract::NOperationResult::kOK;
416     RINOK(InStream_SeekSet(_stream, item.Offset))
417     streamSpec->Init(unpackSize);
418     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
419 
420     if (outStreamSpec->GetRem() != 0)
421       opRes = NExtract::NOperationResult::kDataError;
422     outStreamSpec->ReleaseStream();
423     RINOK(extractCallback->SetOperationResult(opRes))
424   }
425 
426   return S_OK;
427 
428   COM_TRY_END
429 }
430 
431 
GetStream(UInt32 index, ISequentialInStream **stream)432 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
433 {
434   COM_TRY_BEGIN
435   const CParseItem &item = _items[index];
436   return CreateLimitedInStream(_stream, item.Offset, item.Size, stream);
437   COM_TRY_END
438 }
439 
440 }}
441 
442 #endif
443 
444 HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw()
445 {
446   NCOM::CPropVariant prop;
447   result = false;
448   RINOK(arc->GetProperty(index, propID, &prop))
449   if (prop.vt == VT_BOOL)
450     result = VARIANT_BOOLToBool(prop.boolVal);
451   else if (prop.vt != VT_EMPTY)
452     return E_FAIL;
453   return S_OK;
454 }
455 
456 HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw()
457 {
458   return Archive_GetItemBoolProp(arc, index, kpidIsDir, result);
459 }
460 
461 HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw()
462 {
463   return Archive_GetItemBoolProp(arc, index, kpidIsAux, result);
464 }
465 
466 HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw()
467 {
468   return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result);
469 }
470 
471 HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw()
472 {
473   return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result);
474 }
475 
476 static HRESULT Archive_GetArcProp_Bool(IInArchive *arc, PROPID propid, bool &result) throw()
477 {
478   NCOM::CPropVariant prop;
479   result = false;
480   RINOK(arc->GetArchiveProperty(propid, &prop))
481   if (prop.vt == VT_BOOL)
482     result = VARIANT_BOOLToBool(prop.boolVal);
483   else if (prop.vt != VT_EMPTY)
484     return E_FAIL;
485   return S_OK;
486 }
487 
488 static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined)
489 {
490   defined = false;
491   NCOM::CPropVariant prop;
492   RINOK(arc->GetArchiveProperty(propid, &prop))
493   switch (prop.vt)
494   {
495     case VT_UI4: result = prop.ulVal; break;
496     case VT_I4:  result = (UInt64)(Int64)prop.lVal; break;
497     case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; break;
498     case VT_I8:  result = (UInt64)prop.hVal.QuadPart; break;
499     case VT_EMPTY: return S_OK;
500     default: return E_FAIL;
501   }
502   defined = true;
503   return S_OK;
504 }
505 
506 static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined)
507 {
508   defined = false;
509   NCOM::CPropVariant prop;
510   RINOK(arc->GetArchiveProperty(propid, &prop))
511   switch (prop.vt)
512   {
513     case VT_UI4: result = prop.ulVal; break;
514     case VT_I4:  result = prop.lVal; break;
515     case VT_UI8: result = (Int64)prop.uhVal.QuadPart; break;
516     case VT_I8:  result = (Int64)prop.hVal.QuadPart; break;
517     case VT_EMPTY: return S_OK;
518     default: return E_FAIL;
519   }
520   defined = true;
521   return S_OK;
522 }
523 
524 #ifndef Z7_SFX
525 
526 HRESULT CArc::GetItem_PathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const
527 {
528   if (!GetRawProps)
529     return E_FAIL;
530   if (index == parent)
531     return S_OK;
532   UInt32 curIndex = index;
533 
534   UString s;
535 
536   bool prevWasAltStream = false;
537 
538   for (;;)
539   {
540     #ifdef MY_CPU_LE
541     const void *p;
542     UInt32 size;
543     UInt32 propType;
544     RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType))
545     if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE)
546       s = (const wchar_t *)p;
547     else
548     #endif
549     {
550       NCOM::CPropVariant prop;
551       RINOK(Archive->GetProperty(curIndex, kpidName, &prop))
552       if (prop.vt == VT_BSTR && prop.bstrVal)
553         s.SetFromBstr(prop.bstrVal);
554       else if (prop.vt == VT_EMPTY)
555         s.Empty();
556       else
557         return E_FAIL;
558     }
559 
560     UInt32 curParent = (UInt32)(Int32)-1;
561     UInt32 parentType = 0;
562     RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType))
563 
564     // 18.06: fixed : we don't want to split name to parts
565     /*
566     if (parentType != NParentType::kAltStream)
567     {
568       for (;;)
569       {
570         int pos = s.ReverseFind_PathSepar();
571         if (pos < 0)
572         {
573           break;
574         }
575         parts.Insert(0, s.Ptr(pos + 1));
576         s.DeleteFrom(pos);
577       }
578     }
579     */
580 
581     parts.Insert(0, s);
582 
583     if (prevWasAltStream)
584     {
585       {
586         UString &s2 = parts[parts.Size() - 2];
587         s2 += ':';
588         s2 += parts.Back();
589       }
590       parts.DeleteBack();
591     }
592 
593     if (parent == curParent)
594       return S_OK;
595 
596     prevWasAltStream = false;
597     if (parentType == NParentType::kAltStream)
598       prevWasAltStream = true;
599 
600     if (curParent == (UInt32)(Int32)-1)
601       return E_FAIL;
602     curIndex = curParent;
603   }
604 }
605 
606 #endif
607 
608 
609 
610 HRESULT CArc::GetItem_Path(UInt32 index, UString &result) const
611 {
612   #ifdef MY_CPU_LE
613   if (GetRawProps)
614   {
615     const void *p;
616     UInt32 size;
617     UInt32 propType;
618     if (!IsTree)
619     {
620       if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK &&
621           propType == NPropDataType::kUtf16z)
622       {
623         unsigned len = size / 2 - 1;
624         // (len) doesn't include null terminator
625 
626         /*
627         #if WCHAR_MAX > 0xffff
628         len = (unsigned)Utf16LE__Get_Num_WCHARs(p, len);
629 
630         wchar_t *s = result.GetBuf(len);
631         wchar_t *sEnd = Utf16LE__To_WCHARs_Sep(p, len, s);
632         if (s + len != sEnd) return E_FAIL;
633         *sEnd = 0;
634 
635         #else
636         */
637 
638         wchar_t *s = result.GetBuf(len);
639         for (unsigned i = 0; i < len; i++)
640         {
641           wchar_t c = GetUi16(p);
642           p = (const void *)((const Byte *)p + 2);
643 
644           #if WCHAR_PATH_SEPARATOR != L'/'
645           if (c == L'/')
646             c = WCHAR_PATH_SEPARATOR;
647           else if (c == L'\\')
648             c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme
649           #endif
650 
651           *s++ = c;
652         }
653         *s = 0;
654 
655         // #endif
656 
657         result.ReleaseBuf_SetLen(len);
658 
659         Convert_UnicodeEsc16_To_UnicodeEscHigh(result);
660         if (len != 0)
661           return S_OK;
662       }
663     }
664     /*
665     else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK &&
666         p && propType == NPropDataType::kUtf16z)
667     {
668       size -= 2;
669       UInt32 totalSize = size;
670       bool isOK = false;
671 
672       {
673         UInt32 index2 = index;
674         for (;;)
675         {
676           UInt32 parent = (UInt32)(Int32)-1;
677           UInt32 parentType = 0;
678           if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
679             break;
680           if (parent == (UInt32)(Int32)-1)
681           {
682             if (parentType != 0)
683               totalSize += 2;
684             isOK = true;
685             break;
686           }
687           index2 = parent;
688           UInt32 size2;
689           const void *p2;
690           if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK &&
691               p2 && propType == NPropDataType::kUtf16z)
692             break;
693           totalSize += size2;
694         }
695       }
696 
697       if (isOK)
698       {
699         wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2);
700         UInt32 pos = totalSize - size;
701         memcpy((Byte *)sz + pos, p, size);
702         UInt32 index2 = index;
703         for (;;)
704         {
705           UInt32 parent = (UInt32)(Int32)-1;
706           UInt32 parentType = 0;
707           if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
708             break;
709           if (parent == (UInt32)(Int32)-1)
710           {
711             if (parentType != 0)
712               sz[pos / 2 - 1] = L':';
713             break;
714           }
715           index2 = parent;
716           UInt32 size2;
717           const void *p2;
718           if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK)
719             break;
720           pos -= size2;
721           memcpy((Byte *)sz + pos, p2, size2);
722           sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':';
723         }
724         #ifdef _WIN32
725         // result.Replace(L'/', WCHAR_PATH_SEPARATOR);
726         #endif
727         return S_OK;
728       }
729     }
730     */
731   }
732   #endif
733 
734   {
735     NCOM::CPropVariant prop;
736     RINOK(Archive->GetProperty(index, kpidPath, &prop))
737     if (prop.vt == VT_BSTR && prop.bstrVal)
738       result.SetFromBstr(prop.bstrVal);
739     else if (prop.vt == VT_EMPTY)
740       result.Empty();
741     else
742       return E_FAIL;
743   }
744 
745   if (result.IsEmpty())
746     return GetItem_DefaultPath(index, result);
747 
748   Convert_UnicodeEsc16_To_UnicodeEscHigh(result);
749   return S_OK;
750 }
751 
752 HRESULT CArc::GetItem_DefaultPath(UInt32 index, UString &result) const
753 {
754   result.Empty();
755   bool isDir;
756   RINOK(Archive_IsItem_Dir(Archive, index, isDir))
757   if (!isDir)
758   {
759     result = DefaultName;
760     NCOM::CPropVariant prop;
761     RINOK(Archive->GetProperty(index, kpidExtension, &prop))
762     if (prop.vt == VT_BSTR)
763     {
764       result.Add_Dot();
765       result += prop.bstrVal;
766     }
767     else if (prop.vt != VT_EMPTY)
768       return E_FAIL;
769   }
770   return S_OK;
771 }
772 
773 HRESULT CArc::GetItem_Path2(UInt32 index, UString &result) const
774 {
775   RINOK(GetItem_Path(index, result))
776   if (Ask_Deleted)
777   {
778     bool isDeleted = false;
779     RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted))
780     if (isDeleted)
781       result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR);
782   }
783   return S_OK;
784 }
785 
786 #ifdef SUPPORT_ALT_STREAMS
787 
788 int FindAltStreamColon_in_Path(const wchar_t *path)
789 {
790   unsigned i = 0;
791   int colonPos = -1;
792   for (;; i++)
793   {
794     wchar_t c = path[i];
795     if (c == 0)
796       return colonPos;
797     if (c == ':')
798     {
799       if (colonPos < 0)
800         colonPos = (int)i;
801       continue;
802     }
803     if (c == WCHAR_PATH_SEPARATOR)
804       colonPos = -1;
805   }
806 }
807 
808 #endif
809 
810 HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const
811 {
812   #ifdef SUPPORT_ALT_STREAMS
813   item.IsAltStream = false;
814   item.AltStreamName.Empty();
815   item.MainPath.Empty();
816   #endif
817 
818   item.IsDir = false;
819   item.Path.Empty();
820   item.ParentIndex = (UInt32)(Int32)-1;
821 
822   item.PathParts.Clear();
823 
824   RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir))
825   item.MainIsDir = item.IsDir;
826 
827   RINOK(GetItem_Path2(index, item.Path))
828 
829   #ifndef Z7_SFX
830   UInt32 mainIndex = index;
831   #endif
832 
833   #ifdef SUPPORT_ALT_STREAMS
834 
835   item.MainPath = item.Path;
836   if (Ask_AltStream)
837   {
838     RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream))
839   }
840 
841   bool needFindAltStream = false;
842 
843   if (item.IsAltStream)
844   {
845     needFindAltStream = true;
846     if (GetRawProps)
847     {
848       UInt32 parentType = 0;
849       UInt32 parentIndex;
850       RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType))
851       if (parentType == NParentType::kAltStream)
852       {
853         NCOM::CPropVariant prop;
854         RINOK(Archive->GetProperty(index, kpidName, &prop))
855         if (prop.vt == VT_BSTR && prop.bstrVal)
856           item.AltStreamName.SetFromBstr(prop.bstrVal);
857         else if (prop.vt != VT_EMPTY)
858           return E_FAIL;
859         else
860         {
861           // item.IsAltStream = false;
862         }
863         /*
864         if (item.AltStreamName.IsEmpty())
865           item.IsAltStream = false;
866         */
867 
868         needFindAltStream = false;
869         item.ParentIndex = parentIndex;
870         mainIndex = parentIndex;
871 
872         if (parentIndex == (UInt32)(Int32)-1)
873         {
874           item.MainPath.Empty();
875           item.MainIsDir = true;
876         }
877         else
878         {
879           RINOK(GetItem_Path2(parentIndex, item.MainPath))
880           RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir))
881         }
882       }
883     }
884   }
885 
886   if (item.WriteToAltStreamIfColon || needFindAltStream)
887   {
888     /* Good handler must support GetRawProps::GetParent for alt streams.
889        So the following code currently is not used */
890     int colon = FindAltStreamColon_in_Path(item.Path);
891     if (colon >= 0)
892     {
893       item.MainPath.DeleteFrom((unsigned)colon);
894       item.AltStreamName = item.Path.Ptr((unsigned)(colon + 1));
895       item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[(unsigned)colon - 1]));
896       item.IsAltStream = true;
897     }
898   }
899 
900   #endif
901 
902   #ifndef Z7_SFX
903   if (item._use_baseParentFolder_mode)
904   {
905     RINOK(GetItem_PathToParent(mainIndex, (unsigned)item._baseParentFolder, item.PathParts))
906 
907     #ifdef SUPPORT_ALT_STREAMS
908     if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty())
909     {
910       int colon;
911       {
912         UString &s = item.PathParts.Back();
913         colon = FindAltStreamColon_in_Path(s);
914         if (colon >= 0)
915         {
916           item.AltStreamName = s.Ptr((unsigned)(colon + 1));
917           item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1]));
918           item.IsAltStream = true;
919           s.DeleteFrom((unsigned)colon);
920         }
921       }
922       if (colon == 0)
923         item.PathParts.DeleteBack();
924     }
925     #endif
926 
927   }
928   else
929   #endif
930     SplitPathToParts(
931           #ifdef SUPPORT_ALT_STREAMS
932             item.MainPath
933           #else
934             item.Path
935           #endif
936       , item.PathParts);
937 
938   return S_OK;
939 }
940 
941 #ifndef Z7_SFX
942 
943 static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined)
944 {
945   NCOM::CPropVariant prop;
946   defined = false;
947   size = 0;
948   RINOK(archive->GetProperty(index, kpidSize, &prop))
949   switch (prop.vt)
950   {
951     case VT_UI1: size = prop.bVal; break;
952     case VT_UI2: size = prop.uiVal; break;
953     case VT_UI4: size = prop.ulVal; break;
954     case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
955     case VT_EMPTY: return S_OK;
956     default: return E_FAIL;
957   }
958   defined = true;
959   return S_OK;
960 }
961 
962 #endif
963 
964 HRESULT CArc::GetItem_Size(UInt32 index, UInt64 &size, bool &defined) const
965 {
966   NCOM::CPropVariant prop;
967   defined = false;
968   size = 0;
969   RINOK(Archive->GetProperty(index, kpidSize, &prop))
970   switch (prop.vt)
971   {
972     case VT_UI1: size = prop.bVal; break;
973     case VT_UI2: size = prop.uiVal; break;
974     case VT_UI4: size = prop.ulVal; break;
975     case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
976     case VT_EMPTY: return S_OK;
977     default: return E_FAIL;
978   }
979   defined = true;
980   return S_OK;
981 }
982 
983 HRESULT CArc::GetItem_MTime(UInt32 index, CArcTime &at) const
984 {
985   at.Clear();
986   NCOM::CPropVariant prop;
987   RINOK(Archive->GetProperty(index, kpidMTime, &prop))
988 
989   if (prop.vt == VT_FILETIME)
990   {
991     /*
992     // for debug
993     if (FILETIME_IsZero(prop.at) && MTime.Def)
994     {
995       at = MTime;
996       return S_OK;
997     }
998     */
999     at.Set_From_Prop(prop);
1000     if (at.Prec == 0)
1001     {
1002       // (at.Prec == 0) before version 22.
1003       // so kpidTimeType is required for that code
1004       prop.Clear();
1005       RINOK(Archive->GetProperty(index, kpidTimeType, &prop))
1006       if (prop.vt == VT_UI4)
1007       {
1008         UInt32 val = prop.ulVal;
1009         if (val == NFileTimeType::kWindows)
1010           val = k_PropVar_TimePrec_100ns;
1011         /*
1012         else if (val > k_PropVar_TimePrec_1ns)
1013         {
1014           val = k_PropVar_TimePrec_100ns;
1015           // val = k_PropVar_TimePrec_1ns;
1016           // return E_FAIL; // for debug
1017         }
1018         */
1019         at.Prec = (UInt16)val;
1020       }
1021     }
1022     return S_OK;
1023   }
1024 
1025   if (prop.vt != VT_EMPTY)
1026     return E_FAIL;
1027   if (MTime.Def)
1028     at = MTime;
1029   return S_OK;
1030 }
1031 
1032 #ifndef Z7_SFX
1033 
1034 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
1035 {
1036   for (size_t i = 0; i < size; i++)
1037     if (p1[i] != p2[i])
1038       return false;
1039   return true;
1040 }
1041 
1042 
1043 static void MakeCheckOrder(CCodecs *codecs,
1044     CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2,
1045     const Byte *data, size_t dataSize)
1046 {
1047   for (unsigned i = 0; i < numTypes; i++)
1048   {
1049     const int index = orderIndices[i];
1050     if (index < 0)
1051       continue;
1052     const CArcInfoEx &ai = codecs->Formats[(unsigned)index];
1053     if (ai.SignatureOffset == 0)
1054     {
1055       if (ai.Signatures.IsEmpty())
1056       {
1057         if (dataSize != 0) // 21.04: no Signature means Empty Signature
1058           continue;
1059       }
1060       else
1061       {
1062         unsigned k;
1063         const CObjectVector<CByteBuffer> &sigs = ai.Signatures;
1064         for (k = 0; k < sigs.Size(); k++)
1065         {
1066           const CByteBuffer &sig = sigs[k];
1067           if (sig.Size() <= dataSize && TestSignature(data, sig, sig.Size()))
1068             break;
1069         }
1070         if (k == sigs.Size())
1071           continue;
1072       }
1073     }
1074     orderIndices2.Add(index);
1075     orderIndices[i] = -1;
1076   }
1077 }
1078 
1079 #ifdef UNDER_CE
1080   static const unsigned kNumHashBytes = 1;
1081   #define HASH_VAL(buf) ((buf)[0])
1082 #else
1083   static const unsigned kNumHashBytes = 2;
1084   // #define HASH_VAL(buf) ((buf)[0] | ((UInt32)(buf)[1] << 8))
1085   #define HASH_VAL(buf) GetUi16(buf)
1086 #endif
1087 
1088 static bool IsExeExt(const UString &ext)
1089 {
1090   return ext.IsEqualTo_Ascii_NoCase("exe");
1091 }
1092 
1093 static const char * const k_PreArcFormats[] =
1094 {
1095     "pe"
1096   , "elf"
1097   , "macho"
1098   , "mub"
1099   , "te"
1100 };
1101 
1102 static bool IsNameFromList(const UString &s, const char * const names[], size_t num)
1103 {
1104   for (unsigned i = 0; i < num; i++)
1105     if (StringsAreEqualNoCase_Ascii(s, names[i]))
1106       return true;
1107   return false;
1108 }
1109 
1110 
1111 static bool IsPreArcFormat(const CArcInfoEx &ai)
1112 {
1113   if (ai.Flags_PreArc())
1114     return true;
1115   return IsNameFromList(ai.Name, k_PreArcFormats, Z7_ARRAY_SIZE(k_PreArcFormats));
1116 }
1117 
1118 static const char * const k_Formats_with_simple_signuature[] =
1119 {
1120     "7z"
1121   , "xz"
1122   , "rar"
1123   , "bzip2"
1124   , "gzip"
1125   , "cab"
1126   , "wim"
1127   , "rpm"
1128   , "vhd"
1129   , "xar"
1130 };
1131 
1132 static bool IsNewStyleSignature(const CArcInfoEx &ai)
1133 {
1134   // if (ai.Version >= 0x91F)
1135   if (ai.NewInterface)
1136     return true;
1137   return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, Z7_ARRAY_SIZE(k_Formats_with_simple_signuature));
1138 }
1139 
1140 
1141 
1142 class CArchiveOpenCallback_Offset Z7_final:
1143   public IArchiveOpenCallback,
1144   public IArchiveOpenVolumeCallback,
1145  #ifndef Z7_NO_CRYPTO
1146   public ICryptoGetTextPassword,
1147  #endif
1148   public CMyUnknownImp
1149 {
1150   Z7_COM_QI_BEGIN2(IArchiveOpenCallback)
1151   Z7_COM_QI_ENTRY(IArchiveOpenVolumeCallback)
1152  #ifndef Z7_NO_CRYPTO
1153   Z7_COM_QI_ENTRY(ICryptoGetTextPassword)
1154  #endif
1155   Z7_COM_QI_END
1156   Z7_COM_ADDREF_RELEASE
1157 
1158   Z7_IFACE_COM7_IMP(IArchiveOpenCallback)
1159   Z7_IFACE_COM7_IMP(IArchiveOpenVolumeCallback)
1160  #ifndef Z7_NO_CRYPTO
1161   Z7_IFACE_COM7_IMP(ICryptoGetTextPassword)
1162  #endif
1163 
1164 public:
1165   CMyComPtr<IArchiveOpenCallback> Callback;
1166   CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback;
1167   UInt64 Files;
1168   UInt64 Offset;
1169 
1170   #ifndef Z7_NO_CRYPTO
1171   CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
1172   #endif
1173 };
1174 
1175 #ifndef Z7_NO_CRYPTO
1176 Z7_COM7F_IMF(CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password))
1177 {
1178   COM_TRY_BEGIN
1179   if (GetTextPassword)
1180     return GetTextPassword->CryptoGetTextPassword(password);
1181   return E_NOTIMPL;
1182   COM_TRY_END
1183 }
1184 #endif
1185 
1186 Z7_COM7F_IMF(CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *))
1187 {
1188   return S_OK;
1189 }
1190 
1191 Z7_COM7F_IMF(CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes))
1192 {
1193   if (!Callback)
1194     return S_OK;
1195   UInt64 value = Offset;
1196   if (bytes)
1197     value += *bytes;
1198   return Callback->SetCompleted(&Files, &value);
1199 }
1200 
1201 Z7_COM7F_IMF(CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value))
1202 {
1203   if (OpenVolumeCallback)
1204     return OpenVolumeCallback->GetProperty(propID, value);
1205   NCOM::PropVariant_Clear(value);
1206   return S_OK;
1207   // return E_NOTIMPL;
1208 }
1209 
1210 Z7_COM7F_IMF(CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream))
1211 {
1212   if (OpenVolumeCallback)
1213     return OpenVolumeCallback->GetStream(name, inStream);
1214   return S_FALSE;
1215 }
1216 
1217 #endif
1218 
1219 
1220 UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp)
1221 {
1222   if (isDefinedProp != NULL)
1223     *isDefinedProp = false;
1224 
1225   switch (prop.vt)
1226   {
1227     case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart;
1228     case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal;
1229     case VT_EMPTY: return 0;
1230     default: throw 151199;
1231   }
1232 }
1233 
1234 void CArcErrorInfo::ClearErrors()
1235 {
1236   // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!!
1237 
1238   ThereIsTail = false;
1239   UnexpecedEnd = false;
1240   IgnoreTail = false;
1241   // NonZerosTail = false;
1242   ErrorFlags_Defined = false;
1243   ErrorFlags = 0;
1244   WarningFlags = 0;
1245   TailSize = 0;
1246 
1247   ErrorMessage.Empty();
1248   WarningMessage.Empty();
1249 }
1250 
1251 HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes)
1252 {
1253   // OkPhySize_Defined = false;
1254   PhySize_Defined = false;
1255   PhySize = 0;
1256   Offset = 0;
1257   AvailPhySize = FileSize - startPos;
1258 
1259   ErrorInfo.ClearErrors();
1260   {
1261     NCOM::CPropVariant prop;
1262     RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop))
1263     ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined);
1264   }
1265   {
1266     NCOM::CPropVariant prop;
1267     RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop))
1268     ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop);
1269   }
1270 
1271   {
1272     NCOM::CPropVariant prop;
1273     RINOK(archive->GetArchiveProperty(kpidError, &prop))
1274     if (prop.vt != VT_EMPTY)
1275       ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown error");
1276   }
1277 
1278   {
1279     NCOM::CPropVariant prop;
1280     RINOK(archive->GetArchiveProperty(kpidWarning, &prop))
1281     if (prop.vt != VT_EMPTY)
1282       ErrorInfo.WarningMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown warning");
1283   }
1284 
1285   if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen())
1286   {
1287     RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySize_Defined))
1288     /*
1289     RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined));
1290     if (!OkPhySize_Defined)
1291     {
1292       OkPhySize_Defined = PhySize_Defined;
1293       OkPhySize = PhySize;
1294     }
1295     */
1296 
1297     bool offsetDefined;
1298     RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined))
1299 
1300     Int64 globalOffset = (Int64)startPos + Offset;
1301     AvailPhySize = (UInt64)((Int64)FileSize - globalOffset);
1302     if (PhySize_Defined)
1303     {
1304       UInt64 endPos = (UInt64)(globalOffset + (Int64)PhySize);
1305       if (endPos < FileSize)
1306       {
1307         AvailPhySize = PhySize;
1308         ErrorInfo.ThereIsTail = true;
1309         ErrorInfo.TailSize = FileSize - endPos;
1310       }
1311       else if (endPos > FileSize)
1312         ErrorInfo.UnexpecedEnd = true;
1313     }
1314   }
1315 
1316   return S_OK;
1317 }
1318 
1319 /*
1320 static void PrintNumber(const char *s, int n)
1321 {
1322   char temp[100];
1323   sprintf(temp, "%s %d", s, n);
1324   // OutputDebugStringA(temp);
1325   printf(temp);
1326 }
1327 */
1328 
1329 HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive)
1330 {
1331   // OutputDebugStringA("a1");
1332   // PrintNumber("formatIndex", formatIndex);
1333 
1334   RINOK(op.codecs->CreateInArchive(formatIndex, archive))
1335   // OutputDebugStringA("a2");
1336   if (!archive)
1337     return S_OK;
1338 
1339   #ifdef Z7_EXTERNAL_CODECS
1340   if (op.codecs->NeedSetLibCodecs)
1341   {
1342     const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1343     if (ai.LibIndex >= 0 ?
1344         !op.codecs->Libs[(unsigned)ai.LibIndex].SetCodecs :
1345         !op.codecs->Libs.IsEmpty())
1346     {
1347       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
1348       archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
1349       if (setCompressCodecsInfo)
1350       {
1351         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs))
1352       }
1353     }
1354   }
1355   #endif
1356 
1357 
1358   #ifndef Z7_SFX
1359 
1360   const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1361 
1362   // OutputDebugStringW(ai.Name);
1363   // OutputDebugStringA("a3");
1364 
1365   if (ai.Flags_PreArc())
1366   {
1367     /* we notify parsers that extract executables, that they don't need
1368        to open archive, if there is tail after executable (for SFX cases) */
1369     CMyComPtr<IArchiveAllowTail> allowTail;
1370     archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail);
1371     if (allowTail)
1372       allowTail->AllowTail(BoolToInt(true));
1373   }
1374 
1375   if (op.props)
1376   {
1377     /*
1378     FOR_VECTOR (y, op.props)
1379     {
1380       const COptionalOpenProperties &optProps = (*op.props)[y];
1381       if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0)
1382       {
1383         RINOK(SetProperties(archive, optProps.Props));
1384         break;
1385       }
1386     }
1387     */
1388     RINOK(SetProperties(archive, *op.props))
1389   }
1390 
1391   #endif
1392   return S_OK;
1393 }
1394 
1395 #ifndef Z7_SFX
1396 
1397 static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi)
1398 {
1399   pi.Extension = ai.GetMainExt();
1400   pi.FileTime_Defined = false;
1401   pi.ArcType = ai.Name;
1402 
1403   RINOK(Archive_GetArcProp_Bool(archive, kpidIsNotArcType, pi.IsNotArcType))
1404 
1405   // RINOK(Archive_GetArcProp_Bool(archive, kpidIsSelfExe, pi.IsSelfExe));
1406   pi.IsSelfExe = ai.Flags_PreArc();
1407 
1408   {
1409     NCOM::CPropVariant prop;
1410     RINOK(archive->GetArchiveProperty(kpidMTime, &prop))
1411     if (prop.vt == VT_FILETIME)
1412     {
1413       pi.FileTime_Defined = true;
1414       pi.FileTime = prop.filetime;
1415     }
1416   }
1417 
1418   if (!pi.FileTime_Defined)
1419   {
1420     NCOM::CPropVariant prop;
1421     RINOK(archive->GetArchiveProperty(kpidCTime, &prop))
1422     if (prop.vt == VT_FILETIME)
1423     {
1424       pi.FileTime_Defined = true;
1425       pi.FileTime = prop.filetime;
1426     }
1427   }
1428 
1429   {
1430     NCOM::CPropVariant prop;
1431     RINOK(archive->GetArchiveProperty(kpidName, &prop))
1432     if (prop.vt == VT_BSTR)
1433     {
1434       pi.Name.SetFromBstr(prop.bstrVal);
1435       pi.Extension.Empty();
1436     }
1437     else
1438     {
1439       RINOK(archive->GetArchiveProperty(kpidExtension, &prop))
1440       if (prop.vt == VT_BSTR)
1441         pi.Extension.SetFromBstr(prop.bstrVal);
1442     }
1443   }
1444 
1445   {
1446     NCOM::CPropVariant prop;
1447     RINOK(archive->GetArchiveProperty(kpidShortComment, &prop))
1448     if (prop.vt == VT_BSTR)
1449       pi.Comment.SetFromBstr(prop.bstrVal);
1450   }
1451 
1452 
1453   UInt32 numItems;
1454   RINOK(archive->GetNumberOfItems(&numItems))
1455 
1456   // pi.NumSubFiles = numItems;
1457   // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined));
1458   // if (!pi.UnpackSize_Defined)
1459   {
1460     pi.NumSubFiles = 0;
1461     pi.NumSubDirs = 0;
1462     pi.UnpackSize = 0;
1463     for (UInt32 i = 0; i < numItems; i++)
1464     {
1465       UInt64 size = 0;
1466       bool defined = false;
1467       Archive_GetItem_Size(archive, i, size, defined);
1468       if (defined)
1469       {
1470         pi.UnpackSize_Defined = true;
1471         pi.UnpackSize += size;
1472       }
1473 
1474       bool isDir = false;
1475       Archive_IsItem_Dir(archive, i, isDir);
1476       if (isDir)
1477         pi.NumSubDirs++;
1478       else
1479         pi.NumSubFiles++;
1480     }
1481     if (pi.NumSubDirs != 0)
1482       pi.NumSubDirs_Defined = true;
1483     pi.NumSubFiles_Defined = true;
1484   }
1485 
1486   return S_OK;
1487 }
1488 
1489 #endif
1490 
1491 HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset)
1492 {
1493   if (!op.stream)
1494     return S_OK;
1495   RINOK(InStream_SeekSet(op.stream, offset))
1496   const UInt32 kBufSize = 1 << 11;
1497   Byte buf[kBufSize];
1498 
1499   for (;;)
1500   {
1501     UInt32 processed = 0;
1502     RINOK(op.stream->Read(buf, kBufSize, &processed))
1503     if (processed == 0)
1504     {
1505       // ErrorInfo.NonZerosTail = false;
1506       ErrorInfo.IgnoreTail = true;
1507       return S_OK;
1508     }
1509     for (size_t i = 0; i < processed; i++)
1510     {
1511       if (buf[i] != 0)
1512       {
1513         // ErrorInfo.IgnoreTail = false;
1514         // ErrorInfo.NonZerosTail = true;
1515         return S_OK;
1516       }
1517     }
1518   }
1519 }
1520 
1521 
1522 
1523 #ifndef Z7_SFX
1524 
1525 Z7_CLASS_IMP_COM_2(
1526   CExtractCallback_To_OpenCallback
1527   , IArchiveExtractCallback
1528   , ICompressProgressInfo
1529 )
1530   Z7_IFACE_COM7_IMP(IProgress)
1531 public:
1532   CMyComPtr<IArchiveOpenCallback> Callback;
1533   UInt64 Files;
1534   UInt64 Offset;
1535 
1536   void Init(IArchiveOpenCallback *callback)
1537   {
1538     Callback = callback;
1539     Files = 0;
1540     Offset = 0;
1541   }
1542 };
1543 
1544 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */))
1545 {
1546   return S_OK;
1547 }
1548 
1549 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */))
1550 {
1551   return S_OK;
1552 }
1553 
1554 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */))
1555 {
1556   if (Callback)
1557   {
1558     UInt64 value = Offset;
1559     if (inSize)
1560       value += *inSize;
1561     return Callback->SetCompleted(&Files, &value);
1562   }
1563   return S_OK;
1564 }
1565 
1566 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */))
1567 {
1568   *outStream = NULL;
1569   return S_OK;
1570 }
1571 
1572 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */))
1573 {
1574   return S_OK;
1575 }
1576 
1577 Z7_COM7F_IMF(CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */))
1578 {
1579   return S_OK;
1580 }
1581 
1582 
1583 static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize,
1584     IInStream *stream, const UInt64 *maxCheckStartPosition,
1585     IArchiveOpenCallback *openCallback,
1586     IArchiveExtractCallback *extractCallback)
1587 {
1588   /*
1589   if (needPhySize)
1590   {
1591     Z7_DECL_CMyComPtr_QI_FROM(
1592         IArchiveOpen2,
1593         open2, archive)
1594     if (open2)
1595       return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback);
1596   }
1597   */
1598   RINOK(archive->Open(stream, maxCheckStartPosition, openCallback))
1599   if (needPhySize)
1600   {
1601     bool phySize_Defined = false;
1602     UInt64 phySize = 0;
1603     RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined))
1604     if (phySize_Defined)
1605       return S_OK;
1606 
1607     bool phySizeCantBeDetected = false;
1608     RINOK(Archive_GetArcProp_Bool(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected))
1609 
1610     if (!phySizeCantBeDetected)
1611     {
1612       PRF(printf("\n-- !phySize_Defined after Open, call archive->Extract()"));
1613       // It's for bzip2/gz and some xz archives, where Open operation doesn't know phySize.
1614       // But the Handler will know phySize after full archive testing.
1615       RINOK(archive->Extract(NULL, (UInt32)(Int32)-1, BoolToInt(true), extractCallback))
1616       PRF(printf("\n-- OK"));
1617     }
1618   }
1619   return S_OK;
1620 }
1621 
1622 
1623 
1624 static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name)
1625 {
1626   FOR_VECTOR (i, orderIndices)
1627   {
1628     int oi = orderIndices[i];
1629     if (oi >= 0)
1630       if (StringsAreEqualNoCase_Ascii(codecs->Formats[(unsigned)oi].Name, name))
1631         return (int)i;
1632   }
1633   return -1;
1634 }
1635 
1636 #endif
1637 
1638 HRESULT CArc::OpenStream2(const COpenOptions &op)
1639 {
1640   // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout);
1641 
1642   Archive.Release();
1643   GetRawProps.Release();
1644   GetRootProps.Release();
1645 
1646   ErrorInfo.ClearErrors();
1647   ErrorInfo.ErrorFormatIndex = -1;
1648 
1649   IsParseArc = false;
1650   ArcStreamOffset = 0;
1651 
1652   // OutputDebugStringA("1");
1653   // OutputDebugStringW(Path);
1654 
1655   const UString fileName = ExtractFileNameFromPath(Path);
1656   UString extension;
1657   {
1658     const int dotPos = fileName.ReverseFind_Dot();
1659     if (dotPos >= 0)
1660       extension = fileName.Ptr((unsigned)(dotPos + 1));
1661   }
1662 
1663   CIntVector orderIndices;
1664 
1665   bool searchMarkerInHandler = false;
1666   #ifdef Z7_SFX
1667     searchMarkerInHandler = true;
1668   #endif
1669 
1670   CBoolArr isMainFormatArr(op.codecs->Formats.Size());
1671   {
1672     FOR_VECTOR(i, op.codecs->Formats)
1673       isMainFormatArr[i] = false;
1674   }
1675 
1676   const UInt64 maxStartOffset =
1677       op.openType.MaxStartOffset_Defined ?
1678       op.openType.MaxStartOffset :
1679       kMaxCheckStartPosition;
1680 
1681   #ifndef Z7_SFX
1682   bool isUnknownExt = false;
1683   #endif
1684 
1685   #ifndef Z7_SFX
1686   bool isForced = false;
1687   #endif
1688 
1689   unsigned numMainTypes = 0;
1690   const int formatIndex = op.openType.FormatIndex;
1691 
1692   if (formatIndex >= 0)
1693   {
1694     #ifndef Z7_SFX
1695     isForced = true;
1696     #endif
1697     orderIndices.Add(formatIndex);
1698     numMainTypes = 1;
1699     isMainFormatArr[(unsigned)formatIndex] = true;
1700 
1701     searchMarkerInHandler = true;
1702   }
1703   else
1704   {
1705     unsigned numFinded = 0;
1706     #ifndef Z7_SFX
1707     bool isPrearcExt = false;
1708     #endif
1709 
1710     {
1711       #ifndef Z7_SFX
1712 
1713       bool isZip = false;
1714       bool isRar = false;
1715 
1716       const wchar_t c = extension[0];
1717       if (c == 'z' || c == 'Z' || c == 'r' || c == 'R')
1718       {
1719         bool isNumber = false;
1720         for (unsigned k = 1;; k++)
1721         {
1722           const wchar_t d = extension[k];
1723           if (d == 0)
1724             break;
1725           if (d < '0' || d > '9')
1726           {
1727             isNumber = false;
1728             break;
1729           }
1730           isNumber = true;
1731         }
1732         if (isNumber)
1733         {
1734           if (c == 'z' || c == 'Z')
1735             isZip = true;
1736           else
1737             isRar = true;
1738         }
1739       }
1740 
1741       #endif
1742 
1743       FOR_VECTOR (i, op.codecs->Formats)
1744       {
1745         const CArcInfoEx &ai = op.codecs->Formats[i];
1746 
1747         if (IgnoreSplit || !op.openType.CanReturnArc)
1748           if (ai.Is_Split())
1749             continue;
1750         if (op.excludedFormats->FindInSorted((int)i) >= 0)
1751           continue;
1752 
1753         #ifndef Z7_SFX
1754         if (IsPreArcFormat(ai))
1755           isPrearcExt = true;
1756         #endif
1757 
1758         if (ai.FindExtension(extension) >= 0
1759             #ifndef Z7_SFX
1760             || (isZip && ai.Is_Zip())
1761             || (isRar && ai.Is_Rar())
1762             #endif
1763             )
1764         {
1765           // PrintNumber("orderIndices.Insert", i);
1766           orderIndices.Insert(numFinded++, (int)i);
1767           isMainFormatArr[i] = true;
1768         }
1769         else
1770           orderIndices.Add((int)i);
1771       }
1772     }
1773 
1774     if (!op.stream)
1775     {
1776       if (numFinded != 1)
1777         return E_NOTIMPL;
1778       orderIndices.DeleteFrom(1);
1779     }
1780     // PrintNumber("numFinded", numFinded );
1781 
1782     /*
1783     if (op.openOnlySpecifiedByExtension)
1784     {
1785       if (numFinded != 0 && !IsExeExt(extension))
1786         orderIndices.DeleteFrom(numFinded);
1787     }
1788     */
1789 
1790     #ifndef Z7_SFX
1791 
1792       if (op.stream && orderIndices.Size() >= 2)
1793       {
1794         RINOK(InStream_SeekToBegin(op.stream))
1795         CByteBuffer byteBuffer;
1796         CIntVector orderIndices2;
1797         if (numFinded == 0 || IsExeExt(extension))
1798         {
1799           // signature search was here
1800         }
1801         else if (extension.IsEqualTo("000") || extension.IsEqualTo("001"))
1802         {
1803           const int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar");
1804           if (i >= 0)
1805           {
1806             const size_t kBufSize = (1 << 10);
1807             byteBuffer.Alloc(kBufSize);
1808             size_t processedSize = kBufSize;
1809             RINOK(ReadStream(op.stream, byteBuffer, &processedSize))
1810             if (processedSize >= 16)
1811             {
1812               const Byte *buf = byteBuffer;
1813               const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
1814               if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0)
1815               {
1816                 orderIndices2.Add(orderIndices[(unsigned)i]);
1817                 orderIndices[(unsigned)i] = -1;
1818                 if (i >= (int)numFinded)
1819                   numFinded++;
1820               }
1821             }
1822           }
1823         }
1824         else
1825         {
1826           const size_t kBufSize = (1 << 10);
1827           byteBuffer.Alloc(kBufSize);
1828           size_t processedSize = kBufSize;
1829           RINOK(ReadStream(op.stream, byteBuffer, &processedSize))
1830           if (processedSize == 0)
1831             return S_FALSE;
1832 
1833           /*
1834           check type order:
1835             0) matched_extension && Backward
1836             1) matched_extension && (no_signuature || SignatureOffset != 0)
1837             2) matched_extension && (matched_signature)
1838             // 3) no signuature
1839             // 4) matched signuature
1840           */
1841           // we move index from orderIndices to orderIndices2 for priority handlers.
1842 
1843           for (unsigned i = 0; i < numFinded; i++)
1844           {
1845             const int index = orderIndices[i];
1846             if (index < 0)
1847               continue;
1848             const CArcInfoEx &ai = op.codecs->Formats[(unsigned)index];
1849             if (ai.Flags_BackwardOpen())
1850             {
1851               // backward doesn't need start signatures
1852               orderIndices2.Add(index);
1853               orderIndices[i] = -1;
1854             }
1855           }
1856 
1857           MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0);
1858           MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize);
1859           // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0);
1860           // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize);
1861         }
1862 
1863         FOR_VECTOR (i, orderIndices)
1864         {
1865           const int val = orderIndices[i];
1866           if (val != -1)
1867             orderIndices2.Add(val);
1868         }
1869         orderIndices = orderIndices2;
1870       }
1871 
1872       if (orderIndices.Size() >= 2)
1873       {
1874         const int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso");
1875         const int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf");
1876         if (iUdf > iIso && iIso >= 0)
1877         {
1878           const int isoIndex = orderIndices[(unsigned)iIso];
1879           const int udfIndex = orderIndices[(unsigned)iUdf];
1880           orderIndices[(unsigned)iUdf] = isoIndex;
1881           orderIndices[(unsigned)iIso] = udfIndex;
1882         }
1883       }
1884 
1885       numMainTypes = numFinded;
1886       isUnknownExt = (numMainTypes == 0) || isPrearcExt;
1887 
1888     #else // Z7_SFX
1889 
1890       numMainTypes = orderIndices.Size();
1891 
1892       // we need correct numMainTypes for mutlivolume SFX (if some volume is missing)
1893       if (numFinded != 0)
1894         numMainTypes = numFinded;
1895 
1896     #endif
1897   }
1898 
1899   UInt64 fileSize = 0;
1900   if (op.stream)
1901   {
1902     RINOK(InStream_GetSize_SeekToBegin(op.stream, fileSize))
1903   }
1904   FileSize = fileSize;
1905 
1906 
1907   #ifndef Z7_SFX
1908 
1909   CBoolArr skipFrontalFormat(op.codecs->Formats.Size());
1910   {
1911     FOR_VECTOR(i, op.codecs->Formats)
1912       skipFrontalFormat[i] = false;
1913   }
1914 
1915   #endif
1916 
1917   const COpenType &mode = op.openType;
1918 
1919 
1920 
1921 
1922 
1923   if (mode.CanReturnArc)
1924   {
1925     // ---------- OPEN main type by extenssion ----------
1926 
1927     unsigned numCheckTypes = orderIndices.Size();
1928     if (formatIndex >= 0)
1929       numCheckTypes = numMainTypes;
1930 
1931     for (unsigned i = 0; i < numCheckTypes; i++)
1932     {
1933       FormatIndex = orderIndices[i];
1934 
1935       // orderIndices[] item cannot be negative here
1936 
1937       bool exactOnly = false;
1938 
1939       #ifndef Z7_SFX
1940 
1941       const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
1942       // OutputDebugStringW(ai.Name);
1943       if (i >= numMainTypes)
1944       {
1945         // here we allow mismatched extension only for backward handlers
1946         if (!ai.Flags_BackwardOpen()
1947             // && !ai.Flags_PureStartOpen()
1948             )
1949           continue;
1950         exactOnly = true;
1951       }
1952 
1953       #endif
1954 
1955       // Some handlers do not set total bytes. So we set it here
1956       if (op.callback)
1957         RINOK(op.callback->SetTotal(NULL, &fileSize))
1958 
1959       if (op.stream)
1960       {
1961         RINOK(InStream_SeekToBegin(op.stream))
1962       }
1963 
1964       CMyComPtr<IInArchive> archive;
1965 
1966       RINOK(PrepareToOpen(op, (unsigned)FormatIndex, archive))
1967       if (!archive)
1968         continue;
1969 
1970       HRESULT result;
1971       if (op.stream)
1972       {
1973         UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0;
1974         result = archive->Open(op.stream, &searchLimit, op.callback);
1975       }
1976       else
1977       {
1978         CMyComPtr<IArchiveOpenSeq> openSeq;
1979         archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq);
1980         if (!openSeq)
1981           return E_NOTIMPL;
1982         result = openSeq->OpenSeq(op.seqStream);
1983       }
1984 
1985       RINOK(ReadBasicProps(archive, 0, result))
1986 
1987       if (result == S_FALSE)
1988       {
1989         bool isArc = ErrorInfo.IsArc_After_NonOpen();
1990 
1991         #ifndef Z7_SFX
1992         // if it's archive, we allow another open attempt for parser
1993         if (!mode.CanReturnParser || !isArc)
1994           skipFrontalFormat[(unsigned)FormatIndex] = true;
1995         #endif
1996 
1997         if (exactOnly)
1998           continue;
1999 
2000         if (i == 0 && numMainTypes == 1)
2001         {
2002           // we set NonOpenErrorInfo, only if there is only one main format (defined by extension).
2003           ErrorInfo.ErrorFormatIndex = FormatIndex;
2004           NonOpen_ErrorInfo = ErrorInfo;
2005 
2006           if (!mode.CanReturnParser && isArc)
2007           {
2008             // if (formatIndex < 0 && !searchMarkerInHandler)
2009             {
2010               // if bad archive was detected, we don't need additional open attempts
2011               #ifndef Z7_SFX
2012               if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */)
2013               #endif
2014                 return S_FALSE;
2015             }
2016           }
2017         }
2018 
2019         /*
2020         #ifndef Z7_SFX
2021         if (IsExeExt(extension) || ai.Flags_PreArc())
2022         {
2023         // openOnlyFullArc = false;
2024         // canReturnTailArc = true;
2025         // limitSignatureSearch = true;
2026         }
2027         #endif
2028         */
2029 
2030         continue;
2031       }
2032 
2033       RINOK(result)
2034 
2035       #ifndef Z7_SFX
2036 
2037       bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
2038       const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2039 
2040       bool thereIsTail = ErrorInfo.ThereIsTail;
2041       if (thereIsTail && mode.ZerosTailIsAllowed)
2042       {
2043         RINOK(CheckZerosTail(op, (UInt64)(Offset + (Int64)PhySize)))
2044         if (ErrorInfo.IgnoreTail)
2045           thereIsTail = false;
2046       }
2047 
2048       if (Offset > 0)
2049       {
2050         if (exactOnly
2051             || !searchMarkerInHandler
2052             || !specFlags.CanReturn_NonStart()
2053             || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset))
2054           continue;
2055       }
2056       if (thereIsTail)
2057       {
2058         if (Offset > 0)
2059         {
2060           if (!specFlags.CanReturnMid)
2061             continue;
2062         }
2063         else if (!specFlags.CanReturnFrontal)
2064           continue;
2065       }
2066 
2067       if (Offset > 0 || thereIsTail)
2068       {
2069         if (formatIndex < 0)
2070         {
2071           if (IsPreArcFormat(ai))
2072           {
2073             // openOnlyFullArc = false;
2074             // canReturnTailArc = true;
2075             /*
2076             if (mode.SkipSfxStub)
2077             limitSignatureSearch = true;
2078             */
2079             // if (mode.SkipSfxStub)
2080             {
2081               // skipFrontalFormat[FormatIndex] = true;
2082               continue;
2083             }
2084           }
2085         }
2086       }
2087 
2088       #endif
2089 
2090       Archive = archive;
2091       return S_OK;
2092     }
2093   }
2094 
2095 
2096 
2097   #ifndef Z7_SFX
2098 
2099   if (!op.stream)
2100     return S_FALSE;
2101 
2102   if (formatIndex >= 0 && !mode.CanReturnParser)
2103   {
2104     if (mode.MaxStartOffset_Defined)
2105     {
2106       if (mode.MaxStartOffset == 0)
2107         return S_FALSE;
2108     }
2109     else
2110     {
2111       const CArcInfoEx &ai = op.codecs->Formats[(unsigned)formatIndex];
2112       if (ai.FindExtension(extension) >= 0)
2113       {
2114         if (ai.Flags_FindSignature() && searchMarkerInHandler)
2115           return S_FALSE;
2116       }
2117     }
2118   }
2119 
2120   NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler;
2121   CMyComPtr<IInArchive> handler = handlerSpec;
2122 
2123   CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback;
2124   CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec;
2125   extractCallback_To_OpenCallback_Spec->Init(op.callback);
2126 
2127   {
2128     // ---------- Check all possible START archives ----------
2129     // this code is better for full file archives than Parser's code.
2130 
2131     CByteBuffer byteBuffer;
2132     bool endOfFile = false;
2133     size_t processedSize;
2134     {
2135       size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF)
2136       if (bufSize > fileSize)
2137       {
2138         bufSize = (size_t)fileSize;
2139         endOfFile = true;
2140       }
2141       byteBuffer.Alloc(bufSize);
2142       RINOK(InStream_SeekToBegin(op.stream))
2143       processedSize = bufSize;
2144       RINOK(ReadStream(op.stream, byteBuffer, &processedSize))
2145       if (processedSize == 0)
2146         return S_FALSE;
2147       if (processedSize < bufSize)
2148         endOfFile = true;
2149     }
2150     CUIntVector sortedFormats;
2151 
2152     unsigned i;
2153 
2154     int splitIndex = -1;
2155 
2156     for (i = 0; i < orderIndices.Size(); i++)
2157     {
2158       // orderIndices[] item cannot be negative here
2159       unsigned form = (unsigned)orderIndices[i];
2160       if (skipFrontalFormat[form])
2161         continue;
2162 
2163       const CArcInfoEx &ai = op.codecs->Formats[form];
2164 
2165       if (ai.Is_Split())
2166       {
2167         splitIndex = (int)form;
2168         continue;
2169       }
2170 
2171       if (ai.Flags_ByExtOnlyOpen())
2172         continue;
2173 
2174       if (ai.IsArcFunc)
2175       {
2176         UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize);
2177         if (isArcRes == k_IsArc_Res_NO)
2178           continue;
2179         if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2180           continue;
2181         // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue;
2182         sortedFormats.Insert(0, form);
2183         continue;
2184       }
2185 
2186       const bool isNewStyleSignature = IsNewStyleSignature(ai);
2187       bool needCheck = !isNewStyleSignature
2188           || ai.Signatures.IsEmpty()
2189           || ai.Flags_PureStartOpen()
2190           || ai.Flags_StartOpen()
2191           || ai.Flags_BackwardOpen();
2192 
2193       if (isNewStyleSignature && !ai.Signatures.IsEmpty())
2194       {
2195         unsigned k;
2196         for (k = 0; k < ai.Signatures.Size(); k++)
2197         {
2198           const CByteBuffer &sig = ai.Signatures[k];
2199           if (processedSize < ai.SignatureOffset + sig.Size())
2200           {
2201             if (!endOfFile)
2202               needCheck = true;
2203           }
2204           else if (TestSignature(sig, byteBuffer + ai.SignatureOffset, sig.Size()))
2205             break;
2206         }
2207         if (k != ai.Signatures.Size())
2208         {
2209           sortedFormats.Insert(0, form);
2210           continue;
2211         }
2212       }
2213       if (needCheck)
2214         sortedFormats.Add(form);
2215     }
2216 
2217     if (splitIndex >= 0)
2218       sortedFormats.Insert(0, (unsigned)splitIndex);
2219 
2220     for (i = 0; i < sortedFormats.Size(); i++)
2221     {
2222       FormatIndex = (int)sortedFormats[i];
2223       const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
2224 
2225       if (op.callback)
2226         RINOK(op.callback->SetTotal(NULL, &fileSize))
2227 
2228       RINOK(InStream_SeekToBegin(op.stream))
2229 
2230       CMyComPtr<IInArchive> archive;
2231       RINOK(PrepareToOpen(op, (unsigned)FormatIndex, archive))
2232       if (!archive)
2233         continue;
2234 
2235       PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name));
2236       HRESULT result;
2237       {
2238         UInt64 searchLimit = 0;
2239         /*
2240         if (mode.CanReturnArc)
2241           result = archive->Open(op.stream, &searchLimit, op.callback);
2242         else
2243         */
2244         // if (!CanReturnArc), it's ParserMode, and we need phy size
2245         result = OpenArchiveSpec(archive,
2246             !mode.CanReturnArc, // needPhySize
2247             op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback);
2248       }
2249 
2250       if (result == S_FALSE)
2251       {
2252         skipFrontalFormat[(unsigned)FormatIndex] = true;
2253         // FIXME: maybe we must use LenIsUnknown.
2254         // printf("  OpenForSize Error");
2255         continue;
2256       }
2257       RINOK(result)
2258 
2259       RINOK(ReadBasicProps(archive, 0, result))
2260 
2261       if (Offset > 0)
2262       {
2263         continue; // good handler doesn't return such Offset > 0
2264         // but there are some cases like false prefixed PK00 archive, when
2265         // we can support it?
2266       }
2267 
2268       NArchive::NParser::CParseItem pi;
2269       pi.Offset = (UInt64)Offset;
2270       pi.Size = AvailPhySize;
2271 
2272       // bool needScan = false;
2273 
2274       if (!PhySize_Defined)
2275       {
2276         // it's for Z format
2277         pi.LenIsUnknown = true;
2278         // needScan = true;
2279         // phySize = arcRem;
2280         // nextNeedCheckStartOpen = false;
2281       }
2282 
2283       /*
2284       if (OkPhySize_Defined)
2285         pi.OkSize = pi.OkPhySize;
2286       else
2287         pi.OkSize = pi.Size;
2288       */
2289 
2290       pi.NormalizeOffset();
2291       // printf("  phySize = %8d", (unsigned)phySize);
2292 
2293 
2294       if (mode.CanReturnArc)
2295       {
2296         const bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
2297         const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2298         bool openCur = false;
2299 
2300         if (!ErrorInfo.ThereIsTail)
2301           openCur = true;
2302         else
2303         {
2304           if (mode.ZerosTailIsAllowed)
2305           {
2306             RINOK(CheckZerosTail(op, (UInt64)(Offset + (Int64)PhySize)))
2307             if (ErrorInfo.IgnoreTail)
2308               openCur = true;
2309           }
2310           if (!openCur)
2311           {
2312             openCur = specFlags.CanReturnFrontal;
2313             if (formatIndex < 0) // format is not forced
2314             {
2315               if (IsPreArcFormat(ai))
2316               {
2317                 // if (mode.SkipSfxStub)
2318                 {
2319                   openCur = false;
2320                 }
2321               }
2322             }
2323           }
2324         }
2325 
2326         if (openCur)
2327         {
2328           InStream = op.stream;
2329           Archive = archive;
2330           return S_OK;
2331         }
2332       }
2333 
2334       skipFrontalFormat[(unsigned)FormatIndex] = true;
2335 
2336 
2337       // if (!mode.CanReturnArc)
2338       /*
2339       if (!ErrorInfo.ThereIsTail)
2340           continue;
2341       */
2342       if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2343         continue;
2344 
2345       // printf("\nAdd offset = %d", (int)pi.Offset);
2346       RINOK(ReadParseItemProps(archive, ai, pi))
2347       handlerSpec->AddItem(pi);
2348     }
2349   }
2350 
2351 
2352 
2353 
2354 
2355   // ---------- PARSER ----------
2356 
2357   CUIntVector arc2sig; // formatIndex to signatureIndex
2358   CUIntVector sig2arc; // signatureIndex to formatIndex;
2359   {
2360     unsigned sum = 0;
2361     FOR_VECTOR (i, op.codecs->Formats)
2362     {
2363       arc2sig.Add(sum);
2364       const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures;
2365       sum += sigs.Size();
2366       FOR_VECTOR (k, sigs)
2367         sig2arc.Add(i);
2368     }
2369   }
2370 
2371   {
2372     const size_t kBeforeSize = 1 << 16;
2373     const size_t kAfterSize  = 1 << 20;
2374     const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize
2375 
2376     const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8);
2377     CByteArr hashBuffer(kNumVals);
2378     Byte *hash = hashBuffer;
2379     memset(hash, 0xFF, kNumVals);
2380     Byte prevs[256];
2381     memset(prevs, 0xFF, sizeof(prevs));
2382     if (sig2arc.Size() >= 0xFF)
2383       return S_FALSE;
2384 
2385     CUIntVector difficultFormats;
2386     CBoolArr difficultBools(256);
2387     {
2388       for (unsigned i = 0; i < 256; i++)
2389         difficultBools[i] = false;
2390     }
2391 
2392     bool thereAreHandlersForSearch = false;
2393 
2394     // UInt32 maxSignatureEnd = 0;
2395 
2396     FOR_VECTOR (i, orderIndices)
2397     {
2398       int index = orderIndices[i];
2399       if (index < 0)
2400         continue;
2401       const CArcInfoEx &ai = op.codecs->Formats[(unsigned)index];
2402       if (ai.Flags_ByExtOnlyOpen())
2403         continue;
2404       bool isDifficult = false;
2405       // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31)
2406       if (!ai.NewInterface)
2407         isDifficult = true;
2408       else
2409       {
2410         if (ai.Flags_StartOpen())
2411           isDifficult = true;
2412         FOR_VECTOR (k, ai.Signatures)
2413         {
2414           const CByteBuffer &sig = ai.Signatures[k];
2415           /*
2416           UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
2417           if (maxSignatureEnd < signatureEnd)
2418             maxSignatureEnd = signatureEnd;
2419           */
2420           if (sig.Size() < kNumHashBytes)
2421           {
2422             isDifficult = true;
2423             continue;
2424           }
2425           thereAreHandlersForSearch = true;
2426           UInt32 v = HASH_VAL(sig);
2427           unsigned sigIndex = arc2sig[(unsigned)index] + k;
2428           prevs[sigIndex] = hash[v];
2429           hash[v] = (Byte)sigIndex;
2430         }
2431       }
2432       if (isDifficult)
2433       {
2434         difficultFormats.Add((unsigned)index);
2435         difficultBools[(unsigned)index] = true;
2436       }
2437     }
2438 
2439     if (!thereAreHandlersForSearch)
2440     {
2441       // openOnlyFullArc = true;
2442       // canReturnTailArc = true;
2443     }
2444 
2445     RINOK(InStream_SeekToBegin(op.stream))
2446 
2447     CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream;
2448     CMyComPtr<IInStream> limitedStream = limitedStreamSpec;
2449     limitedStreamSpec->SetStream(op.stream);
2450 
2451     CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL;
2452     CMyComPtr<IArchiveOpenCallback> openCallback_Offset;
2453     if (op.callback)
2454     {
2455       openCallback_Offset_Spec = new CArchiveOpenCallback_Offset;
2456       openCallback_Offset = openCallback_Offset_Spec;
2457       openCallback_Offset_Spec->Callback = op.callback;
2458       openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback);
2459       #ifndef Z7_NO_CRYPTO
2460       openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword);
2461       #endif
2462     }
2463 
2464     if (op.callback)
2465       RINOK(op.callback->SetTotal(NULL, &fileSize))
2466 
2467     CByteBuffer &byteBuffer = limitedStreamSpec->Buffer;
2468     byteBuffer.Alloc(kBufSize);
2469 
2470     UInt64 callbackPrev = 0;
2471     bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos.
2472 
2473     bool endOfFile = false;
2474     UInt64 bufPhyPos = 0;
2475     size_t bytesInBuf = 0;
2476     // UInt64 prevPos = 0;
2477 
2478     // ---------- Main Scan Loop ----------
2479 
2480     UInt64 pos = 0;
2481 
2482     if (!mode.EachPos && handlerSpec->_items.Size() == 1)
2483     {
2484       NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2485       if (!pi.LenIsUnknown && pi.Offset == 0)
2486         pos = pi.Size;
2487     }
2488 
2489     for (;;)
2490     {
2491       // printf("\nPos = %d", (int)pos);
2492       UInt64 posInBuf = pos - bufPhyPos;
2493 
2494       // if (pos > ((UInt64)1 << 35)) break;
2495 
2496       if (!endOfFile)
2497       {
2498         if (bytesInBuf < kBufSize)
2499         {
2500           size_t processedSize = kBufSize - bytesInBuf;
2501           // printf("\nRead ask = %d", (unsigned)processedSize);
2502           UInt64 seekPos = bufPhyPos + bytesInBuf;
2503           RINOK(InStream_SeekSet(op.stream, bufPhyPos + bytesInBuf))
2504           RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize))
2505           // printf("   processed = %d", (unsigned)processedSize);
2506           if (processedSize == 0)
2507           {
2508             fileSize = seekPos;
2509             endOfFile = true;
2510           }
2511           else
2512           {
2513             bytesInBuf += processedSize;
2514             limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos);
2515           }
2516           continue;
2517         }
2518 
2519         if (bytesInBuf < posInBuf)
2520         {
2521           UInt64 skipSize = posInBuf - bytesInBuf;
2522           if (skipSize <= kBeforeSize)
2523           {
2524             size_t keepSize = (size_t)(kBeforeSize - skipSize);
2525             // printf("\nmemmove skip = %d", (int)keepSize);
2526             memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize);
2527             bytesInBuf = keepSize;
2528             bufPhyPos = pos - keepSize;
2529             continue;
2530           }
2531           // printf("\nSkip %d", (int)(skipSize - kBeforeSize));
2532           // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL));
2533           bytesInBuf = 0;
2534           bufPhyPos = pos - kBeforeSize;
2535           continue;
2536         }
2537 
2538         if (bytesInBuf - posInBuf < kAfterSize)
2539         {
2540           size_t beg = (size_t)posInBuf - kBeforeSize;
2541           // printf("\nmemmove for after beg = %d", (int)beg);
2542           memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg);
2543           bufPhyPos += beg;
2544           bytesInBuf -= beg;
2545           continue;
2546         }
2547       }
2548 
2549       if (bytesInBuf <= (size_t)posInBuf)
2550         break;
2551 
2552       bool useOffsetCallback = false;
2553       if (openCallback_Offset)
2554       {
2555         openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2556         openCallback_Offset_Spec->Offset = pos;
2557 
2558         useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1);
2559 
2560         if (pos >= callbackPrev + (1 << 23))
2561         {
2562           RINOK(openCallback_Offset->SetCompleted(NULL, NULL))
2563           callbackPrev = pos;
2564         }
2565       }
2566 
2567       {
2568         UInt64 endPos = bufPhyPos + bytesInBuf;
2569         if (fileSize < endPos)
2570         {
2571           FileSize = fileSize; // why ????
2572           fileSize = endPos;
2573         }
2574       }
2575 
2576       const size_t availSize = bytesInBuf - (size_t)posInBuf;
2577       if (availSize < kNumHashBytes)
2578         break;
2579       size_t scanSize = availSize -
2580           ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes);
2581 
2582       {
2583         /*
2584         UInt64 scanLimit = openOnlyFullArc ?
2585             maxSignatureEnd :
2586             op.openType.ScanSize + maxSignatureEnd;
2587         */
2588         if (!mode.CanReturnParser)
2589         {
2590           if (pos > maxStartOffset)
2591             break;
2592           UInt64 remScan = maxStartOffset - pos;
2593           if (scanSize > remScan)
2594             scanSize = (size_t)remScan;
2595         }
2596       }
2597 
2598       scanSize++;
2599 
2600       const Byte *buf = byteBuffer + (size_t)posInBuf;
2601       const Byte *bufLimit = buf + scanSize;
2602       size_t ppp = 0;
2603 
2604       if (!needCheckStartOpen)
2605       {
2606         for (; buf < bufLimit && hash[HASH_VAL(buf)] == 0xFF; buf++);
2607         ppp = (size_t)(buf - (byteBuffer + (size_t)posInBuf));
2608         pos += ppp;
2609         if (buf == bufLimit)
2610           continue;
2611       }
2612 
2613       UInt32 v = HASH_VAL(buf);
2614       bool nextNeedCheckStartOpen = true;
2615       unsigned i = hash[v];
2616       unsigned indexOfDifficult = 0;
2617 
2618       // ---------- Open Loop for Current Pos ----------
2619       bool wasOpen = false;
2620 
2621       for (;;)
2622       {
2623         unsigned index;
2624         bool isDifficult;
2625         if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size())
2626         {
2627           index = difficultFormats[indexOfDifficult++];
2628           isDifficult = true;
2629         }
2630         else
2631         {
2632           if (i == 0xFF)
2633             break;
2634           index = sig2arc[i];
2635           unsigned sigIndex = i - arc2sig[index];
2636           i = prevs[i];
2637           if (needCheckStartOpen && difficultBools[index])
2638             continue;
2639           const CArcInfoEx &ai = op.codecs->Formats[index];
2640 
2641           if (pos < ai.SignatureOffset)
2642             continue;
2643 
2644           /*
2645           if (openOnlyFullArc)
2646             if (pos != ai.SignatureOffset)
2647               continue;
2648           */
2649 
2650           const CByteBuffer &sig = ai.Signatures[sigIndex];
2651 
2652           if (ppp + sig.Size() > availSize
2653               || !TestSignature(buf, sig, sig.Size()))
2654             continue;
2655           // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos));
2656           // prevPos = pos;
2657           isDifficult = false;
2658         }
2659 
2660         const CArcInfoEx &ai = op.codecs->Formats[index];
2661 
2662 
2663         if ((isDifficult && pos == 0) || ai.SignatureOffset == pos)
2664         {
2665           // we don't check same archive second time */
2666           if (skipFrontalFormat[index])
2667             continue;
2668         }
2669 
2670         UInt64 startArcPos = pos;
2671         if (!isDifficult)
2672         {
2673           if (pos < ai.SignatureOffset)
2674             continue;
2675           startArcPos = pos - ai.SignatureOffset;
2676           /*
2677           // we don't need the check for Z files
2678           if (startArcPos < handlerSpec->GetLastEnd())
2679             continue;
2680           */
2681         }
2682 
2683         if (ai.IsArcFunc && startArcPos >= bufPhyPos)
2684         {
2685           const size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos);
2686           if (offsetInBuf < bytesInBuf)
2687           {
2688             const UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf);
2689             if (isArcRes == k_IsArc_Res_NO)
2690               continue;
2691             if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2692               continue;
2693             /*
2694             if (isArcRes == k_IsArc_Res_YES_LOW_PROB)
2695             {
2696               // if (pos != ai.SignatureOffset)
2697               continue;
2698             }
2699             */
2700           }
2701           // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name);
2702         }
2703 
2704         PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name));
2705 
2706         const bool isMainFormat = isMainFormatArr[index];
2707         const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2708 
2709         CMyComPtr<IInArchive> archive;
2710         RINOK(PrepareToOpen(op, index, archive))
2711         if (!archive)
2712           return E_FAIL;
2713 
2714         // OutputDebugStringW(ai.Name);
2715 
2716         const UInt64 rem = fileSize - startArcPos;
2717 
2718         UInt64 arcStreamOffset = 0;
2719 
2720         if (ai.Flags_UseGlobalOffset())
2721         {
2722           RINOK(limitedStreamSpec->InitAndSeek(0, fileSize))
2723           RINOK(InStream_SeekSet(limitedStream, startArcPos))
2724         }
2725         else
2726         {
2727           RINOK(limitedStreamSpec->InitAndSeek(startArcPos, rem))
2728           arcStreamOffset = startArcPos;
2729         }
2730 
2731         UInt64 maxCheckStartPosition = 0;
2732 
2733         if (openCallback_Offset)
2734         {
2735           openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2736           openCallback_Offset_Spec->Offset = startArcPos;
2737         }
2738 
2739         // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset);
2740         extractCallback_To_OpenCallback_Spec->Files = 0;
2741         extractCallback_To_OpenCallback_Spec->Offset = startArcPos;
2742 
2743         HRESULT result = OpenArchiveSpec(archive,
2744             true, // needPhySize
2745             limitedStream, &maxCheckStartPosition,
2746             useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback,
2747             extractCallback_To_OpenCallback);
2748 
2749         RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result))
2750 
2751         bool isOpen = false;
2752 
2753         if (result == S_FALSE)
2754         {
2755           if (!mode.CanReturnParser)
2756           {
2757             if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen())
2758             {
2759               ErrorInfo.ErrorFormatIndex = (int)index;
2760               NonOpen_ErrorInfo = ErrorInfo;
2761               // if archive was detected, we don't need additional open attempts
2762               return S_FALSE;
2763             }
2764             continue;
2765           }
2766           if (!ErrorInfo.IsArc_After_NonOpen() || !PhySize_Defined || PhySize == 0)
2767             continue;
2768         }
2769         else
2770         {
2771           if (PhySize_Defined && PhySize == 0)
2772           {
2773             PRF(printf("  phySize_Defined && PhySize == 0 "));
2774             // we skip that epmty archive case with unusual unexpected (PhySize == 0) from Code function.
2775             continue;
2776           }
2777           isOpen = true;
2778           RINOK(result)
2779           PRF(printf("  OK "));
2780         }
2781 
2782         // fprintf(stderr, "\n %8X  %S", startArcPos, Path);
2783         // printf("\nOpen OK: %S", ai.Name);
2784 
2785 
2786         NArchive::NParser::CParseItem pi;
2787         pi.Offset = startArcPos;
2788 
2789         if (ai.Flags_UseGlobalOffset())
2790           pi.Offset = (UInt64)Offset;
2791         else if (Offset != 0)
2792           return E_FAIL;
2793 
2794         const UInt64 arcRem = FileSize - pi.Offset;
2795         UInt64 phySize = arcRem;
2796         const bool phySize_Defined = PhySize_Defined;
2797         if (phySize_Defined)
2798         {
2799           if (pi.Offset + PhySize > FileSize)
2800           {
2801             // ErrorInfo.ThereIsTail = true;
2802             PhySize = FileSize - pi.Offset;
2803           }
2804           phySize = PhySize;
2805         }
2806         if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63))
2807           return E_FAIL;
2808 
2809         /*
2810         if (!ai.UseGlobalOffset)
2811         {
2812           if (phySize > arcRem)
2813           {
2814             ThereIsTail = true;
2815             phySize = arcRem;
2816           }
2817         }
2818         */
2819 
2820         bool needScan = false;
2821 
2822 
2823         if (isOpen && !phySize_Defined)
2824         {
2825           // it's for Z format, or bzip2,gz,xz with phySize that was not detected
2826           pi.LenIsUnknown = true;
2827           needScan = true;
2828           phySize = arcRem;
2829           nextNeedCheckStartOpen = false;
2830         }
2831 
2832         pi.Size = phySize;
2833         /*
2834         if (OkPhySize_Defined)
2835           pi.OkSize = OkPhySize;
2836         */
2837         pi.NormalizeOffset();
2838         // printf("  phySize = %8d", (unsigned)phySize);
2839 
2840         /*
2841         if (needSkipFullArc)
2842           if (pi.Offset == 0 && phySize_Defined && pi.Size >= fileSize)
2843             continue;
2844         */
2845         if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2846         {
2847           // it's possible for dmg archives
2848           if (!mode.CanReturnArc)
2849             continue;
2850         }
2851 
2852         if (mode.EachPos)
2853           pos++;
2854         else if (needScan)
2855         {
2856           pos++;
2857           /*
2858           if (!OkPhySize_Defined)
2859             pos++;
2860           else
2861             pos = pi.Offset + pi.OkSize;
2862           */
2863         }
2864         else
2865           pos = pi.Offset + pi.Size;
2866 
2867 
2868         RINOK(ReadParseItemProps(archive, ai, pi))
2869 
2870         if (pi.Offset < startArcPos && !mode.EachPos /* && phySize_Defined */)
2871         {
2872           /* It's for DMG format.
2873           This code deletes all previous items that are included to current item */
2874 
2875           while (!handlerSpec->_items.IsEmpty())
2876           {
2877             {
2878               const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back();
2879               if (back.Offset < pi.Offset)
2880                 break;
2881               if (back.Offset + back.Size > pi.Offset + pi.Size)
2882                 break;
2883             }
2884             handlerSpec->_items.DeleteBack();
2885           }
2886         }
2887 
2888 
2889         if (isOpen && mode.CanReturnArc && phySize_Defined)
2890         {
2891           // if (pi.Offset + pi.Size >= fileSize)
2892           bool openCur = false;
2893 
2894           bool thereIsTail = ErrorInfo.ThereIsTail;
2895           if (thereIsTail && mode.ZerosTailIsAllowed)
2896           {
2897             RINOK(CheckZerosTail(op, (UInt64)((Int64)arcStreamOffset + Offset + (Int64)PhySize)))
2898             if (ErrorInfo.IgnoreTail)
2899               thereIsTail = false;
2900           }
2901 
2902           if (pi.Offset != 0)
2903           {
2904             if (!pi.IsNotArcType)
2905             {
2906               if (thereIsTail)
2907                 openCur = specFlags.CanReturnMid;
2908               else
2909                 openCur = specFlags.CanReturnTail;
2910             }
2911           }
2912           else
2913           {
2914             if (!thereIsTail)
2915               openCur = true;
2916             else
2917               openCur = specFlags.CanReturnFrontal;
2918 
2919             if (formatIndex >= -2)
2920               openCur = true;
2921           }
2922 
2923           if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */)
2924             openCur = false;
2925 
2926           // We open file as SFX, if there is front archive or first archive is "Self Executable"
2927           if (!openCur && !pi.IsSelfExe && !thereIsTail &&
2928               (!pi.IsNotArcType || pi.Offset == 0))
2929           {
2930             if (handlerSpec->_items.IsEmpty())
2931             {
2932               if (specFlags.CanReturnTail)
2933                 openCur = true;
2934             }
2935             else if (handlerSpec->_items.Size() == 1)
2936             {
2937               if (handlerSpec->_items[0].IsSelfExe)
2938               {
2939                 if (mode.SpecUnknownExt.CanReturnTail)
2940                   openCur = true;
2941               }
2942             }
2943           }
2944 
2945           if (openCur)
2946           {
2947             InStream = op.stream;
2948             Archive = archive;
2949             FormatIndex = (int)index;
2950             ArcStreamOffset = arcStreamOffset;
2951             return S_OK;
2952           }
2953         }
2954 
2955         /*
2956         if (openOnlyFullArc)
2957         {
2958           ErrorInfo.ClearErrors();
2959           return S_FALSE;
2960         }
2961         */
2962 
2963         pi.FormatIndex = (int)index;
2964 
2965         // printf("\nAdd offset = %d", (int)pi.Offset);
2966         handlerSpec->AddItem(pi);
2967         wasOpen = true;
2968         break;
2969       }
2970       // ---------- End of Open Loop for Current Pos ----------
2971 
2972       if (!wasOpen)
2973         pos++;
2974       needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen);
2975     }
2976     // ---------- End of Main Scan Loop ----------
2977 
2978     /*
2979     if (handlerSpec->_items.Size() == 1)
2980     {
2981       const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2982       if (pi.Size == fileSize && pi.Offset == 0)
2983       {
2984         Archive = archive;
2985         FormatIndex2 = pi.FormatIndex;
2986         return S_OK;
2987       }
2988     }
2989     */
2990 
2991     if (mode.CanReturnParser)
2992     {
2993       bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing
2994       handlerSpec->AddUnknownItem(fileSize);
2995       if (handlerSpec->_items.Size() == 0)
2996         return S_FALSE;
2997       if (returnParser || handlerSpec->_items.Size() != 1)
2998       {
2999         // return S_FALSE;
3000         handlerSpec->_stream = op.stream;
3001         Archive = handler;
3002         ErrorInfo.ClearErrors();
3003         IsParseArc = true;
3004         FormatIndex = -1; // It's parser
3005         Offset = 0;
3006         return S_OK;
3007       }
3008     }
3009   }
3010 
3011   #endif
3012 
3013   if (!Archive)
3014     return S_FALSE;
3015   return S_OK;
3016 }
3017 
3018 
3019 
3020 
3021 HRESULT CArc::OpenStream(const COpenOptions &op)
3022 {
3023   RINOK(OpenStream2(op))
3024   // PrintNumber("op.formatIndex 3", op.formatIndex);
3025 
3026   if (Archive)
3027   {
3028     GetRawProps.Release();
3029     GetRootProps.Release();
3030     Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps);
3031     Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps);
3032 
3033     RINOK(Archive_GetArcProp_Bool(Archive, kpidIsTree, IsTree))
3034     RINOK(Archive_GetArcProp_Bool(Archive, kpidIsDeleted, Ask_Deleted))
3035     RINOK(Archive_GetArcProp_Bool(Archive, kpidIsAltStream, Ask_AltStream))
3036     RINOK(Archive_GetArcProp_Bool(Archive, kpidIsAux, Ask_Aux))
3037     RINOK(Archive_GetArcProp_Bool(Archive, kpidINode, Ask_INode))
3038     RINOK(Archive_GetArcProp_Bool(Archive, kpidReadOnly, IsReadOnly))
3039 
3040     const UString fileName = ExtractFileNameFromPath(Path);
3041     UString extension;
3042     {
3043       int dotPos = fileName.ReverseFind_Dot();
3044       if (dotPos >= 0)
3045         extension = fileName.Ptr((unsigned)(dotPos + 1));
3046     }
3047 
3048     DefaultName.Empty();
3049     if (FormatIndex >= 0)
3050     {
3051       const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
3052       if (ai.Exts.Size() == 0)
3053         DefaultName = GetDefaultName2(fileName, UString(), UString());
3054       else
3055       {
3056         int subExtIndex = ai.FindExtension(extension);
3057         if (subExtIndex < 0)
3058           subExtIndex = 0;
3059         const CArcExtInfo &extInfo = ai.Exts[(unsigned)subExtIndex];
3060         DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt);
3061       }
3062     }
3063   }
3064 
3065   return S_OK;
3066 }
3067 
3068 #ifdef Z7_SFX
3069 
3070 #ifdef _WIN32
3071   #define k_ExeExt ".exe"
3072   static const unsigned k_ExeExt_Len = 4;
3073 #else
3074   #define k_ExeExt ""
3075   static const unsigned k_ExeExt_Len = 0;
3076 #endif
3077 
3078 #endif
3079 
3080 HRESULT CArc::OpenStreamOrFile(COpenOptions &op)
3081 {
3082   CMyComPtr<IInStream> fileStream;
3083   CMyComPtr<ISequentialInStream> seqStream;
3084   CInFileStream *fileStreamSpec = NULL;
3085 
3086   if (op.stdInMode)
3087   {
3088     seqStream = new CStdInFileStream;
3089     op.seqStream = seqStream;
3090   }
3091   else if (!op.stream)
3092   {
3093     fileStreamSpec = new CInFileStream;
3094     fileStream = fileStreamSpec;
3095     Path = filePath;
3096     if (!fileStreamSpec->Open(us2fs(Path)))
3097       return GetLastError_noZero_HRESULT();
3098     op.stream = fileStream;
3099     #ifdef Z7_SFX
3100     IgnoreSplit = true;
3101     #endif
3102   }
3103 
3104   /*
3105   if (callback)
3106   {
3107     UInt64 fileSize;
3108     RINOK(InStream_GetSize_SeekToEnd(op.stream, fileSize));
3109     RINOK(op.callback->SetTotal(NULL, &fileSize))
3110   }
3111   */
3112 
3113   HRESULT res = OpenStream(op);
3114   IgnoreSplit = false;
3115 
3116   #ifdef Z7_SFX
3117 
3118   if (res != S_FALSE
3119       || !fileStreamSpec
3120       || !op.callbackSpec
3121       || NonOpen_ErrorInfo.IsArc_After_NonOpen())
3122     return res;
3123 
3124   {
3125     if (filePath.Len() > k_ExeExt_Len
3126         && StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt))
3127     {
3128       const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len);
3129       FOR_VECTOR (i, op.codecs->Formats)
3130       {
3131         const CArcInfoEx &ai = op.codecs->Formats[i];
3132         if (ai.Is_Split())
3133           continue;
3134         UString path3 = path2;
3135         path3.Add_Dot();
3136         path3 += ai.GetMainExt(); // "7z"  for SFX.
3137         Path = path3;
3138         Path += ".001";
3139         bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3140         if (!isOk)
3141         {
3142           Path = path3;
3143           isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3144         }
3145         if (isOk)
3146         {
3147           if (fileStreamSpec->Open(us2fs(Path)))
3148           {
3149             op.stream = fileStream;
3150             NonOpen_ErrorInfo.ClearErrors_Full();
3151             if (OpenStream(op) == S_OK)
3152               return S_OK;
3153           }
3154         }
3155       }
3156     }
3157   }
3158 
3159   #endif
3160 
3161   return res;
3162 }
3163 
3164 void CArchiveLink::KeepModeForNextOpen()
3165 {
3166   for (unsigned i = Arcs.Size(); i != 0;)
3167   {
3168     i--;
3169     CMyComPtr<IArchiveKeepModeForNextOpen> keep;
3170     Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep);
3171     if (keep)
3172       keep->KeepModeForNextOpen();
3173   }
3174 }
3175 
3176 HRESULT CArchiveLink::Close()
3177 {
3178   for (unsigned i = Arcs.Size(); i != 0;)
3179   {
3180     i--;
3181     RINOK(Arcs[i].Close())
3182   }
3183   IsOpen = false;
3184   // ErrorsText.Empty();
3185   return S_OK;
3186 }
3187 
3188 void CArchiveLink::Release()
3189 {
3190   // NonOpenErrorFormatIndex = -1;
3191   NonOpen_ErrorInfo.ClearErrors();
3192   NonOpen_ArcPath.Empty();
3193   while (!Arcs.IsEmpty())
3194     Arcs.DeleteBack();
3195 }
3196 
3197 /*
3198 void CArchiveLink::Set_ErrorsText()
3199 {
3200   FOR_VECTOR(i, Arcs)
3201   {
3202     const CArc &arc = Arcs[i];
3203     if (!arc.ErrorFlagsText.IsEmpty())
3204     {
3205       if (!ErrorsText.IsEmpty())
3206         ErrorsText.Add_LF();
3207       ErrorsText += GetUnicodeString(arc.ErrorFlagsText);
3208     }
3209     if (!arc.ErrorMessage.IsEmpty())
3210     {
3211       if (!ErrorsText.IsEmpty())
3212         ErrorsText.Add_LF();
3213       ErrorsText += arc.ErrorMessage;
3214     }
3215 
3216     if (!arc.WarningMessage.IsEmpty())
3217     {
3218       if (!ErrorsText.IsEmpty())
3219         ErrorsText.Add_LF();
3220       ErrorsText += arc.WarningMessage;
3221     }
3222   }
3223 }
3224 */
3225 
3226 HRESULT CArchiveLink::Open(COpenOptions &op)
3227 {
3228   Release();
3229   if (op.types->Size() >= 32)
3230     return E_NOTIMPL;
3231 
3232   HRESULT resSpec;
3233 
3234   for (;;)
3235   {
3236     resSpec = S_OK;
3237 
3238     op.openType = COpenType();
3239     if (op.types->Size() >= 1)
3240     {
3241       COpenType latest;
3242       if (Arcs.Size() < op.types->Size())
3243         latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3244       else
3245       {
3246         latest = (*op.types)[0];
3247         if (!latest.Recursive)
3248           break;
3249       }
3250       op.openType = latest;
3251     }
3252     else if (Arcs.Size() >= 32)
3253       break;
3254 
3255     /*
3256     op.formatIndex = -1;
3257     if (op.types->Size() >= 1)
3258     {
3259       int latest;
3260       if (Arcs.Size() < op.types->Size())
3261         latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3262       else
3263       {
3264         latest = (*op.types)[0];
3265         if (latest != -2 && latest != -3)
3266           break;
3267       }
3268       if (latest >= 0)
3269         op.formatIndex = latest;
3270       else if (latest == -1 || latest == -2)
3271       {
3272         // default
3273       }
3274       else if (latest == -3)
3275         op.formatIndex = -2;
3276       else
3277         op.formatIndex = latest + 2;
3278     }
3279     else if (Arcs.Size() >= 32)
3280       break;
3281     */
3282 
3283     if (Arcs.IsEmpty())
3284     {
3285       CArc arc;
3286       arc.filePath = op.filePath;
3287       arc.Path = op.filePath;
3288       arc.SubfileIndex = (UInt32)(Int32)-1;
3289       HRESULT result = arc.OpenStreamOrFile(op);
3290       if (result != S_OK)
3291       {
3292         if (result == S_FALSE)
3293         {
3294           NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo;
3295           // NonOpenErrorFormatIndex = arc.ErrorFormatIndex;
3296           NonOpen_ArcPath = arc.Path;
3297         }
3298         return result;
3299       }
3300       Arcs.Add(arc);
3301       continue;
3302     }
3303 
3304     // PrintNumber("op.formatIndex 11", op.formatIndex);
3305 
3306     const CArc &arc = Arcs.Back();
3307 
3308     if (op.types->Size() > Arcs.Size())
3309       resSpec = E_NOTIMPL;
3310 
3311     UInt32 mainSubfile;
3312     {
3313       NCOM::CPropVariant prop;
3314       RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop))
3315       if (prop.vt == VT_UI4)
3316         mainSubfile = prop.ulVal;
3317       else
3318         break;
3319       UInt32 numItems;
3320       RINOK(arc.Archive->GetNumberOfItems(&numItems))
3321       if (mainSubfile >= numItems)
3322         break;
3323     }
3324 
3325 
3326     CMyComPtr<IInArchiveGetStream> getStream;
3327     if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream)
3328       break;
3329 
3330     CMyComPtr<ISequentialInStream> subSeqStream;
3331     if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream)
3332       break;
3333 
3334     CMyComPtr<IInStream> subStream;
3335     if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream)
3336       break;
3337 
3338     CArc arc2;
3339     RINOK(arc.GetItem_Path(mainSubfile, arc2.Path))
3340 
3341     bool zerosTailIsAllowed;
3342     RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed))
3343 
3344 
3345     if (op.callback)
3346     {
3347       Z7_DECL_CMyComPtr_QI_FROM(
3348           IArchiveOpenSetSubArchiveName,
3349           setSubArchiveName, op.callback)
3350       if (setSubArchiveName)
3351         setSubArchiveName->SetSubArchiveName(arc2.Path);
3352     }
3353 
3354     arc2.SubfileIndex = mainSubfile;
3355 
3356     // CIntVector incl;
3357     CIntVector excl;
3358 
3359     COpenOptions op2;
3360     #ifndef Z7_SFX
3361     op2.props = op.props;
3362     #endif
3363     op2.codecs = op.codecs;
3364     // op2.types = &incl;
3365     op2.openType = op.openType;
3366     op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed;
3367     op2.excludedFormats = &excl;
3368     op2.stdInMode = false;
3369     op2.stream = subStream;
3370     op2.filePath = arc2.Path;
3371     op2.callback = op.callback;
3372     op2.callbackSpec = op.callbackSpec;
3373 
3374 
3375     HRESULT result = arc2.OpenStream(op2);
3376     resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE);
3377     if (result == S_FALSE)
3378     {
3379       NonOpen_ErrorInfo = arc2.ErrorInfo;
3380       NonOpen_ArcPath = arc2.Path;
3381       break;
3382     }
3383     RINOK(result)
3384     RINOK(arc.GetItem_MTime(mainSubfile, arc2.MTime))
3385     Arcs.Add(arc2);
3386   }
3387   IsOpen = !Arcs.IsEmpty();
3388   return resSpec;
3389 }
3390 
3391 HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI)
3392 {
3393   VolumesSize = 0;
3394   COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3395   CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec;
3396   openCallbackSpec->Callback = callbackUI;
3397 
3398   FString prefix, name;
3399 
3400   if (!op.stream && !op.stdInMode)
3401   {
3402     NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name);
3403     RINOK(openCallbackSpec->Init2(prefix, name))
3404   }
3405   else
3406   {
3407     openCallbackSpec->SetSubArchiveName(op.filePath);
3408   }
3409 
3410   op.callback = callback;
3411   op.callbackSpec = openCallbackSpec;
3412 
3413   HRESULT res = Open(op);
3414 
3415   PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3416   // Password = openCallbackSpec->Password;
3417 
3418   RINOK(res)
3419   // VolumePaths.Add(fs2us(prefix + name));
3420 
3421   FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed)
3422   {
3423     if (openCallbackSpec->FileNames_WasUsed[i])
3424     {
3425       VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]);
3426       VolumesSize += openCallbackSpec->FileSizes[i];
3427     }
3428   }
3429   // VolumesSize = openCallbackSpec->TotalSize;
3430   return S_OK;
3431 }
3432 
3433 HRESULT CArc::ReOpen(const COpenOptions &op, IArchiveOpenCallback *openCallback_Additional)
3434 {
3435   ErrorInfo.ClearErrors();
3436   ErrorInfo.ErrorFormatIndex = -1;
3437 
3438   UInt64 fileSize = 0;
3439   if (op.stream)
3440   {
3441     RINOK(InStream_SeekToBegin(op.stream))
3442     RINOK(InStream_AtBegin_GetSize(op.stream, fileSize))
3443     // RINOK(InStream_GetSize_SeekToBegin(op.stream, fileSize))
3444   }
3445   FileSize = fileSize;
3446 
3447   CMyComPtr<IInStream> stream2;
3448   Int64 globalOffset = GetGlobalOffset();
3449   if (globalOffset <= 0)
3450     stream2 = op.stream;
3451   else
3452   {
3453     CTailInStream *tailStreamSpec = new CTailInStream;
3454     stream2 = tailStreamSpec;
3455     tailStreamSpec->Stream = op.stream;
3456     tailStreamSpec->Offset = (UInt64)globalOffset;
3457     tailStreamSpec->Init();
3458     RINOK(tailStreamSpec->SeekToStart())
3459   }
3460 
3461   // There are archives with embedded STUBs (like ZIP), so we must support signature scanning
3462   // But for another archives we can use 0 here. So the code can be fixed !!!
3463   UInt64 maxStartPosition = kMaxCheckStartPosition;
3464   IArchiveOpenCallback *openCallback = openCallback_Additional;
3465   if (!openCallback)
3466     openCallback = op.callback;
3467   HRESULT res = Archive->Open(stream2, &maxStartPosition, openCallback);
3468 
3469   if (res == S_OK)
3470   {
3471     RINOK(ReadBasicProps(Archive, (UInt64)globalOffset, res))
3472     ArcStreamOffset = (UInt64)globalOffset;
3473     if (ArcStreamOffset != 0)
3474       InStream = op.stream;
3475   }
3476   return res;
3477 }
3478 
3479 HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI)
3480 {
3481   HRESULT res = Open2(op, callbackUI);
3482   if (callbackUI)
3483   {
3484     RINOK(callbackUI->Open_Finished())
3485   }
3486   return res;
3487 }
3488 
3489 HRESULT CArchiveLink::ReOpen(COpenOptions &op)
3490 {
3491   if (Arcs.Size() > 1)
3492     return E_NOTIMPL;
3493 
3494   CObjectVector<COpenType> inc;
3495   CIntVector excl;
3496 
3497   op.types = &inc;
3498   op.excludedFormats = &excl;
3499   op.stdInMode = false;
3500   op.stream = NULL;
3501   if (Arcs.Size() == 0) // ???
3502     return Open2(op, NULL);
3503 
3504   /* if archive is multivolume (unsupported here still)
3505      COpenCallbackImp object will exist after Open stage. */
3506   COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3507   CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec;
3508 
3509   openCallbackSpec->Callback = NULL;
3510   openCallbackSpec->ReOpenCallback = op.callback;
3511   {
3512     FString dirPrefix, fileName;
3513     NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName);
3514     RINOK(openCallbackSpec->Init2(dirPrefix, fileName))
3515   }
3516 
3517 
3518   CInFileStream *fileStreamSpec = new CInFileStream;
3519   CMyComPtr<IInStream> stream(fileStreamSpec);
3520   if (!fileStreamSpec->Open(us2fs(op.filePath)))
3521     return GetLastError_noZero_HRESULT();
3522   op.stream = stream;
3523 
3524   CArc &arc = Arcs[0];
3525   const HRESULT res = arc.ReOpen(op, openCallbackNew);
3526 
3527   openCallbackSpec->ReOpenCallback = NULL;
3528 
3529   PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3530   // Password = openCallbackSpec->Password;
3531 
3532   IsOpen = (res == S_OK);
3533   return res;
3534 }
3535 
3536 #ifndef Z7_SFX
3537 
3538 bool ParseComplexSize(const wchar_t *s, UInt64 &result);
3539 bool ParseComplexSize(const wchar_t *s, UInt64 &result)
3540 {
3541   result = 0;
3542   const wchar_t *end;
3543   UInt64 number = ConvertStringToUInt64(s, &end);
3544   if (end == s)
3545     return false;
3546   if (*end == 0)
3547   {
3548     result = number;
3549     return true;
3550   }
3551   if (end[1] != 0)
3552     return false;
3553   unsigned numBits;
3554   switch (MyCharLower_Ascii(*end))
3555   {
3556     case 'b': result = number; return true;
3557     case 'k': numBits = 10; break;
3558     case 'm': numBits = 20; break;
3559     case 'g': numBits = 30; break;
3560     case 't': numBits = 40; break;
3561     default: return false;
3562   }
3563   if (number >= ((UInt64)1 << (64 - numBits)))
3564     return false;
3565   result = number << numBits;
3566   return true;
3567 }
3568 
3569 static bool ParseTypeParams(const UString &s, COpenType &type)
3570 {
3571   if (s[0] == 0)
3572     return true;
3573   if (s[1] == 0)
3574   {
3575     switch ((unsigned)(Byte)s[0])
3576     {
3577       case 'e': type.EachPos = true; return true;
3578       case 'a': type.CanReturnArc = true; return true;
3579       case 'r': type.Recursive = true; return true;
3580     }
3581     return false;
3582   }
3583   if (s[0] == 's')
3584   {
3585     UInt64 result;
3586     if (!ParseComplexSize(s.Ptr(1), result))
3587       return false;
3588     type.MaxStartOffset = result;
3589     type.MaxStartOffset_Defined = true;
3590     return true;
3591   }
3592 
3593   return false;
3594 }
3595 
3596 static bool ParseType(CCodecs &codecs, const UString &s, COpenType &type)
3597 {
3598   int pos2 = s.Find(L':');
3599 
3600   {
3601   UString name;
3602   if (pos2 < 0)
3603   {
3604     name = s;
3605     pos2 = (int)s.Len();
3606   }
3607   else
3608   {
3609     name = s.Left((unsigned)pos2);
3610     pos2++;
3611   }
3612 
3613   int index = codecs.FindFormatForArchiveType(name);
3614   type.Recursive = false;
3615 
3616   if (index < 0)
3617   {
3618     if (name[0] == '*')
3619     {
3620       if (name[1] != 0)
3621         return false;
3622     }
3623     else if (name[0] == '#')
3624     {
3625       if (name[1] != 0)
3626         return false;
3627       type.CanReturnArc = false;
3628       type.CanReturnParser = true;
3629     }
3630     else if (name.IsEqualTo_Ascii_NoCase("hash"))
3631     {
3632       // type.CanReturnArc = false;
3633       // type.CanReturnParser = false;
3634       type.IsHashType = true;
3635     }
3636     else
3637       return false;
3638   }
3639 
3640   type.FormatIndex = index;
3641 
3642   }
3643 
3644   for (unsigned i = (unsigned)pos2; i < s.Len();)
3645   {
3646     int next = s.Find(L':', i);
3647     if (next < 0)
3648       next = (int)s.Len();
3649     const UString name = s.Mid(i, (unsigned)next - i);
3650     if (name.IsEmpty())
3651       return false;
3652     if (!ParseTypeParams(name, type))
3653       return false;
3654     i = (unsigned)next + 1;
3655   }
3656 
3657   return true;
3658 }
3659 
3660 bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types)
3661 {
3662   types.Clear();
3663   bool isHashType = false;
3664   for (unsigned pos = 0; pos < s.Len();)
3665   {
3666     int pos2 = s.Find(L'.', pos);
3667     if (pos2 < 0)
3668       pos2 = (int)s.Len();
3669     UString name = s.Mid(pos, (unsigned)pos2 - pos);
3670     if (name.IsEmpty())
3671       return false;
3672     COpenType type;
3673     if (!ParseType(codecs, name, type))
3674       return false;
3675     if (isHashType)
3676       return false;
3677     if (type.IsHashType)
3678       isHashType = true;
3679     types.Add(type);
3680     pos = (unsigned)pos2 + 1;
3681   }
3682   return true;
3683 }
3684 
3685 /*
3686 bool IsHashType(const CObjectVector<COpenType> &types)
3687 {
3688   if (types.Size() != 1)
3689     return false;
3690   return types[0].IsHashType;
3691 }
3692 */
3693 
3694 
3695 #endif
3696