Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / Bz2Handler.cpp
1 // Bz2Handler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Common/ComTry.h"\r
6 \r
7 #include "Windows/PropVariant.h"\r
8 \r
9 #ifndef _7ZIP_ST\r
10 #include "../../Windows/System.h"\r
11 #endif\r
12 \r
13 #include "../Common/CreateCoder.h"\r
14 #include "../Common/ProgressUtils.h"\r
15 #include "../Common/RegisterArc.h"\r
16 #include "../Common/StreamUtils.h"\r
17 \r
18 #include "../Compress/BZip2Decoder.h"\r
19 #include "../Compress/BZip2Encoder.h"\r
20 #include "../Compress/CopyCoder.h"\r
21 \r
22 #include "Common/DummyOutStream.h"\r
23 #include "Common/ParseProperties.h"\r
24 \r
25 using namespace NWindows;\r
26 \r
27 namespace NArchive {\r
28 namespace NBz2 {\r
29 \r
30 static const UInt32 kNumPassesX1 = 1;\r
31 static const UInt32 kNumPassesX7 = 2;\r
32 static const UInt32 kNumPassesX9 = 7;\r
33 \r
34 static const UInt32 kDicSizeX1 = 100000;\r
35 static const UInt32 kDicSizeX3 = 500000;\r
36 static const UInt32 kDicSizeX5 = 900000;\r
37 \r
38 class CHandler:\r
39   public IInArchive,\r
40   public IArchiveOpenSeq,\r
41   public IOutArchive,\r
42   public ISetProperties,\r
43   public CMyUnknownImp\r
44 {\r
45   CMyComPtr<IInStream> _stream;\r
46   CMyComPtr<ISequentialInStream> _seqStream;\r
47   UInt64 _packSize;\r
48   UInt64 _startPosition;\r
49   bool _packSizeDefined;\r
50 \r
51   UInt32 _level;\r
52   UInt32 _dicSize;\r
53   UInt32 _numPasses;\r
54   #ifndef _7ZIP_ST\r
55   UInt32 _numThreads;\r
56   #endif\r
57 \r
58   void InitMethodProperties()\r
59   {\r
60     _level = 5;\r
61     _dicSize =\r
62     _numPasses = 0xFFFFFFFF;\r
63     #ifndef _7ZIP_ST\r
64     _numThreads = NWindows::NSystem::GetNumberOfProcessors();;\r
65     #endif\r
66   }\r
67 \r
68 public:\r
69   MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties)\r
70 \r
71   INTERFACE_IInArchive(;)\r
72   INTERFACE_IOutArchive(;)\r
73   STDMETHOD(OpenSeq)(ISequentialInStream *stream);\r
74   STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps);\r
75 \r
76   CHandler() { InitMethodProperties(); }\r
77 };\r
78 \r
79 STATPROPSTG kProps[] =\r
80 {\r
81   { NULL, kpidPackSize, VT_UI8}\r
82 };\r
83 \r
84 IMP_IInArchive_Props\r
85 IMP_IInArchive_ArcProps_NO_Table\r
86 \r
87 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
88 {\r
89   NCOM::CPropVariant prop;\r
90   switch(propID)\r
91   {\r
92     case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;\r
93   }\r
94   prop.Detach(value);\r
95   return S_OK;\r
96 }\r
97 \r
98 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
99 {\r
100   *numItems = 1;\r
101   return S_OK;\r
102 }\r
103 \r
104 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID,  PROPVARIANT *value)\r
105 {\r
106   NWindows::NCOM::CPropVariant prop;\r
107   switch(propID)\r
108   {\r
109     case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;\r
110   }\r
111   prop.Detach(value);\r
112   return S_OK;\r
113 }\r
114 \r
115 STDMETHODIMP CHandler::Open(IInStream *stream,\r
116     const UInt64 * /* maxCheckStartPosition */,\r
117     IArchiveOpenCallback * /* openArchiveCallback */)\r
118 {\r
119   COM_TRY_BEGIN\r
120   try\r
121   {\r
122     Close();\r
123     RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPosition));\r
124     const int kSignatureSize = 3;\r
125     Byte buf[kSignatureSize];\r
126     RINOK(ReadStream_FALSE(stream, buf, kSignatureSize));\r
127     if (buf[0] != 'B' || buf[1] != 'Z' || buf[2] != 'h')\r
128       return S_FALSE;\r
129 \r
130     UInt64 endPosition;\r
131     RINOK(stream->Seek(0, STREAM_SEEK_END, &endPosition));\r
132     _packSize = endPosition - _startPosition;\r
133     _packSizeDefined = true;\r
134     _stream = stream;\r
135     _seqStream = stream;\r
136   }\r
137   catch(...) { return S_FALSE; }\r
138   return S_OK;\r
139   COM_TRY_END\r
140 }\r
141 \r
142 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)\r
143 {\r
144   Close();\r
145   _seqStream = stream;\r
146   return S_OK;\r
147 }\r
148 \r
149 STDMETHODIMP CHandler::Close()\r
150 {\r
151   _packSizeDefined = false;\r
152   _seqStream.Release();\r
153   _stream.Release();\r
154   return S_OK;\r
155 }\r
156 \r
157 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
158     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
159 {\r
160   COM_TRY_BEGIN\r
161   if (numItems == 0)\r
162     return S_OK;\r
163   if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))\r
164     return E_INVALIDARG;\r
165 \r
166   if (_stream)\r
167     extractCallback->SetTotal(_packSize);\r
168   UInt64 currentTotalPacked = 0;\r
169   RINOK(extractCallback->SetCompleted(&currentTotalPacked));\r
170   CMyComPtr<ISequentialOutStream> realOutStream;\r
171   Int32 askMode = testMode ?\r
172       NExtract::NAskMode::kTest :\r
173       NExtract::NAskMode::kExtract;\r
174   RINOK(extractCallback->GetStream(0, &realOutStream, askMode));\r
175   if (!testMode && !realOutStream)\r
176     return S_OK;\r
177 \r
178   extractCallback->PrepareOperation(askMode);\r
179 \r
180   NCompress::NBZip2::CDecoder *decoderSpec = new NCompress::NBZip2::CDecoder;\r
181   CMyComPtr<ICompressCoder> decoder = decoderSpec;\r
182 \r
183   if (_stream)\r
184   {\r
185     RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));\r
186   }\r
187 \r
188   decoderSpec->SetInStream(_seqStream);\r
189 \r
190   #ifndef _7ZIP_ST\r
191   RINOK(decoderSpec->SetNumberOfThreads(_numThreads));\r
192   #endif\r
193 \r
194   CDummyOutStream *outStreamSpec = new CDummyOutStream;\r
195   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);\r
196   outStreamSpec->SetStream(realOutStream);\r
197   outStreamSpec->Init();\r
198   \r
199   realOutStream.Release();\r
200 \r
201   CLocalProgress *lps = new CLocalProgress;\r
202   CMyComPtr<ICompressProgressInfo> progress = lps;\r
203   lps->Init(extractCallback, true);\r
204 \r
205   HRESULT result = S_OK;\r
206 \r
207   bool firstItem = true;\r
208   for (;;)\r
209   {\r
210     lps->InSize = currentTotalPacked;\r
211     lps->OutSize = outStreamSpec->GetSize();\r
212 \r
213     RINOK(lps->SetCur());\r
214 \r
215     bool isBz2;\r
216     result = decoderSpec->CodeResume(outStream, isBz2, progress);\r
217 \r
218     if (result != S_OK)\r
219       break;\r
220     if (!isBz2)\r
221     {\r
222       if (firstItem)\r
223         result = S_FALSE;\r
224       break;\r
225     }\r
226     firstItem = false;\r
227 \r
228     _packSize = currentTotalPacked = decoderSpec->GetInputProcessedSize();\r
229     _packSizeDefined = true;\r
230   }\r
231   decoderSpec->ReleaseInStream();\r
232   outStream.Release();\r
233 \r
234   Int32 retResult;\r
235   if (result == S_OK)\r
236     retResult = NExtract::NOperationResult::kOK;\r
237   else if (result == S_FALSE)\r
238     retResult = NExtract::NOperationResult::kDataError;\r
239   else\r
240     return result;\r
241   return extractCallback->SetOperationResult(retResult);\r
242 \r
243   COM_TRY_END\r
244 }\r
245 \r
246 static HRESULT UpdateArchive(\r
247     UInt64 unpackSize,\r
248     ISequentialOutStream *outStream,\r
249     int indexInClient,\r
250     UInt32 dictionary,\r
251     UInt32 numPasses,\r
252     #ifndef _7ZIP_ST\r
253     UInt32 numThreads,\r
254     #endif\r
255     IArchiveUpdateCallback *updateCallback)\r
256 {\r
257   RINOK(updateCallback->SetTotal(unpackSize));\r
258   UInt64 complexity = 0;\r
259   RINOK(updateCallback->SetCompleted(&complexity));\r
260 \r
261   CMyComPtr<ISequentialInStream> fileInStream;\r
262 \r
263   RINOK(updateCallback->GetStream(indexInClient, &fileInStream));\r
264 \r
265   CLocalProgress *localProgressSpec = new CLocalProgress;\r
266   CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec;\r
267   localProgressSpec->Init(updateCallback, true);\r
268   \r
269   NCompress::NBZip2::CEncoder *encoderSpec = new NCompress::NBZip2::CEncoder;\r
270   CMyComPtr<ICompressCoder> encoder = encoderSpec;\r
271   {\r
272     NWindows::NCOM::CPropVariant properties[] =\r
273     {\r
274       dictionary,\r
275       numPasses\r
276       #ifndef _7ZIP_ST\r
277       , numThreads\r
278       #endif\r
279     };\r
280     PROPID propIDs[] =\r
281     {\r
282       NCoderPropID::kDictionarySize,\r
283       NCoderPropID::kNumPasses\r
284       #ifndef _7ZIP_ST\r
285       , NCoderPropID::kNumThreads\r
286       #endif\r
287     };\r
288     RINOK(encoderSpec->SetCoderProperties(propIDs, properties, sizeof(propIDs) / sizeof(propIDs[0])));\r
289   }\r
290   \r
291   RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress));\r
292   \r
293   return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);\r
294 }\r
295 \r
296 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)\r
297 {\r
298   *type = NFileTimeType::kUnix;\r
299   return S_OK;\r
300 }\r
301 \r
302 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,\r
303     IArchiveUpdateCallback *updateCallback)\r
304 {\r
305   if (numItems != 1)\r
306     return E_INVALIDARG;\r
307 \r
308   Int32 newData, newProps;\r
309   UInt32 indexInArchive;\r
310   if (!updateCallback)\r
311     return E_FAIL;\r
312   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));\r
313  \r
314   if (IntToBool(newProps))\r
315   {\r
316     {\r
317       NCOM::CPropVariant prop;\r
318       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));\r
319       if (prop.vt == VT_BOOL)\r
320       {\r
321         if (prop.boolVal != VARIANT_FALSE)\r
322           return E_INVALIDARG;\r
323       }\r
324       else if (prop.vt != VT_EMPTY)\r
325         return E_INVALIDARG;\r
326     }\r
327   }\r
328   \r
329   if (IntToBool(newData))\r
330   {\r
331     UInt64 size;\r
332     {\r
333       NCOM::CPropVariant prop;\r
334       RINOK(updateCallback->GetProperty(0, kpidSize, &prop));\r
335       if (prop.vt != VT_UI8)\r
336         return E_INVALIDARG;\r
337       size = prop.uhVal.QuadPart;\r
338     }\r
339   \r
340     UInt32 dicSize = _dicSize;\r
341     if (dicSize == 0xFFFFFFFF)\r
342       dicSize = (_level >= 5 ? kDicSizeX5 :\r
343                 (_level >= 3 ? kDicSizeX3 :\r
344                                kDicSizeX1));\r
345 \r
346     UInt32 numPasses = _numPasses;\r
347     if (numPasses == 0xFFFFFFFF)\r
348       numPasses = (_level >= 9 ? kNumPassesX9 :\r
349                   (_level >= 7 ? kNumPassesX7 :\r
350                                  kNumPassesX1));\r
351 \r
352     return UpdateArchive(\r
353         size, outStream, 0, dicSize, numPasses,\r
354         #ifndef _7ZIP_ST\r
355         _numThreads,\r
356         #endif\r
357         updateCallback);\r
358   }\r
359   if (indexInArchive != 0)\r
360     return E_INVALIDARG;\r
361   if (_stream)\r
362     RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));\r
363   return NCompress::CopyStream(_stream, outStream, NULL);\r
364 }\r
365 \r
366 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)\r
367 {\r
368   InitMethodProperties();\r
369   #ifndef _7ZIP_ST\r
370   const UInt32 numProcessors = NSystem::GetNumberOfProcessors();\r
371   _numThreads = numProcessors;\r
372   #endif\r
373 \r
374   for (int i = 0; i < numProps; i++)\r
375   {\r
376     UString name = names[i];\r
377     name.MakeUpper();\r
378     if (name.IsEmpty())\r
379       return E_INVALIDARG;\r
380     const PROPVARIANT &prop = values[i];\r
381     if (name[0] == L'X')\r
382     {\r
383       UInt32 level = 9;\r
384       RINOK(ParsePropValue(name.Mid(1), prop, level));\r
385       _level = level;\r
386     }\r
387     else if (name[0] == L'D')\r
388     {\r
389       UInt32 dicSize = kDicSizeX5;\r
390       RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize));\r
391       _dicSize = dicSize;\r
392     }\r
393     else if (name.Left(4) == L"PASS")\r
394     {\r
395       UInt32 num = kNumPassesX9;\r
396       RINOK(ParsePropValue(name.Mid(4), prop, num));\r
397       _numPasses = num;\r
398     }\r
399     else if (name.Left(2) == L"MT")\r
400     {\r
401       #ifndef _7ZIP_ST\r
402       RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads));\r
403       #endif\r
404     }\r
405     else\r
406       return E_INVALIDARG;\r
407   }\r
408   return S_OK;\r
409 }\r
410 \r
411 static IInArchive *CreateArc() { return new CHandler; }\r
412 #ifndef EXTRACT_ONLY\r
413 static IOutArchive *CreateArcOut() { return new CHandler; }\r
414 #else\r
415 #define CreateArcOut 0\r
416 #endif\r
417 \r
418 static CArcInfo g_ArcInfo =\r
419   { L"bzip2", L"bz2 bzip2 tbz2 tbz", L"* * .tar .tar", 2, { 'B', 'Z', 'h' }, 3, true, CreateArc, CreateArcOut };\r
420 \r
421 REGISTER_ARC(BZip2)\r
422 \r
423 }}\r