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