OSDN Git Service

add japanese site html
[sevenzip/7-Zip.git] / 7z457 / CPP / 7zip / Archive / Chm / ChmHandler.cpp
1 // Chm/Handler.cpp
2
3 #include "StdAfx.h"
4
5 #include "Common/StringConvert.h"
6 #include "Common/Defs.h"
7 #include "Common/UTFConvert.h"
8 #include "Common/ComTry.h"
9
10 #include "Windows/PropVariant.h"
11 #include "Windows/Time.h"
12
13 #include "../../Common/LimitedStreams.h"
14 #include "../../Common/StreamUtils.h"
15 #include "../../Common/ProgressUtils.h"
16
17 #include "../../Compress/Copy/CopyCoder.h"
18 #include "../../Compress/Lzx/LzxDecoder.h"
19
20 #include "../Common/ItemNameUtils.h"
21
22 #include "ChmHandler.h"
23
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 STATPROPSTG kProps[] = 
43 {
44   { NULL, kpidPath, VT_BSTR},
45   // { NULL, kpidIsFolder, VT_BOOL},
46   { NULL, kpidSize, VT_UI8},
47   { NULL, kpidMethod, VT_BSTR},
48   { NULL, kpidBlock, VT_UI4}
49   
50   #ifdef _CHM_DETAILS
51   ,
52   { L"Section", kpidSection, VT_UI4},
53   { NULL, kpidOffset, VT_UI4}
54   #endif
55 };
56
57 STATPROPSTG kArcProps[] = 
58 {
59   { NULL, kpidNumBlocks, VT_UI8}
60 };
61
62 IMP_IInArchive_Props
63
64 IMP_IInArchive_ArcProps_NO
65 /*
66 IMP_IInArchive_ArcProps
67
68 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
69 {
70   COM_TRY_BEGIN
71   NWindows::NCOM::CPropVariant prop;
72   switch(propID)
73   {
74     case kpidNumBlocks: 
75     {
76       UInt64 numBlocks = 0;
77       for (int i = 0; i < m_Database.Sections.Size(); i++)
78       {
79         const CSectionInfo &s = m_Database.Sections[i];
80         for (int j = 0; j < s.Methods.Size(); j++)
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   prop.Detach(value);
92   return S_OK;
93   COM_TRY_END
94 }
95 */
96
97 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
98 {
99   COM_TRY_BEGIN
100   NWindows::NCOM::CPropVariant prop;
101   if (m_Database.NewFormat)
102   {
103     switch(propID)
104     {
105       case kpidSize:
106         prop = (UInt64)m_Database.NewFormatString.Length();
107       break;
108     }
109     prop.Detach(value);
110     return S_OK;
111   }
112   int entryIndex;
113   if (m_Database.LowLevel)
114     entryIndex = index;
115   else
116     entryIndex = m_Database.Indices[index];
117   const CItem &item = m_Database.Items[entryIndex];
118   switch(propID)
119   {
120     case kpidPath:
121     {
122       UString us;
123       if (ConvertUTF8ToUnicode(item.Name, us))
124       {
125         if (!m_Database.LowLevel)
126         {
127           if (us.Length() > 1)
128             if (us[0] == L'/')
129               us.Delete(0);
130         }
131         prop = NItemName::GetOSName2(us);
132       }
133       break;
134     }
135     case kpidIsFolder:
136       prop = item.IsDirectory();
137       break;
138     case kpidSize:
139       prop = item.Size;
140       break;
141     case kpidMethod:
142     {
143       if (!item.IsDirectory())
144         if (item.Section == 0)
145           prop = L"Copy";
146         else if (item.Section < m_Database.Sections.Size())
147           prop = m_Database.Sections[(int)item.Section].GetMethodName();
148       break;
149     }
150     case kpidBlock:
151       if (m_Database.LowLevel)
152         prop = item.Section;
153       else if (item.Section != 0)
154         prop = m_Database.GetFolder(index);
155       break;
156     
157     #ifdef _CHM_DETAILS
158     
159     case kpidSection:
160       prop = (UInt32)item.Section;
161       break;
162     case kpidOffset:
163       prop = (UInt32)item.Offset;
164       break;
165
166     #endif
167   }
168   prop.Detach(value);
169   return S_OK;
170   COM_TRY_END
171 }
172
173 class CPropgressImp: public CProgressVirt
174 {
175   CMyComPtr<IArchiveOpenCallback> m_OpenArchiveCallback;
176 public:
177   STDMETHOD(SetTotal)(const UInt64 *numFiles);
178   STDMETHOD(SetCompleted)(const UInt64 *numFiles);
179   void Init(IArchiveOpenCallback *openArchiveCallback)
180     { m_OpenArchiveCallback = openArchiveCallback; }
181 };
182
183 STDMETHODIMP CPropgressImp::SetTotal(const UInt64 *numFiles)
184 {
185   if (m_OpenArchiveCallback)
186     return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);
187   return S_OK;
188 }
189
190 STDMETHODIMP CPropgressImp::SetCompleted(const UInt64 *numFiles)
191 {
192   if (m_OpenArchiveCallback)
193     return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);
194   return S_OK;
195 }
196
197 STDMETHODIMP CHandler::Open(IInStream *inStream, 
198     const UInt64 *maxCheckStartPosition,
199     IArchiveOpenCallback *openArchiveCallback)
200 {
201   COM_TRY_BEGIN
202   m_Stream.Release();
203   try
204   {
205     CInArchive archive;
206     CPropgressImp progressImp;
207     progressImp.Init(openArchiveCallback);
208     RINOK(archive.Open(inStream, maxCheckStartPosition, m_Database));
209     /*
210     if (m_Database.LowLevel)
211       return S_FALSE;
212     */
213     m_Stream = inStream;
214   }
215   catch(...)
216   {
217     return S_FALSE;
218   }
219   return S_OK;
220   COM_TRY_END
221 }
222
223 STDMETHODIMP CHandler::Close()
224 {
225   m_Database.Clear();
226   m_Stream.Release();
227   return S_OK;
228 }
229
230 class CChmFolderOutStream: 
231   public ISequentialOutStream,
232   public CMyUnknownImp
233 {
234 public:
235   MY_UNKNOWN_IMP
236
237   HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK);
238   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
239
240   UInt64 m_FolderSize;
241   UInt64 m_PosInFolder;
242   UInt64 m_PosInSection;
243   const CRecordVector<bool> *m_ExtractStatuses;
244   int m_StartIndex;
245   int m_CurrentIndex;
246   int m_NumFiles;
247
248 private:
249   const CFilesDatabase *m_Database;
250   CMyComPtr<IArchiveExtractCallback> m_ExtractCallback;
251   bool m_TestMode;
252
253   bool m_IsOk;
254   bool m_FileIsOpen;
255   UInt64 m_RemainFileSize;
256   CMyComPtr<ISequentialOutStream> m_RealOutStream;
257
258   HRESULT OpenFile();
259   HRESULT WriteEmptyFiles();
260 public:
261   void Init(
262     const CFilesDatabase *database,
263     IArchiveExtractCallback *extractCallback,
264     bool testMode);
265   HRESULT FlushCorrupted(UInt64 maxSize);
266 };
267
268 void CChmFolderOutStream::Init(
269     const CFilesDatabase *database,
270     IArchiveExtractCallback *extractCallback,
271     bool testMode)
272 {
273   m_Database = database;
274   m_ExtractCallback = extractCallback;
275   m_TestMode = testMode;
276
277   m_CurrentIndex = 0;
278   m_FileIsOpen = false;
279 }
280
281 HRESULT CChmFolderOutStream::OpenFile()
282 {
283   Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ? 
284       NExtract::NAskMode::kTest :
285       NExtract::NAskMode::kExtract) :
286       NExtract::NAskMode::kSkip;
287   m_RealOutStream.Release();
288   RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode));
289   if (!m_RealOutStream && !m_TestMode)
290     askMode = NArchive::NExtract::NAskMode::kSkip;
291   return m_ExtractCallback->PrepareOperation(askMode);
292 }
293
294 HRESULT CChmFolderOutStream::WriteEmptyFiles()
295 {
296   if (m_FileIsOpen)
297     return S_OK;
298   for (;m_CurrentIndex < m_NumFiles; m_CurrentIndex++)
299   {
300     UInt64 fileSize = m_Database->GetFileSize(m_StartIndex + m_CurrentIndex);
301     if (fileSize != 0)
302       return S_OK;
303     HRESULT result = OpenFile();
304     m_RealOutStream.Release();
305     RINOK(result);
306     RINOK(m_ExtractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
307   }
308   return S_OK;
309 }
310
311 // This is WritePart function
312 HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK)
313 {
314   UInt32 realProcessed = 0;
315   if (processedSize != NULL)
316    *processedSize = 0;
317   while(size != 0)
318   {
319     if (m_FileIsOpen)
320     {
321       UInt32 numBytesToWrite = (UInt32)MyMin(m_RemainFileSize, (UInt64)(size));
322       HRESULT res = S_OK;
323       if (numBytesToWrite > 0)
324       {
325         if (!isOK)
326           m_IsOk = false;
327         if (m_RealOutStream)
328         {
329           UInt32 processedSizeLocal = 0;
330           res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal);
331           numBytesToWrite = processedSizeLocal;
332         }
333       }
334       realProcessed += numBytesToWrite;
335       if (processedSize != NULL)
336         *processedSize = realProcessed;
337       data = (const void *)((const Byte *)data + numBytesToWrite);
338       size -= numBytesToWrite;
339       m_RemainFileSize -= numBytesToWrite;
340       m_PosInSection += numBytesToWrite;
341       m_PosInFolder += numBytesToWrite;
342       if (res != S_OK)
343         return res;
344       if (m_RemainFileSize == 0)
345       {
346         m_RealOutStream.Release();
347         RINOK(m_ExtractCallback->SetOperationResult(
348           m_IsOk ? 
349             NArchive::NExtract::NOperationResult::kOK:
350             NArchive::NExtract::NOperationResult::kDataError));
351         m_FileIsOpen = false;
352       }
353       if (realProcessed > 0)
354         break; // with this break this function works as write part
355     }
356     else
357     {
358       if (m_CurrentIndex >= m_NumFiles)
359         return E_FAIL; 
360       int fullIndex = m_StartIndex + m_CurrentIndex;
361       m_RemainFileSize = m_Database->GetFileSize(fullIndex);
362       UInt64 fileOffset = m_Database->GetFileOffset(fullIndex);
363       if (fileOffset < m_PosInSection)
364         return E_FAIL;
365       if (fileOffset > m_PosInSection)
366       {
367         UInt32 numBytesToWrite = (UInt32)MyMin(fileOffset - m_PosInSection, UInt64(size));
368         realProcessed += numBytesToWrite;
369         if (processedSize != NULL)
370           *processedSize = realProcessed;
371         data = (const void *)((const Byte *)data + numBytesToWrite);
372         size -= numBytesToWrite;
373         m_PosInSection += numBytesToWrite;
374         m_PosInFolder += numBytesToWrite;
375       }
376       if (fileOffset == m_PosInSection)
377       {
378         RINOK(OpenFile());
379         m_FileIsOpen = true;
380         m_CurrentIndex++;
381         m_IsOk = true;
382       }
383     }
384   }
385   return WriteEmptyFiles();
386 }
387
388 STDMETHODIMP CChmFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
389 {
390   return Write2(data, size, processedSize, true);
391 }
392
393 HRESULT CChmFolderOutStream::FlushCorrupted(UInt64 maxSize)
394 {
395   const UInt32 kBufferSize = (1 << 10);
396   Byte buffer[kBufferSize];
397   for (int i = 0; i < kBufferSize; i++)
398     buffer[i] = 0;
399   if (maxSize > m_FolderSize)
400     maxSize = m_FolderSize;
401   while (m_PosInFolder < maxSize)
402   {
403     UInt32 size = (UInt32)MyMin(maxSize - m_PosInFolder, (UInt64)kBufferSize);
404     UInt32 processedSizeLocal = 0;
405     RINOK(Write2(buffer, size, &processedSizeLocal, false));
406     if (processedSizeLocal == 0)
407       return S_OK;
408   }
409   return S_OK;
410 }
411
412
413 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
414     Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
415 {
416   COM_TRY_BEGIN
417   bool allFilesMode = (numItems == UInt32(-1));
418
419   if (allFilesMode)
420     numItems = m_Database.NewFormat ? 1:
421       (m_Database.LowLevel ?
422       m_Database.Items.Size():
423       m_Database.Indices.Size());
424   if (numItems == 0)
425     return S_OK;
426   bool testMode = (_aTestMode != 0);
427
428   UInt64 currentTotalSize = 0;
429
430   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
431   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
432   UInt32 i;
433
434   CLocalProgress *lps = new CLocalProgress;
435   CMyComPtr<ICompressProgressInfo> progress = lps;
436   lps->Init(extractCallback, false);
437
438   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
439   CMyComPtr<ISequentialInStream> inStream(streamSpec);
440   streamSpec->SetStream(m_Stream);
441
442   if (m_Database.LowLevel)
443   {
444     UInt64 currentItemSize = 0;
445     UInt64 totalSize = 0;
446     if (m_Database.NewFormat)
447       totalSize = m_Database.NewFormatString.Length();
448     else
449       for (i = 0; i < numItems; i++)
450         totalSize += m_Database.Items[allFilesMode ? i : indices[i]].Size;
451     extractCallback->SetTotal(totalSize);
452     
453     for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
454     {
455       currentItemSize = 0;
456       lps->InSize = currentTotalSize; // Change it
457       lps->OutSize = currentTotalSize;
458
459       RINOK(lps->SetCur());
460       CMyComPtr<ISequentialOutStream> realOutStream;
461       Int32 askMode= testMode ? 
462           NArchive::NExtract::NAskMode::kTest :
463           NArchive::NExtract::NAskMode::kExtract;
464       Int32 index = allFilesMode ? i : indices[i];
465       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
466
467       if (m_Database.NewFormat)
468       {
469         if (index != 0)
470           return E_FAIL;
471         if (!testMode && (!realOutStream))
472           continue;
473         if (!testMode)
474         {
475           UInt32 size = m_Database.NewFormatString.Length();
476           RINOK(WriteStream(realOutStream, (const char *)m_Database.NewFormatString, size, 0));
477         }
478         RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
479         continue;
480       }
481       const CItem &item = m_Database.Items[index];
482       
483       currentItemSize = item.Size;
484       
485       if (!testMode && (!realOutStream))
486         continue;
487       RINOK(extractCallback->PrepareOperation(askMode));
488       if (item.Section != 0)
489       {
490         RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
491         continue;
492       }
493
494       if (testMode)
495       {
496         RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
497         continue;
498       }
499       
500       RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
501       streamSpec->Init(item.Size);
502       
503       RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
504       realOutStream.Release();
505       RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? 
506           NArchive::NExtract::NOperationResult::kOK:
507           NArchive::NExtract::NOperationResult::kDataError));
508     }
509     return S_OK;
510   }
511   
512   UInt64 lastFolderIndex = ((UInt64)0 - 1);
513   for (i = 0; i < numItems; i++)
514   {
515     UInt32 index = allFilesMode ? i : indices[i];
516     int entryIndex = m_Database.Indices[index];
517     const CItem &item = m_Database.Items[entryIndex];
518     UInt64 sectionIndex = item.Section;
519     if (item.IsDirectory() || item.Size == 0)
520       continue;
521     if (sectionIndex == 0)
522     {
523       currentTotalSize += item.Size;
524       continue;
525     }
526     const CSectionInfo &section = m_Database.Sections[(int)item.Section];
527     if (section.IsLzx())
528     {
529       const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;
530       UInt64 folderIndex = m_Database.GetFolder(index);
531       if (lastFolderIndex == folderIndex)
532         folderIndex++;
533       lastFolderIndex = m_Database.GetLastFolder(index);
534       for (; folderIndex <= lastFolderIndex; folderIndex++)
535         currentTotalSize += lzxInfo.GetFolderSize();
536     }
537   }
538
539   RINOK(extractCallback->SetTotal(currentTotalSize));
540
541   NCompress::NLzx::CDecoder *lzxDecoderSpec = 0;
542   CMyComPtr<ICompressCoder> lzxDecoder;
543   CChmFolderOutStream *chmFolderOutStream = 0;
544   CMyComPtr<ISequentialOutStream> outStream;
545
546   currentTotalSize = 0;
547
548   CRecordVector<bool> extractStatuses;
549   for (i = 0; i < numItems;)
550   {
551     RINOK(extractCallback->SetCompleted(&currentTotalSize));
552     UInt32 index = allFilesMode ? i : indices[i];
553     i++;
554     int entryIndex = m_Database.Indices[index];
555     const CItem &item = m_Database.Items[entryIndex];
556     UInt64 sectionIndex = item.Section;
557     Int32 askMode= testMode ? 
558         NArchive::NExtract::NAskMode::kTest :
559         NArchive::NExtract::NAskMode::kExtract;
560     if (item.IsDirectory())
561     {
562       CMyComPtr<ISequentialOutStream> realOutStream;
563       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
564       RINOK(extractCallback->PrepareOperation(askMode));
565       realOutStream.Release();
566       RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
567       continue;
568     }
569
570     lps->InSize = currentTotalSize; // Change it
571     lps->OutSize = currentTotalSize;
572
573     if (item.Size == 0 || sectionIndex == 0)
574     {
575       CMyComPtr<ISequentialOutStream> realOutStream;
576       RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
577       if (!testMode && (!realOutStream))
578         continue;
579       RINOK(extractCallback->PrepareOperation(askMode));
580       Int32 opRes = NArchive::NExtract::NOperationResult::kOK;
581       if (!testMode && item.Size != 0)
582       {
583         RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL));
584         streamSpec->Init(item.Size);
585         RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
586         if (copyCoderSpec->TotalSize != item.Size)
587           opRes = NArchive::NExtract::NOperationResult::kDataError;
588       }
589       realOutStream.Release();
590       RINOK(extractCallback->SetOperationResult(opRes));
591       currentTotalSize += item.Size;
592       continue;
593     }
594   
595     const CSectionInfo &section = m_Database.Sections[(int)sectionIndex];
596
597     if (!section.IsLzx())
598     {
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(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
605       continue;
606     }
607
608     const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo;
609
610     if (chmFolderOutStream == 0)
611     {
612       chmFolderOutStream = new CChmFolderOutStream;
613       outStream = chmFolderOutStream;
614     }
615
616     chmFolderOutStream->Init(&m_Database, extractCallback, testMode);
617
618     if(lzxDecoderSpec == NULL)
619     {
620       lzxDecoderSpec = new NCompress::NLzx::CDecoder;
621       lzxDecoder = lzxDecoderSpec;
622     }
623
624     UInt64 folderIndex = m_Database.GetFolder(index);
625
626     UInt64 compressedPos = m_Database.ContentOffset + section.Offset;
627     UInt32 numDictBits = lzxInfo.GetNumDictBits();
628     RINOK(lzxDecoderSpec->SetParams(numDictBits));
629
630     const CItem *lastItem = &item;
631     extractStatuses.Clear();
632     extractStatuses.Add(true);
633
634     for (;; folderIndex++)
635     {
636       RINOK(extractCallback->SetCompleted(&currentTotalSize));
637
638       UInt64 startPos = lzxInfo.GetFolderPos(folderIndex);
639       UInt64 finishPos = lastItem->Offset + lastItem->Size;
640       UInt64 limitFolderIndex = lzxInfo.GetFolder(finishPos);
641
642       lastFolderIndex = m_Database.GetLastFolder(index);
643       UInt64 folderSize = lzxInfo.GetFolderSize();
644       UInt64 unPackSize = folderSize;
645       if (extractStatuses.IsEmpty())
646         chmFolderOutStream->m_StartIndex = index + 1;
647       else
648         chmFolderOutStream->m_StartIndex = index;
649       if (limitFolderIndex == folderIndex)
650       {
651         for (; i < numItems; i++)
652         {
653           UInt32 nextIndex = allFilesMode ? i : indices[i];
654           int entryIndex = m_Database.Indices[nextIndex];
655           const CItem &nextItem = m_Database.Items[entryIndex];
656           if (nextItem.Section != sectionIndex)
657             break;
658           UInt64 nextFolderIndex = m_Database.GetFolder(nextIndex);
659           if (nextFolderIndex != folderIndex)
660             break;
661           for (index++; index < nextIndex; index++)
662             extractStatuses.Add(false);
663           extractStatuses.Add(true);
664           index = nextIndex;
665           lastItem = &nextItem;
666           if (nextItem.Size != 0)
667             finishPos = nextItem.Offset + nextItem.Size;
668           lastFolderIndex = m_Database.GetLastFolder(index);
669         }
670       }
671       unPackSize = MyMin(finishPos - startPos, unPackSize);
672
673       chmFolderOutStream->m_FolderSize = folderSize;
674       chmFolderOutStream->m_PosInFolder = 0;
675       chmFolderOutStream->m_PosInSection = startPos;
676       chmFolderOutStream->m_ExtractStatuses = &extractStatuses;
677       chmFolderOutStream->m_NumFiles = extractStatuses.Size();
678       chmFolderOutStream->m_CurrentIndex = 0;
679       try
680       {
681         UInt64 startBlock = lzxInfo.GetBlockIndexFromFolderIndex(folderIndex);
682         const CResetTable &rt = lzxInfo.ResetTable;
683         UInt32 numBlocks = (UInt32)rt.GetNumBlocks(unPackSize);
684         for (UInt32 b = 0; b < numBlocks; b++)
685         {
686           UInt64 completedSize = currentTotalSize + chmFolderOutStream->m_PosInSection - startPos;
687           RINOK(extractCallback->SetCompleted(&completedSize));
688           UInt64 bCur = startBlock + b;
689           if (bCur >= rt.ResetOffsets.Size())
690             return E_FAIL;
691           UInt64 offset = rt.ResetOffsets[(int)bCur];
692           UInt64 compressedSize;
693           rt.GetCompressedSizeOfBlock(bCur, compressedSize);
694           UInt64 rem = finishPos - chmFolderOutStream->m_PosInSection;
695           if (rem > rt.BlockSize)
696             rem = rt.BlockSize;
697           RINOK(m_Stream->Seek(compressedPos + offset, STREAM_SEEK_SET, NULL));
698           streamSpec->SetStream(m_Stream);
699           streamSpec->Init(compressedSize);
700           lzxDecoderSpec->SetKeepHistory(b > 0);
701           HRESULT res = lzxDecoder->Code(inStream, outStream, NULL, &rem, NULL);
702           if (res != S_OK)
703           {
704             if (res != S_FALSE)
705               return res;
706             throw 1;
707           }
708         }
709       }
710       catch(...)
711       {
712         RINOK(chmFolderOutStream->FlushCorrupted(unPackSize));
713       }
714       currentTotalSize += folderSize;
715       if (folderIndex == lastFolderIndex)
716         break;
717       extractStatuses.Clear();
718     }
719   }
720   return S_OK;
721   COM_TRY_END
722 }
723
724 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
725 {
726     *numItems = m_Database.NewFormat ? 1:
727       (m_Database.LowLevel ?
728       m_Database.Items.Size():
729       m_Database.Indices.Size());
730   return S_OK;
731 }
732
733 }}