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