"Fossies" - the Fresh Open Source Software Archive

Member "p7zip_16.02/CPP/7zip/Archive/Chm/ChmHandler.cpp" (20 May 2016, 22818 Bytes) of package /linux/misc/p7zip_16.02_src_all.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "ChmHandler.cpp": 15.14.1_src_all_vs_16.02_src_all.

    1 // ChmHandler.cpp
    2 
    3 #include "StdAfx.h"
    4 
    5 #include "../../../Common/ComTry.h"
    6 #include "../../../Common/StringConvert.h"
    7 #include "../../../Common/UTFConvert.h"
    8 
    9 #include "../../../Windows/PropVariant.h"
   10 #include "../../../Windows/TimeUtils.h"
   11 
   12 #include "../../Common/LimitedStreams.h"
   13 #include "../../Common/ProgressUtils.h"
   14 #include "../../Common/StreamUtils.h"
   15 #include "../../Common/RegisterArc.h"
   16 
   17 #include "../../Compress/CopyCoder.h"
   18 #include "../../Compress/LzxDecoder.h"
   19 
   20 #include "../Common/ItemNameUtils.h"
   21 
   22 #include "ChmHandler.h"
   23 
   24 using namespace NWindows;
   25 using namespace NTime;
   26 
   27 namespace NArchive {
   28 namespace NChm {
   29 
   30 // #define _CHM_DETAILS
   31 
   32 #ifdef _CHM_DETAILS
   33 
   34 enum
   35 {
   36   kpidSection = kpidUserDefined
   37 };
   38 
   39 #endif
   40 
   41 static const Byte kProps[] =
   42 {
   43   kpidPath,
   44   kpidSize,
   45   kpidMethod,
   46   kpidBlock
   47   
   48   #ifdef _CHM_DETAILS
   49   ,
   50   L"Section", kpidSection,
   51   kpidOffset
   52   #endif
   53 };
   54 
   55 /*
   56 static const Byte kArcProps[] =
   57 {
   58   // kpidNumBlocks,
   59 };
   60 */
   61 
   62 IMP_IInArchive_Props
   63 
   64 IMP_IInArchive_ArcProps_NO_Table
   65 
   66 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
   67 {
   68   // COM_TRY_BEGIN
   69   NCOM::CPropVariant prop;
   70   switch (propID)
   71   {
   72     /*
   73     case kpidNumBlocks:
   74     {
   75       UInt64 numBlocks = 0;
   76       FOR_VECTOR(i, m_Database.Sections)
   77       {
   78         const CSectionInfo &s = m_Database.Sections[i];
   79         FOR_VECTOR(j, s.Methods)
   80         {
   81           const CMethodInfo &m = s.Methods[j];
   82           if (m.IsLzx())
   83             numBlocks += m.LzxInfo.ResetTable.GetNumBlocks();
   84         }
   85       }
   86       prop = numBlocks;
   87       break;
   88     }
   89     */
   90     case kpidOffset: prop = m_Database.StartPosition; break;
   91     case kpidPhySize: prop = m_Database.PhySize; break;
   92 
   93     case kpidErrorFlags: prop = m_ErrorFlags; break;
   94   }
   95   prop.Detach(value);
   96   return S_OK;
   97   // COM_TRY_END
   98 }
   99 
  100 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
  101 {
  102   COM_TRY_BEGIN
  103   NCOM::CPropVariant prop;
  104   
  105   if (m_Database.NewFormat)
  106   {
  107     switch (propID)
  108     {
  109       case kpidSize:
  110         prop = (UInt64)m_Database.NewFormatString.Len();
  111       break;
  112     }
  113     prop.Detach(value);
  114     return S_OK;
  115   }
  116   
  117   unsigned entryIndex;
  118   if (m_Database.LowLevel)
  119     entryIndex = index;
  120   else
  121     entryIndex = m_Database.Indices[index];
  122   
  123   const CItem &item = m_Database.Items[entryIndex];
  124   
  125   switch (propID)
  126   {
  127     case kpidPath:
  128     {
  129       UString us;
  130       // if (
  131       ConvertUTF8ToUnicode(item.Name, us);
  132       {
  133         if (!m_Database.LowLevel)
  134         {
  135           if (us.Len() > 1 && us[0] == L'/')
  136             us.Delete(0);
  137         }
  138         NItemName::ConvertToOSName2(us);
  139         prop = us;
  140       }
  141       break;
  142     }
  143     case kpidIsDir:  prop = item.IsDir(); break;
  144     case kpidSize:  prop = item.Size; break;
  145     case kpidMethod:
  146     {
  147       if (!item.IsDir())
  148         if (item.Section == 0)
  149           prop = "Copy";
  150         else if (item.Section < m_Database.Sections.Size())
  151           prop = m_Database.Sections[(unsigned)item.Section].GetMethodName();
  152       break;
  153     }
  154     case kpidBlock:
  155       if (m_Database.LowLevel)
  156         prop = item.Section;
  157       else if (item.Section != 0 && item.Section < m_Database.Sections.Size())
  158         prop = m_Database.GetFolder(index);
  159       break;
  160     
  161     #ifdef _CHM_DETAILS
  162     
  163     case kpidSection:  prop = (UInt32)item.Section; break;
  164     case kpidOffset:  prop = (UInt32)item.Offset; break;
  165 
  166     #endif
  167   }
  168   
  169   prop.Detach(value);
  170   return S_OK;
  171   COM_TRY_END
  172 }
  173 
  174 /*
  175 class CProgressImp: public CProgressVirt
  176 {
  177   CMyComPtr<IArchiveOpenCallback> _callback;
  178 public:
  179   STDMETHOD(SetTotal)(const UInt64 *numFiles);
  180   STDMETHOD(SetCompleted)(const UInt64 *numFiles);
  181   CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {};
  182 };
  183 
  184 STDMETHODIMP CProgressImp::SetTotal(const UInt64 *numFiles)
  185 {
  186   if (_callback)
  187     return _callback->SetCompleted(numFiles, NULL);
  188   return S_OK;
  189 }
  190 
  191 STDMETHODIMP CProgressImp::SetCompleted(const UInt64 *numFiles)
  192 {
  193   if (_callback)
  194     return _callback->SetCompleted(numFiles, NULL);
  195   return S_OK;
  196 }
  197 */
  198 
  199 STDMETHODIMP CHandler::Open(IInStream *inStream,
  200     const UInt64 *maxCheckStartPosition,
  201     IArchiveOpenCallback * /* openArchiveCallback */)
  202 {
  203   COM_TRY_BEGIN
  204   Close();
  205   try
  206   {
  207     CInArchive archive(_help2);
  208     // CProgressImp progressImp(openArchiveCallback);
  209     HRESULT res = archive.Open(inStream, maxCheckStartPosition, m_Database);
  210     if (!archive.IsArc) m_ErrorFlags |= kpv_ErrorFlags_IsNotArc;
  211     if (archive.HeadersError) m_ErrorFlags |= kpv_ErrorFlags_HeadersError;
  212     if (archive.UnexpectedEnd)  m_ErrorFlags |= kpv_ErrorFlags_UnexpectedEnd;
  213     if (archive.UnsupportedFeature)  m_ErrorFlags |= kpv_ErrorFlags_UnsupportedFeature;
  214     
  215     RINOK(res);
  216     /*
  217     if (m_Database.LowLevel)
  218       return S_FALSE;
  219     */
  220     m_Stream = inStream;
  221   }
  222   catch(...)
  223   {
  224     return S_FALSE;
  225   }
  226   return S_OK;
  227   COM_TRY_END
  228 }
  229 
  230 STDMETHODIMP CHandler::Close()
  231 {
  232   m_ErrorFlags = 0;
  233   m_Database.Clear();
  234   m_Stream.Release();
  235   return S_OK;
  236 }
  237 
  238 class CChmFolderOutStream:
  239   public ISequentialOutStream,
  240   public CMyUnknownImp
  241 {
  242 public:
  243   MY_UNKNOWN_IMP
  244 
  245   HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK);
  246   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  247 
  248   UInt64 m_FolderSize;
  249   UInt64 m_PosInFolder;
  250   UInt64 m_PosInSection;
  251   const CRecordVector<bool> *m_ExtractStatuses;
  252   unsigned m_StartIndex;
  253   unsigned m_CurrentIndex;
  254   unsigned m_NumFiles;
  255 
  256 private:
  257   const CFilesDatabase *m_Database;
  258   CMyComPtr<IArchiveExtractCallback> m_ExtractCallback;
  259   bool m_TestMode;
  260 
  261   bool m_IsOk;
  262   bool m_FileIsOpen;
  263   UInt64 m_RemainFileSize;
  264   CMyComPtr<ISequentialOutStream> m_RealOutStream;
  265 
  266   HRESULT OpenFile();
  267   HRESULT WriteEmptyFiles();
  268 public:
  269   void Init(
  270     const CFilesDatabase *database,
  271     IArchiveExtractCallback *extractCallback,
  272     bool testMode);
  273   HRESULT FlushCorrupted(UInt64 maxSize);
  274 };
  275 
  276 void CChmFolderOutStream::Init(
  277     const CFilesDatabase *database,
  278     IArchiveExtractCallback *extractCallback,
  279     bool testMode)
  280 {
  281   m_Database = database;
  282   m_ExtractCallback = extractCallback;
  283   m_TestMode = testMode;
  284 
  285   m_CurrentIndex = 0;
  286   m_FileIsOpen = false;
  287 }
  288 
  289 HRESULT CChmFolderOutStream::OpenFile()
  290 {
  291   Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ?
  292       NExtract::NAskMode::kTest :
  293       NExtract::NAskMode::kExtract) :
  294       NExtract::NAskMode::kSkip;
  295   m_RealOutStream.Release();
  296   RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode));
  297   if (!m_RealOutStream && !m_TestMode)
  298     askMode = NExtract::NAskMode::kSkip;
  299   return m_ExtractCallback->PrepareOperation(askMode);
  300 }
  301 
  302 HRESULT CChmFolderOutStream::WriteEmptyFiles()
  303 {
  304   if (m_FileIsOpen)
  305     return S_OK;
  306   for (; m_CurrentIndex < m_NumFiles; m_CurrentIndex++)
  307   {
  308     UInt64 fileSize = m_Database->GetFileSize(m_StartIndex + m_CurrentIndex);
  309     if (fileSize != 0)
  310       return S_OK;
  311     HRESULT result = OpenFile();
  312     m_RealOutStream.Release();
  313     RINOK(result);
  314     RINOK(m_ExtractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
  315   }
  316   return S_OK;
  317 }
  318 
  319 // This is WritePart function
  320 HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK)
  321 {
  322   UInt32 realProcessed = 0;
  323   if (processedSize)
  324    *processedSize = 0;
  325   
  326   while (size != 0)
  327   {
  328     if (m_FileIsOpen)
  329     {
  330       UInt32 numBytesToWrite = (UInt32)MyMin(m_RemainFileSize, (UInt64)(size));
  331       HRESULT res = S_OK;
  332       if (numBytesToWrite > 0)
  333       {
  334         if (!isOK)
  335           m_IsOk = false;
  336         if (m_RealOutStream)
  337         {
  338           UInt32 processedSizeLocal = 0;
  339           res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal);
  340           numBytesToWrite = processedSizeLocal;
  341         }
  342       }
  343       realProcessed += numBytesToWrite;
  344       if (processedSize)
  345         *processedSize = realProcessed;
  346       data = (const void *)((const Byte *)data + numBytesToWrite);
  347       size -= numBytesToWrite;
  348       m_RemainFileSize -= numBytesToWrite;
  349       m_PosInSection += numBytesToWrite;
  350       m_PosInFolder += numBytesToWrite;
  351       if (res != S_OK)
  352         return res;
  353       if (m_RemainFileSize == 0)
  354       {
  355         m_RealOutStream.Release();
  356         RINOK(m_ExtractCallback->SetOperationResult(
  357           m_IsOk ?
  358             NExtract::NOperationResult::kOK:
  359             NExtract::NOperationResult::kDataError));
  360         m_FileIsOpen = false;
  361       }
  362       if (realProcessed > 0)
  363         break; // with this break this function works as write part
  364     }
  365     else
  366     {
  367       if (m_CurrentIndex >= m_NumFiles)
  368       {
  369         realProcessed += size;
  370         if (processedSize)
  371           *processedSize = realProcessed;
  372         return S_OK;
  373         // return E_FAIL;
  374       }
  375 
  376       unsigned fullIndex = m_StartIndex + m_CurrentIndex;
  377       m_RemainFileSize = m_Database->GetFileSize(fullIndex);
  378       UInt64 fileOffset = m_Database->GetFileOffset(fullIndex);
  379       if (fileOffset < m_PosInSection)
  380         return E_FAIL;
  381       
  382       if (fileOffset > m_PosInSection)
  383       {
  384         UInt32 numBytesToWrite = (UInt32)MyMin(fileOffset - m_PosInSection, UInt64(size));
  385         realProcessed += numBytesToWrite;
  386         if (processedSize)
  387           *processedSize = realProcessed;
  388         data = (const void *)((const Byte *)data + numBytesToWrite);
  389         size -= numBytesToWrite;
  390         m_PosInSection += numBytesToWrite;
  391         m_PosInFolder += numBytesToWrite;
  392       }
  393       
  394       if (fileOffset == m_PosInSection)
  395       {
  396         RINOK(OpenFile());
  397         m_FileIsOpen = true;
  398         m_CurrentIndex++;
  399         m_IsOk = true;
  400       }
  401     }
  402   }
  403   
  404   return WriteEmptyFiles();
  405 }
  406 
  407 STDMETHODIMP CChmFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
  408 {
  409   return Write2(data, size, processedSize, true);
  410 }
  411 
  412 HRESULT CChmFolderOutStream::FlushCorrupted(UInt64 maxSize)
  413 {
  414   const UInt32 kBufferSize = (1 << 10);
  415   Byte buffer[kBufferSize];
  416   for (unsigned i = 0; i < kBufferSize; i++)
  417     buffer[i] = 0;
  418   if (maxSize > m_FolderSize)
  419     maxSize = m_FolderSize;
  420   while (m_PosInFolder < maxSize)
  421   {
  422     UInt32 size = (UInt32)MyMin(maxSize - m_PosInFolder, (UInt64)kBufferSize);
  423     UInt32 processedSizeLocal = 0;
  424     RINOK(Write2(buffer, size, &processedSizeLocal, false));
  425     if (processedSizeLocal == 0)
  426       return S_OK;
  427   }
  428   return S_OK;
  429 }
  430 
  431 
  432 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
  433     Int32 testModeSpec, IArchiveExtractCallback *extractCallback)
  434 {
  435   COM_TRY_BEGIN
  436   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
  437 
  438   if (allFilesMode)
  439     numItems = m_Database.NewFormat ? 1:
  440       (m_Database.LowLevel ?
  441       m_Database.Items.Size():
  442       m_Database.Indices.Size());
  443   if (numItems == 0)
  444     return S_OK;
  445   bool testMode = (testModeSpec != 0);
  446 
  447   UInt64 currentTotalSize = 0;
  448 
  449   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
  450   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
  451   UInt32 i;
  452 
  453   CLocalProgress *lps = new CLocalProgress;
  454   CMyComPtr<ICompressProgressInfo> progress = lps;
  455   lps->Init(extractCallback, false);
  456 
  457   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
  458   CMyComPtr<ISequentialInStream> inStream(streamSpec);
  459   streamSpec->SetStream(m_Stream);
  460 
  461   if (m_Database.LowLevel)
  462   {
  463     UInt64 currentItemSize = 0;
  464     UInt64 totalSize = 0;
  465     
  466     if (m_Database.NewFormat)
  467       totalSize = m_Database.NewFormatString.Len();
  468     else
  469       for (i = 0; i < numItems; i++)
  470         totalSize += m_Database.Items[allFilesMode ? i : indices[i]].Size;
  471     
  472     extractCallback->SetTotal(totalSize);
  473     
  474     for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
  475     {
  476       currentItemSize = 0;
  477       lps->InSize = currentTotalSize; // Change it
  478       lps->OutSize = currentTotalSize;
  479 
  480       RINOK(lps->SetCur());
  481       CMyComPtr<ISequentialOutStream> realOutStream;
  482       Int32 askMode= testMode ?
  483           NExtract::NAskMode::kTest :
  484           NExtract::NAskMode::kExtract;
  485       Int32 index = allFilesMode ? i : indices[i];
  486       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
  487 
  488       if (m_Database.NewFormat)
  489       {
  490         if (index != 0)
  491           return E_FAIL;
  492         if (!testMode && !realOutStream)
  493           continue;
  494         if (!testMode)
  495         {
  496           UInt32 size = m_Database.NewFormatString.Len();
  497           RINOK(WriteStream(realOutStream, (const char *)m_Database.NewFormatString, size));
  498         }
  499         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
  500         continue;
  501       }
  502       
  503       const CItem &item = m_Database.Items[index];
  504       
  505       currentItemSize = item.Size;
  506       
  507       if (!testMode && !realOutStream)
  508         continue;
  509       RINOK(extractCallback->PrepareOperation(askMode));
  510       if (item.Section != 0)
  511       {
  512         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod));
  513         continue;
  514       }
  515 
  516       if (testMode)
  517       {
  518         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
  519         continue;
  520       }
  521       
  522       RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
  523       streamSpec->Init(item.Size);
  524       
  525       RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
  526       realOutStream.Release();
  527       RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ?
  528           NExtract::NOperationResult::kOK:
  529           NExtract::NOperationResult::kDataError));
  530     }
  531     return S_OK;
  532   }
  533   
  534   UInt64 lastFolderIndex = ((UInt64)0 - 1);
  535   
  536   for (i = 0; i < numItems; i++)
  537   {
  538     UInt32 index = allFilesMode ? i : indices[i];
  539     const CItem &item = m_Database.Items[m_Database.Indices[index]];
  540     const UInt64 sectionIndex = item.Section;
  541     if (item.IsDir() || item.Size == 0)
  542       continue;
  543     if (sectionIndex == 0)
  544     {
  545       currentTotalSize += item.Size;
  546       continue;
  547     }
  548 
  549     if (sectionIndex >= m_Database.Sections.Size())
  550       continue;
  551 
  552     const CSectionInfo &section = m_Database.Sections[(unsigned)sectionIndex];
  553     if (section.IsLzx())
  554     {
  555       const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;
  556       UInt64 folderIndex = m_Database.GetFolder(index);
  557       if (lastFolderIndex == folderIndex)
  558         folderIndex++;
  559       lastFolderIndex = m_Database.GetLastFolder(index);
  560       for (; folderIndex <= lastFolderIndex; folderIndex++)
  561         currentTotalSize += lzxInfo.GetFolderSize();
  562     }
  563   }
  564 
  565   RINOK(extractCallback->SetTotal(currentTotalSize));
  566 
  567   NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL;
  568   CMyComPtr<IUnknown> lzxDecoder;
  569   CChmFolderOutStream *chmFolderOutStream = 0;
  570   CMyComPtr<ISequentialOutStream> outStream;
  571 
  572   currentTotalSize = 0;
  573 
  574   CRecordVector<bool> extractStatuses;
  575 
  576   CByteBuffer packBuf;
  577   
  578   for (i = 0;;)
  579   {
  580     RINOK(extractCallback->SetCompleted(&currentTotalSize));
  581 
  582     if (i >= numItems)
  583       break;
  584 
  585     UInt32 index = allFilesMode ? i : indices[i];
  586     i++;
  587     const CItem &item = m_Database.Items[m_Database.Indices[index]];
  588     const UInt64 sectionIndex = item.Section;
  589     Int32 askMode= testMode ?
  590         NExtract::NAskMode::kTest :
  591         NExtract::NAskMode::kExtract;
  592     
  593     if (item.IsDir())
  594     {
  595       CMyComPtr<ISequentialOutStream> realOutStream;
  596       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
  597       RINOK(extractCallback->PrepareOperation(askMode));
  598       realOutStream.Release();
  599       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
  600       continue;
  601     }
  602 
  603     lps->InSize = currentTotalSize; // Change it
  604     lps->OutSize = currentTotalSize;
  605 
  606     if (item.Size == 0 || sectionIndex == 0)
  607     {
  608       CMyComPtr<ISequentialOutStream> realOutStream;
  609       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
  610       if (!testMode && !realOutStream)
  611         continue;
  612       RINOK(extractCallback->PrepareOperation(askMode));
  613       Int32 opRes = NExtract::NOperationResult::kOK;
  614       if (!testMode && item.Size != 0)
  615       {
  616         RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
  617         streamSpec->Init(item.Size);
  618         RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
  619         if (copyCoderSpec->TotalSize != item.Size)
  620           opRes = NExtract::NOperationResult::kDataError;
  621       }
  622       realOutStream.Release();
  623       RINOK(extractCallback->SetOperationResult(opRes));
  624       currentTotalSize += item.Size;
  625       continue;
  626     }
  627   
  628     if (sectionIndex >= m_Database.Sections.Size())
  629     {
  630       // we must report error here;
  631       CMyComPtr<ISequentialOutStream> realOutStream;
  632       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
  633       if (!testMode && !realOutStream)
  634         continue;
  635       RINOK(extractCallback->PrepareOperation(askMode));
  636       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kHeadersError));
  637       continue;
  638     }
  639 
  640     const CSectionInfo &section = m_Database.Sections[(unsigned)sectionIndex];
  641 
  642     if (!section.IsLzx())
  643     {
  644       CMyComPtr<ISequentialOutStream> realOutStream;
  645       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
  646       if (!testMode && !realOutStream)
  647         continue;
  648       RINOK(extractCallback->PrepareOperation(askMode));
  649       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnsupportedMethod));
  650       continue;
  651     }
  652 
  653     const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;
  654 
  655     if (!chmFolderOutStream)
  656     {
  657       chmFolderOutStream = new CChmFolderOutStream;
  658       outStream = chmFolderOutStream;
  659     }
  660 
  661     chmFolderOutStream->Init(&m_Database, extractCallback, testMode);
  662 
  663     if (!lzxDecoderSpec)
  664     {
  665       lzxDecoderSpec = new NCompress::NLzx::CDecoder;
  666       lzxDecoder = lzxDecoderSpec;
  667     }
  668 
  669     UInt64 folderIndex = m_Database.GetFolder(index);
  670 
  671     const UInt64 compressedPos = m_Database.ContentOffset + section.Offset;
  672     RINOK(lzxDecoderSpec->SetParams_and_Alloc(lzxInfo.GetNumDictBits()));
  673 
  674     const CItem *lastItem = &item;
  675     extractStatuses.Clear();
  676     extractStatuses.Add(true);
  677 
  678     for (;; folderIndex++)
  679     {
  680       RINOK(extractCallback->SetCompleted(&currentTotalSize));
  681 
  682       UInt64 startPos = lzxInfo.GetFolderPos(folderIndex);
  683       UInt64 finishPos = lastItem->Offset + lastItem->Size;
  684       UInt64 limitFolderIndex = lzxInfo.GetFolder(finishPos);
  685 
  686       lastFolderIndex = m_Database.GetLastFolder(index);
  687       UInt64 folderSize = lzxInfo.GetFolderSize();
  688       UInt64 unPackSize = folderSize;
  689       
  690       if (extractStatuses.IsEmpty())
  691         chmFolderOutStream->m_StartIndex = index + 1;
  692       else
  693         chmFolderOutStream->m_StartIndex = index;
  694       
  695       if (limitFolderIndex == folderIndex)
  696       {
  697         for (; i < numItems; i++)
  698         {
  699           const UInt32 nextIndex = allFilesMode ? i : indices[i];
  700           const CItem &nextItem = m_Database.Items[m_Database.Indices[nextIndex]];
  701           if (nextItem.Section != sectionIndex)
  702             break;
  703           UInt64 nextFolderIndex = m_Database.GetFolder(nextIndex);
  704           if (nextFolderIndex != folderIndex)
  705             break;
  706           for (index++; index < nextIndex; index++)
  707             extractStatuses.Add(false);
  708           extractStatuses.Add(true);
  709           index = nextIndex;
  710           lastItem = &nextItem;
  711           if (nextItem.Size != 0)
  712             finishPos = nextItem.Offset + nextItem.Size;
  713           lastFolderIndex = m_Database.GetLastFolder(index);
  714         }
  715       }
  716       
  717       unPackSize = MyMin(finishPos - startPos, unPackSize);
  718 
  719       chmFolderOutStream->m_FolderSize = folderSize;
  720       chmFolderOutStream->m_PosInFolder = 0;
  721       chmFolderOutStream->m_PosInSection = startPos;
  722       chmFolderOutStream->m_ExtractStatuses = &extractStatuses;
  723       chmFolderOutStream->m_NumFiles = extractStatuses.Size();
  724       chmFolderOutStream->m_CurrentIndex = 0;
  725       
  726       try
  727       {
  728         UInt64 startBlock = lzxInfo.GetBlockIndexFromFolderIndex(folderIndex);
  729         const CResetTable &rt = lzxInfo.ResetTable;
  730         UInt32 numBlocks = (UInt32)rt.GetNumBlocks(unPackSize);
  731         
  732         for (UInt32 b = 0; b < numBlocks; b++)
  733         {
  734           UInt64 completedSize = currentTotalSize + chmFolderOutStream->m_PosInSection - startPos;
  735           RINOK(extractCallback->SetCompleted(&completedSize));
  736           UInt64 bCur = startBlock + b;
  737           if (bCur >= rt.ResetOffsets.Size())
  738             return E_FAIL;
  739           UInt64 offset = rt.ResetOffsets[(unsigned)bCur];
  740           UInt64 compressedSize;
  741           rt.GetCompressedSizeOfBlock(bCur, compressedSize);
  742           
  743           // chm writes full blocks. So we don't need to use reduced size for last block
  744 
  745           RINOK(m_Stream->Seek(compressedPos + offset, STREAM_SEEK_SET, NULL));
  746           streamSpec->SetStream(m_Stream);
  747           streamSpec->Init(compressedSize);
  748           
  749           lzxDecoderSpec->SetKeepHistory(b > 0);
  750       
  751           size_t compressedSizeT = (size_t)compressedSize;
  752           if (compressedSizeT != compressedSize)
  753             throw 2;
  754           packBuf.AllocAtLeast(compressedSizeT);
  755 
  756           HRESULT res = ReadStream_FALSE(inStream, packBuf, compressedSizeT);
  757           
  758           if (res == S_OK)
  759           {
  760             lzxDecoderSpec->KeepHistoryForNext = true;
  761             res = lzxDecoderSpec->Code(packBuf, compressedSizeT, kBlockSize); // rt.BlockSize;
  762             if (res == S_OK)
  763               res = WriteStream(chmFolderOutStream,
  764                   lzxDecoderSpec->GetUnpackData(),
  765                   lzxDecoderSpec->GetUnpackSize());
  766           }
  767           
  768           if (res != S_OK)
  769           {
  770             if (res != S_FALSE)
  771               return res;
  772             throw 1;
  773           }
  774         }
  775       }
  776       catch(...)
  777       {
  778         RINOK(chmFolderOutStream->FlushCorrupted(unPackSize));
  779       }
  780       
  781       currentTotalSize += folderSize;
  782       if (folderIndex == lastFolderIndex)
  783         break;
  784       extractStatuses.Clear();
  785     }
  786   }
  787   return S_OK;
  788   COM_TRY_END
  789 }
  790 
  791 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
  792 {
  793     *numItems = m_Database.NewFormat ? 1:
  794       (m_Database.LowLevel ?
  795       m_Database.Items.Size():
  796       m_Database.Indices.Size());
  797   return S_OK;
  798 }
  799 
  800 namespace NChm {
  801 
  802 static const Byte k_Signature[] = { 'I', 'T', 'S', 'F', 3, 0, 0, 0, 0x60, 0,  0, 0 };
  803 
  804 REGISTER_ARC_I_CLS(
  805   CHandler(false),
  806   "Chm", "chm chi chq chw", 0, 0xE9,
  807   k_Signature,
  808   0,
  809   0,
  810   NULL)
  811 
  812 }
  813 
  814 namespace NHxs {
  815 
  816 static const Byte k_Signature[] = { 'I', 'T', 'O', 'L', 'I', 'T', 'L', 'S', 1, 0, 0, 0, 0x28, 0, 0, 0 };
  817 
  818 REGISTER_ARC_I_CLS(
  819   CHandler(true),
  820   "Hxs", "hxs hxi hxr hxq hxw lit", 0, 0xCE,
  821   k_Signature,
  822   0,
  823   NArcInfoFlags::kFindSignature,
  824   NULL)
  825 
  826 }
  827 
  828 }}