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