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