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