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