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