diff options
Diffstat (limited to 'CPP/7zip/Archive/Chm/ChmHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/Chm/ChmHandler.cpp | 830 |
1 files changed, 830 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Chm/ChmHandler.cpp b/CPP/7zip/Archive/Chm/ChmHandler.cpp new file mode 100644 index 0000000..03e7ddd --- /dev/null +++ b/CPP/7zip/Archive/Chm/ChmHandler.cpp | |||
@@ -0,0 +1,830 @@ | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | ||
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 | |||
409 | STDMETHODIMP CChmFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) | ||
410 | { | ||
411 | return Write2(data, size, processedSize, true); | ||
412 | } | ||
413 | |||
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 | |||
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 | |||
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 | }} | ||