OSDN Git Service

add japanese site html
[sevenzip/7-Zip.git] / 7z457 / CPP / 7zip / Archive / Lzh / LzhHandler.cpp
1 // LzhHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "Common/Defs.h"
6 #include "Common/StringConvert.h"
7 #include "Common/ComTry.h"
8
9 #include "Windows/Time.h"
10 #include "Windows/PropVariant.h"
11
12 #include "LzhHandler.h"
13 #include "LzhOutStreamWithCRC.h"
14
15 #include "../../ICoder.h"
16
17 #include "../../Common/ProgressUtils.h"
18 #include "../../Common/LimitedStreams.h"
19
20 #include "../../Compress/Copy/CopyCoder.h"
21 #include "../../Compress/Lzh/LzhDecoder.h"
22
23 #include "../Common/ItemNameUtils.h"
24
25 using namespace NWindows;
26 using namespace NTime;
27
28 namespace NArchive {
29 namespace NLzh{
30
31 struct COsPair
32 {
33   Byte Id;
34   const wchar_t *Name;
35 };
36
37 COsPair g_OsPairs[] = 
38 {
39   { 'M', L"MS-DOS" },
40   { '2', L"OS/2" },
41   { '9', L"OS9" },
42   { 'K', L"OS/68K" },
43   { '3', L"OS/386" },
44   { 'H', L"HUMAN" },
45   { 'U', L"UNIX" },
46   { 'C', L"CP/M" },
47   { 'F', L"FLEX" },
48   { 'm', L"Mac" },
49   { 'R', L"Runser" },
50   { 'T', L"TownsOS" },
51   { 'X', L"XOSK" },
52   { 'w', L"Windows95" },
53   { 'W', L"WindowsNT" },
54   {  0,  L"MS-DOS" },
55   { 'J', L"Java VM" }
56 };
57
58 const wchar_t *kUnknownOS = L"Unknown";
59
60 const int kNumHostOSes = sizeof(g_OsPairs) / sizeof(g_OsPairs[0]);
61
62 static const wchar_t *GetOS(Byte osId)
63 {
64   for (int i = 0; i < kNumHostOSes; i++)
65     if (g_OsPairs[i].Id == osId)
66       return g_OsPairs[i].Name;
67   return kUnknownOS;
68 };
69
70 STATPROPSTG kProps[] = 
71 {
72   { NULL, kpidPath, VT_BSTR},
73   { NULL, kpidIsFolder, VT_BOOL},
74   { NULL, kpidSize, VT_UI8},
75   { NULL, kpidPackedSize, VT_UI8},
76   { NULL, kpidLastWriteTime, VT_FILETIME},
77   { NULL, kpidAttributes, VT_UI4},
78
79   // { NULL, kpidCommented, VT_BOOL},
80     
81   { NULL, kpidCRC, VT_UI4},
82
83   { NULL, kpidMethod, VT_UI1},
84   { NULL, kpidHostOS, VT_BSTR}
85
86 };
87
88 IMP_IInArchive_Props
89 IMP_IInArchive_ArcProps_NO
90
91 CHandler::CHandler() {}
92
93 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
94 {
95   *numItems = _items.Size();
96   return S_OK;
97 }
98
99 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
100 {
101   COM_TRY_BEGIN
102   NWindows::NCOM::CPropVariant prop;
103   const CItemEx &item = _items[index];
104   switch(propID)
105   {
106     case kpidPath:
107     {
108       UString s = NItemName::WinNameToOSName(MultiByteToUnicodeString(item.GetName(), CP_OEMCP));
109       if (!s.IsEmpty())
110       {
111         if (s[s.Length() - 1] == WCHAR_PATH_SEPARATOR)
112            s.Delete(s.Length() - 1);
113         prop = s;
114       }
115       break;
116     }
117     case kpidIsFolder:
118       prop = item.IsDirectory();
119       break;
120     case kpidSize:
121       prop = item.Size;
122       break;
123     case kpidPackedSize:
124       prop = item.PackSize;
125       break;
126     case kpidLastWriteTime:
127     {
128       FILETIME utcFileTime;
129       UInt32 unixTime;
130       if (item.GetUnixTime(unixTime))
131       {
132         NTime::UnixTimeToFileTime(unixTime, utcFileTime);
133       }
134       else
135       {
136         FILETIME localFileTime;
137         if (DosTimeToFileTime(item.ModifiedTime, localFileTime))
138         {
139           if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))
140             utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
141         }
142         else
143           utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
144       }
145       prop = utcFileTime;
146       break;
147     }
148     /*
149     case kpidAttributes:
150       prop = (UInt32)item.Attributes;
151       break;
152     case kpidCommented:
153       prop = item.IsCommented();
154       break;
155     */
156     case kpidCRC:
157       prop = (UInt32)item.CRC;
158       break;
159     case kpidMethod:
160     {
161       wchar_t method2[kMethodIdSize + 1];
162       method2[kMethodIdSize] = 0;
163       for (int i = 0; i < kMethodIdSize; i++)
164         method2[i] = item.Method[i];
165       prop = method2;
166       break;
167     }
168     case kpidHostOS:
169       prop = GetOS(item.OsId);
170       break;
171   }
172   prop.Detach(value);
173   return S_OK;
174   COM_TRY_END
175 }
176
177 /*
178 class CPropgressImp: public CProgressVirt
179 {
180 public:
181   CMyComPtr<IArchiveOpenCallback> Callback;
182   STDMETHOD(SetCompleted)(const UInt64 *numFiles);
183 };
184
185 STDMETHODIMP CPropgressImp::SetCompleted(const UInt64 *numFiles)
186 {
187   if (Callback)
188     return Callback->SetCompleted(numFiles, NULL);
189   return S_OK;
190 }
191 */
192
193 STDMETHODIMP CHandler::Open(IInStream *inStream, 
194     const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback)
195 {
196   COM_TRY_BEGIN
197   try
198   {
199     _items.Clear();
200     CInArchive archive;
201     RINOK(archive.Open(inStream));
202     if (callback != NULL)
203     {
204       RINOK(callback->SetTotal(NULL, NULL));
205       UInt64 numFiles = _items.Size();
206       RINOK(callback->SetCompleted(&numFiles, NULL));
207     }
208     for (;;)
209     {
210       CItemEx item;
211       bool filled;
212       HRESULT result = archive.GetNextItem(filled, item);
213       if (result == S_FALSE)
214         return S_FALSE;
215       if (result != S_OK)
216         return S_FALSE;
217       if (!filled)
218         break;
219       _items.Add(item);
220       archive.Skeep(item.PackSize);
221       if (callback != NULL)
222       {
223         UInt64 numFiles = _items.Size();
224         RINOK(callback->SetCompleted(&numFiles, NULL));
225       }
226     }
227     if (_items.IsEmpty())
228       return S_FALSE;
229
230     _stream = inStream;
231   }
232   catch(...)
233   {
234     return S_FALSE;
235   }
236   COM_TRY_END
237   return S_OK;
238 }
239
240 STDMETHODIMP CHandler::Close()
241 {
242   _items.Clear();
243   _stream.Release();
244   return S_OK;
245 }
246
247
248
249 //////////////////////////////////////
250 // CHandler::DecompressItems
251
252 STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
253     Int32 testModeSpec, IArchiveExtractCallback *extractCallback)
254 {
255   COM_TRY_BEGIN
256   bool testMode = (testModeSpec != 0);
257   UInt64 totalUnPacked = 0, totalPacked = 0;
258   bool allFilesMode = (numItems == UInt32(-1));
259   if (allFilesMode)
260     numItems = _items.Size();
261   if(numItems == 0)
262     return S_OK;
263   UInt32 i;
264   for(i = 0; i < numItems; i++)
265   {
266     const CItemEx &item = _items[allFilesMode ? i : indices[i]];
267     totalUnPacked += item.Size;
268     totalPacked += item.PackSize;
269   }
270   extractCallback->SetTotal(totalUnPacked);
271
272   UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0;
273   UInt64 currentItemUnPacked, currentItemPacked;
274   
275   NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0;
276   CMyComPtr<ICompressCoder> lzhDecoder;
277   CMyComPtr<ICompressCoder> lzh1Decoder;
278   CMyComPtr<ICompressCoder> arj2Decoder;
279
280   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
281   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
282
283   CLocalProgress *lps = new CLocalProgress;
284   CMyComPtr<ICompressProgressInfo> progress = lps;
285   lps->Init(extractCallback, false);
286
287   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
288   CMyComPtr<ISequentialInStream> inStream(streamSpec);
289   streamSpec->SetStream(_stream);
290
291   for(i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked,
292       currentTotalPacked += currentItemPacked)
293   {
294     currentItemUnPacked = 0;
295     currentItemPacked = 0;
296
297     lps->InSize = currentTotalPacked;
298     lps->OutSize = currentTotalUnPacked;
299     RINOK(lps->SetCur());
300
301     CMyComPtr<ISequentialOutStream> realOutStream;
302     Int32 askMode;
303     askMode = testMode ? NExtract::NAskMode::kTest :
304         NExtract::NAskMode::kExtract;
305     Int32 index = allFilesMode ? i : indices[i];
306     const CItemEx &item = _items[index];
307     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
308
309     if(item.IsDirectory())
310     {
311       // if (!testMode)
312       {
313         RINOK(extractCallback->PrepareOperation(askMode));
314         RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
315       }
316       continue;
317     }
318
319     if (!testMode && (!realOutStream)) 
320       continue;
321
322     RINOK(extractCallback->PrepareOperation(askMode));
323     currentItemUnPacked = item.Size;
324     currentItemPacked = item.PackSize;
325
326     {
327       COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
328       CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
329       outStreamSpec->Init(realOutStream);
330       realOutStream.Release();
331       
332       UInt64 pos;
333       _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos);
334
335       streamSpec->Init(item.PackSize);
336
337       HRESULT result = S_OK;
338       Int32 opRes = NExtract::NOperationResult::kOK;
339
340       if (item.IsCopyMethod())
341       {
342         result = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
343         if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize)
344           result = S_FALSE;
345       }
346       else if (item.IsLh4GroupMethod())
347       {
348         if(!lzhDecoder)
349         {
350           lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder;
351           lzhDecoder = lzhDecoderSpec;
352         }
353         lzhDecoderSpec->SetDictionary(item.GetNumDictBits());
354         result = lzhDecoder->Code(inStream, outStream, NULL, &currentItemUnPacked, progress);
355       }
356       /*
357       else if (item.IsLh1GroupMethod())
358       {
359         if(!lzh1Decoder)
360         {
361           lzh1DecoderSpec = new NCompress::NLzh1::NDecoder::CCoder;
362           lzh1Decoder = lzh1DecoderSpec;
363         }
364         lzh1DecoderSpec->SetDictionary(item.GetNumDictBits());
365         result = lzh1Decoder->Code(inStream, outStream, NULL, &currentItemUnPacked, progress);
366       }
367       */
368       else
369         opRes = NExtract::NOperationResult::kUnSupportedMethod;
370
371       if (opRes == NExtract::NOperationResult::kOK)
372       {
373         if (result == S_FALSE)
374           opRes = NExtract::NOperationResult::kDataError;
375         else
376         {
377           RINOK(result);
378           if (outStreamSpec->GetCRC() != item.CRC)
379             opRes = NExtract::NOperationResult::kCRCError;
380         }
381       }
382       outStream.Release();
383       RINOK(extractCallback->SetOperationResult(opRes));
384     }
385   }
386   return S_OK;
387   COM_TRY_END
388 }
389
390
391 }}