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