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 §ion = 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(¤tTotalSize))
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 §ion = 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(¤tTotalSize))
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