HRESULT CArc::OpenStream2()

in other-licenses/7zstub/src/CPP/7zip/UI/Common/OpenArchive.cpp [1568:2903]


HRESULT CArc::OpenStream2(const COpenOptions &op)
{
  // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout);

  Archive.Release();
  GetRawProps.Release();
  GetRootProps.Release();

  ErrorInfo.ClearErrors();
  ErrorInfo.ErrorFormatIndex = -1;

  IsParseArc = false;
  ArcStreamOffset = 0;
  
  // OutputDebugStringA("1");
  // OutputDebugStringW(Path);

  const UString fileName = ExtractFileNameFromPath(Path);
  UString extension;
  {
    int dotPos = fileName.ReverseFind_Dot();
    if (dotPos >= 0)
      extension = fileName.Ptr(dotPos + 1);
  }
  
  CIntVector orderIndices;
  
  bool searchMarkerInHandler = false;
  #ifdef _SFX
    searchMarkerInHandler = true;
  #endif

  CBoolArr isMainFormatArr(op.codecs->Formats.Size());
  {
    FOR_VECTOR(i, op.codecs->Formats)
      isMainFormatArr[i] = false;
  }

  UInt64 maxStartOffset =
      op.openType.MaxStartOffset_Defined ?
      op.openType.MaxStartOffset :
      kMaxCheckStartPosition;

  #ifndef _SFX
  bool isUnknownExt = false;
  #endif

  bool isForced = false;
  unsigned numMainTypes = 0;
  int formatIndex = op.openType.FormatIndex;

  if (formatIndex >= 0)
  {
    isForced = true;
    orderIndices.Add(formatIndex);
    numMainTypes = 1;
    isMainFormatArr[(unsigned)formatIndex] = true;

    searchMarkerInHandler = true;
  }
  else
  {
    unsigned numFinded = 0;
    #ifndef _SFX
    bool isPrearcExt = false;
    #endif
    
    {
      #ifndef _SFX
      
      bool isZip = false;
      bool isRar = false;
      
      const wchar_t c = extension[0];
      if (c == 'z' || c == 'Z' || c == 'r' || c == 'R')
      {
        bool isNumber = false;
        for (unsigned k = 1;; k++)
        {
          const wchar_t d = extension[k];
          if (d == 0)
            break;
          if (d < '0' || d > '9')
          {
            isNumber = false;
            break;
          }
          isNumber = true;
        }
        if (isNumber)
          if (c == 'z' || c == 'Z')
            isZip = true;
          else
            isRar = true;
      }
      
      #endif

      FOR_VECTOR (i, op.codecs->Formats)
      {
        const CArcInfoEx &ai = op.codecs->Formats[i];

        if (IgnoreSplit || !op.openType.CanReturnArc)
          if (ai.IsSplit())
            continue;
        if (op.excludedFormats->FindInSorted(i) >= 0)
          continue;

        #ifndef _SFX
        if (IsPreArcFormat(ai))
          isPrearcExt = true;
        #endif

        if (ai.FindExtension(extension) >= 0
            #ifndef _SFX
            || isZip && StringsAreEqualNoCase_Ascii(ai.Name, "zip")
            || isRar && StringsAreEqualNoCase_Ascii(ai.Name, "rar")
            #endif
            )
        {
          // PrintNumber("orderIndices.Insert", i);
          orderIndices.Insert(numFinded++, i);
          isMainFormatArr[i] = true;
        }
        else
          orderIndices.Add(i);
      }
    }
  
    if (!op.stream)
    {
      if (numFinded != 1)
        return E_NOTIMPL;
      orderIndices.DeleteFrom(1);
    }
    // PrintNumber("numFinded", numFinded );

    /*
    if (op.openOnlySpecifiedByExtension)
    {
      if (numFinded != 0 && !IsExeExt(extension))
        orderIndices.DeleteFrom(numFinded);
    }
    */

    #ifndef _SFX

      if (op.stream && orderIndices.Size() >= 2)
      {
        RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
        CByteBuffer byteBuffer;
        CIntVector orderIndices2;
        if (numFinded == 0 || IsExeExt(extension))
        {
          // signature search was here
        }
        else if (extension.IsEqualTo("000") || extension.IsEqualTo("001"))
        {
          int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar");
          if (i >= 0)
          {
            const size_t kBufSize = (1 << 10);
            byteBuffer.Alloc(kBufSize);
            size_t processedSize = kBufSize;
            RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
            if (processedSize >= 16)
            {
              const Byte *buf = byteBuffer;
              const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
              if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0)
              {
                orderIndices2.Add(orderIndices[i]);
                orderIndices[i] = -1;
                if (i >= (int)numFinded)
                  numFinded++;
              }
            }
          }
        }
        else
        {
          const size_t kBufSize = (1 << 10);
          byteBuffer.Alloc(kBufSize);
          size_t processedSize = kBufSize;
          RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
          if (processedSize == 0)
            return S_FALSE;
          
          /*
          check type order:
            1) matched extension, no signuature
            2) matched extension, matched signuature
            // 3) no signuature
            // 4) matched signuature
          */

          MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0);
          MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize);
          // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0);
          // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize);
        }
      
        FOR_VECTOR (i, orderIndices)
        {
          int val = orderIndices[i];
          if (val != -1)
            orderIndices2.Add(val);
        }
        orderIndices = orderIndices2;
      }
      
      if (orderIndices.Size() >= 2)
      {
        int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso");
        int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf");
        if (iUdf > iIso && iIso >= 0)
        {
          int isoIndex = orderIndices[iIso];
          int udfIndex = orderIndices[iUdf];
          orderIndices[iUdf] = isoIndex;
          orderIndices[iIso] = udfIndex;
        }
      }

      numMainTypes = numFinded;
      isUnknownExt = (numMainTypes == 0) || isPrearcExt;

    #else // _SFX

      numMainTypes = orderIndices.Size();

      // we need correct numMainTypes for mutlivolume SFX (if some volume is missing)
      if (numFinded != 0)
        numMainTypes = numFinded;
    
    #endif
  }

  UInt64 fileSize = 0;
  if (op.stream)
  {
    RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
    RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
  }
  FileSize = fileSize;


  #ifndef _SFX

  CBoolArr skipFrontalFormat(op.codecs->Formats.Size());
  {
    FOR_VECTOR(i, op.codecs->Formats)
      skipFrontalFormat[i] = false;
  }
  
  #endif

  const COpenType &mode = op.openType;

  
  

  
  if (mode.CanReturnArc)
  {
    // ---------- OPEN main type by extenssion ----------
  
    unsigned numCheckTypes = orderIndices.Size();
    if (formatIndex >= 0)
      numCheckTypes = numMainTypes;
    
    for (unsigned i = 0; i < numCheckTypes; i++)
    {
      FormatIndex = orderIndices[i];
      
      bool exactOnly = false;

      #ifndef _SFX
    
      const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];
      // OutputDebugStringW(ai.Name);
      if (i >= numMainTypes)
      {
        if (!ai.Flags_BackwardOpen()
            // && !ai.Flags_PureStartOpen()
            )
          continue;
        exactOnly = true;
      }

      #endif
      
      // Some handlers do not set total bytes. So we set it here
      if (op.callback)
        RINOK(op.callback->SetTotal(NULL, &fileSize));

      if (op.stream)
      {
        RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
      }
      
      CMyComPtr<IInArchive> archive;
      
      RINOK(PrepareToOpen(op, FormatIndex, archive));
      if (!archive)
        continue;
      
      HRESULT result;
      if (op.stream)
      {
        UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0;
        result = archive->Open(op.stream, &searchLimit, op.callback);
      }
      else
      {
        CMyComPtr<IArchiveOpenSeq> openSeq;
        archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq);
        if (!openSeq)
          return E_NOTIMPL;
        result = openSeq->OpenSeq(op.seqStream);
      }
      
      RINOK(ReadBasicProps(archive, 0, result));
      
      if (result == S_FALSE)
      {
        bool isArc = ErrorInfo.IsArc_After_NonOpen();

        #ifndef _SFX
        // if it's archive, we allow another open attempt for parser
        if (!mode.CanReturnParser || !isArc)
          skipFrontalFormat[(unsigned)FormatIndex] = true;
        #endif
        
        if (exactOnly)
          continue;
        
        if (i == 0 && numMainTypes == 1)
        {
          // we set NonOpenErrorInfo, only if there is only one main format (defined by extension).
          ErrorInfo.ErrorFormatIndex = FormatIndex;
          NonOpen_ErrorInfo = ErrorInfo;
       
          if (!mode.CanReturnParser && isArc)
          {
            // if (formatIndex < 0 && !searchMarkerInHandler)
            {
              // if bad archive was detected, we don't need additional open attempts
              #ifndef _SFX
              if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */)
              #endif
                return S_FALSE;
            }
          }
        }
        
        /*
        #ifndef _SFX
        if (IsExeExt(extension) || ai.Flags_PreArc())
        {
        // openOnlyFullArc = false;
        // canReturnTailArc = true;
        // limitSignatureSearch = true;
        }
        #endif
        */
        
        continue;
      }
      
      RINOK(result);
      
      #ifndef _SFX

      bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
      const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);

      bool thereIsTail = ErrorInfo.ThereIsTail;
      if (thereIsTail && mode.ZerosTailIsAllowed)
      {
        RINOK(CheckZerosTail(op, Offset + PhySize));
        if (ErrorInfo.IgnoreTail)
          thereIsTail = false;
      }

      if (Offset > 0)
      {
        if (exactOnly
            || !searchMarkerInHandler
            || !specFlags.CanReturn_NonStart()
            || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset))
          continue;
      }
      if (thereIsTail)
      {
        if (Offset > 0)
        {
          if (!specFlags.CanReturnMid)
            continue;
        }
        else if (!specFlags.CanReturnFrontal)
          continue;
      }

      if (Offset > 0 || thereIsTail)
      {
        if (formatIndex < 0)
        {
          if (IsPreArcFormat(ai))
          {
            // openOnlyFullArc = false;
            // canReturnTailArc = true;
            /*
            if (mode.SkipSfxStub)
            limitSignatureSearch = true;
            */
            // if (mode.SkipSfxStub)
            {
              // skipFrontalFormat[FormatIndex] = true;
              continue;
            }
          }
        }
      }
     
      #endif

      Archive = archive;
      return S_OK;
    }
  }

  

  #ifndef _SFX

  if (!op.stream)
    return S_FALSE;

  if (formatIndex >= 0 && !mode.CanReturnParser)
  {
    if (mode.MaxStartOffset_Defined)
    {
      if (mode.MaxStartOffset == 0)
        return S_FALSE;
    }
    else
    {
      const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
      if (ai.FindExtension(extension) >= 0)
      {
        if (ai.Flags_FindSignature() && searchMarkerInHandler)
          return S_FALSE;
      }
    }
  }

  NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler;
  CMyComPtr<IInArchive> handler = handlerSpec;

  CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback;
  CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec;
  extractCallback_To_OpenCallback_Spec->Init(op.callback);

  {
    // ---------- Check all possible START archives ----------
    // this code is better for full file archives than Parser's code.

    CByteBuffer byteBuffer;
    bool endOfFile = false;
    size_t processedSize;
    {
      size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF)
      if (bufSize > fileSize)
      {
        bufSize = (size_t)fileSize;
        endOfFile = true;
      }
      byteBuffer.Alloc(bufSize);
      RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
      processedSize = bufSize;
      RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
      if (processedSize == 0)
        return S_FALSE;
      if (processedSize < bufSize)
        endOfFile = true;
    }
    CUIntVector sortedFormats;

    unsigned i;

    int splitIndex = -1;

    for (i = 0; i < orderIndices.Size(); i++)
    {
      unsigned form = orderIndices[i];
      if (skipFrontalFormat[form])
        continue;
      const CArcInfoEx &ai = op.codecs->Formats[form];
      if (ai.IsSplit())
      {
        splitIndex = form;
        continue;
      }

      if (ai.IsArcFunc)
      {
        UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize);
        if (isArcRes == k_IsArc_Res_NO)
          continue;
        if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
          continue;
        // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue;
        sortedFormats.Insert(0, form);
        continue;
      }

      bool isNewStyleSignature = IsNewStyleSignature(ai);
      bool needCheck = !isNewStyleSignature
          || ai.Signatures.IsEmpty()
          || ai.Flags_PureStartOpen()
          || ai.Flags_StartOpen()
          || ai.Flags_BackwardOpen();
    
      if (isNewStyleSignature && !ai.Signatures.IsEmpty())
      {
        unsigned k;
        for (k = 0; k < ai.Signatures.Size(); k++)
        {
          const CByteBuffer &sig = ai.Signatures[k];
          UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
          if (processedSize < signatureEnd)
          {
            if (!endOfFile)
              needCheck = true;
          }
          else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0)
            break;
        }
        if (k != ai.Signatures.Size())
        {
          sortedFormats.Insert(0, form);
          continue;
        }
      }
      if (needCheck)
        sortedFormats.Add(form);
    }

    if (splitIndex >= 0)
      sortedFormats.Insert(0, splitIndex);

    for (i = 0; i < sortedFormats.Size(); i++)
    {
      FormatIndex = sortedFormats[i];
      const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];

      if (op.callback)
        RINOK(op.callback->SetTotal(NULL, &fileSize));

      RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));

      CMyComPtr<IInArchive> archive;
      RINOK(PrepareToOpen(op, FormatIndex, archive));
      if (!archive)
        continue;
      
      PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name));
      HRESULT result;
      {
        UInt64 searchLimit = 0;
        /*
        if (mode.CanReturnArc)
          result = archive->Open(op.stream, &searchLimit, op.callback);
        else
        */
        result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback);
      }
      
      if (result == S_FALSE)
      {
        skipFrontalFormat[(unsigned)FormatIndex] = true;
        // FIXME: maybe we must use LenIsUnknown.
        // printf("  OpenForSize Error");
        continue;
      }
      RINOK(result);

      RINOK(ReadBasicProps(archive, 0, result));

      if (Offset > 0)
      {
        continue; // good handler doesn't return such Offset > 0
        // but there are some cases like false prefixed PK00 archive, when
        // we can support it?
      }

      NArchive::NParser::CParseItem pi;
      pi.Offset = Offset;
      pi.Size = AvailPhySize;
      
      // bool needScan = false;

      if (!PhySizeDefined)
      {
        // it's for Z format
        pi.LenIsUnknown = true;
        // needScan = true;
        // phySize = arcRem;
        // nextNeedCheckStartOpen = false;
      }

      /*
      if (OkPhySize_Defined)
        pi.OkSize = pi.OkPhySize;
      else
        pi.OkSize = pi.Size;
      */

      pi.NormalizeOffset();
      // printf("  phySize = %8d", (unsigned)phySize);


      if (mode.CanReturnArc)
      {
        bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
        const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
        bool openCur = false;

        if (!ErrorInfo.ThereIsTail)
          openCur = true;
        else
        {
          if (mode.ZerosTailIsAllowed)
          {
            RINOK(CheckZerosTail(op, Offset + PhySize));
            if (ErrorInfo.IgnoreTail)
              openCur = true;
          }
          if (!openCur)
          {
            openCur = specFlags.CanReturnFrontal;
            if (formatIndex < 0) // format is not forced
            {
              if (IsPreArcFormat(ai))
              {
                // if (mode.SkipSfxStub)
                {
                  openCur = false;
                }
              }
            }
          }
        }
        
        if (openCur)
        {
          InStream = op.stream;
          Archive = archive;
          return S_OK;
        }
      }
        
      skipFrontalFormat[(unsigned)FormatIndex] = true;


      // if (!mode.CanReturnArc)
      /*
      if (!ErrorInfo.ThereIsTail)
          continue;
      */
      if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
        continue;

      // printf("\nAdd offset = %d", (int)pi.Offset);
      RINOK(ReadParseItemProps(archive, ai, pi));
      handlerSpec->AddItem(pi);
    }
  }

  

  
  
  // ---------- PARSER ----------

  CUIntVector arc2sig; // formatIndex to signatureIndex
  CUIntVector sig2arc; // signatureIndex to formatIndex;
  {
    unsigned sum = 0;
    FOR_VECTOR (i, op.codecs->Formats)
    {
      arc2sig.Add(sum);
      const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures;
      sum += sigs.Size();
      FOR_VECTOR (k, sigs)
        sig2arc.Add(i);
    }
  }
  
  {
    const size_t kBeforeSize = 1 << 16;
    const size_t kAfterSize  = 1 << 20;
    const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize

    const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8);
    CByteArr hashBuffer(kNumVals);
    Byte *hash = hashBuffer;
    memset(hash, 0xFF, kNumVals);
    Byte prevs[256];
    memset(prevs, 0xFF, sizeof(prevs));
    if (sig2arc.Size() >= 0xFF)
      return S_FALSE;

    CUIntVector difficultFormats;
    CBoolArr difficultBools(256);
    {
      for (unsigned i = 0; i < 256; i++)
        difficultBools[i] = false;
    }

    bool thereAreHandlersForSearch = false;

    // UInt32 maxSignatureEnd = 0;
    
    FOR_VECTOR (i, orderIndices)
    {
      int index = orderIndices[i];
      if (index < 0)
        continue;
      const CArcInfoEx &ai = op.codecs->Formats[(unsigned)index];
      bool isDifficult = false;
      // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31)
      if (!ai.NewInterface)
        isDifficult = true;
      else
      {
        if (ai.Flags_StartOpen())
          isDifficult = true;
        FOR_VECTOR (k, ai.Signatures)
        {
          const CByteBuffer &sig = ai.Signatures[k];
          /*
          UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
          if (maxSignatureEnd < signatureEnd)
            maxSignatureEnd = signatureEnd;
          */
          if (sig.Size() < kNumHashBytes)
          {
            isDifficult = true;
            continue;
          }
          thereAreHandlersForSearch = true;
          UInt32 v = HASH_VAL(sig);
          unsigned sigIndex = arc2sig[(unsigned)index] + k;
          prevs[sigIndex] = hash[v];
          hash[v] = (Byte)sigIndex;
        }
      }
      if (isDifficult)
      {
        difficultFormats.Add(index);
        difficultBools[(unsigned)index] = true;
      }
    }
    
    if (!thereAreHandlersForSearch)
    {
      // openOnlyFullArc = true;
      // canReturnTailArc = true;
    }
    
    RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));

    CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream;
    CMyComPtr<IInStream> limitedStream = limitedStreamSpec;
    limitedStreamSpec->SetStream(op.stream);

    CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL;
    CMyComPtr<IArchiveOpenCallback> openCallback_Offset;
    if (op.callback)
    {
      openCallback_Offset_Spec = new CArchiveOpenCallback_Offset;
      openCallback_Offset = openCallback_Offset_Spec;
      openCallback_Offset_Spec->Callback = op.callback;
      openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback);
      #ifndef _NO_CRYPTO
      openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword);
      #endif
    }

    if (op.callback)
      RINOK(op.callback->SetTotal(NULL, &fileSize));
  
    CByteBuffer &byteBuffer = limitedStreamSpec->Buffer;
    byteBuffer.Alloc(kBufSize);

    UInt64 callbackPrev = 0;
    bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos.

    bool endOfFile = false;
    UInt64 bufPhyPos = 0;
    size_t bytesInBuf = 0;
    // UInt64 prevPos = 0;
    
    // ---------- Main Scan Loop ----------

    UInt64 pos = 0;

    if (!mode.EachPos && handlerSpec->_items.Size() == 1)
    {
      NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
      if (!pi.LenIsUnknown && pi.Offset == 0)
        pos = pi.Size;
    }

    for (;;)
    {
      // printf("\nPos = %d", (int)pos);
      UInt64 posInBuf = pos - bufPhyPos;
      
      // if (pos > ((UInt64)1 << 35)) break;
      
      if (!endOfFile)
      {
        if (bytesInBuf < kBufSize)
        {
          size_t processedSize = kBufSize - bytesInBuf;
          // printf("\nRead ask = %d", (unsigned)processedSize);
          UInt64 seekPos = bufPhyPos + bytesInBuf;
          RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL));
          RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize));
          // printf("   processed = %d", (unsigned)processedSize);
          if (processedSize == 0)
          {
            fileSize = seekPos;
            endOfFile = true;
          }
          else
          {
            bytesInBuf += processedSize;
            limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos);
          }
          continue;
        }
        
        if (bytesInBuf < posInBuf)
        {
          UInt64 skipSize = posInBuf - bytesInBuf;
          if (skipSize <= kBeforeSize)
          {
            size_t keepSize = (size_t)(kBeforeSize - skipSize);
            // printf("\nmemmove skip = %d", (int)keepSize);
            memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize);
            bytesInBuf = keepSize;
            bufPhyPos = pos - keepSize;
            continue;
          }
          // printf("\nSkip %d", (int)(skipSize - kBeforeSize));
          // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL));
          bytesInBuf = 0;
          bufPhyPos = pos - kBeforeSize;
          continue;
        }
        
        if (bytesInBuf - posInBuf < kAfterSize)
        {
          size_t beg = (size_t)posInBuf - kBeforeSize;
          // printf("\nmemmove for after beg = %d", (int)beg);
          memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg);
          bufPhyPos += beg;
          bytesInBuf -= beg;
          continue;
        }
      }

      if (bytesInBuf <= (size_t)posInBuf)
        break;

      bool useOffsetCallback = false;
      if (openCallback_Offset)
      {
        openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
        openCallback_Offset_Spec->Offset = pos;

        useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1);
 
        if (pos >= callbackPrev + (1 << 23))
        {
          RINOK(openCallback_Offset_Spec->SetCompleted(NULL, NULL));
          callbackPrev = pos;
        }
      }

      {
        UInt64 endPos = bufPhyPos + bytesInBuf;
        if (fileSize < endPos)
        {
          FileSize = fileSize; // why ????
          fileSize = endPos;
        }
      }

      size_t availSize = bytesInBuf - (size_t)posInBuf;
      if (availSize < kNumHashBytes)
        break;
      size_t scanSize = availSize -
          ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes);
  
      {
        /*
        UInt64 scanLimit = openOnlyFullArc ?
            maxSignatureEnd :
            op.openType.ScanSize + maxSignatureEnd;
        */
        if (!mode.CanReturnParser)
        {
          if (pos > maxStartOffset)
            break;
          UInt64 remScan = maxStartOffset - pos;
          if (scanSize > remScan)
            scanSize = (size_t)remScan;
        }
      }

      scanSize++;

      const Byte *buf = byteBuffer + (size_t)posInBuf;
      const Byte *bufLimit = buf + scanSize;
      size_t ppp = 0;
      
      if (!needCheckStartOpen)
      {
        for (; buf < bufLimit && hash[HASH_VAL(buf)] == 0xFF; buf++);
        ppp = buf - (byteBuffer + (size_t)posInBuf);
        pos += ppp;
        if (buf == bufLimit)
          continue;
      }
      
      UInt32 v = HASH_VAL(buf);
      bool nextNeedCheckStartOpen = true;
      unsigned i = hash[v];
      unsigned indexOfDifficult = 0;

      // ---------- Open Loop for Current Pos ----------
      bool wasOpen = false;
      
      for (;;)
      {
        unsigned index;
        bool isDifficult;
        if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size())
        {
          index = difficultFormats[indexOfDifficult++];
          isDifficult = true;
        }
        else
        {
          if (i == 0xFF)
            break;
          index = sig2arc[i];
          unsigned sigIndex = i - arc2sig[index];
          i = prevs[i];
          if (needCheckStartOpen && difficultBools[index])
            continue;
          const CArcInfoEx &ai = op.codecs->Formats[index];

          if (pos < ai.SignatureOffset)
            continue;

          /*
          if (openOnlyFullArc)
            if (pos != ai.SignatureOffset)
              continue;
          */
  
          const CByteBuffer &sig = ai.Signatures[sigIndex];

          if (ppp + sig.Size() > availSize
              || !TestSignature(buf, sig, sig.Size()))
            continue;
          // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos));
          // prevPos = pos;
          isDifficult = false;
        }

        const CArcInfoEx &ai = op.codecs->Formats[index];


        if ((isDifficult && pos == 0) || ai.SignatureOffset == pos)
        {
          // we don't check same archive second time */
          if (skipFrontalFormat[index])
            continue;
        }

        UInt64 startArcPos = pos;
        if (!isDifficult)
        {
          if (pos < ai.SignatureOffset)
            continue;
          startArcPos = pos - ai.SignatureOffset;
          /*
          // we don't need the check for Z files
          if (startArcPos < handlerSpec->GetLastEnd())
            continue;
          */
        }
        
        if (ai.IsArcFunc && startArcPos >= bufPhyPos)
        {
          size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos);
          if (offsetInBuf < bytesInBuf)
          {
            UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf);
            if (isArcRes == k_IsArc_Res_NO)
              continue;
            if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
              continue;
            /*
            if (isArcRes == k_IsArc_Res_YES_LOW_PROB)
            {
              // if (pos != ai.SignatureOffset)
              continue;
            }
            */
          }
          // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name);
        }
        
        /*
        if (pos == 67109888)
          pos = pos;
        */
        PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name));

        bool isMainFormat = isMainFormatArr[index];
        const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
        
        CMyComPtr<IInArchive> archive;
        RINOK(PrepareToOpen(op, index, archive));
        if (!archive)
          return E_FAIL;
        
        // OutputDebugStringW(ai.Name);
        
        UInt64 rem = fileSize - startArcPos;
        
        UInt64 arcStreamOffset = 0;

        if (ai.Flags_UseGlobalOffset())
        {
          limitedStreamSpec->InitAndSeek(0, fileSize);
          limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL);
        }
        else
        {
          limitedStreamSpec->InitAndSeek(startArcPos, rem);
          arcStreamOffset = startArcPos;
        }
        
        UInt64 maxCheckStartPosition = 0;
        
        if (openCallback_Offset)
        {
          openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
          openCallback_Offset_Spec->Offset = startArcPos;
        }

        // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset);
        extractCallback_To_OpenCallback_Spec->Files = 0;
        extractCallback_To_OpenCallback_Spec->Offset = startArcPos;

        HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition,
            useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback,
            extractCallback_To_OpenCallback);

        RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result));

        bool isOpen = false;
        if (result == S_FALSE)
        {
          if (!mode.CanReturnParser)
          {
            if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen())
            {
              ErrorInfo.ErrorFormatIndex = index;
              NonOpen_ErrorInfo = ErrorInfo;
              // if archive was detected, we don't need additional open attempts
              return S_FALSE;
            }
            continue;
          }
          if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0)
            continue;
        }
        else
        {
          isOpen = true;
          RINOK(result);
          PRF(printf("  OK "));
        }

        // fprintf(stderr, "\n %8X  %S", startArcPos, Path);
        // printf("\nOpen OK: %S", ai.Name);
        
        
        NArchive::NParser::CParseItem pi;
        pi.Offset = startArcPos;

        if (ai.Flags_UseGlobalOffset())
          pi.Offset = Offset;
        else if (Offset != 0)
          return E_FAIL;
        UInt64 arcRem = FileSize - pi.Offset;
        UInt64 phySize = arcRem;
        bool phySizeDefined = PhySizeDefined;
        if (phySizeDefined)
        {
          if (pi.Offset + PhySize > FileSize)
          {
            // ErrorInfo.ThereIsTail = true;
            PhySize = FileSize - pi.Offset;
          }
          phySize = PhySize;
        }
        if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63))
          return E_FAIL;

        /*
        if (!ai.UseGlobalOffset)
        {
          if (phySize > arcRem)
          {
            ThereIsTail = true;
            phySize = arcRem;
          }
        }
        */
        
        bool needScan = false;

 
        if (isOpen && !phySizeDefined)
        {
          // it's for Z format
          pi.LenIsUnknown = true;
          needScan = true;
          phySize = arcRem;
          nextNeedCheckStartOpen = false;
        }

        pi.Size = phySize;
        /*
        if (OkPhySize_Defined)
          pi.OkSize = OkPhySize;
        */
        pi.NormalizeOffset();
        // printf("  phySize = %8d", (unsigned)phySize);

        /*
        if (needSkipFullArc)
          if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize)
            continue;
        */
        if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
        {
          // it's possible for dmg archives
          if (!mode.CanReturnArc)
            continue;
        }

        if (mode.EachPos)
          pos++;
        else if (needScan)
        {
          pos++;
          /*
          if (!OkPhySize_Defined)
            pos++;
          else
            pos = pi.Offset + pi.OkSize;
          */
        }
        else
          pos = pi.Offset + pi.Size;

       
        RINOK(ReadParseItemProps(archive, ai, pi));

        if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */)
        {
          /* It's for DMG format.
          This code deletes all previous items that are included to current item */
            
          while (!handlerSpec->_items.IsEmpty())
          {
            {
              const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back();
              if (back.Offset < pi.Offset)
                break;
              if (back.Offset + back.Size > pi.Offset + pi.Size)
                break;
            }
            handlerSpec->_items.DeleteBack();
          }
        }
        

        if (isOpen && mode.CanReturnArc && phySizeDefined)
        {
          // if (pi.Offset + pi.Size >= fileSize)
          bool openCur = false;

          bool thereIsTail = ErrorInfo.ThereIsTail;
          if (thereIsTail && mode.ZerosTailIsAllowed)
          {
            RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize));
            if (ErrorInfo.IgnoreTail)
              thereIsTail = false;
          }

          if (pi.Offset != 0)
          {
            if (!pi.IsNotArcType)
              if (thereIsTail)
                openCur = specFlags.CanReturnMid;
              else
                openCur = specFlags.CanReturnTail;
          }
          else
          {
            if (!thereIsTail)
              openCur = true;
            else
              openCur = specFlags.CanReturnFrontal;
              

            if (formatIndex >= -2)
              openCur = true;
          }
          if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */)
            openCur = false;

          // We open file as SFX, if there is front archive or first archive is "Self Executable"
          if (!openCur && !pi.IsSelfExe && !thereIsTail &&
              (!pi.IsNotArcType || pi.Offset == 0))
          {
            if (handlerSpec->_items.IsEmpty())
            {
              if (specFlags.CanReturnTail)
                openCur = true;
            }
            else if (handlerSpec->_items.Size() == 1)
            {
              if (handlerSpec->_items[0].IsSelfExe)
              {
                if (mode.SpecUnknownExt.CanReturnTail)
                  openCur = true;
              }
            }
          }

          if (openCur)
          {
            InStream = op.stream;
            Archive = archive;
            FormatIndex = index;
            ArcStreamOffset = arcStreamOffset;
            return S_OK;
          }
        }

        /*
        if (openOnlyFullArc)
        {
          ErrorInfo.ClearErrors();
          return S_FALSE;
        }
        */

        pi.FormatIndex = index;

        // printf("\nAdd offset = %d", (int)pi.Offset);
        handlerSpec->AddItem(pi);
        wasOpen = true;
        break;
      }
      // ---------- End of Open Loop for Current Pos ----------
     
      if (!wasOpen)
        pos++;
      needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen);
    }
    // ---------- End of Main Scan Loop ----------

    /*
    if (handlerSpec->_items.Size() == 1)
    {
      const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
      if (pi.Size == fileSize && pi.Offset == 0)
      {
        Archive = archive;
        FormatIndex2 = pi.FormatIndex;
        return S_OK;
      }
    }
    */

    if (mode.CanReturnParser)
    {
      bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing
      handlerSpec->AddUnknownItem(fileSize);
      if (handlerSpec->_items.Size() == 0)
        return S_FALSE;
      if (returnParser || handlerSpec->_items.Size() != 1)
      {
        // return S_FALSE;
        handlerSpec->_stream = op.stream;
        Archive = handler;
        ErrorInfo.ClearErrors();
        IsParseArc = true;
        FormatIndex = -1; // It's parser
        Offset = 0;
        return S_OK;
      }
    }
  }

  #endif

  if (!Archive)
    return S_FALSE;
  return S_OK;
}