5 #include "Common/ComTry.h"
\r
7 #include "Windows/PropVariant.h"
\r
10 #include "../../Windows/System.h"
\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
18 #include "../Compress/BZip2Decoder.h"
\r
19 #include "../Compress/BZip2Encoder.h"
\r
20 #include "../Compress/CopyCoder.h"
\r
22 #include "Common/DummyOutStream.h"
\r
23 #include "Common/ParseProperties.h"
\r
25 using namespace NWindows;
\r
27 namespace NArchive {
\r
30 static const UInt32 kNumPassesX1 = 1;
\r
31 static const UInt32 kNumPassesX7 = 2;
\r
32 static const UInt32 kNumPassesX9 = 7;
\r
34 static const UInt32 kDicSizeX1 = 100000;
\r
35 static const UInt32 kDicSizeX3 = 500000;
\r
36 static const UInt32 kDicSizeX5 = 900000;
\r
40 public IArchiveOpenSeq,
\r
42 public ISetProperties,
\r
43 public CMyUnknownImp
\r
45 CMyComPtr<IInStream> _stream;
\r
46 CMyComPtr<ISequentialInStream> _seqStream;
\r
48 UInt64 _startPosition;
\r
49 bool _packSizeDefined;
\r
58 void InitMethodProperties()
\r
62 _numPasses = 0xFFFFFFFF;
\r
64 _numThreads = NWindows::NSystem::GetNumberOfProcessors();;
\r
69 MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties)
\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
76 CHandler() { InitMethodProperties(); }
\r
79 STATPROPSTG kProps[] =
\r
81 { NULL, kpidPackSize, VT_UI8}
\r
84 IMP_IInArchive_Props
\r
85 IMP_IInArchive_ArcProps_NO_Table
\r
87 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
\r
89 NCOM::CPropVariant prop;
\r
92 case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
\r
98 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
104 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
\r
106 NWindows::NCOM::CPropVariant prop;
\r
109 case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
\r
111 prop.Detach(value);
\r
115 STDMETHODIMP CHandler::Open(IInStream *stream,
\r
116 const UInt64 * /* maxCheckStartPosition */,
\r
117 IArchiveOpenCallback * /* openArchiveCallback */)
\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
130 UInt64 endPosition;
\r
131 RINOK(stream->Seek(0, STREAM_SEEK_END, &endPosition));
\r
132 _packSize = endPosition - _startPosition;
\r
133 _packSizeDefined = true;
\r
135 _seqStream = stream;
\r
137 catch(...) { return S_FALSE; }
\r
142 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
\r
145 _seqStream = stream;
\r
149 STDMETHODIMP CHandler::Close()
\r
151 _packSizeDefined = false;
\r
152 _seqStream.Release();
\r
157 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
158 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
163 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
\r
164 return E_INVALIDARG;
\r
167 extractCallback->SetTotal(_packSize);
\r
168 UInt64 currentTotalPacked = 0;
\r
169 RINOK(extractCallback->SetCompleted(¤tTotalPacked));
\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
178 extractCallback->PrepareOperation(askMode);
\r
180 NCompress::NBZip2::CDecoder *decoderSpec = new NCompress::NBZip2::CDecoder;
\r
181 CMyComPtr<ICompressCoder> decoder = decoderSpec;
\r
185 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
\r
188 decoderSpec->SetInStream(_seqStream);
\r
191 RINOK(decoderSpec->SetNumberOfThreads(_numThreads));
\r
194 CDummyOutStream *outStreamSpec = new CDummyOutStream;
\r
195 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
\r
196 outStreamSpec->SetStream(realOutStream);
\r
197 outStreamSpec->Init();
\r
199 realOutStream.Release();
\r
201 CLocalProgress *lps = new CLocalProgress;
\r
202 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
203 lps->Init(extractCallback, true);
\r
205 HRESULT result = S_OK;
\r
207 bool firstItem = true;
\r
210 lps->InSize = currentTotalPacked;
\r
211 lps->OutSize = outStreamSpec->GetSize();
\r
213 RINOK(lps->SetCur());
\r
216 result = decoderSpec->CodeResume(outStream, isBz2, progress);
\r
218 if (result != S_OK)
\r
228 _packSize = currentTotalPacked = decoderSpec->GetInputProcessedSize();
\r
229 _packSizeDefined = true;
\r
231 decoderSpec->ReleaseInStream();
\r
232 outStream.Release();
\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
241 return extractCallback->SetOperationResult(retResult);
\r
246 static HRESULT UpdateArchive(
\r
248 ISequentialOutStream *outStream,
\r
255 IArchiveUpdateCallback *updateCallback)
\r
257 RINOK(updateCallback->SetTotal(unpackSize));
\r
258 UInt64 complexity = 0;
\r
259 RINOK(updateCallback->SetCompleted(&complexity));
\r
261 CMyComPtr<ISequentialInStream> fileInStream;
\r
263 RINOK(updateCallback->GetStream(indexInClient, &fileInStream));
\r
265 CLocalProgress *localProgressSpec = new CLocalProgress;
\r
266 CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec;
\r
267 localProgressSpec->Init(updateCallback, true);
\r
269 NCompress::NBZip2::CEncoder *encoderSpec = new NCompress::NBZip2::CEncoder;
\r
270 CMyComPtr<ICompressCoder> encoder = encoderSpec;
\r
272 NWindows::NCOM::CPropVariant properties[] =
\r
282 NCoderPropID::kDictionarySize,
\r
283 NCoderPropID::kNumPasses
\r
285 , NCoderPropID::kNumThreads
\r
288 RINOK(encoderSpec->SetCoderProperties(propIDs, properties, sizeof(propIDs) / sizeof(propIDs[0])));
\r
291 RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress));
\r
293 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
\r
296 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
\r
298 *type = NFileTimeType::kUnix;
\r
302 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
\r
303 IArchiveUpdateCallback *updateCallback)
\r
306 return E_INVALIDARG;
\r
308 Int32 newData, newProps;
\r
309 UInt32 indexInArchive;
\r
310 if (!updateCallback)
\r
312 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
\r
314 if (IntToBool(newProps))
\r
317 NCOM::CPropVariant prop;
\r
318 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
\r
319 if (prop.vt == VT_BOOL)
\r
321 if (prop.boolVal != VARIANT_FALSE)
\r
322 return E_INVALIDARG;
\r
324 else if (prop.vt != VT_EMPTY)
\r
325 return E_INVALIDARG;
\r
329 if (IntToBool(newData))
\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
340 UInt32 dicSize = _dicSize;
\r
341 if (dicSize == 0xFFFFFFFF)
\r
342 dicSize = (_level >= 5 ? kDicSizeX5 :
\r
343 (_level >= 3 ? kDicSizeX3 :
\r
346 UInt32 numPasses = _numPasses;
\r
347 if (numPasses == 0xFFFFFFFF)
\r
348 numPasses = (_level >= 9 ? kNumPassesX9 :
\r
349 (_level >= 7 ? kNumPassesX7 :
\r
352 return UpdateArchive(
\r
353 size, outStream, 0, dicSize, numPasses,
\r
359 if (indexInArchive != 0)
\r
360 return E_INVALIDARG;
\r
362 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
\r
363 return NCompress::CopyStream(_stream, outStream, NULL);
\r
366 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
\r
368 InitMethodProperties();
\r
370 const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
\r
371 _numThreads = numProcessors;
\r
374 for (int i = 0; i < numProps; i++)
\r
376 UString name = names[i];
\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
384 RINOK(ParsePropValue(name.Mid(1), prop, level));
\r
387 else if (name[0] == L'D')
\r
389 UInt32 dicSize = kDicSizeX5;
\r
390 RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize));
\r
391 _dicSize = dicSize;
\r
393 else if (name.Left(4) == L"PASS")
\r
395 UInt32 num = kNumPassesX9;
\r
396 RINOK(ParsePropValue(name.Mid(4), prop, num));
\r
399 else if (name.Left(2) == L"MT")
\r
402 RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads));
\r
406 return E_INVALIDARG;
\r
411 static IInArchive *CreateArc() { return new CHandler; }
\r
412 #ifndef EXTRACT_ONLY
\r
413 static IOutArchive *CreateArcOut() { return new CHandler; }
\r
415 #define CreateArcOut 0
\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
421 REGISTER_ARC(BZip2)
\r