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