1 // LzmaHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9
10 #include "../../Windows/PropVariant.h"
11
12 #include "../Common/FilterCoder.h"
13 #include "../Common/ProgressUtils.h"
14 #include "../Common/RegisterArc.h"
15 #include "../Common/StreamUtils.h"
16
17 #include "../Compress/BcjCoder.h"
18 #include "../Compress/LzmaDecoder.h"
19
20 #include "Common/DummyOutStream.h"
21
22 using namespace NWindows;
23
24 namespace NArchive {
25 namespace NLzma {
26
CheckDicSize(const Byte * p)27 static bool CheckDicSize(const Byte *p)
28 {
29 UInt32 dicSize = GetUi32(p);
30 if (dicSize == 1)
31 return true;
32 for (unsigned i = 0; i <= 30; i++)
33 if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i))
34 return true;
35 return (dicSize == 0xFFFFFFFF);
36 }
37
38 static const Byte kProps[] =
39 {
40 kpidSize,
41 kpidPackSize,
42 kpidMethod
43 };
44
45 static const Byte kArcProps[] =
46 {
47 kpidNumStreams
48 };
49
50 struct CHeader
51 {
52 UInt64 Size;
53 Byte FilterID;
54 Byte LzmaProps[5];
55
GetDicSizeNArchive::NLzma::CHeader56 UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); }
HasSizeNArchive::NLzma::CHeader57 bool HasSize() const { return (Size != (UInt64)(Int64)-1); }
58 bool Parse(const Byte *buf, bool isThereFilter);
59 };
60
Parse(const Byte * buf,bool isThereFilter)61 bool CHeader::Parse(const Byte *buf, bool isThereFilter)
62 {
63 FilterID = 0;
64 if (isThereFilter)
65 FilterID = buf[0];
66 const Byte *sig = buf + (isThereFilter ? 1 : 0);
67 for (int i = 0; i < 5; i++)
68 LzmaProps[i] = sig[i];
69 Size = GetUi64(sig + 5);
70 return
71 LzmaProps[0] < 5 * 5 * 9 &&
72 FilterID < 2 &&
73 (!HasSize() || Size < ((UInt64)1 << 56))
74 && CheckDicSize(LzmaProps + 1);
75 }
76
77 class CDecoder
78 {
79 CMyComPtr<ISequentialOutStream> _bcjStream;
80 CFilterCoder *_filterCoder;
81 CMyComPtr<ICompressCoder> _lzmaDecoder;
82 public:
83 NCompress::NLzma::CDecoder *_lzmaDecoderSpec;
84
85 ~CDecoder();
86 HRESULT Create(bool filtered, ISequentialInStream *inStream);
87
88 HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress);
89
GetInputProcessedSize() const90 UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); }
91
ReleaseInStream()92 void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); }
93
ReadInput(Byte * data,UInt32 size,UInt32 * processedSize)94 HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize)
95 { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); }
96 };
97
Create(bool filteredMode,ISequentialInStream * inStream)98 HRESULT CDecoder::Create(bool filteredMode, ISequentialInStream *inStream)
99 {
100 if (!_lzmaDecoder)
101 {
102 _lzmaDecoderSpec = new NCompress::NLzma::CDecoder;
103 _lzmaDecoderSpec->FinishStream = true;
104 _lzmaDecoder = _lzmaDecoderSpec;
105 }
106
107 if (filteredMode)
108 {
109 if (!_bcjStream)
110 {
111 _filterCoder = new CFilterCoder(false);
112 CMyComPtr<ICompressCoder> coder = _filterCoder;
113 _filterCoder->Filter = new NCompress::NBcj::CCoder(false);
114 _bcjStream = _filterCoder;
115 }
116 }
117
118 return _lzmaDecoderSpec->SetInStream(inStream);
119 }
120
~CDecoder()121 CDecoder::~CDecoder()
122 {
123 ReleaseInStream();
124 }
125
Code(const CHeader & header,ISequentialOutStream * outStream,ICompressProgressInfo * progress)126 HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream,
127 ICompressProgressInfo *progress)
128 {
129 if (header.FilterID > 1)
130 return E_NOTIMPL;
131
132 {
133 CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
134 _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
135 if (!setDecoderProperties)
136 return E_NOTIMPL;
137 RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5));
138 }
139
140 bool filteredMode = (header.FilterID == 1);
141
142 if (filteredMode)
143 {
144 RINOK(_filterCoder->SetOutStream(outStream));
145 outStream = _bcjStream;
146 RINOK(_filterCoder->SetOutStreamSize(NULL));
147 }
148
149 const UInt64 *Size = header.HasSize() ? &header.Size : NULL;
150 HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress);
151
152 if (filteredMode)
153 {
154 {
155 HRESULT res2 = _filterCoder->OutStreamFinish();
156 if (res == S_OK)
157 res = res2;
158 }
159 HRESULT res2 = _filterCoder->ReleaseOutStream();
160 if (res == S_OK)
161 res = res2;
162 }
163
164 RINOK(res);
165
166 if (header.HasSize())
167 if (_lzmaDecoderSpec->GetOutputProcessedSize() != header.Size)
168 return S_FALSE;
169
170 return S_OK;
171 }
172
173
174 class CHandler:
175 public IInArchive,
176 public IArchiveOpenSeq,
177 public CMyUnknownImp
178 {
179 CHeader _header;
180 bool _lzma86;
181 CMyComPtr<IInStream> _stream;
182 CMyComPtr<ISequentialInStream> _seqStream;
183
184 bool _isArc;
185 bool _needSeekToStart;
186 bool _dataAfterEnd;
187 bool _needMoreInput;
188
189 bool _packSize_Defined;
190 bool _unpackSize_Defined;
191 bool _numStreams_Defined;
192
193 bool _unsupported;
194 bool _dataError;
195
196 UInt64 _packSize;
197 UInt64 _unpackSize;
198 UInt64 _numStreams;
199
200 public:
201 MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq)
202
203 INTERFACE_IInArchive(;)
204 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
205
CHandler(bool lzma86)206 CHandler(bool lzma86) { _lzma86 = lzma86; }
207
GetHeaderSize() const208 unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); }
209
210 };
211
212 IMP_IInArchive_Props
213 IMP_IInArchive_ArcProps
214
GetArchiveProperty(PROPID propID,PROPVARIANT * value)215 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
216 {
217 NCOM::CPropVariant prop;
218 switch (propID)
219 {
220 case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
221 case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break;
222 case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break;
223 case kpidErrorFlags:
224 {
225 UInt32 v = 0;
226 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
227 if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
228 if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
229 if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
230 if (_dataError) v |= kpv_ErrorFlags_DataError;
231 prop = v;
232 }
233 }
234 prop.Detach(value);
235 return S_OK;
236 }
237
GetNumberOfItems(UInt32 * numItems)238 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
239 {
240 *numItems = 1;
241 return S_OK;
242 }
243
DictSizeToString(UInt32 value,char * s)244 static void DictSizeToString(UInt32 value, char *s)
245 {
246 for (int i = 0; i <= 31; i++)
247 if (((UInt32)1 << i) == value)
248 {
249 ::ConvertUInt32ToString(i, s);
250 return;
251 }
252 char c = 'b';
253 if ((value & ((1 << 20) - 1)) == 0) { value >>= 20; c = 'm'; }
254 else if ((value & ((1 << 10) - 1)) == 0) { value >>= 10; c = 'k'; }
255 ::ConvertUInt32ToString(value, s);
256 s += MyStringLen(s);
257 *s++ = c;
258 *s = 0;
259 }
260
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)261 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
262 {
263 NCOM::CPropVariant prop;
264 switch (propID)
265 {
266 case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break;
267 case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
268 case kpidMethod:
269 if (_stream)
270 {
271 char sz[64];
272 char *s = sz;
273 if (_header.FilterID != 0)
274 s = MyStpCpy(s, "BCJ ");
275 s = MyStpCpy(s, "LZMA:");
276 DictSizeToString(_header.GetDicSize(), s);
277 prop = sz;
278 }
279 break;
280 }
281 prop.Detach(value);
282 return S_OK;
283 }
284
IsArc_Lzma(const Byte * p,size_t size)285 API_FUNC_static_IsArc IsArc_Lzma(const Byte *p, size_t size)
286 {
287 const UInt32 kHeaderSize = 1 + 4 + 8;
288 if (size < kHeaderSize)
289 return k_IsArc_Res_NEED_MORE;
290 if (p[0] >= 5 * 5 * 9)
291 return k_IsArc_Res_NO;
292 UInt64 unpackSize = GetUi64(p + 1 + 4);
293 if (unpackSize != (UInt64)(Int64)-1)
294 {
295 if (size >= ((UInt64)1 << 56))
296 return k_IsArc_Res_NO;
297 }
298 if (unpackSize != 0)
299 {
300 if (size < kHeaderSize + 2)
301 return k_IsArc_Res_NEED_MORE;
302 if (p[kHeaderSize] != 0)
303 return k_IsArc_Res_NO;
304 if (unpackSize != (UInt64)(Int64)-1)
305 {
306 if ((p[kHeaderSize + 1] & 0x80) != 0)
307 return k_IsArc_Res_NO;
308 }
309 }
310 if (!CheckDicSize(p + 1))
311 // return k_IsArc_Res_YES_LOW_PROB;
312 return k_IsArc_Res_NO;
313 return k_IsArc_Res_YES;
314 }
315 }
316
IsArc_Lzma86(const Byte * p,size_t size)317 API_FUNC_static_IsArc IsArc_Lzma86(const Byte *p, size_t size)
318 {
319 if (size < 1)
320 return k_IsArc_Res_NEED_MORE;
321 Byte filterID = p[0];
322 if (filterID != 0 && filterID != 1)
323 return k_IsArc_Res_NO;
324 return IsArc_Lzma(p + 1, size - 1);
325 }
326 }
327
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback *)328 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *)
329 {
330 Close();
331
332 const UInt32 kBufSize = 1 + 5 + 8 + 2;
333 Byte buf[kBufSize];
334
335 RINOK(ReadStream_FALSE(inStream, buf, kBufSize));
336
337 if (!_header.Parse(buf, _lzma86))
338 return S_FALSE;
339 const Byte *start = buf + GetHeaderSize();
340 if (start[0] != 0 /* || (start[1] & 0x80) != 0 */ ) // empty stream with EOS is not 0x80
341 return S_FALSE;
342
343 RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize));
344 if (_packSize >= 24 && _header.Size == 0 && _header.FilterID == 0 && _header.LzmaProps[0] == 0)
345 return S_FALSE;
346 _isArc = true;
347 _stream = inStream;
348 _seqStream = inStream;
349 _needSeekToStart = true;
350 return S_OK;
351 }
352
OpenSeq(ISequentialInStream * stream)353 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
354 {
355 Close();
356 _isArc = true;
357 _seqStream = stream;
358 return S_OK;
359 }
360
Close()361 STDMETHODIMP CHandler::Close()
362 {
363 _isArc = false;
364 _packSize_Defined = false;
365 _unpackSize_Defined = false;
366 _numStreams_Defined = false;
367
368 _dataAfterEnd = false;
369 _needMoreInput = false;
370 _unsupported = false;
371 _dataError = false;
372
373 _packSize = 0;
374
375 _needSeekToStart = false;
376
377 _stream.Release();
378 _seqStream.Release();
379 return S_OK;
380 }
381
382 class CCompressProgressInfoImp:
383 public ICompressProgressInfo,
384 public CMyUnknownImp
385 {
386 CMyComPtr<IArchiveOpenCallback> Callback;
387 public:
388 UInt64 Offset;
389
390 MY_UNKNOWN_IMP1(ICompressProgressInfo)
391 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
Init(IArchiveOpenCallback * callback)392 void Init(IArchiveOpenCallback *callback) { Callback = callback; }
393 };
394
SetRatioInfo(const UInt64 * inSize,const UInt64 *)395 STDMETHODIMP CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */)
396 {
397 if (Callback)
398 {
399 UInt64 files = 0;
400 UInt64 value = Offset + *inSize;
401 return Callback->SetCompleted(&files, &value);
402 }
403 return S_OK;
404 }
405
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)406 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
407 Int32 testMode, IArchiveExtractCallback *extractCallback)
408 {
409 COM_TRY_BEGIN
410
411 if (numItems == 0)
412 return S_OK;
413 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
414 return E_INVALIDARG;
415
416 if (_packSize_Defined)
417 extractCallback->SetTotal(_packSize);
418
419
420 CMyComPtr<ISequentialOutStream> realOutStream;
421 Int32 askMode = testMode ?
422 NExtract::NAskMode::kTest :
423 NExtract::NAskMode::kExtract;
424 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
425 if (!testMode && !realOutStream)
426 return S_OK;
427
428 extractCallback->PrepareOperation(askMode);
429
430 CDummyOutStream *outStreamSpec = new CDummyOutStream;
431 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
432 outStreamSpec->SetStream(realOutStream);
433 outStreamSpec->Init();
434 realOutStream.Release();
435
436 CLocalProgress *lps = new CLocalProgress;
437 CMyComPtr<ICompressProgressInfo> progress = lps;
438 lps->Init(extractCallback, true);
439
440 if (_needSeekToStart)
441 {
442 if (!_stream)
443 return E_FAIL;
444 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
445 }
446 else
447 _needSeekToStart = true;
448
449 CDecoder decoder;
450 HRESULT result = decoder.Create(_lzma86, _seqStream);
451 RINOK(result);
452
453 bool firstItem = true;
454
455 UInt64 packSize = 0;
456 UInt64 unpackSize = 0;
457 UInt64 numStreams = 0;
458
459 bool dataAfterEnd = false;
460
461 for (;;)
462 {
463 lps->InSize = packSize;
464 lps->OutSize = unpackSize;
465 RINOK(lps->SetCur());
466
467 const UInt32 kBufSize = 1 + 5 + 8;
468 Byte buf[kBufSize];
469 const UInt32 headerSize = GetHeaderSize();
470 UInt32 processed;
471 RINOK(decoder.ReadInput(buf, headerSize, &processed));
472 if (processed != headerSize)
473 {
474 if (processed != 0)
475 dataAfterEnd = true;
476 break;
477 }
478
479 CHeader st;
480 if (!st.Parse(buf, _lzma86))
481 {
482 dataAfterEnd = true;
483 break;
484 }
485 numStreams++;
486 firstItem = false;
487
488 result = decoder.Code(st, outStream, progress);
489
490 packSize = decoder.GetInputProcessedSize();
491 unpackSize = outStreamSpec->GetSize();
492
493 if (result == E_NOTIMPL)
494 {
495 _unsupported = true;
496 result = S_FALSE;
497 break;
498 }
499 if (result == S_FALSE)
500 break;
501 RINOK(result);
502 }
503
504 if (firstItem)
505 {
506 _isArc = false;
507 result = S_FALSE;
508 }
509 else if (result == S_OK || result == S_FALSE)
510 {
511 if (dataAfterEnd)
512 _dataAfterEnd = true;
513 else if (decoder._lzmaDecoderSpec->NeedMoreInput)
514 _needMoreInput = true;
515
516 _packSize = packSize;
517 _unpackSize = unpackSize;
518 _numStreams = numStreams;
519
520 _packSize_Defined = true;
521 _unpackSize_Defined = true;
522 _numStreams_Defined = true;
523 }
524
525 Int32 opResult = NExtract::NOperationResult::kOK;
526
527 if (!_isArc)
528 opResult = NExtract::NOperationResult::kIsNotArc;
529 else if (_needMoreInput)
530 opResult = NExtract::NOperationResult::kUnexpectedEnd;
531 else if (_unsupported)
532 opResult = NExtract::NOperationResult::kUnsupportedMethod;
533 else if (_dataAfterEnd)
534 opResult = NExtract::NOperationResult::kDataAfterEnd;
535 else if (result == S_FALSE)
536 opResult = NExtract::NOperationResult::kDataError;
537 else if (result == S_OK)
538 opResult = NExtract::NOperationResult::kOK;
539 else
540 return result;
541
542 outStream.Release();
543 return extractCallback->SetOperationResult(opResult);
544
545 COM_TRY_END
546 }
547
548 namespace NLzmaAr {
549
550 // 2, { 0x5D, 0x00 },
551
552 REGISTER_ARC_I_CLS_NO_SIG(
553 CHandler(false),
554 "lzma", "lzma", 0, 0xA,
555 0,
556 NArcInfoFlags::kStartOpen |
557 NArcInfoFlags::kKeepName,
558 IsArc_Lzma)
559
560 }
561
562 namespace NLzma86Ar {
563
564 REGISTER_ARC_I_CLS_NO_SIG(
565 CHandler(true),
566 "lzma86", "lzma86", 0, 0xB,
567 0,
568 NArcInfoFlags::kKeepName,
569 IsArc_Lzma86)
570
571 }
572
573 }}
574