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