Home | History | Annotate | Download | only in Common
      1 // OpenArchive.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 // #define SHOW_DEBUG_INFO
      6 
      7 #ifdef SHOW_DEBUG_INFO
      8 #include <stdio.h>
      9 #endif
     10 
     11 #include "../../../../C/CpuArch.h"
     12 
     13 #include "../../../Common/ComTry.h"
     14 #include "../../../Common/IntToString.h"
     15 #include "../../../Common/StringConvert.h"
     16 #include "../../../Common/StringToInt.h"
     17 #include "../../../Common/Wildcard.h"
     18 
     19 #include "../../../Windows/FileDir.h"
     20 
     21 #include "../../Common/FileStreams.h"
     22 #include "../../Common/LimitedStreams.h"
     23 #include "../../Common/ProgressUtils.h"
     24 #include "../../Common/StreamUtils.h"
     25 
     26 #include "../../Compress/CopyCoder.h"
     27 
     28 #include "DefaultName.h"
     29 #include "OpenArchive.h"
     30 
     31 #ifndef _SFX
     32 #include "SetProperties.h"
     33 #endif
     34 
     35 #ifdef SHOW_DEBUG_INFO
     36 #define PRF(x) x
     37 #else
     38 #define PRF(x)
     39 #endif
     40 
     41 // increase it, if you need to support larger SFX stubs
     42 static const UInt64 kMaxCheckStartPosition = 1 << 22;
     43 
     44 /*
     45 Open:
     46   - formatIndex >= 0 (exact Format)
     47        1) Open with main type. Archive handler is allowed to use archive start finder.
     48           Warning, if there is tail.
     49 
     50   - formatIndex = -1 (Parser:0) (default)
     51     - same as #1 but doesn't return Parser
     52 
     53   - formatIndex = -2 (#1)
     54     - file has supported extension (like a.7z)
     55       Open with that main type (only starting from start of file).
     56         - open OK:
     57             - if there is no tail - return OK
     58             - if there is tail:
     59               - archive is not "Self Exe" - return OK with Warning, that there is tail
     60               - archive is "Self Exe"
     61                 ignore "Self Exe" stub, and tries to open tail
     62                   - tail can be open as archive - shows that archive and stub size property.
     63                   - tail can't be open as archive - shows Parser ???
     64         - open FAIL:
     65            Try to open with all other types from offset 0 only.
     66            If some open type is OK and physical archive size is uequal or larger
     67            than file size, then return that archive with warning that can not be open as [extension type].
     68            If extension was EXE, it will try to open as unknown_extension case
     69     - file has unknown extension (like a.hhh)
     70        It tries to open via parser code.
     71          - if there is full archive or tail archive and unknown block or "Self Exe"
     72            at front, it shows tail archive and stub size property.
     73          - in another cases, if there is some archive inside file, it returns parser/
     74          - in another cases, it retuens S_FALSE
     75 
     76 
     77   - formatIndex = -3 (#2)
     78     - same as #1, but
     79     - stub (EXE) + archive is open in Parser
     80 
     81   - formatIndex = -4 (#3)
     82     - returns only Parser. skip full file archive. And show other sub-archives
     83 
     84   - formatIndex = -5 (#4)
     85     - returns only Parser. skip full file archive. And show other sub-archives for each byte pos
     86 
     87 */
     88 
     89 
     90 
     91 
     92 using namespace NWindows;
     93 
     94 /*
     95 #ifdef _SFX
     96 #define OPEN_PROPS_PARAM
     97 #else
     98 #define OPEN_PROPS_PARAM  , props
     99 #endif
    100 */
    101 
    102 /*
    103 CArc::~CArc()
    104 {
    105   GetRawProps.Release();
    106   Archive.Release();
    107   printf("\nCArc::~CArc()\n");
    108 }
    109 */
    110 
    111 #ifndef _SFX
    112 
    113 namespace NArchive {
    114 namespace NParser {
    115 
    116 struct CParseItem
    117 {
    118   UInt64 Offset;
    119   UInt64 Size;
    120   // UInt64 OkSize;
    121   UString Name;
    122   UString Extension;
    123   FILETIME FileTime;
    124   UString Comment;
    125   UString ArcType;
    126 
    127   bool FileTime_Defined;
    128   bool UnpackSize_Defined;
    129   bool NumSubDirs_Defined;
    130   bool NumSubFiles_Defined;
    131 
    132   bool IsSelfExe;
    133   bool IsNotArcType;
    134 
    135   UInt64 UnpackSize;
    136   UInt64 NumSubDirs;
    137   UInt64 NumSubFiles;
    138 
    139   int FormatIndex;
    140 
    141   bool LenIsUnknown;
    142 
    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 
    172 class CHandler:
    173   public IInArchive,
    174   public IInArchiveGetStream,
    175   public CMyUnknownImp
    176 {
    177 public:
    178   CObjectVector<CParseItem> _items;
    179   UInt64 _maxEndOffset;
    180   CMyComPtr<IInStream> _stream;
    181 
    182   MY_UNKNOWN_IMP2(
    183     IInArchive,
    184     IInArchiveGetStream)
    185 
    186   INTERFACE_IInArchive(;)
    187   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
    188 
    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 
    208 int 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 
    232 void 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 
    261 void 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 /*
    275 static 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 
    288 static const Byte kProps[] =
    289 {
    290   kpidPath,
    291   kpidSize,
    292   kpidMTime,
    293   kpidType,
    294   kpidComment,
    295   kpidOffset,
    296   kpidUnpackSize
    297 };
    298 
    299 IMP_IInArchive_Props
    300 IMP_IInArchive_ArcProps_NO
    301 
    302 STDMETHODIMP 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 
    313 STDMETHODIMP CHandler::Close()
    314 {
    315   _items.Clear();
    316   _stream.Release();
    317   return S_OK;
    318 }
    319 
    320 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
    321 {
    322   *numItems = _items.Size();
    323   return S_OK;
    324 }
    325 
    326 STDMETHODIMP 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 
    367 HRESULT 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 
    437 STDMETHODIMP 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 
    449 HRESULT 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 
    461 HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw()
    462 {
    463   return Archive_GetItemBoolProp(arc, index, kpidIsDir, result);
    464 }
    465 
    466 HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw()
    467 {
    468   return Archive_GetItemBoolProp(arc, index, kpidIsAux, result);
    469 }
    470 
    471 HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw()
    472 {
    473   return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result);
    474 }
    475 
    476 HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw()
    477 {
    478   return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result);
    479 }
    480 
    481 static 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 
    493 static 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 
    510 static 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 
    527 HRESULT 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 
    574 HRESULT 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 
    693 HRESULT 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 
    708 static 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 
    729 HRESULT 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 
    748 HRESULT 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 
    771 static 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 
    779 static 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 
    825 static bool IsExeExt(const UString &ext)
    826 {
    827   return ext.IsEqualToNoCase(L"exe");
    828 }
    829 
    830 static const char *k_PreArcFormats[] =
    831 {
    832     "pe"
    833   , "elf"
    834   , "macho"
    835   , "mub"
    836   , "te"
    837 };
    838 
    839 static 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 
    848 static 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 
    855 static 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 
    869 static 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 
    877 class CArchiveOpenCallback_Offset:
    878   public IArchiveOpenCallback,
    879   #ifndef _NO_CRYPTO
    880   public ICryptoGetTextPassword,
    881   #endif
    882   public CMyUnknownImp
    883 {
    884 public:
    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
    905 STDMETHODIMP 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 
    915 STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
    916 {
    917   return S_OK;
    918 }
    919 
    920 STDMETHODIMP 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 
    932 UInt32 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 
    946 void 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 
    963 HRESULT 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 /*
   1032 static PrintNumber(const char *s, int n)
   1033 {
   1034   char temp[100];
   1035   sprintf(temp, "%s %d", s, n);
   1036   OutputDebugStringA(temp);
   1037 }
   1038 */
   1039 
   1040 HRESULT 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 
   1096 static 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 
   1190 HRESULT 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 
   1222 class CExtractCallback_To_OpenCallback:
   1223   public IArchiveExtractCallback,
   1224   public ICompressProgressInfo,
   1225   public CMyUnknownImp
   1226 {
   1227 public:
   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 
   1243 STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */)
   1244 {
   1245   return S_OK;
   1246 }
   1247 
   1248 STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */)
   1249 {
   1250   return S_OK;
   1251 }
   1252 
   1253 STDMETHODIMP 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 
   1265 STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */)
   1266 {
   1267   *outStream = 0;
   1268   return S_OK;
   1269 }
   1270 
   1271 STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */)
   1272 {
   1273   return S_OK;
   1274 }
   1275 
   1276 STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */)
   1277 {
   1278   return S_OK;
   1279 }
   1280 
   1281 static 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 
   1315 static 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 
   1325 HRESULT 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 
   2593 HRESULT 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 
   2651 HRESULT 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 
   2734 void 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 
   2745 HRESULT 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 
   2756 void CArchiveLink::Release()
   2757 {
   2758   // NonOpenErrorFormatIndex = -1;
   2759   NonOpen_ErrorInfo.ClearErrors();
   2760   NonOpen_ArcPath.Empty();
   2761   while (!Arcs.IsEmpty())
   2762     Arcs.DeleteBack();
   2763 }
   2764 
   2765 /*
   2766 void 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 
   2794 HRESULT 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 
   2954 static 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 
   2969 HRESULT 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 
   3005 HRESULT 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 
   3048 HRESULT 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 
   3080 bool 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 
   3110 static 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 
   3137 bool 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 
   3191 bool 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