diff options
Diffstat (limited to 'CPP/7zip/Archive/LzhHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/LzhHandler.cpp | 756 |
1 files changed, 756 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/LzhHandler.cpp b/CPP/7zip/Archive/LzhHandler.cpp new file mode 100644 index 0000000..e1984d2 --- /dev/null +++ b/CPP/7zip/Archive/LzhHandler.cpp | |||
@@ -0,0 +1,756 @@ | |||
1 | // LzhHandler.cpp | ||
2 | |||
3 | #include "StdAfx.h" | ||
4 | |||
5 | #include "../../../C/CpuArch.h" | ||
6 | |||
7 | #include "../../Common/ComTry.h" | ||
8 | #include "../../Common/MyBuffer.h" | ||
9 | #include "../../Common/StringConvert.h" | ||
10 | |||
11 | #include "../../Windows/PropVariant.h" | ||
12 | #include "../../Windows/PropVariantUtils.h" | ||
13 | #include "../../Windows/TimeUtils.h" | ||
14 | |||
15 | #include "../ICoder.h" | ||
16 | |||
17 | #include "../Common/LimitedStreams.h" | ||
18 | #include "../Common/ProgressUtils.h" | ||
19 | #include "../Common/RegisterArc.h" | ||
20 | #include "../Common/StreamUtils.h" | ||
21 | |||
22 | #include "../Compress/CopyCoder.h" | ||
23 | #include "../Compress/LzhDecoder.h" | ||
24 | |||
25 | #include "IArchive.h" | ||
26 | |||
27 | #include "Common/ItemNameUtils.h" | ||
28 | |||
29 | using namespace NWindows; | ||
30 | using namespace NTime; | ||
31 | |||
32 | #define Get16(p) GetUi16(p) | ||
33 | #define Get32(p) GetUi32(p) | ||
34 | |||
35 | |||
36 | // CRC-16 (-IBM, -ANSI). The poly is 0x8005 (x^16 + x^15 + x^2 + 1) | ||
37 | |||
38 | static const UInt16 kCrc16Poly = 0xA001; | ||
39 | |||
40 | static UInt16 g_LzhCrc16Table[256]; | ||
41 | |||
42 | #define CRC16_UPDATE_BYTE(crc, b) (g_LzhCrc16Table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) | ||
43 | |||
44 | UInt32 LzhCrc16Update(UInt32 crc, const void *data, size_t size); | ||
45 | UInt32 LzhCrc16Update(UInt32 crc, const void *data, size_t size) | ||
46 | { | ||
47 | const Byte *p = (const Byte *)data; | ||
48 | const Byte *pEnd = p + size; | ||
49 | for (; p != pEnd; p++) | ||
50 | crc = CRC16_UPDATE_BYTE(crc, *p); | ||
51 | return crc; | ||
52 | } | ||
53 | |||
54 | static class CLzhCrc16TableInit | ||
55 | { | ||
56 | public: | ||
57 | CLzhCrc16TableInit() | ||
58 | { | ||
59 | for (UInt32 i = 0; i < 256; i++) | ||
60 | { | ||
61 | UInt32 r = i; | ||
62 | for (unsigned j = 0; j < 8; j++) | ||
63 | r = (r >> 1) ^ (kCrc16Poly & ((UInt32)0 - (r & 1))); | ||
64 | g_LzhCrc16Table[i] = (UInt16)r; | ||
65 | } | ||
66 | } | ||
67 | } g_LzhCrc16TableInit; | ||
68 | |||
69 | |||
70 | namespace NArchive { | ||
71 | namespace NLzh{ | ||
72 | |||
73 | const unsigned kMethodIdSize = 5; | ||
74 | |||
75 | const Byte kExtIdFileName = 0x01; | ||
76 | const Byte kExtIdDirName = 0x02; | ||
77 | const Byte kExtIdUnixTime = 0x54; | ||
78 | |||
79 | struct CExtension | ||
80 | { | ||
81 | Byte Type; | ||
82 | CByteBuffer Data; | ||
83 | |||
84 | AString GetString() const | ||
85 | { | ||
86 | AString s; | ||
87 | s.SetFrom_CalcLen((const char *)(const Byte *)Data, (unsigned)Data.Size()); | ||
88 | return s; | ||
89 | } | ||
90 | }; | ||
91 | |||
92 | const UInt32 kBasicPartSize = 22; | ||
93 | |||
94 | API_FUNC_static_IsArc IsArc_Lzh(const Byte *p, size_t size) | ||
95 | { | ||
96 | if (size < 2 + kBasicPartSize) | ||
97 | return k_IsArc_Res_NEED_MORE; | ||
98 | if (p[2] != '-' || p[3] != 'l' || p[4] != 'h' || p[6] != '-') | ||
99 | return k_IsArc_Res_NO; | ||
100 | Byte n = p[5]; | ||
101 | if (n != 'd') | ||
102 | if (n < '0' || n > '7') | ||
103 | return k_IsArc_Res_NO; | ||
104 | return k_IsArc_Res_YES; | ||
105 | } | ||
106 | } | ||
107 | |||
108 | struct CItem | ||
109 | { | ||
110 | AString Name; | ||
111 | Byte Method[kMethodIdSize]; | ||
112 | Byte Attributes; | ||
113 | Byte Level; | ||
114 | Byte OsId; | ||
115 | UInt32 PackSize; | ||
116 | UInt32 Size; | ||
117 | UInt32 ModifiedTime; | ||
118 | UInt16 CRC; | ||
119 | CObjectVector<CExtension> Extensions; | ||
120 | |||
121 | bool IsValidMethod() const { return (Method[0] == '-' && Method[1] == 'l' && Method[4] == '-'); } | ||
122 | bool IsLhMethod() const {return (IsValidMethod() && Method[2] == 'h'); } | ||
123 | bool IsDir() const {return (IsLhMethod() && Method[3] == 'd'); } | ||
124 | |||
125 | bool IsCopyMethod() const | ||
126 | { | ||
127 | return (IsLhMethod() && Method[3] == '0') || | ||
128 | (IsValidMethod() && Method[2] == 'z' && Method[3] == '4'); | ||
129 | } | ||
130 | |||
131 | bool IsLh1GroupMethod() const | ||
132 | { | ||
133 | if (!IsLhMethod()) | ||
134 | return false; | ||
135 | switch (Method[3]) | ||
136 | { | ||
137 | case '1': | ||
138 | return true; | ||
139 | } | ||
140 | return false; | ||
141 | } | ||
142 | |||
143 | bool IsLh4GroupMethod() const | ||
144 | { | ||
145 | if (!IsLhMethod()) | ||
146 | return false; | ||
147 | switch (Method[3]) | ||
148 | { | ||
149 | case '4': | ||
150 | case '5': | ||
151 | case '6': | ||
152 | case '7': | ||
153 | return true; | ||
154 | } | ||
155 | return false; | ||
156 | } | ||
157 | |||
158 | unsigned GetNumDictBits() const | ||
159 | { | ||
160 | if (!IsLhMethod()) | ||
161 | return 0; | ||
162 | switch (Method[3]) | ||
163 | { | ||
164 | case '1': return 12; | ||
165 | case '2': return 13; | ||
166 | case '3': return 13; | ||
167 | case '4': return 12; | ||
168 | case '5': return 13; | ||
169 | case '6': return 15; | ||
170 | case '7': return 16; | ||
171 | } | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | int FindExt(Byte type) const | ||
176 | { | ||
177 | FOR_VECTOR (i, Extensions) | ||
178 | if (Extensions[i].Type == type) | ||
179 | return i; | ||
180 | return -1; | ||
181 | } | ||
182 | |||
183 | bool GetUnixTime(UInt32 &value) const | ||
184 | { | ||
185 | value = 0; | ||
186 | int index = FindExt(kExtIdUnixTime); | ||
187 | if (index < 0 | ||
188 | || Extensions[index].Data.Size() < 4) | ||
189 | { | ||
190 | if (Level == 2) | ||
191 | { | ||
192 | value = ModifiedTime; | ||
193 | return true; | ||
194 | } | ||
195 | return false; | ||
196 | } | ||
197 | const Byte *data = (const Byte *)(Extensions[index].Data); | ||
198 | value = GetUi32(data); | ||
199 | return true; | ||
200 | } | ||
201 | |||
202 | AString GetDirName() const | ||
203 | { | ||
204 | int index = FindExt(kExtIdDirName); | ||
205 | if (index < 0) | ||
206 | return AString(); | ||
207 | return Extensions[index].GetString(); | ||
208 | } | ||
209 | |||
210 | AString GetFileName() const | ||
211 | { | ||
212 | int index = FindExt(kExtIdFileName); | ||
213 | if (index < 0) | ||
214 | return Name; | ||
215 | return Extensions[index].GetString(); | ||
216 | } | ||
217 | |||
218 | AString GetName() const | ||
219 | { | ||
220 | AString s (GetDirName()); | ||
221 | const char kDirSeparator = '\\'; | ||
222 | // check kDirSeparator in Linux | ||
223 | s.Replace((char)(unsigned char)0xFF, kDirSeparator); | ||
224 | if (!s.IsEmpty() && s.Back() != kDirSeparator) | ||
225 | s += kDirSeparator; | ||
226 | s += GetFileName(); | ||
227 | return s; | ||
228 | } | ||
229 | }; | ||
230 | |||
231 | static const Byte *ReadUInt16(const Byte *p, UInt16 &v) | ||
232 | { | ||
233 | v = Get16(p); | ||
234 | return p + 2; | ||
235 | } | ||
236 | |||
237 | static const Byte *ReadString(const Byte *p, size_t size, AString &s) | ||
238 | { | ||
239 | s.Empty(); | ||
240 | for (size_t i = 0; i < size; i++) | ||
241 | { | ||
242 | char c = p[i]; | ||
243 | if (c == 0) | ||
244 | break; | ||
245 | s += c; | ||
246 | } | ||
247 | return p + size; | ||
248 | } | ||
249 | |||
250 | static Byte CalcSum(const Byte *data, size_t size) | ||
251 | { | ||
252 | Byte sum = 0; | ||
253 | for (size_t i = 0; i < size; i++) | ||
254 | sum = (Byte)(sum + data[i]); | ||
255 | return sum; | ||
256 | } | ||
257 | |||
258 | static HRESULT GetNextItem(ISequentialInStream *stream, bool &filled, CItem &item) | ||
259 | { | ||
260 | filled = false; | ||
261 | |||
262 | size_t processedSize = 2; | ||
263 | Byte startHeader[2]; | ||
264 | RINOK(ReadStream(stream, startHeader, &processedSize)) | ||
265 | if (processedSize == 0) | ||
266 | return S_OK; | ||
267 | if (processedSize == 1) | ||
268 | return (startHeader[0] == 0) ? S_OK: S_FALSE; | ||
269 | if (startHeader[0] == 0 && startHeader[1] == 0) | ||
270 | return S_OK; | ||
271 | |||
272 | Byte header[256]; | ||
273 | processedSize = kBasicPartSize; | ||
274 | RINOK(ReadStream(stream, header, &processedSize)); | ||
275 | if (processedSize != kBasicPartSize) | ||
276 | return (startHeader[0] == 0) ? S_OK: S_FALSE; | ||
277 | |||
278 | const Byte *p = header; | ||
279 | memcpy(item.Method, p, kMethodIdSize); | ||
280 | if (!item.IsValidMethod()) | ||
281 | return S_OK; | ||
282 | p += kMethodIdSize; | ||
283 | item.PackSize = Get32(p); | ||
284 | item.Size = Get32(p + 4); | ||
285 | item.ModifiedTime = Get32(p + 8); | ||
286 | item.Attributes = p[12]; | ||
287 | item.Level = p[13]; | ||
288 | p += 14; | ||
289 | if (item.Level > 2) | ||
290 | return S_FALSE; | ||
291 | UInt32 headerSize; | ||
292 | if (item.Level < 2) | ||
293 | { | ||
294 | headerSize = startHeader[0]; | ||
295 | if (headerSize < kBasicPartSize) | ||
296 | return S_FALSE; | ||
297 | RINOK(ReadStream_FALSE(stream, header + kBasicPartSize, headerSize - kBasicPartSize)); | ||
298 | if (startHeader[1] != CalcSum(header, headerSize)) | ||
299 | return S_FALSE; | ||
300 | size_t nameLength = *p++; | ||
301 | if ((p - header) + nameLength + 2 > headerSize) | ||
302 | return S_FALSE; | ||
303 | p = ReadString(p, nameLength, item.Name); | ||
304 | } | ||
305 | else | ||
306 | headerSize = startHeader[0] | ((UInt32)startHeader[1] << 8); | ||
307 | p = ReadUInt16(p, item.CRC); | ||
308 | if (item.Level != 0) | ||
309 | { | ||
310 | if (item.Level == 2) | ||
311 | { | ||
312 | RINOK(ReadStream_FALSE(stream, header + kBasicPartSize, 2)); | ||
313 | } | ||
314 | if ((size_t)(p - header) + 3 > headerSize) | ||
315 | return S_FALSE; | ||
316 | item.OsId = *p++; | ||
317 | UInt16 nextSize; | ||
318 | p = ReadUInt16(p, nextSize); | ||
319 | while (nextSize != 0) | ||
320 | { | ||
321 | if (nextSize < 3) | ||
322 | return S_FALSE; | ||
323 | if (item.Level == 1) | ||
324 | { | ||
325 | if (item.PackSize < nextSize) | ||
326 | return S_FALSE; | ||
327 | item.PackSize -= nextSize; | ||
328 | } | ||
329 | if (item.Extensions.Size() >= (1 << 8)) | ||
330 | return S_FALSE; | ||
331 | CExtension ext; | ||
332 | RINOK(ReadStream_FALSE(stream, &ext.Type, 1)) | ||
333 | nextSize = (UInt16)(nextSize - 3); | ||
334 | ext.Data.Alloc(nextSize); | ||
335 | RINOK(ReadStream_FALSE(stream, (Byte *)ext.Data, nextSize)) | ||
336 | item.Extensions.Add(ext); | ||
337 | Byte hdr2[2]; | ||
338 | RINOK(ReadStream_FALSE(stream, hdr2, 2)); | ||
339 | ReadUInt16(hdr2, nextSize); | ||
340 | } | ||
341 | } | ||
342 | filled = true; | ||
343 | return S_OK; | ||
344 | } | ||
345 | |||
346 | |||
347 | static const CUInt32PCharPair g_OsPairs[] = | ||
348 | { | ||
349 | { 0, "MS-DOS" }, | ||
350 | { 'M', "MS-DOS" }, | ||
351 | { '2', "OS/2" }, | ||
352 | { '9', "OS9" }, | ||
353 | { 'K', "OS/68K" }, | ||
354 | { '3', "OS/386" }, | ||
355 | { 'H', "HUMAN" }, | ||
356 | { 'U', "UNIX" }, | ||
357 | { 'C', "CP/M" }, | ||
358 | { 'F', "FLEX" }, | ||
359 | { 'm', "Mac" }, | ||
360 | { 'R', "Runser" }, | ||
361 | { 'T', "TownsOS" }, | ||
362 | { 'X', "XOSK" }, | ||
363 | { 'w', "Windows 95" }, | ||
364 | { 'W', "Windows NT" }, | ||
365 | { 'J', "Java VM" } | ||
366 | }; | ||
367 | |||
368 | |||
369 | static const Byte kProps[] = | ||
370 | { | ||
371 | kpidPath, | ||
372 | kpidIsDir, | ||
373 | kpidSize, | ||
374 | kpidPackSize, | ||
375 | kpidMTime, | ||
376 | // kpidAttrib, | ||
377 | kpidCRC, | ||
378 | kpidMethod, | ||
379 | kpidHostOS | ||
380 | }; | ||
381 | |||
382 | |||
383 | class COutStreamWithCRC: | ||
384 | public ISequentialOutStream, | ||
385 | public CMyUnknownImp | ||
386 | { | ||
387 | public: | ||
388 | MY_UNKNOWN_IMP | ||
389 | |||
390 | STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); | ||
391 | private: | ||
392 | UInt32 _crc; | ||
393 | CMyComPtr<ISequentialOutStream> _stream; | ||
394 | public: | ||
395 | void Init(ISequentialOutStream *stream) | ||
396 | { | ||
397 | _stream = stream; | ||
398 | _crc = 0; | ||
399 | } | ||
400 | void ReleaseStream() { _stream.Release(); } | ||
401 | UInt32 GetCRC() const { return _crc; } | ||
402 | }; | ||
403 | |||
404 | STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) | ||
405 | { | ||
406 | HRESULT res = S_OK; | ||
407 | if (_stream) | ||
408 | res = _stream->Write(data, size, &size); | ||
409 | _crc = LzhCrc16Update(_crc, data, size); | ||
410 | if (processedSize) | ||
411 | *processedSize = size; | ||
412 | return res; | ||
413 | } | ||
414 | |||
415 | |||
416 | struct CItemEx: public CItem | ||
417 | { | ||
418 | UInt64 DataPosition; | ||
419 | }; | ||
420 | |||
421 | |||
422 | class CHandler: | ||
423 | public IInArchive, | ||
424 | public CMyUnknownImp | ||
425 | { | ||
426 | CObjectVector<CItemEx> _items; | ||
427 | CMyComPtr<IInStream> _stream; | ||
428 | UInt64 _phySize; | ||
429 | UInt32 _errorFlags; | ||
430 | bool _isArc; | ||
431 | public: | ||
432 | MY_UNKNOWN_IMP1(IInArchive) | ||
433 | INTERFACE_IInArchive(;) | ||
434 | CHandler(); | ||
435 | }; | ||
436 | |||
437 | IMP_IInArchive_Props | ||
438 | IMP_IInArchive_ArcProps_NO_Table | ||
439 | |||
440 | CHandler::CHandler() {} | ||
441 | |||
442 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
443 | { | ||
444 | *numItems = _items.Size(); | ||
445 | return S_OK; | ||
446 | } | ||
447 | |||
448 | STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) | ||
449 | { | ||
450 | NCOM::CPropVariant prop; | ||
451 | switch (propID) | ||
452 | { | ||
453 | case kpidPhySize: prop = _phySize; break; | ||
454 | |||
455 | case kpidErrorFlags: | ||
456 | UInt32 v = _errorFlags; | ||
457 | if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; | ||
458 | prop = v; | ||
459 | break; | ||
460 | } | ||
461 | prop.Detach(value); | ||
462 | return S_OK; | ||
463 | } | ||
464 | |||
465 | STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) | ||
466 | { | ||
467 | COM_TRY_BEGIN | ||
468 | NCOM::CPropVariant prop; | ||
469 | const CItemEx &item = _items[index]; | ||
470 | switch (propID) | ||
471 | { | ||
472 | case kpidPath: | ||
473 | { | ||
474 | UString s = NItemName::WinPathToOsPath(MultiByteToUnicodeString(item.GetName(), CP_OEMCP)); | ||
475 | if (!s.IsEmpty()) | ||
476 | { | ||
477 | if (s.Back() == WCHAR_PATH_SEPARATOR) | ||
478 | s.DeleteBack(); | ||
479 | prop = s; | ||
480 | } | ||
481 | break; | ||
482 | } | ||
483 | case kpidIsDir: prop = item.IsDir(); break; | ||
484 | case kpidSize: prop = item.Size; break; | ||
485 | case kpidPackSize: prop = item.PackSize; break; | ||
486 | case kpidCRC: prop = (UInt32)item.CRC; break; | ||
487 | case kpidHostOS: PAIR_TO_PROP(g_OsPairs, item.OsId, prop); break; | ||
488 | case kpidMTime: | ||
489 | { | ||
490 | FILETIME utc; | ||
491 | UInt32 unixTime; | ||
492 | if (item.GetUnixTime(unixTime)) | ||
493 | NTime::UnixTimeToFileTime(unixTime, utc); | ||
494 | else | ||
495 | { | ||
496 | FILETIME localFileTime; | ||
497 | if (DosTimeToFileTime(item.ModifiedTime, localFileTime)) | ||
498 | { | ||
499 | if (!LocalFileTimeToFileTime(&localFileTime, &utc)) | ||
500 | utc.dwHighDateTime = utc.dwLowDateTime = 0; | ||
501 | } | ||
502 | else | ||
503 | utc.dwHighDateTime = utc.dwLowDateTime = 0; | ||
504 | } | ||
505 | prop = utc; | ||
506 | break; | ||
507 | } | ||
508 | // case kpidAttrib: prop = (UInt32)item.Attributes; break; | ||
509 | case kpidMethod: | ||
510 | { | ||
511 | char method2[kMethodIdSize + 1]; | ||
512 | method2[kMethodIdSize] = 0; | ||
513 | memcpy(method2, item.Method, kMethodIdSize); | ||
514 | prop = method2; | ||
515 | break; | ||
516 | } | ||
517 | } | ||
518 | prop.Detach(value); | ||
519 | return S_OK; | ||
520 | COM_TRY_END | ||
521 | } | ||
522 | |||
523 | STDMETHODIMP CHandler::Open(IInStream *stream, | ||
524 | const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback) | ||
525 | { | ||
526 | COM_TRY_BEGIN | ||
527 | Close(); | ||
528 | try | ||
529 | { | ||
530 | _items.Clear(); | ||
531 | |||
532 | UInt64 endPos = 0; | ||
533 | bool needSetTotal = true; | ||
534 | |||
535 | RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); | ||
536 | RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); | ||
537 | |||
538 | for (;;) | ||
539 | { | ||
540 | CItemEx item; | ||
541 | bool filled; | ||
542 | HRESULT res = GetNextItem(stream, filled, item); | ||
543 | RINOK(stream->Seek(0, STREAM_SEEK_CUR, &item.DataPosition)); | ||
544 | if (res == S_FALSE) | ||
545 | { | ||
546 | _errorFlags = kpv_ErrorFlags_HeadersError; | ||
547 | break; | ||
548 | } | ||
549 | |||
550 | if (res != S_OK) | ||
551 | return S_FALSE; | ||
552 | _phySize = item.DataPosition; | ||
553 | if (!filled) | ||
554 | break; | ||
555 | _items.Add(item); | ||
556 | |||
557 | _isArc = true; | ||
558 | |||
559 | UInt64 newPostion; | ||
560 | RINOK(stream->Seek(item.PackSize, STREAM_SEEK_CUR, &newPostion)); | ||
561 | if (newPostion > endPos) | ||
562 | { | ||
563 | _phySize = endPos; | ||
564 | _errorFlags = kpv_ErrorFlags_UnexpectedEnd; | ||
565 | break; | ||
566 | } | ||
567 | _phySize = newPostion; | ||
568 | if (callback) | ||
569 | { | ||
570 | if (needSetTotal) | ||
571 | { | ||
572 | RINOK(callback->SetTotal(NULL, &endPos)); | ||
573 | needSetTotal = false; | ||
574 | } | ||
575 | if (_items.Size() % 100 == 0) | ||
576 | { | ||
577 | UInt64 numFiles = _items.Size(); | ||
578 | UInt64 numBytes = item.DataPosition; | ||
579 | RINOK(callback->SetCompleted(&numFiles, &numBytes)); | ||
580 | } | ||
581 | } | ||
582 | } | ||
583 | if (_items.IsEmpty()) | ||
584 | return S_FALSE; | ||
585 | |||
586 | _stream = stream; | ||
587 | } | ||
588 | catch(...) | ||
589 | { | ||
590 | return S_FALSE; | ||
591 | } | ||
592 | COM_TRY_END | ||
593 | return S_OK; | ||
594 | } | ||
595 | |||
596 | STDMETHODIMP CHandler::Close() | ||
597 | { | ||
598 | _isArc = false; | ||
599 | _phySize = 0; | ||
600 | _errorFlags = 0; | ||
601 | _items.Clear(); | ||
602 | _stream.Release(); | ||
603 | return S_OK; | ||
604 | } | ||
605 | |||
606 | STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, | ||
607 | Int32 testModeSpec, IArchiveExtractCallback *extractCallback) | ||
608 | { | ||
609 | COM_TRY_BEGIN | ||
610 | bool testMode = (testModeSpec != 0); | ||
611 | UInt64 totalUnPacked = 0, totalPacked = 0; | ||
612 | bool allFilesMode = (numItems == (UInt32)(Int32)-1); | ||
613 | if (allFilesMode) | ||
614 | numItems = _items.Size(); | ||
615 | if (numItems == 0) | ||
616 | return S_OK; | ||
617 | UInt32 i; | ||
618 | for (i = 0; i < numItems; i++) | ||
619 | { | ||
620 | const CItemEx &item = _items[allFilesMode ? i : indices[i]]; | ||
621 | totalUnPacked += item.Size; | ||
622 | totalPacked += item.PackSize; | ||
623 | } | ||
624 | RINOK(extractCallback->SetTotal(totalUnPacked)); | ||
625 | |||
626 | UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0; | ||
627 | UInt64 currentItemUnPacked, currentItemPacked; | ||
628 | |||
629 | NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0; | ||
630 | CMyComPtr<ICompressCoder> lzhDecoder; | ||
631 | // CMyComPtr<ICompressCoder> lzh1Decoder; | ||
632 | // CMyComPtr<ICompressCoder> arj2Decoder; | ||
633 | |||
634 | NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); | ||
635 | CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; | ||
636 | |||
637 | CLocalProgress *lps = new CLocalProgress; | ||
638 | CMyComPtr<ICompressProgressInfo> progress = lps; | ||
639 | lps->Init(extractCallback, false); | ||
640 | |||
641 | CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; | ||
642 | CMyComPtr<ISequentialInStream> inStream(streamSpec); | ||
643 | streamSpec->SetStream(_stream); | ||
644 | |||
645 | for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked, | ||
646 | currentTotalPacked += currentItemPacked) | ||
647 | { | ||
648 | currentItemUnPacked = 0; | ||
649 | currentItemPacked = 0; | ||
650 | |||
651 | lps->InSize = currentTotalPacked; | ||
652 | lps->OutSize = currentTotalUnPacked; | ||
653 | RINOK(lps->SetCur()); | ||
654 | |||
655 | CMyComPtr<ISequentialOutStream> realOutStream; | ||
656 | Int32 askMode; | ||
657 | askMode = testMode ? NExtract::NAskMode::kTest : | ||
658 | NExtract::NAskMode::kExtract; | ||
659 | Int32 index = allFilesMode ? i : indices[i]; | ||
660 | const CItemEx &item = _items[index]; | ||
661 | RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); | ||
662 | |||
663 | if (item.IsDir()) | ||
664 | { | ||
665 | // if (!testMode) | ||
666 | { | ||
667 | RINOK(extractCallback->PrepareOperation(askMode)); | ||
668 | RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); | ||
669 | } | ||
670 | continue; | ||
671 | } | ||
672 | |||
673 | if (!testMode && !realOutStream) | ||
674 | continue; | ||
675 | |||
676 | RINOK(extractCallback->PrepareOperation(askMode)); | ||
677 | currentItemUnPacked = item.Size; | ||
678 | currentItemPacked = item.PackSize; | ||
679 | |||
680 | { | ||
681 | COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; | ||
682 | CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); | ||
683 | outStreamSpec->Init(realOutStream); | ||
684 | realOutStream.Release(); | ||
685 | |||
686 | UInt64 pos; | ||
687 | _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos); | ||
688 | |||
689 | streamSpec->Init(item.PackSize); | ||
690 | |||
691 | HRESULT res = S_OK; | ||
692 | Int32 opRes = NExtract::NOperationResult::kOK; | ||
693 | |||
694 | if (item.IsCopyMethod()) | ||
695 | { | ||
696 | res = copyCoder->Code(inStream, outStream, NULL, NULL, progress); | ||
697 | if (res == S_OK && copyCoderSpec->TotalSize != item.PackSize) | ||
698 | res = S_FALSE; | ||
699 | } | ||
700 | else if (item.IsLh4GroupMethod()) | ||
701 | { | ||
702 | if (!lzhDecoder) | ||
703 | { | ||
704 | lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder; | ||
705 | lzhDecoder = lzhDecoderSpec; | ||
706 | } | ||
707 | lzhDecoderSpec->FinishMode = true; | ||
708 | lzhDecoderSpec->SetDictSize(1 << item.GetNumDictBits()); | ||
709 | res = lzhDecoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); | ||
710 | if (res == S_OK && lzhDecoderSpec->GetInputProcessedSize() != item.PackSize) | ||
711 | res = S_FALSE; | ||
712 | } | ||
713 | /* | ||
714 | else if (item.IsLh1GroupMethod()) | ||
715 | { | ||
716 | if (!lzh1Decoder) | ||
717 | { | ||
718 | lzh1DecoderSpec = new NCompress::NLzh1::NDecoder::CCoder; | ||
719 | lzh1Decoder = lzh1DecoderSpec; | ||
720 | } | ||
721 | lzh1DecoderSpec->SetDictionary(item.GetNumDictBits()); | ||
722 | res = lzh1Decoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); | ||
723 | } | ||
724 | */ | ||
725 | else | ||
726 | opRes = NExtract::NOperationResult::kUnsupportedMethod; | ||
727 | |||
728 | if (opRes == NExtract::NOperationResult::kOK) | ||
729 | { | ||
730 | if (res == S_FALSE) | ||
731 | opRes = NExtract::NOperationResult::kDataError; | ||
732 | else | ||
733 | { | ||
734 | RINOK(res); | ||
735 | if (outStreamSpec->GetCRC() != item.CRC) | ||
736 | opRes = NExtract::NOperationResult::kCRCError; | ||
737 | } | ||
738 | } | ||
739 | outStream.Release(); | ||
740 | RINOK(extractCallback->SetOperationResult(opRes)); | ||
741 | } | ||
742 | } | ||
743 | return S_OK; | ||
744 | COM_TRY_END | ||
745 | } | ||
746 | |||
747 | static const Byte k_Signature[] = { '-', 'l', 'h' }; | ||
748 | |||
749 | REGISTER_ARC_I( | ||
750 | "Lzh", "lzh lha", 0, 6, | ||
751 | k_Signature, | ||
752 | 2, | ||
753 | 0, | ||
754 | IsArc_Lzh) | ||
755 | |||
756 | }} | ||