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