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