1 // Bz2Handler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../Common/ComTry.h"
6
7 #include "../Common/ProgressUtils.h"
8 #include "../Common/RegisterArc.h"
9 #include "../Common/StreamUtils.h"
10
11 #include "../Compress/BZip2Decoder.h"
12 #include "../Compress/BZip2Encoder.h"
13 #include "../Compress/CopyCoder.h"
14
15 #include "Common/DummyOutStream.h"
16 #include "Common/HandlerOut.h"
17
18 using namespace NWindows;
19
20 namespace NArchive {
21 namespace NBz2 {
22
23 class CHandler:
24 public IInArchive,
25 public IArchiveOpenSeq,
26 public IOutArchive,
27 public ISetProperties,
28 public CMyUnknownImp
29 {
30 CMyComPtr<IInStream> _stream;
31 CMyComPtr<ISequentialInStream> _seqStream;
32
33 bool _isArc;
34 bool _needSeekToStart;
35 bool _dataAfterEnd;
36 bool _needMoreInput;
37
38 bool _packSize_Defined;
39 bool _unpackSize_Defined;
40 bool _numStreams_Defined;
41 bool _numBlocks_Defined;
42
43 UInt64 _packSize;
44 UInt64 _unpackSize;
45 UInt64 _numStreams;
46 UInt64 _numBlocks;
47
48 CSingleMethodProps _props;
49
50 public:
51 MY_UNKNOWN_IMP4(
52 IInArchive,
53 IArchiveOpenSeq,
54 IOutArchive,
55 ISetProperties)
56 INTERFACE_IInArchive(;)
57 INTERFACE_IOutArchive(;)
58 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
59 STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
60
CHandler()61 CHandler() { }
62 };
63
64 static const Byte kProps[] =
65 {
66 kpidSize,
67 kpidPackSize
68 };
69
70 static const Byte kArcProps[] =
71 {
72 kpidNumStreams,
73 kpidNumBlocks
74 };
75
76 IMP_IInArchive_Props
77 IMP_IInArchive_ArcProps
78
GetArchiveProperty(PROPID propID,PROPVARIANT * value)79 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
80 {
81 NCOM::CPropVariant prop;
82 switch (propID)
83 {
84 case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
85 case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break;
86 case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break;
87 case kpidNumBlocks: if (_numBlocks_Defined) prop = _numBlocks; break;
88 case kpidErrorFlags:
89 {
90 UInt32 v = 0;
91 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
92 if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
93 if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
94 prop = v;
95 }
96 }
97 prop.Detach(value);
98 return S_OK;
99 }
100
GetNumberOfItems(UInt32 * numItems)101 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
102 {
103 *numItems = 1;
104 return S_OK;
105 }
106
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)107 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
108 {
109 NCOM::CPropVariant prop;
110 switch (propID)
111 {
112 case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
113 case kpidSize: if (_unpackSize_Defined) prop = _unpackSize; break;
114 }
115 prop.Detach(value);
116 return S_OK;
117 }
118
119 static const unsigned kSignatureCheckSize = 10;
120
IsArc_BZip2(const Byte * p,size_t size)121 API_FUNC_static_IsArc IsArc_BZip2(const Byte *p, size_t size)
122 {
123 if (size < kSignatureCheckSize)
124 return k_IsArc_Res_NEED_MORE;
125 if (p[0] != 'B' || p[1] != 'Z' || p[2] != 'h' || p[3] < '1' || p[3] > '9')
126 return k_IsArc_Res_NO;
127 p += 4;
128 if (NCompress::NBZip2::IsBlockSig(p))
129 return k_IsArc_Res_YES;
130 if (NCompress::NBZip2::IsEndSig(p))
131 return k_IsArc_Res_YES;
132 return k_IsArc_Res_NO;
133 }
134 }
135
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)136 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)
137 {
138 COM_TRY_BEGIN
139 Close();
140 {
141 Byte buf[kSignatureCheckSize];
142 RINOK(ReadStream_FALSE(stream, buf, kSignatureCheckSize));
143 if (IsArc_BZip2(buf, kSignatureCheckSize) == k_IsArc_Res_NO)
144 return S_FALSE;
145 _isArc = true;
146 _stream = stream;
147 _seqStream = stream;
148 _needSeekToStart = true;
149 }
150 return S_OK;
151 COM_TRY_END
152 }
153
154
OpenSeq(ISequentialInStream * stream)155 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
156 {
157 Close();
158 _isArc = true;
159 _seqStream = stream;
160 return S_OK;
161 }
162
Close()163 STDMETHODIMP CHandler::Close()
164 {
165 _isArc = false;
166 _needSeekToStart = false;
167 _dataAfterEnd = false;
168 _needMoreInput = false;
169
170 _packSize_Defined = false;
171 _unpackSize_Defined = false;
172 _numStreams_Defined = false;
173 _numBlocks_Defined = false;
174
175 _packSize = 0;
176
177 _seqStream.Release();
178 _stream.Release();
179 return S_OK;
180 }
181
182
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)183 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
184 Int32 testMode, IArchiveExtractCallback *extractCallback)
185 {
186 COM_TRY_BEGIN
187 if (numItems == 0)
188 return S_OK;
189 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
190 return E_INVALIDARG;
191
192 if (_packSize_Defined)
193 extractCallback->SetTotal(_packSize);
194
195 CMyComPtr<ISequentialOutStream> realOutStream;
196 Int32 askMode = testMode ?
197 NExtract::NAskMode::kTest :
198 NExtract::NAskMode::kExtract;
199 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
200 if (!testMode && !realOutStream)
201 return S_OK;
202
203 extractCallback->PrepareOperation(askMode);
204
205 if (_needSeekToStart)
206 {
207 if (!_stream)
208 return E_FAIL;
209 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
210 }
211 else
212 _needSeekToStart = true;
213
214 // try {
215
216 NCompress::NBZip2::CDecoder *decoderSpec = new NCompress::NBZip2::CDecoder;
217 CMyComPtr<ICompressCoder> decoder = decoderSpec;
218
219 #ifndef _7ZIP_ST
220 RINOK(decoderSpec->SetNumberOfThreads(_props._numThreads));
221 #endif
222
223 CDummyOutStream *outStreamSpec = new CDummyOutStream;
224 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
225 outStreamSpec->SetStream(realOutStream);
226 outStreamSpec->Init();
227
228 realOutStream.Release();
229
230 CLocalProgress *lps = new CLocalProgress;
231 CMyComPtr<ICompressProgressInfo> progress = lps;
232 lps->Init(extractCallback, true);
233
234 decoderSpec->FinishMode = true;
235 decoderSpec->Base.DecodeAllStreams = true;
236
237 _dataAfterEnd = false;
238 _needMoreInput = false;
239
240 lps->InSize = 0;
241 lps->OutSize = 0;
242
243 HRESULT result = decoderSpec->Code(_seqStream, outStream, NULL, NULL, progress);
244
245 if (result != S_FALSE && result != S_OK)
246 return result;
247
248 if (decoderSpec->Base.NumStreams == 0)
249 {
250 _isArc = false;
251 result = S_FALSE;
252 }
253 else
254 {
255 const UInt64 inProcessedSize = decoderSpec->GetInputProcessedSize();
256 UInt64 packSize = inProcessedSize;
257
258 if (decoderSpec->Base.NeedMoreInput)
259 _needMoreInput = true;
260
261 if (!decoderSpec->Base.IsBz)
262 {
263 packSize = decoderSpec->Base.FinishedPackSize;
264 if (packSize != inProcessedSize)
265 _dataAfterEnd = true;
266 }
267
268 _packSize = packSize;
269 _unpackSize = decoderSpec->GetOutProcessedSize();
270 _numStreams = decoderSpec->Base.NumStreams;
271 _numBlocks = decoderSpec->GetNumBlocks();
272
273 _packSize_Defined = true;
274 _unpackSize_Defined = true;
275 _numStreams_Defined = true;
276 _numBlocks_Defined = true;
277 }
278
279 outStream.Release();
280
281 Int32 opRes;
282
283 if (!_isArc)
284 opRes = NExtract::NOperationResult::kIsNotArc;
285 else if (_needMoreInput)
286 opRes = NExtract::NOperationResult::kUnexpectedEnd;
287 else if (decoderSpec->GetCrcError())
288 opRes = NExtract::NOperationResult::kCRCError;
289 else if (_dataAfterEnd)
290 opRes = NExtract::NOperationResult::kDataAfterEnd;
291 else if (result == S_FALSE)
292 opRes = NExtract::NOperationResult::kDataError;
293 else if (decoderSpec->Base.MinorError)
294 opRes = NExtract::NOperationResult::kDataError;
295 else if (result == S_OK)
296 opRes = NExtract::NOperationResult::kOK;
297 else
298 return result;
299
300 return extractCallback->SetOperationResult(opRes);
301
302 // } catch(...) { return E_FAIL; }
303
304 COM_TRY_END
305 }
306
307
308
UpdateArchive(UInt64 unpackSize,ISequentialOutStream * outStream,const CProps & props,IArchiveUpdateCallback * updateCallback)309 static HRESULT UpdateArchive(
310 UInt64 unpackSize,
311 ISequentialOutStream *outStream,
312 const CProps &props,
313 IArchiveUpdateCallback *updateCallback)
314 {
315 RINOK(updateCallback->SetTotal(unpackSize));
316 CMyComPtr<ISequentialInStream> fileInStream;
317 RINOK(updateCallback->GetStream(0, &fileInStream));
318 CLocalProgress *localProgressSpec = new CLocalProgress;
319 CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec;
320 localProgressSpec->Init(updateCallback, true);
321 NCompress::NBZip2::CEncoder *encoderSpec = new NCompress::NBZip2::CEncoder;
322 CMyComPtr<ICompressCoder> encoder = encoderSpec;
323 RINOK(props.SetCoderProps(encoderSpec, NULL));
324 RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress));
325 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
326 }
327
GetFileTimeType(UInt32 * type)328 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
329 {
330 *type = NFileTimeType::kUnix;
331 return S_OK;
332 }
333
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)334 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
335 IArchiveUpdateCallback *updateCallback)
336 {
337 COM_TRY_BEGIN
338
339 if (numItems != 1)
340 return E_INVALIDARG;
341
342 Int32 newData, newProps;
343 UInt32 indexInArchive;
344 if (!updateCallback)
345 return E_FAIL;
346 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
347
348 if (IntToBool(newProps))
349 {
350 {
351 NCOM::CPropVariant prop;
352 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
353 if (prop.vt != VT_EMPTY)
354 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
355 return E_INVALIDARG;
356 }
357 }
358
359 if (IntToBool(newData))
360 {
361 UInt64 size;
362 {
363 NCOM::CPropVariant prop;
364 RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
365 if (prop.vt != VT_UI8)
366 return E_INVALIDARG;
367 size = prop.uhVal.QuadPart;
368 }
369
370 CMethodProps props2 = _props;
371 #ifndef _7ZIP_ST
372 props2.AddProp_NumThreads(_props._numThreads);
373 #endif
374
375 return UpdateArchive(size, outStream, props2, updateCallback);
376 }
377
378 if (indexInArchive != 0)
379 return E_INVALIDARG;
380
381 CLocalProgress *lps = new CLocalProgress;
382 CMyComPtr<ICompressProgressInfo> progress = lps;
383 lps->Init(updateCallback, true);
384
385 CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
386 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
387 if (opCallback)
388 {
389 RINOK(opCallback->ReportOperation(
390 NEventIndexType::kInArcIndex, 0,
391 NUpdateNotifyOp::kReplicate))
392 }
393
394 if (_stream)
395 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
396
397 return NCompress::CopyStream(_stream, outStream, progress);
398
399 COM_TRY_END
400 }
401
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)402 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
403 {
404 return _props.SetProperties(names, values, numProps);
405 }
406
407 static const Byte k_Signature[] = { 'B', 'Z', 'h' };
408
409 REGISTER_ARC_IO(
410 "bzip2", "bz2 bzip2 tbz2 tbz", "* * .tar .tar", 2,
411 k_Signature,
412 0,
413 NArcInfoFlags::kKeepName,
414 IsArc_BZip2)
415
416 }}
417