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