1 // XzHandler.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/Alloc.h" 6 #include "../../../C/XzCrc64.h" 7 #include "../../../C/XzEnc.h" 8 9 #include "../../Common/ComTry.h" 10 #include "../../Common/Defs.h" 11 #include "../../Common/IntToString.h" 12 13 #include "../../Windows/PropVariant.h" 14 15 #include "../ICoder.h" 16 17 #include "../Common/CWrappers.h" 18 #include "../Common/ProgressUtils.h" 19 #include "../Common/RegisterArc.h" 20 #include "../Common/StreamUtils.h" 21 22 #include "../Compress/CopyCoder.h" 23 24 #include "IArchive.h" 25 26 #ifndef EXTRACT_ONLY 27 #include "Common/HandlerOut.h" 28 #endif 29 30 #include "XzHandler.h" 31 32 using namespace NWindows; 33 34 namespace NCompress { 35 namespace NLzma2 { 36 37 HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props); 38 39 }} 40 41 namespace NArchive { 42 namespace NXz { 43 44 struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit; 45 46 static const char *k_LZMA2_Name = "LZMA2"; 47 48 void CStatInfo::Clear() 49 { 50 InSize = 0; 51 OutSize = 0; 52 PhySize = 0; 53 54 NumStreams = 0; 55 NumBlocks = 0; 56 57 UnpackSize_Defined = false; 58 59 NumStreams_Defined = false; 60 NumBlocks_Defined = false; 61 62 IsArc = false; 63 UnexpectedEnd = false; 64 DataAfterEnd = false; 65 Unsupported = false; 66 HeadersError = false; 67 DataError = false; 68 CrcError = false; 69 } 70 71 class CHandler: 72 public IInArchive, 73 public IArchiveOpenSeq, 74 #ifndef EXTRACT_ONLY 75 public IOutArchive, 76 public ISetProperties, 77 public CMultiMethodProps, 78 #endif 79 public CMyUnknownImp 80 { 81 CStatInfo _stat; 82 83 bool _isArc; 84 bool _needSeekToStart; 85 bool _phySize_Defined; 86 87 CMyComPtr<IInStream> _stream; 88 CMyComPtr<ISequentialInStream> _seqStream; 89 90 AString _methodsString; 91 92 #ifndef EXTRACT_ONLY 93 94 UInt32 _filterId; 95 96 void Init() 97 { 98 _filterId = 0; 99 CMultiMethodProps::Init(); 100 } 101 102 #endif 103 104 HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); 105 106 HRESULT Decode2(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, 107 CDecoder &decoder, ICompressProgressInfo *progress) 108 { 109 RINOK(decoder.Decode(seqInStream, outStream, progress)); 110 _stat = decoder; 111 _phySize_Defined = true; 112 return S_OK; 113 } 114 115 public: 116 MY_QUERYINTERFACE_BEGIN2(IInArchive) 117 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) 118 #ifndef EXTRACT_ONLY 119 MY_QUERYINTERFACE_ENTRY(IOutArchive) 120 MY_QUERYINTERFACE_ENTRY(ISetProperties) 121 #endif 122 MY_QUERYINTERFACE_END 123 MY_ADDREF_RELEASE 124 125 INTERFACE_IInArchive(;) 126 STDMETHOD(OpenSeq)(ISequentialInStream *stream); 127 128 #ifndef EXTRACT_ONLY 129 INTERFACE_IOutArchive(;) 130 STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps); 131 #endif 132 133 CHandler(); 134 }; 135 136 CHandler::CHandler() 137 { 138 #ifndef EXTRACT_ONLY 139 Init(); 140 #endif 141 } 142 143 144 static const Byte kProps[] = 145 { 146 kpidSize, 147 kpidPackSize, 148 kpidMethod 149 }; 150 151 static const Byte kArcProps[] = 152 { 153 kpidMethod, 154 kpidNumStreams, 155 kpidNumBlocks 156 }; 157 158 IMP_IInArchive_Props 159 IMP_IInArchive_ArcProps 160 161 static inline char GetHex(unsigned value) 162 { 163 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); 164 } 165 166 static inline void AddHexToString(AString &s, Byte value) 167 { 168 s += GetHex(value >> 4); 169 s += GetHex(value & 0xF); 170 } 171 172 static void AddUInt32ToString(AString &s, UInt32 value) 173 { 174 char temp[16]; 175 ConvertUInt32ToString(value, temp); 176 s += temp; 177 } 178 179 static void Lzma2PropToString(AString &s, unsigned prop) 180 { 181 char c = 0; 182 UInt32 size; 183 if ((prop & 1) == 0) 184 size = prop / 2 + 12; 185 else 186 { 187 c = 'k'; 188 size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1); 189 if (prop > 17) 190 { 191 size >>= 10; 192 c = 'm'; 193 } 194 } 195 AddUInt32ToString(s, size); 196 if (c != 0) 197 s += c; 198 } 199 200 struct CMethodNamePair 201 { 202 UInt32 Id; 203 const char *Name; 204 }; 205 206 static const CMethodNamePair g_NamePairs[] = 207 { 208 { XZ_ID_Subblock, "SB" }, 209 { XZ_ID_Delta, "Delta" }, 210 { XZ_ID_X86, "BCJ" }, 211 { XZ_ID_PPC, "PPC" }, 212 { XZ_ID_IA64, "IA64" }, 213 { XZ_ID_ARM, "ARM" }, 214 { XZ_ID_ARMT, "ARMT" }, 215 { XZ_ID_SPARC, "SPARC" }, 216 { XZ_ID_LZMA2, "LZMA2" } 217 }; 218 219 static AString GetMethodString(const CXzFilter &f) 220 { 221 const char *p = NULL; 222 for (unsigned i = 0; i < ARRAY_SIZE(g_NamePairs); i++) 223 if (g_NamePairs[i].Id == f.id) 224 { 225 p = g_NamePairs[i].Name; 226 break; 227 } 228 char temp[32]; 229 if (!p) 230 { 231 ::ConvertUInt64ToString(f.id, temp); 232 p = temp; 233 } 234 235 AString s = p; 236 237 if (f.propsSize > 0) 238 { 239 s += ':'; 240 if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) 241 Lzma2PropToString(s, f.props[0]); 242 else if (f.id == XZ_ID_Delta && f.propsSize == 1) 243 AddUInt32ToString(s, (UInt32)f.props[0] + 1); 244 else 245 { 246 s += '['; 247 for (UInt32 bi = 0; bi < f.propsSize; bi++) 248 AddHexToString(s, f.props[bi]); 249 s += ']'; 250 } 251 } 252 return s; 253 } 254 255 static void AddString(AString &dest, const AString &src) 256 { 257 dest.Add_Space_if_NotEmpty(); 258 dest += src; 259 } 260 261 static const char * const kChecks[] = 262 { 263 "NoCheck" 264 , "CRC32" 265 , NULL 266 , NULL 267 , "CRC64" 268 , NULL 269 , NULL 270 , NULL 271 , NULL 272 , NULL 273 , "SHA256" 274 , NULL 275 , NULL 276 , NULL 277 , NULL 278 , NULL 279 }; 280 281 static AString GetCheckString(const CXzs &xzs) 282 { 283 size_t i; 284 UInt32 mask = 0; 285 for (i = 0; i < xzs.num; i++) 286 mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); 287 AString s; 288 for (i = 0; i <= XZ_CHECK_MASK; i++) 289 if (((mask >> i) & 1) != 0) 290 { 291 AString s2; 292 if (kChecks[i]) 293 s2 = kChecks[i]; 294 else 295 { 296 s2 = "Check-"; 297 AddUInt32ToString(s2, (UInt32)i); 298 } 299 AddString(s, s2); 300 } 301 return s; 302 } 303 304 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) 305 { 306 COM_TRY_BEGIN 307 NCOM::CPropVariant prop; 308 switch (propID) 309 { 310 case kpidPhySize: if (_phySize_Defined) prop = _stat.PhySize; break; 311 case kpidNumStreams: if (_stat.NumStreams_Defined) prop = _stat.NumStreams; break; 312 case kpidNumBlocks: if (_stat.NumBlocks_Defined) prop = _stat.NumBlocks; break; 313 case kpidUnpackSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; 314 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; 315 case kpidErrorFlags: 316 { 317 UInt32 v = 0; 318 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; 319 if (_stat.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; 320 if (_stat.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; 321 if (_stat.HeadersError) v |= kpv_ErrorFlags_HeadersError; 322 if (_stat.Unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; 323 if (_stat.DataError) v |= kpv_ErrorFlags_DataError; 324 if (_stat.CrcError) v |= kpv_ErrorFlags_CrcError; 325 prop = v; 326 } 327 } 328 prop.Detach(value); 329 return S_OK; 330 COM_TRY_END 331 } 332 333 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) 334 { 335 *numItems = 1; 336 return S_OK; 337 } 338 339 STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value) 340 { 341 COM_TRY_BEGIN 342 NCOM::CPropVariant prop; 343 switch (propID) 344 { 345 case kpidSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; 346 case kpidPackSize: if (_phySize_Defined) prop = _stat.PhySize; break; 347 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; 348 } 349 prop.Detach(value); 350 return S_OK; 351 COM_TRY_END 352 } 353 354 355 struct COpenCallbackWrap 356 { 357 ICompressProgress p; 358 IArchiveOpenCallback *OpenCallback; 359 HRESULT Res; 360 COpenCallbackWrap(IArchiveOpenCallback *progress); 361 }; 362 363 static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) 364 { 365 COpenCallbackWrap *p = (COpenCallbackWrap *)pp; 366 if (p->OpenCallback) 367 p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); 368 return (SRes)p->Res; 369 } 370 371 COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback) 372 { 373 p.Progress = OpenCallbackProgress; 374 OpenCallback = callback; 375 Res = SZ_OK; 376 } 377 378 struct CXzsCPP 379 { 380 CXzs p; 381 CXzsCPP() { Xzs_Construct(&p); } 382 ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } 383 }; 384 385 static HRESULT SRes_to_Open_HRESULT(SRes res) 386 { 387 switch (res) 388 { 389 case SZ_OK: return S_OK; 390 case SZ_ERROR_MEM: return E_OUTOFMEMORY; 391 case SZ_ERROR_PROGRESS: return E_ABORT; 392 /* 393 case SZ_ERROR_UNSUPPORTED: 394 case SZ_ERROR_CRC: 395 case SZ_ERROR_DATA: 396 case SZ_ERROR_ARCHIVE: 397 case SZ_ERROR_NO_ARCHIVE: 398 return S_FALSE; 399 */ 400 } 401 return S_FALSE; 402 } 403 404 HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback) 405 { 406 _needSeekToStart = true; 407 408 { 409 CXzStreamFlags st; 410 CSeqInStreamWrap inStreamWrap(inStream); 411 SRes res = Xz_ReadHeader(&st, &inStreamWrap.p); 412 if (res != SZ_OK) 413 return SRes_to_Open_HRESULT(res); 414 415 { 416 CXzBlock block; 417 Bool isIndex; 418 UInt32 headerSizeRes; 419 SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes); 420 if (res2 == SZ_OK && !isIndex) 421 { 422 unsigned numFilters = XzBlock_GetNumFilters(&block); 423 for (unsigned i = 0; i < numFilters; i++) 424 AddString(_methodsString, GetMethodString(block.filters[i])); 425 } 426 } 427 } 428 429 RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize)); 430 if (callback) 431 { 432 RINOK(callback->SetTotal(NULL, &_stat.PhySize)); 433 } 434 435 CSeekInStreamWrap inStreamImp(inStream); 436 437 CLookToRead lookStream; 438 LookToRead_CreateVTable(&lookStream, True); 439 lookStream.realStream = &inStreamImp.p; 440 LookToRead_Init(&lookStream); 441 442 COpenCallbackWrap openWrap(callback); 443 444 CXzsCPP xzs; 445 Int64 startPosition; 446 SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &startPosition, &openWrap.p, &g_Alloc); 447 if (res == SZ_ERROR_PROGRESS) 448 return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res; 449 /* 450 if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) 451 res = SZ_OK; 452 */ 453 if (res == SZ_OK && startPosition == 0) 454 { 455 _phySize_Defined = true; 456 457 _stat.OutSize = Xzs_GetUnpackSize(&xzs.p); 458 _stat.UnpackSize_Defined = true; 459 460 _stat.NumStreams = xzs.p.num; 461 _stat.NumStreams_Defined = true; 462 463 _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p); 464 _stat.NumBlocks_Defined = true; 465 466 AddString(_methodsString, GetCheckString(xzs.p)); 467 } 468 else 469 { 470 res = SZ_OK; 471 } 472 473 RINOK(SRes_to_Open_HRESULT(res)); 474 _stream = inStream; 475 _seqStream = inStream; 476 _isArc = true; 477 return S_OK; 478 } 479 480 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) 481 { 482 COM_TRY_BEGIN 483 { 484 Close(); 485 return Open2(inStream, callback); 486 } 487 COM_TRY_END 488 } 489 490 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) 491 { 492 Close(); 493 _seqStream = stream; 494 _isArc = true; 495 _needSeekToStart = false; 496 return S_OK; 497 } 498 499 STDMETHODIMP CHandler::Close() 500 { 501 _stat.Clear(); 502 503 _isArc = false; 504 _needSeekToStart = false; 505 506 _phySize_Defined = false; 507 508 _methodsString.Empty(); 509 _stream.Release(); 510 _seqStream.Release(); 511 return S_OK; 512 } 513 514 class CSeekToSeqStream: 515 public IInStream, 516 public CMyUnknownImp 517 { 518 public: 519 CMyComPtr<ISequentialInStream> Stream; 520 MY_UNKNOWN_IMP1(IInStream) 521 522 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); 523 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); 524 }; 525 526 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize) 527 { 528 return Stream->Read(data, size, processedSize); 529 } 530 531 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; } 532 533 CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(0), OutBuf(0) 534 { 535 XzUnpacker_Construct(&p, &g_Alloc); 536 } 537 538 CXzUnpackerCPP::~CXzUnpackerCPP() 539 { 540 XzUnpacker_Free(&p); 541 MyFree(InBuf); 542 MyFree(OutBuf); 543 } 544 545 HRESULT CDecoder::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress) 546 { 547 const size_t kInBufSize = 1 << 15; 548 const size_t kOutBufSize = 1 << 21; 549 550 Clear(); 551 DecodeRes = SZ_OK; 552 553 XzUnpacker_Init(&xzu.p); 554 if (!xzu.InBuf) 555 xzu.InBuf = (Byte *)MyAlloc(kInBufSize); 556 if (!xzu.OutBuf) 557 xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize); 558 559 UInt32 inSize = 0; 560 SizeT inPos = 0; 561 SizeT outPos = 0; 562 563 for (;;) 564 { 565 if (inPos == inSize) 566 { 567 inPos = inSize = 0; 568 RINOK(seqInStream->Read(xzu.InBuf, kInBufSize, &inSize)); 569 } 570 571 SizeT inLen = inSize - inPos; 572 SizeT outLen = kOutBufSize - outPos; 573 ECoderStatus status; 574 575 SRes res = XzUnpacker_Code(&xzu.p, 576 xzu.OutBuf + outPos, &outLen, 577 xzu.InBuf + inPos, &inLen, 578 (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status); 579 580 inPos += inLen; 581 outPos += outLen; 582 583 InSize += inLen; 584 OutSize += outLen; 585 586 DecodeRes = res; 587 588 bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK); 589 590 if (outStream) 591 { 592 if (outPos == kOutBufSize || finished) 593 { 594 if (outPos != 0) 595 { 596 RINOK(WriteStream(outStream, xzu.OutBuf, outPos)); 597 outPos = 0; 598 } 599 } 600 } 601 else 602 outPos = 0; 603 604 if (progress) 605 { 606 RINOK(progress->SetRatioInfo(&InSize, &OutSize)); 607 } 608 609 if (finished) 610 { 611 PhySize = InSize; 612 NumStreams = xzu.p.numStartedStreams; 613 if (NumStreams > 0) 614 IsArc = true; 615 NumBlocks = xzu.p.numTotalBlocks; 616 617 UnpackSize_Defined = true; 618 NumStreams_Defined = true; 619 NumBlocks_Defined = true; 620 621 UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p); 622 623 if (res == SZ_OK) 624 { 625 if (status == CODER_STATUS_NEEDS_MORE_INPUT) 626 { 627 extraSize = 0; 628 if (!XzUnpacker_IsStreamWasFinished(&xzu.p)) 629 { 630 // finished at padding bytes, but padding is not aligned for 4 631 UnexpectedEnd = true; 632 res = SZ_ERROR_DATA; 633 } 634 } 635 else // status == CODER_STATUS_NOT_FINISHED 636 res = SZ_ERROR_DATA; 637 } 638 else if (res == SZ_ERROR_NO_ARCHIVE) 639 { 640 if (InSize == extraSize) 641 IsArc = false; 642 else 643 { 644 if (extraSize != 0 || inPos != inSize) 645 { 646 DataAfterEnd = true; 647 res = SZ_OK; 648 } 649 } 650 } 651 652 DecodeRes = res; 653 PhySize -= extraSize; 654 655 switch (res) 656 { 657 case SZ_OK: break; 658 case SZ_ERROR_NO_ARCHIVE: IsArc = false; break; 659 case SZ_ERROR_ARCHIVE: HeadersError = true; break; 660 case SZ_ERROR_UNSUPPORTED: Unsupported = true; break; 661 case SZ_ERROR_CRC: CrcError = true; break; 662 case SZ_ERROR_DATA: DataError = true; break; 663 default: DataError = true; break; 664 } 665 666 break; 667 } 668 } 669 670 return S_OK; 671 } 672 673 Int32 CDecoder::Get_Extract_OperationResult() const 674 { 675 Int32 opRes; 676 if (!IsArc) 677 opRes = NExtract::NOperationResult::kIsNotArc; 678 else if (UnexpectedEnd) 679 opRes = NExtract::NOperationResult::kUnexpectedEnd; 680 else if (DataAfterEnd) 681 opRes = NExtract::NOperationResult::kDataAfterEnd; 682 else if (CrcError) 683 opRes = NExtract::NOperationResult::kCRCError; 684 else if (Unsupported) 685 opRes = NExtract::NOperationResult::kUnsupportedMethod; 686 else if (HeadersError) 687 opRes = NExtract::NOperationResult::kDataError; 688 else if (DataError) 689 opRes = NExtract::NOperationResult::kDataError; 690 else if (DecodeRes != SZ_OK) 691 opRes = NExtract::NOperationResult::kDataError; 692 else 693 opRes = NExtract::NOperationResult::kOK; 694 return opRes; 695 } 696 697 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, 698 Int32 testMode, IArchiveExtractCallback *extractCallback) 699 { 700 COM_TRY_BEGIN 701 if (numItems == 0) 702 return S_OK; 703 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) 704 return E_INVALIDARG; 705 706 if (_phySize_Defined) 707 extractCallback->SetTotal(_stat.PhySize); 708 709 UInt64 currentTotalPacked = 0; 710 RINOK(extractCallback->SetCompleted(¤tTotalPacked)); 711 CMyComPtr<ISequentialOutStream> realOutStream; 712 Int32 askMode = testMode ? 713 NExtract::NAskMode::kTest : 714 NExtract::NAskMode::kExtract; 715 716 RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); 717 718 if (!testMode && !realOutStream) 719 return S_OK; 720 721 extractCallback->PrepareOperation(askMode); 722 723 CLocalProgress *lps = new CLocalProgress; 724 CMyComPtr<ICompressProgressInfo> lpsRef = lps; 725 lps->Init(extractCallback, true); 726 727 if (_needSeekToStart) 728 { 729 if (!_stream) 730 return E_FAIL; 731 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); 732 } 733 else 734 _needSeekToStart = true; 735 736 CDecoder decoder; 737 RINOK(Decode2(_seqStream, realOutStream, decoder, lpsRef)); 738 Int32 opRes = decoder.Get_Extract_OperationResult(); 739 740 realOutStream.Release(); 741 return extractCallback->SetOperationResult(opRes); 742 COM_TRY_END 743 } 744 745 #ifndef EXTRACT_ONLY 746 747 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) 748 { 749 *timeType = NFileTimeType::kUnix; 750 return S_OK; 751 } 752 753 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, 754 IArchiveUpdateCallback *updateCallback) 755 { 756 COM_TRY_BEGIN 757 758 CSeqOutStreamWrap seqOutStream(outStream); 759 760 if (numItems == 0) 761 { 762 SRes res = Xz_EncodeEmpty(&seqOutStream.p); 763 return SResToHRESULT(res); 764 } 765 766 if (numItems != 1) 767 return E_INVALIDARG; 768 769 Int32 newData, newProps; 770 UInt32 indexInArchive; 771 if (!updateCallback) 772 return E_FAIL; 773 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); 774 775 if (IntToBool(newProps)) 776 { 777 { 778 NCOM::CPropVariant prop; 779 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); 780 if (prop.vt != VT_EMPTY) 781 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) 782 return E_INVALIDARG; 783 } 784 } 785 786 if (IntToBool(newData)) 787 { 788 UInt64 size; 789 { 790 NCOM::CPropVariant prop; 791 RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); 792 if (prop.vt != VT_UI8) 793 return E_INVALIDARG; 794 size = prop.uhVal.QuadPart; 795 RINOK(updateCallback->SetTotal(size)); 796 } 797 798 CLzma2EncProps lzma2Props; 799 Lzma2EncProps_Init(&lzma2Props); 800 801 lzma2Props.lzmaProps.level = GetLevel(); 802 803 CMyComPtr<ISequentialInStream> fileInStream; 804 RINOK(updateCallback->GetStream(0, &fileInStream)); 805 806 CSeqInStreamWrap seqInStream(fileInStream); 807 808 { 809 NCOM::CPropVariant prop = (UInt64)size; 810 RINOK(NCompress::NLzma2::SetLzma2Prop(NCoderPropID::kReduceSize, prop, lzma2Props)); 811 } 812 813 FOR_VECTOR (i, _methods) 814 { 815 COneMethodInfo &m = _methods[i]; 816 SetGlobalLevelAndThreads(m 817 #ifndef _7ZIP_ST 818 , _numThreads 819 #endif 820 ); 821 { 822 FOR_VECTOR (j, m.Props) 823 { 824 const CProp &prop = m.Props[j]; 825 RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props)); 826 } 827 } 828 } 829 830 #ifndef _7ZIP_ST 831 lzma2Props.numTotalThreads = _numThreads; 832 #endif 833 834 CLocalProgress *lps = new CLocalProgress; 835 CMyComPtr<ICompressProgressInfo> progress = lps; 836 lps->Init(updateCallback, true); 837 838 CCompressProgressWrap progressWrap(progress); 839 CXzProps xzProps; 840 CXzFilterProps filter; 841 XzProps_Init(&xzProps); 842 XzFilterProps_Init(&filter); 843 xzProps.lzma2Props = &lzma2Props; 844 xzProps.filterProps = (_filterId != 0 ? &filter : NULL); 845 switch (_crcSize) 846 { 847 case 0: xzProps.checkId = XZ_CHECK_NO; break; 848 case 4: xzProps.checkId = XZ_CHECK_CRC32; break; 849 case 8: xzProps.checkId = XZ_CHECK_CRC64; break; 850 case 32: xzProps.checkId = XZ_CHECK_SHA256; break; 851 default: return E_INVALIDARG; 852 } 853 filter.id = _filterId; 854 if (_filterId == XZ_ID_Delta) 855 { 856 bool deltaDefined = false; 857 FOR_VECTOR (j, _filterMethod.Props) 858 { 859 const CProp &prop = _filterMethod.Props[j]; 860 if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4) 861 { 862 UInt32 delta = (UInt32)prop.Value.ulVal; 863 if (delta < 1 || delta > 256) 864 return E_INVALIDARG; 865 filter.delta = delta; 866 deltaDefined = true; 867 } 868 } 869 if (!deltaDefined) 870 return E_INVALIDARG; 871 } 872 SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &xzProps, &progressWrap.p); 873 if (res == SZ_OK) 874 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); 875 return SResToHRESULT(res); 876 } 877 878 if (indexInArchive != 0) 879 return E_INVALIDARG; 880 881 CMyComPtr<IArchiveUpdateCallbackFile> opCallback; 882 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback); 883 if (opCallback) 884 { 885 RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate)) 886 } 887 888 if (_stream) 889 { 890 if (_phySize_Defined) 891 RINOK(updateCallback->SetTotal(_stat.PhySize)); 892 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); 893 } 894 895 CLocalProgress *lps = new CLocalProgress; 896 CMyComPtr<ICompressProgressInfo> progress = lps; 897 lps->Init(updateCallback, true); 898 899 return NCompress::CopyStream(_stream, outStream, progress); 900 901 COM_TRY_END 902 } 903 904 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps) 905 { 906 COM_TRY_BEGIN 907 908 Init(); 909 for (UInt32 i = 0; i < numProps; i++) 910 { 911 RINOK(SetProperty(names[i], values[i])); 912 } 913 914 if (!_filterMethod.MethodName.IsEmpty()) 915 { 916 unsigned k; 917 for (k = 0; k < ARRAY_SIZE(g_NamePairs); k++) 918 { 919 const CMethodNamePair &pair = g_NamePairs[k]; 920 if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) 921 { 922 _filterId = pair.Id; 923 break; 924 } 925 } 926 if (k == ARRAY_SIZE(g_NamePairs)) 927 return E_INVALIDARG; 928 } 929 930 _methods.DeleteFrontal(GetNumEmptyMethods()); 931 if (_methods.Size() > 1) 932 return E_INVALIDARG; 933 if (_methods.Size() == 1) 934 { 935 AString &methodName = _methods[0].MethodName; 936 if (methodName.IsEmpty()) 937 methodName = k_LZMA2_Name; 938 else if (!methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name)) 939 return E_INVALIDARG; 940 } 941 942 return S_OK; 943 944 COM_TRY_END 945 } 946 947 #endif 948 949 REGISTER_ARC_IO( 950 "xz", "xz txz", "* .tar", 0xC, 951 XZ_SIG, 952 0, 953 NArcInfoFlags::kKeepName, 954 NULL) 955 956 }} 957