예제 #1
0
파일: ChmHandler.cpp 프로젝트: Dabil/puNES
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    Int32 testModeSpec, IArchiveExtractCallback *extractCallback)
{
  COM_TRY_BEGIN
  bool allFilesMode = (numItems == (UInt32)-1);

  if (allFilesMode)
    numItems = m_Database.NewFormat ? 1:
      (m_Database.LowLevel ?
      m_Database.Items.Size():
      m_Database.Indices.Size());
  if (numItems == 0)
    return S_OK;
  bool testMode = (testModeSpec != 0);

  UInt64 currentTotalSize = 0;

  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
  UInt32 i;

  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(extractCallback, false);

  CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
  CMyComPtr<ISequentialInStream> inStream(streamSpec);
  streamSpec->SetStream(m_Stream);

  if (m_Database.LowLevel)
  {
    UInt64 currentItemSize = 0;
    UInt64 totalSize = 0;
    if (m_Database.NewFormat)
      totalSize = m_Database.NewFormatString.Length();
    else
      for (i = 0; i < numItems; i++)
        totalSize += m_Database.Items[allFilesMode ? i : indices[i]].Size;
    extractCallback->SetTotal(totalSize);
    
    for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
    {
      currentItemSize = 0;
      lps->InSize = currentTotalSize; // Change it
      lps->OutSize = currentTotalSize;

      RINOK(lps->SetCur());
      CMyComPtr<ISequentialOutStream> realOutStream;
      Int32 askMode= testMode ?
          NExtract::NAskMode::kTest :
          NExtract::NAskMode::kExtract;
      Int32 index = allFilesMode ? i : indices[i];
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));

      if (m_Database.NewFormat)
      {
        if (index != 0)
          return E_FAIL;
        if (!testMode && !realOutStream)
          continue;
        if (!testMode)
        {
          UInt32 size = m_Database.NewFormatString.Length();
          RINOK(WriteStream(realOutStream, (const char *)m_Database.NewFormatString, size));
        }
        RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
        continue;
      }
      const CItem &item = m_Database.Items[index];
      
      currentItemSize = item.Size;
      
      if (!testMode && !realOutStream)
        continue;
      RINOK(extractCallback->PrepareOperation(askMode));
      if (item.Section != 0)
      {
        RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod));
        continue;
      }

      if (testMode)
      {
        RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
        continue;
      }
      
      RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
      streamSpec->Init(item.Size);
      
      RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ?
          NExtract::NOperationResult::kOK:
          NExtract::NOperationResult::kDataError));
    }
    return S_OK;
  }
  
  UInt64 lastFolderIndex = ((UInt64)0 - 1);
  for (i = 0; i < numItems; i++)
  {
    UInt32 index = allFilesMode ? i : indices[i];
    int entryIndex = m_Database.Indices[index];
    const CItem &item = m_Database.Items[entryIndex];
    UInt64 sectionIndex = item.Section;
    if (item.IsDir() || item.Size == 0)
      continue;
    if (sectionIndex == 0)
    {
      currentTotalSize += item.Size;
      continue;
    }
    const CSectionInfo &section = m_Database.Sections[(int)item.Section];
    if (section.IsLzx())
    {
      const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;
      UInt64 folderIndex = m_Database.GetFolder(index);
      if (lastFolderIndex == folderIndex)
        folderIndex++;
      lastFolderIndex = m_Database.GetLastFolder(index);
      for (; folderIndex <= lastFolderIndex; folderIndex++)
        currentTotalSize += lzxInfo.GetFolderSize();
    }
  }

  RINOK(extractCallback->SetTotal(currentTotalSize));

  NCompress::NLzx::CDecoder *lzxDecoderSpec = 0;
  CMyComPtr<ICompressCoder> lzxDecoder;
  CChmFolderOutStream *chmFolderOutStream = 0;
  CMyComPtr<ISequentialOutStream> outStream;

  currentTotalSize = 0;

  CRecordVector<bool> extractStatuses;
  for (i = 0; i < numItems;)
  {
    RINOK(extractCallback->SetCompleted(&currentTotalSize));
    UInt32 index = allFilesMode ? i : indices[i];
    i++;
    int entryIndex = m_Database.Indices[index];
    const CItem &item = m_Database.Items[entryIndex];
    UInt64 sectionIndex = item.Section;
    Int32 askMode= testMode ?
        NExtract::NAskMode::kTest :
        NExtract::NAskMode::kExtract;
    if (item.IsDir())
    {
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      RINOK(extractCallback->PrepareOperation(askMode));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
      continue;
    }

    lps->InSize = currentTotalSize; // Change it
    lps->OutSize = currentTotalSize;

    if (item.Size == 0 || sectionIndex == 0)
    {
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      if (!testMode && !realOutStream)
        continue;
      RINOK(extractCallback->PrepareOperation(askMode));
      Int32 opRes = NExtract::NOperationResult::kOK;
      if (!testMode && item.Size != 0)
      {
        RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
        streamSpec->Init(item.Size);
        RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
        if (copyCoderSpec->TotalSize != item.Size)
          opRes = NExtract::NOperationResult::kDataError;
      }
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(opRes));
      currentTotalSize += item.Size;
      continue;
    }
  
    const CSectionInfo &section = m_Database.Sections[(int)sectionIndex];

    if (!section.IsLzx())
    {
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      if (!testMode && !realOutStream)
        continue;
      RINOK(extractCallback->PrepareOperation(askMode));
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod));
      continue;
    }

    const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;

    if (chmFolderOutStream == 0)
    {
      chmFolderOutStream = new CChmFolderOutStream;
      outStream = chmFolderOutStream;
    }

    chmFolderOutStream->Init(&m_Database, extractCallback, testMode);

    if (lzxDecoderSpec == NULL)
    {
      lzxDecoderSpec = new NCompress::NLzx::CDecoder;
      lzxDecoder = lzxDecoderSpec;
    }

    UInt64 folderIndex = m_Database.GetFolder(index);

    UInt64 compressedPos = m_Database.ContentOffset + section.Offset;
    UInt32 numDictBits = lzxInfo.GetNumDictBits();
    RINOK(lzxDecoderSpec->SetParams(numDictBits));

    const CItem *lastItem = &item;
    extractStatuses.Clear();
    extractStatuses.Add(true);

    for (;; folderIndex++)
    {
      RINOK(extractCallback->SetCompleted(&currentTotalSize));

      UInt64 startPos = lzxInfo.GetFolderPos(folderIndex);
      UInt64 finishPos = lastItem->Offset + lastItem->Size;
      UInt64 limitFolderIndex = lzxInfo.GetFolder(finishPos);

      lastFolderIndex = m_Database.GetLastFolder(index);
      UInt64 folderSize = lzxInfo.GetFolderSize();
      UInt64 unPackSize = folderSize;
      if (extractStatuses.IsEmpty())
        chmFolderOutStream->m_StartIndex = index + 1;
      else
        chmFolderOutStream->m_StartIndex = index;
      if (limitFolderIndex == folderIndex)
      {
        for (; i < numItems; i++)
        {
          UInt32 nextIndex = allFilesMode ? i : indices[i];
          int entryIndex = m_Database.Indices[nextIndex];
          const CItem &nextItem = m_Database.Items[entryIndex];
          if (nextItem.Section != sectionIndex)
            break;
          UInt64 nextFolderIndex = m_Database.GetFolder(nextIndex);
          if (nextFolderIndex != folderIndex)
            break;
          for (index++; index < nextIndex; index++)
            extractStatuses.Add(false);
          extractStatuses.Add(true);
          index = nextIndex;
          lastItem = &nextItem;
          if (nextItem.Size != 0)
            finishPos = nextItem.Offset + nextItem.Size;
          lastFolderIndex = m_Database.GetLastFolder(index);
        }
      }
      unPackSize = MyMin(finishPos - startPos, unPackSize);

      chmFolderOutStream->m_FolderSize = folderSize;
      chmFolderOutStream->m_PosInFolder = 0;
      chmFolderOutStream->m_PosInSection = startPos;
      chmFolderOutStream->m_ExtractStatuses = &extractStatuses;
      chmFolderOutStream->m_NumFiles = extractStatuses.Size();
      chmFolderOutStream->m_CurrentIndex = 0;
      try
      {
        UInt64 startBlock = lzxInfo.GetBlockIndexFromFolderIndex(folderIndex);
        const CResetTable &rt = lzxInfo.ResetTable;
        UInt32 numBlocks = (UInt32)rt.GetNumBlocks(unPackSize);
        for (UInt32 b = 0; b < numBlocks; b++)
        {
          UInt64 completedSize = currentTotalSize + chmFolderOutStream->m_PosInSection - startPos;
          RINOK(extractCallback->SetCompleted(&completedSize));
          UInt64 bCur = startBlock + b;
          if (bCur >= rt.ResetOffsets.Size())
            return E_FAIL;
          UInt64 offset = rt.ResetOffsets[(int)bCur];
          UInt64 compressedSize;
          rt.GetCompressedSizeOfBlock(bCur, compressedSize);
          UInt64 rem = finishPos - chmFolderOutStream->m_PosInSection;
          if (rem > rt.BlockSize)
            rem = rt.BlockSize;
          RINOK(m_Stream->Seek(compressedPos + offset, STREAM_SEEK_SET, NULL));
          streamSpec->SetStream(m_Stream);
          streamSpec->Init(compressedSize);
          lzxDecoderSpec->SetKeepHistory(b > 0);
          HRESULT res = lzxDecoder->Code(inStream, outStream, NULL, &rem, NULL);
          if (res != S_OK)
          {
            if (res != S_FALSE)
              return res;
            throw 1;
          }
        }
      }
      catch(...)
      {
        RINOK(chmFolderOutStream->FlushCorrupted(unPackSize));
      }
      currentTotalSize += folderSize;
      if (folderIndex == lastFolderIndex)
        break;
      extractStatuses.Clear();
    }
  }
  return S_OK;
  COM_TRY_END
}
예제 #2
0
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    Int32 testModeSpec, IArchiveExtractCallback *extractCallback)
{
  COM_TRY_BEGIN
  bool allFilesMode = (numItems == (UInt32)-1);
  if (allFilesMode)
    numItems = m_Database.Items.Size();
  if(numItems == 0)
    return S_OK;
  bool testMode = (testModeSpec != 0);
  UInt64 totalUnPacked = 0;

  UInt32 i;
  int lastFolder = -2;
  UInt64 lastFolderSize = 0;
  for(i = 0; i < numItems; i++)
  {
    int index = allFilesMode ? i : indices[i];
    const CMvItem &mvItem = m_Database.Items[index];
    const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex];
    if (item.IsDir())
      continue;
    int folderIndex = m_Database.GetFolderIndex(&mvItem);
    if (folderIndex != lastFolder)
      totalUnPacked += lastFolderSize;
    lastFolder = folderIndex;
    lastFolderSize = item.GetEndOffset();
  }
  totalUnPacked += lastFolderSize;

  extractCallback->SetTotal(totalUnPacked);

  totalUnPacked = 0;

  UInt64 totalPacked = 0;

  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(extractCallback, false);

  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;

  NCompress::NDeflate::NDecoder::CCOMCoder *deflateDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> deflateDecoder;

  NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> lzxDecoder;

  NCompress::NQuantum::CDecoder *quantumDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> quantumDecoder;

  CCabBlockInStream *cabBlockInStreamSpec = new CCabBlockInStream();
  CMyComPtr<ISequentialInStream> cabBlockInStream = cabBlockInStreamSpec;
  if (!cabBlockInStreamSpec->Create())
    return E_OUTOFMEMORY;

  CRecordVector<bool> extractStatuses;
  for(i = 0; i < numItems;)
  {
    int index = allFilesMode ? i : indices[i];

    const CMvItem &mvItem = m_Database.Items[index];
    const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex];
    int itemIndex = mvItem.ItemIndex;
    const CItem &item = db.Items[itemIndex];

    i++;
    if (item.IsDir())
    {
      Int32 askMode = testMode ?
          NExtract::NAskMode::kTest :
          NExtract::NAskMode::kExtract;
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      RINOK(extractCallback->PrepareOperation(askMode));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
      continue;
    }
    int folderIndex = m_Database.GetFolderIndex(&mvItem);
    if (folderIndex < 0)
    {
      // If we need previous archive
      Int32 askMode= testMode ?
          NExtract::NAskMode::kTest :
          NExtract::NAskMode::kExtract;
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      RINOK(extractCallback->PrepareOperation(askMode));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kDataError));
      continue;
    }
    int startIndex2 = m_Database.FolderStartFileIndex[folderIndex];
    int startIndex = startIndex2;
    extractStatuses.Clear();
    for (; startIndex < index; startIndex++)
      extractStatuses.Add(false);
    extractStatuses.Add(true);
    startIndex++;
    UInt64 curUnpack = item.GetEndOffset();
    for(;i < numItems; i++)
    {
      int indexNext = allFilesMode ? i : indices[i];
      const CMvItem &mvItem = m_Database.Items[indexNext];
      const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex];
      if (item.IsDir())
        continue;
      int newFolderIndex = m_Database.GetFolderIndex(&mvItem);

      if (newFolderIndex != folderIndex)
        break;
      for (; startIndex < indexNext; startIndex++)
        extractStatuses.Add(false);
      extractStatuses.Add(true);
      startIndex++;
      curUnpack = item.GetEndOffset();
    }

    lps->OutSize = totalUnPacked;
    lps->InSize = totalPacked;
    RINOK(lps->SetCur());

    CFolderOutStream *cabFolderOutStream = new CFolderOutStream;
    CMyComPtr<ISequentialOutStream> outStream(cabFolderOutStream);

    const CFolder &folder = db.Folders[item.GetFolderIndex(db.Folders.Size())];

    cabFolderOutStream->Init(&m_Database, &extractStatuses, startIndex2,
        curUnpack, extractCallback, testMode);

    cabBlockInStreamSpec->MsZip = false;
    switch(folder.GetCompressionMethod())
    {
      case NHeader::NCompressionMethodMajor::kNone:
        break;
      case NHeader::NCompressionMethodMajor::kMSZip:
        if(deflateDecoderSpec == NULL)
        {
          deflateDecoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder;
          deflateDecoder = deflateDecoderSpec;
        }
        cabBlockInStreamSpec->MsZip = true;
        break;
      case NHeader::NCompressionMethodMajor::kLZX:
        if(lzxDecoderSpec == NULL)
        {
          lzxDecoderSpec = new NCompress::NLzx::CDecoder;
          lzxDecoder = lzxDecoderSpec;
        }
        RINOK(lzxDecoderSpec->SetParams(folder.CompressionTypeMinor));
        break;
      case NHeader::NCompressionMethodMajor::kQuantum:
        if(quantumDecoderSpec == NULL)
        {
          quantumDecoderSpec = new NCompress::NQuantum::CDecoder;
          quantumDecoder = quantumDecoderSpec;
        }
        quantumDecoderSpec->SetParams(folder.CompressionTypeMinor);
        break;
      default:
      {
        RINOK(cabFolderOutStream->Unsupported());
        totalUnPacked += curUnpack;
        continue;
      }
    }

    cabBlockInStreamSpec->InitForNewFolder();

    HRESULT res = S_OK;

    {
      int volIndex = mvItem.VolumeIndex;
      int locFolderIndex = item.GetFolderIndex(db.Folders.Size());
      bool keepHistory = false;
      bool keepInputBuffer = false;
      for (UInt32 f = 0; cabFolderOutStream->GetRemain() != 0;)
      {
        if (volIndex >= m_Database.Volumes.Size())
        {
          res = S_FALSE;
          break;
        }

        const CDatabaseEx &db = m_Database.Volumes[volIndex];
        const CFolder &folder = db.Folders[locFolderIndex];
        if (f == 0)
        {
          cabBlockInStreamSpec->SetStream(db.Stream);
          cabBlockInStreamSpec->ReservedSize = db.ArchiveInfo.GetDataBlockReserveSize();
          RINOK(db.Stream->Seek(db.StartPosition + folder.DataStart, STREAM_SEEK_SET, NULL));
        }
        if (f == folder.NumDataBlocks)
        {
          volIndex++;
          locFolderIndex = 0;
          f = 0;
          continue;
        }
        f++;

        cabBlockInStreamSpec->DataError = false;
        
        if (!keepInputBuffer)
          cabBlockInStreamSpec->InitForNewBlock();

        UInt32 packSize, unpackSize;
        res = cabBlockInStreamSpec->PreRead(packSize, unpackSize);
        if (res == S_FALSE)
          break;
        RINOK(res);
        keepInputBuffer = (unpackSize == 0);
        if (keepInputBuffer)
          continue;

        UInt64 totalUnPacked2 = totalUnPacked + cabFolderOutStream->GetPosInFolder();
        totalPacked += packSize;

        lps->OutSize = totalUnPacked2;
        lps->InSize = totalPacked;
        RINOK(lps->SetCur());

        UInt64 unpackRemain = cabFolderOutStream->GetRemain();

        const UInt32 kBlockSizeMax = (1 << 15);
        if (unpackRemain > kBlockSizeMax)
          unpackRemain = kBlockSizeMax;
        if (unpackRemain > unpackSize)
          unpackRemain  = unpackSize;
   
        switch(folder.GetCompressionMethod())
        {
          case NHeader::NCompressionMethodMajor::kNone:
            res = copyCoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            break;
          case NHeader::NCompressionMethodMajor::kMSZip:
            deflateDecoderSpec->SetKeepHistory(keepHistory);
            res = deflateDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            break;
          case NHeader::NCompressionMethodMajor::kLZX:
            lzxDecoderSpec->SetKeepHistory(keepHistory);
            res = lzxDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            break;
          case NHeader::NCompressionMethodMajor::kQuantum:
            quantumDecoderSpec->SetKeepHistory(keepHistory);
            res = quantumDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
          break;
        }
        if (res != S_OK)
        {
          if (res != S_FALSE)
            RINOK(res);
          break;
        }
        keepHistory = true;
      }
      if (res == S_OK)
      {
        RINOK(cabFolderOutStream->WriteEmptyFiles());
      }
    }
    if (res != S_OK || cabFolderOutStream->GetRemain() != 0)
    {
      RINOK(cabFolderOutStream->FlushCorrupted());
    }
    totalUnPacked += curUnpack;
  }
  return S_OK;
  COM_TRY_END
}
예제 #3
0
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    Int32 testModeSpec, IArchiveExtractCallback *extractCallback)
{
  COM_TRY_BEGIN
  bool allFilesMode = (numItems == (UInt32)(Int32)-1);
  if (allFilesMode)
    numItems = m_Database.Items.Size();
  if (numItems == 0)
    return S_OK;
  bool testMode = (testModeSpec != 0);
  UInt64 totalUnPacked = 0;

  UInt32 i;
  int lastFolder = -2;
  UInt64 lastFolderSize = 0;
  
  for (i = 0; i < numItems; i++)
  {
    unsigned index = allFilesMode ? i : indices[i];
    const CMvItem &mvItem = m_Database.Items[index];
    const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex];
    if (item.IsDir())
      continue;
    int folderIndex = m_Database.GetFolderIndex(&mvItem);
    if (folderIndex != lastFolder)
      totalUnPacked += lastFolderSize;
    lastFolder = folderIndex;
    lastFolderSize = item.GetEndOffset();
  }
  
  totalUnPacked += lastFolderSize;

  extractCallback->SetTotal(totalUnPacked);

  totalUnPacked = 0;

  UInt64 totalPacked = 0;

  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(extractCallback, false);

  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;

  NCompress::NDeflate::NDecoder::CCOMCoder *deflateDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> deflateDecoder;

  NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> lzxDecoder;

  NCompress::NQuantum::CDecoder *quantumDecoderSpec = NULL;
  CMyComPtr<ICompressCoder> quantumDecoder;

  CCabBlockInStream *cabBlockInStreamSpec = new CCabBlockInStream();
  CMyComPtr<ISequentialInStream> cabBlockInStream = cabBlockInStreamSpec;
  if (!cabBlockInStreamSpec->Create())
    return E_OUTOFMEMORY;

  CRecordVector<bool> extractStatuses;
  
  for (i = 0; i < numItems;)
  {
    unsigned index = allFilesMode ? i : indices[i];

    const CMvItem &mvItem = m_Database.Items[index];
    const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex];
    unsigned itemIndex = mvItem.ItemIndex;
    const CItem &item = db.Items[itemIndex];

    i++;
    if (item.IsDir())
    {
      Int32 askMode = testMode ?
          NExtract::NAskMode::kTest :
          NExtract::NAskMode::kExtract;
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      RINOK(extractCallback->PrepareOperation(askMode));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
      continue;
    }
    
    int folderIndex = m_Database.GetFolderIndex(&mvItem);
    
    if (folderIndex < 0)
    {
      // If we need previous archive
      Int32 askMode= testMode ?
          NExtract::NAskMode::kTest :
          NExtract::NAskMode::kExtract;
      CMyComPtr<ISequentialOutStream> realOutStream;
      RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
      RINOK(extractCallback->PrepareOperation(askMode));
      realOutStream.Release();
      RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kDataError));
      continue;
    }
    
    unsigned startIndex2 = m_Database.FolderStartFileIndex[folderIndex];
    unsigned startIndex = startIndex2;
    extractStatuses.Clear();
    for (; startIndex < index; startIndex++)
      extractStatuses.Add(false);
    extractStatuses.Add(true);
    startIndex++;
    UInt64 curUnpack = item.GetEndOffset();
    
    for (; i < numItems; i++)
    {
      unsigned indexNext = allFilesMode ? i : indices[i];
      const CMvItem &mvItem = m_Database.Items[indexNext];
      const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex];
      if (item.IsDir())
        continue;
      int newFolderIndex = m_Database.GetFolderIndex(&mvItem);

      if (newFolderIndex != folderIndex)
        break;
      for (; startIndex < indexNext; startIndex++)
        extractStatuses.Add(false);
      extractStatuses.Add(true);
      startIndex++;
      curUnpack = item.GetEndOffset();
    }

    lps->OutSize = totalUnPacked;
    lps->InSize = totalPacked;
    RINOK(lps->SetCur());

    CFolderOutStream *cabFolderOutStream = new CFolderOutStream;
    CMyComPtr<ISequentialOutStream> outStream(cabFolderOutStream);

    const CFolder &folder = db.Folders[item.GetFolderIndex(db.Folders.Size())];

    cabFolderOutStream->Init(&m_Database, &extractStatuses, startIndex2,
        curUnpack, extractCallback, testMode);

    cabBlockInStreamSpec->MsZip = false;
    HRESULT res = S_OK;
    
    switch (folder.GetMethod())
    {
      case NHeader::NMethod::kNone:
        break;
      case NHeader::NMethod::kMSZip:
        if (!deflateDecoder)
        {
          deflateDecoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder;
          deflateDecoder = deflateDecoderSpec;
        }
        cabBlockInStreamSpec->MsZip = true;
        break;
      case NHeader::NMethod::kLZX:
        if (!lzxDecoder)
        {
          lzxDecoderSpec = new NCompress::NLzx::CDecoder;
          lzxDecoder = lzxDecoderSpec;
        }
        res = lzxDecoderSpec->SetParams(folder.MethodMinor);
        break;
      case NHeader::NMethod::kQuantum:
        if (!quantumDecoder)
        {
          quantumDecoderSpec = new NCompress::NQuantum::CDecoder;
          quantumDecoder = quantumDecoderSpec;
        }
        res = quantumDecoderSpec->SetParams(folder.MethodMinor);
        break;
      default:
        res = E_INVALIDARG;
        break;
    }

    if (res == E_INVALIDARG)
    {
      RINOK(cabFolderOutStream->Unsupported());
      totalUnPacked += curUnpack;
      continue;
    }
    RINOK(res);

    {
      unsigned volIndex = mvItem.VolumeIndex;
      int locFolderIndex = item.GetFolderIndex(db.Folders.Size());
      bool keepHistory = false;
      bool keepInputBuffer = false;
      
      for (UInt32 bl = 0; cabFolderOutStream->GetRemain() != 0;)
      {
        if (volIndex >= m_Database.Volumes.Size())
        {
          res = S_FALSE;
          break;
        }

        const CDatabaseEx &db = m_Database.Volumes[volIndex];
        const CFolder &folder = db.Folders[locFolderIndex];
        
        if (bl == 0)
        {
          cabBlockInStreamSpec->ReservedSize = db.ArcInfo.GetDataBlockReserveSize();
          RINOK(db.Stream->Seek(db.StartPosition + folder.DataStart, STREAM_SEEK_SET, NULL));
        }
        
        if (bl == folder.NumDataBlocks)
        {
          /*
            CFolder::NumDataBlocks (CFFOLDER::cCFData in CAB specification) is 16-bit.
            But there are some big CAB archives from MS that contain more
            than (0xFFFF) CFDATA blocks in folder.
            Old cab extracting software can show error (or ask next volume)
            but cab extracting library in new Windows ignores this error.
            15.00 : We also try to ignore such error, if archive is not multi-volume.
          */
          if (m_Database.Volumes.Size() > 1)
          {
            volIndex++;
            locFolderIndex = 0;
            bl = 0;
            continue;
          }
        }
        bl++;

        if (!keepInputBuffer)
          cabBlockInStreamSpec->InitForNewBlock();

        UInt32 packSize, unpackSize;
        res = cabBlockInStreamSpec->PreRead(db.Stream, packSize, unpackSize);
        if (res == S_FALSE)
          break;
        RINOK(res);
        keepInputBuffer = (unpackSize == 0);
        if (keepInputBuffer)
          continue;

        UInt64 totalUnPacked2 = totalUnPacked + cabFolderOutStream->GetPosInFolder();
        totalPacked += packSize;

        lps->OutSize = totalUnPacked2;
        lps->InSize = totalPacked;
        RINOK(lps->SetCur());

        UInt64 unpackRemain = cabFolderOutStream->GetRemain();

        const UInt32 kBlockSizeMax = (1 << 15);
        if (unpackRemain > kBlockSizeMax)
          unpackRemain = kBlockSizeMax;
        if (unpackRemain > unpackSize)
          unpackRemain = unpackSize;

        switch (folder.GetMethod())
        {
          case NHeader::NMethod::kNone:
            res = copyCoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            break;
          case NHeader::NMethod::kMSZip:
            deflateDecoderSpec->Set_KeepHistory(keepHistory);
            /* v9.31: now we follow MSZIP specification that requires to finish deflate stream at the end of each block.
               But PyCabArc can create CAB archives that doesn't have finish marker at the end of block.
               Cabarc probably ignores such errors in cab archives.
               Maybe we also should ignore that error?
               Or we should extract full file and show the warning? */
            deflateDecoderSpec->Set_NeedFinishInput(true);
            res = deflateDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            if (res == S_OK)
            {
              if (!deflateDecoderSpec->IsFinished())
                res = S_FALSE;
              if (!deflateDecoderSpec->IsFinalBlock())
                res = S_FALSE;
            }

            break;
          case NHeader::NMethod::kLZX:
            lzxDecoderSpec->SetKeepHistory(keepHistory);
            res = lzxDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
            break;
          case NHeader::NMethod::kQuantum:
            quantumDecoderSpec->SetKeepHistory(keepHistory);
            res = quantumDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL);
          break;
        }
      
        if (res != S_OK)
        {
          if (res != S_FALSE)
            RINOK(res);
          break;
        }
        
        keepHistory = true;
      }
      
      if (res == S_OK)
      {
        RINOK(cabFolderOutStream->WriteEmptyFiles());
      }
    }
    if (res != S_OK || cabFolderOutStream->GetRemain() != 0)
    {
      RINOK(cabFolderOutStream->FlushCorrupted());
    }
    totalUnPacked += curUnpack;
  }

  return S_OK;
  COM_TRY_END
}