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