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