1 // XzHandler.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/Alloc.h" 6 7 #include "../../Common/ComTry.h" 8 #include "../../Common/Defs.h" 9 #include "../../Common/IntToString.h" 10 #include "../../Common/MyBuffer.h" 11 #include "../../Common/StringToInt.h" 12 13 #include "../../Windows/PropVariant.h" 14 #include "../../Windows/System.h" 15 16 #include "../Common/CWrappers.h" 17 #include "../Common/ProgressUtils.h" 18 #include "../Common/RegisterArc.h" 19 #include "../Common/StreamUtils.h" 20 21 #include "../Compress/CopyCoder.h" 22 #include "../Compress/XzDecoder.h" 23 #include "../Compress/XzEncoder.h" 24 25 #include "IArchive.h" 26 27 #include "Common/HandlerOut.h" 28 29 using namespace NWindows; 30 31 namespace NArchive { 32 namespace NXz { 33 34 #define k_LZMA2_Name "LZMA2" 35 36 37 struct CBlockInfo 38 { 39 unsigned StreamFlags; 40 UInt64 PackPos; 41 UInt64 PackSize; // pure value from Index record, it doesn't include pad zeros 42 UInt64 UnpackPos; 43 }; 44 45 46 Z7_class_CHandler_final: 47 public IInArchive, 48 public IArchiveOpenSeq, 49 public IInArchiveGetStream, 50 public ISetProperties, 51 #ifndef Z7_EXTRACT_ONLY 52 public IOutArchive, 53 #endif 54 public CMyUnknownImp, 55 #ifndef Z7_EXTRACT_ONLY 56 public CMultiMethodProps 57 #else 58 public CCommonMethodProps 59 #endif 60 { 61 Z7_COM_QI_BEGIN2(IInArchive) 62 Z7_COM_QI_ENTRY(IArchiveOpenSeq) 63 Z7_COM_QI_ENTRY(IInArchiveGetStream) 64 Z7_COM_QI_ENTRY(ISetProperties) 65 #ifndef Z7_EXTRACT_ONLY 66 Z7_COM_QI_ENTRY(IOutArchive) 67 #endif 68 Z7_COM_QI_END 69 Z7_COM_ADDREF_RELEASE 70 71 Z7_IFACE_COM7_IMP(IInArchive) 72 Z7_IFACE_COM7_IMP(IArchiveOpenSeq) 73 Z7_IFACE_COM7_IMP(IInArchiveGetStream) 74 Z7_IFACE_COM7_IMP(ISetProperties) 75 #ifndef Z7_EXTRACT_ONLY 76 Z7_IFACE_COM7_IMP(IOutArchive) 77 #endif 78 79 bool _stat_defined; 80 bool _stat2_defined; 81 bool _isArc; 82 bool _needSeekToStart; 83 bool _firstBlockWasRead; 84 SRes _stat2_decode_SRes; 85 86 CXzStatInfo _stat; // it's stat from backward parsing 87 CXzStatInfo _stat2; // it's data from forward parsing, if the decoder was called 88 89 const CXzStatInfo *GetStat() const 90 { 91 if (_stat_defined) return &_stat; 92 if (_stat2_defined) return &_stat2; 93 return NULL; 94 } 95 96 AString _methodsString; 97 98 99 #ifndef Z7_EXTRACT_ONLY 100 101 UInt32 _filterId; 102 UInt64 _numSolidBytes; 103 104 void InitXz() 105 { 106 _filterId = 0; 107 _numSolidBytes = XZ_PROPS_BLOCK_SIZE_AUTO; 108 } 109 110 #endif 111 112 113 void Init() 114 { 115 #ifndef Z7_EXTRACT_ONLY 116 InitXz(); 117 CMultiMethodProps::Init(); 118 #else 119 CCommonMethodProps::InitCommon(); 120 #endif 121 } 122 123 HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); 124 125 HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); 126 127 HRESULT Decode(NCompress::NXz::CDecoder &decoder, 128 ISequentialInStream *seqInStream, 129 ISequentialOutStream *outStream, 130 ICompressProgressInfo *progress) 131 { 132 #ifndef Z7_ST 133 decoder._numThreads = _numThreads; 134 #endif 135 decoder._memUsage = _memUsage_Decompress; 136 137 const HRESULT hres = decoder.Decode(seqInStream, outStream, 138 NULL, // *outSizeLimit 139 true, // finishStream 140 progress); 141 142 if (decoder.MainDecodeSRes_wasUsed 143 && decoder.MainDecodeSRes != SZ_ERROR_MEM 144 && decoder.MainDecodeSRes != SZ_ERROR_UNSUPPORTED) 145 { 146 // if (!_stat2_defined) 147 { 148 _stat2_decode_SRes = decoder.MainDecodeSRes; 149 _stat2 = decoder.Stat; 150 _stat2_defined = true; 151 } 152 } 153 154 if (hres == S_OK && progress) 155 { 156 // RINOK( 157 progress->SetRatioInfo(&decoder.Stat.InSize, &decoder.Stat.OutSize); 158 } 159 return hres; 160 } 161 162 public: 163 CBlockInfo *_blocks; 164 size_t _blocksArraySize; 165 UInt64 _maxBlocksSize; 166 CMyComPtr<IInStream> _stream; 167 CMyComPtr<ISequentialInStream> _seqStream; 168 169 CXzBlock _firstBlock; 170 171 CHandler(); 172 ~CHandler(); 173 174 HRESULT SeekToPackPos(UInt64 pos) 175 { 176 return InStream_SeekSet(_stream, pos); 177 } 178 }; 179 180 181 CHandler::CHandler(): 182 _blocks(NULL), 183 _blocksArraySize(0) 184 { 185 #ifndef Z7_EXTRACT_ONLY 186 InitXz(); 187 #endif 188 } 189 190 CHandler::~CHandler() 191 { 192 MyFree(_blocks); 193 } 194 195 196 static const Byte kProps[] = 197 { 198 kpidSize, 199 kpidPackSize, 200 kpidMethod 201 }; 202 203 static const Byte kArcProps[] = 204 { 205 kpidMethod, 206 kpidNumStreams, 207 kpidNumBlocks, 208 kpidClusterSize, 209 kpidCharacts 210 }; 211 212 IMP_IInArchive_Props 213 IMP_IInArchive_ArcProps 214 215 static void Lzma2PropToString(AString &s, unsigned prop) 216 { 217 char c = 0; 218 UInt32 size; 219 if ((prop & 1) == 0) 220 size = prop / 2 + 12; 221 else 222 { 223 c = 'k'; 224 size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1); 225 if (prop > 17) 226 { 227 size >>= 10; 228 c = 'm'; 229 } 230 } 231 s.Add_UInt32(size); 232 if (c != 0) 233 s.Add_Char(c); 234 } 235 236 struct CMethodNamePair 237 { 238 UInt32 Id; 239 const char *Name; 240 }; 241 242 static const CMethodNamePair g_NamePairs[] = 243 { 244 { XZ_ID_Subblock, "SB" }, 245 { XZ_ID_Delta, "Delta" }, 246 { XZ_ID_X86, "BCJ" }, 247 { XZ_ID_PPC, "PPC" }, 248 { XZ_ID_IA64, "IA64" }, 249 { XZ_ID_ARM, "ARM" }, 250 { XZ_ID_ARMT, "ARMT" }, 251 { XZ_ID_SPARC, "SPARC" }, 252 { XZ_ID_ARM64, "ARM64" }, 253 { XZ_ID_RISCV, "RISCV" }, 254 { XZ_ID_LZMA2, "LZMA2" } 255 }; 256 257 static void AddMethodString(AString &s, const CXzFilter &f) 258 { 259 const char *p = NULL; 260 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_NamePairs); i++) 261 if (g_NamePairs[i].Id == f.id) 262 { 263 p = g_NamePairs[i].Name; 264 break; 265 } 266 char temp[32]; 267 if (!p) 268 { 269 ::ConvertUInt64ToString(f.id, temp); 270 p = temp; 271 } 272 273 s += p; 274 275 if (f.propsSize > 0) 276 { 277 s.Add_Colon(); 278 if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) 279 Lzma2PropToString(s, f.props[0]); 280 else if (f.id == XZ_ID_Delta && f.propsSize == 1) 281 s.Add_UInt32((UInt32)f.props[0] + 1); 282 else if (f.id == XZ_ID_ARM64 && f.propsSize == 1) 283 s.Add_UInt32((UInt32)f.props[0] + 16 + 2); 284 else 285 { 286 s.Add_Char('['); 287 for (UInt32 bi = 0; bi < f.propsSize; bi++) 288 { 289 const unsigned v = f.props[bi]; 290 s.Add_Char(GET_HEX_CHAR_UPPER(v >> 4)); 291 s.Add_Char(GET_HEX_CHAR_UPPER(v & 15)); 292 } 293 s.Add_Char(']'); 294 } 295 } 296 } 297 298 static const char * const kChecks[] = 299 { 300 "NoCheck" 301 , "CRC32" 302 , NULL 303 , NULL 304 , "CRC64" 305 , NULL 306 , NULL 307 , NULL 308 , NULL 309 , NULL 310 , "SHA256" 311 , NULL 312 , NULL 313 , NULL 314 , NULL 315 , NULL 316 }; 317 318 static void AddCheckString(AString &s, const CXzs &xzs) 319 { 320 size_t i; 321 UInt32 mask = 0; 322 for (i = 0; i < xzs.num; i++) 323 mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); 324 for (i = 0; i <= XZ_CHECK_MASK; i++) 325 if (((mask >> i) & 1) != 0) 326 { 327 s.Add_Space_if_NotEmpty(); 328 if (kChecks[i]) 329 s += kChecks[i]; 330 else 331 { 332 s += "Check-"; 333 s.Add_UInt32((UInt32)i); 334 } 335 } 336 } 337 338 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) 339 { 340 COM_TRY_BEGIN 341 NCOM::CPropVariant prop; 342 343 const CXzStatInfo *stat = GetStat(); 344 345 switch (propID) 346 { 347 case kpidPhySize: if (stat) prop = stat->InSize; break; 348 case kpidNumStreams: if (stat && stat->NumStreams_Defined) prop = stat->NumStreams; break; 349 case kpidNumBlocks: if (stat && stat->NumBlocks_Defined) prop = stat->NumBlocks; break; 350 case kpidUnpackSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break; 351 case kpidClusterSize: if (_stat_defined && _stat.NumBlocks_Defined && stat->NumBlocks > 1) prop = _maxBlocksSize; break; 352 case kpidCharacts: 353 if (_firstBlockWasRead) 354 { 355 AString s; 356 if (XzBlock_HasPackSize(&_firstBlock)) 357 s.Add_OptSpaced("BlockPackSize"); 358 if (XzBlock_HasUnpackSize(&_firstBlock)) 359 s.Add_OptSpaced("BlockUnpackSize"); 360 if (!s.IsEmpty()) 361 prop = s; 362 } 363 break; 364 365 366 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; 367 case kpidErrorFlags: 368 { 369 UInt32 v = 0; 370 SRes sres = _stat2_decode_SRes; 371 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; 372 if (sres == SZ_ERROR_INPUT_EOF) v |= kpv_ErrorFlags_UnexpectedEnd; 373 if (_stat2_defined && _stat2.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; 374 if (sres == SZ_ERROR_ARCHIVE) v |= kpv_ErrorFlags_HeadersError; 375 if (sres == SZ_ERROR_UNSUPPORTED) v |= kpv_ErrorFlags_UnsupportedMethod; 376 if (sres == SZ_ERROR_DATA) v |= kpv_ErrorFlags_DataError; 377 if (sres == SZ_ERROR_CRC) v |= kpv_ErrorFlags_CrcError; 378 if (v != 0) 379 prop = v; 380 break; 381 } 382 383 case kpidMainSubfile: 384 { 385 // debug only, comment it: 386 // if (_blocks) prop = (UInt32)0; 387 break; 388 } 389 default: break; 390 } 391 prop.Detach(value); 392 return S_OK; 393 COM_TRY_END 394 } 395 396 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) 397 { 398 *numItems = 1; 399 return S_OK; 400 } 401 402 Z7_COM7F_IMF(CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value)) 403 { 404 COM_TRY_BEGIN 405 const CXzStatInfo *stat = GetStat(); 406 NCOM::CPropVariant prop; 407 switch (propID) 408 { 409 case kpidSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break; 410 case kpidPackSize: if (stat) prop = stat->InSize; break; 411 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; 412 default: break; 413 } 414 prop.Detach(value); 415 return S_OK; 416 COM_TRY_END 417 } 418 419 420 struct COpenCallbackWrap 421 { 422 ICompressProgress vt; 423 IArchiveOpenCallback *OpenCallback; 424 HRESULT Res; 425 426 // new clang shows "non-POD" warning for offsetof(), if we use constructor instead of Init() 427 void Init(IArchiveOpenCallback *progress); 428 }; 429 430 static SRes OpenCallbackProgress(ICompressProgressPtr pp, UInt64 inSize, UInt64 /* outSize */) 431 { 432 Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(COpenCallbackWrap) 433 if (p->OpenCallback) 434 p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); 435 return HRESULT_To_SRes(p->Res, SZ_ERROR_PROGRESS); 436 } 437 438 void COpenCallbackWrap::Init(IArchiveOpenCallback *callback) 439 { 440 vt.Progress = OpenCallbackProgress; 441 OpenCallback = callback; 442 Res = SZ_OK; 443 } 444 445 446 struct CXzsCPP 447 { 448 CXzs p; 449 CXzsCPP() { Xzs_Construct(&p); } 450 ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } 451 }; 452 453 #define kInputBufSize ((size_t)1 << 10) 454 455 struct CLookToRead2_CPP: public CLookToRead2 456 { 457 CLookToRead2_CPP() 458 { 459 buf = NULL; 460 LookToRead2_CreateVTable(this, 461 True // Lookahead ? 462 ); 463 } 464 void Alloc(size_t allocSize) 465 { 466 buf = (Byte *)MyAlloc(allocSize); 467 if (buf) 468 this->bufSize = allocSize; 469 } 470 ~CLookToRead2_CPP() 471 { 472 MyFree(buf); 473 } 474 }; 475 476 477 static HRESULT SRes_to_Open_HRESULT(SRes res) 478 { 479 switch (res) 480 { 481 case SZ_OK: return S_OK; 482 case SZ_ERROR_MEM: return E_OUTOFMEMORY; 483 case SZ_ERROR_PROGRESS: return E_ABORT; 484 /* 485 case SZ_ERROR_UNSUPPORTED: 486 case SZ_ERROR_CRC: 487 case SZ_ERROR_DATA: 488 case SZ_ERROR_ARCHIVE: 489 case SZ_ERROR_NO_ARCHIVE: 490 return S_FALSE; 491 */ 492 default: break; 493 } 494 return S_FALSE; 495 } 496 497 498 499 HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback) 500 { 501 _needSeekToStart = true; 502 503 { 504 CXzStreamFlags st; 505 CSeqInStreamWrap inStreamWrap; 506 507 inStreamWrap.Init(inStream); 508 509 SRes res = Xz_ReadHeader(&st, &inStreamWrap.vt); 510 511 if (inStreamWrap.Res != S_OK) 512 return inStreamWrap.Res; 513 if (res != SZ_OK) 514 return SRes_to_Open_HRESULT(res); 515 516 { 517 CXzBlock block; 518 BoolInt isIndex; 519 UInt32 headerSizeRes; 520 521 SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.vt, &isIndex, &headerSizeRes); 522 523 if (inStreamWrap.Res != S_OK) 524 return inStreamWrap.Res; 525 526 if (res2 != SZ_OK) 527 { 528 if (res2 == SZ_ERROR_INPUT_EOF) 529 { 530 _stat2_decode_SRes = res2; 531 _stream = inStream; 532 _seqStream = inStream; 533 _isArc = true; 534 return S_OK; 535 } 536 537 if (res2 == SZ_ERROR_ARCHIVE) 538 return S_FALSE; 539 } 540 else if (!isIndex) 541 { 542 _firstBlockWasRead = true; 543 _firstBlock = block; 544 545 unsigned numFilters = XzBlock_GetNumFilters(&block); 546 for (unsigned i = 0; i < numFilters; i++) 547 { 548 _methodsString.Add_Space_if_NotEmpty(); 549 AddMethodString(_methodsString, block.filters[i]); 550 } 551 } 552 } 553 } 554 555 RINOK(InStream_GetSize_SeekToEnd(inStream, _stat.InSize)) 556 if (callback) 557 { 558 RINOK(callback->SetTotal(NULL, &_stat.InSize)) 559 } 560 561 CSeekInStreamWrap inStreamImp; 562 563 inStreamImp.Init(inStream); 564 565 CLookToRead2_CPP lookStream; 566 567 lookStream.Alloc(kInputBufSize); 568 569 if (!lookStream.buf) 570 return E_OUTOFMEMORY; 571 572 lookStream.realStream = &inStreamImp.vt; 573 LookToRead2_INIT(&lookStream) 574 575 COpenCallbackWrap openWrap; 576 openWrap.Init(callback); 577 578 CXzsCPP xzs; 579 Int64 startPosition; 580 SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.vt, &startPosition, &openWrap.vt, &g_Alloc); 581 if (res == SZ_ERROR_PROGRESS) 582 return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res; 583 /* 584 if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) 585 res = SZ_OK; 586 */ 587 if (res == SZ_OK && startPosition == 0) 588 { 589 _stat_defined = true; 590 591 _stat.OutSize = Xzs_GetUnpackSize(&xzs.p); 592 _stat.UnpackSize_Defined = true; 593 594 _stat.NumStreams = xzs.p.num; 595 _stat.NumStreams_Defined = true; 596 597 _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p); 598 _stat.NumBlocks_Defined = true; 599 600 AddCheckString(_methodsString, xzs.p); 601 602 const size_t numBlocks = (size_t)_stat.NumBlocks + 1; 603 const size_t bytesAlloc = numBlocks * sizeof(CBlockInfo); 604 605 if (bytesAlloc / sizeof(CBlockInfo) == _stat.NumBlocks + 1) 606 { 607 _blocks = (CBlockInfo *)MyAlloc(bytesAlloc); 608 if (_blocks) 609 { 610 unsigned blockIndex = 0; 611 UInt64 unpackPos = 0; 612 613 for (size_t si = xzs.p.num; si != 0;) 614 { 615 si--; 616 const CXzStream &str = xzs.p.streams[si]; 617 UInt64 packPos = str.startOffset + XZ_STREAM_HEADER_SIZE; 618 619 for (size_t bi = 0; bi < str.numBlocks; bi++) 620 { 621 const CXzBlockSizes &bs = str.blocks[bi]; 622 const UInt64 packSizeAligned = bs.totalSize + ((0 - (unsigned)bs.totalSize) & 3); 623 624 if (bs.unpackSize != 0) 625 { 626 if (blockIndex >= _stat.NumBlocks) 627 return E_FAIL; 628 629 CBlockInfo &block = _blocks[blockIndex++]; 630 block.StreamFlags = str.flags; 631 block.PackSize = bs.totalSize; // packSizeAligned; 632 block.PackPos = packPos; 633 block.UnpackPos = unpackPos; 634 } 635 packPos += packSizeAligned; 636 unpackPos += bs.unpackSize; 637 if (_maxBlocksSize < bs.unpackSize) 638 _maxBlocksSize = bs.unpackSize; 639 } 640 } 641 642 /* 643 if (blockIndex != _stat.NumBlocks) 644 { 645 // there are Empty blocks; 646 } 647 */ 648 if (_stat.OutSize != unpackPos) 649 return E_FAIL; 650 CBlockInfo &block = _blocks[blockIndex++]; 651 block.StreamFlags = 0; 652 block.PackSize = 0; 653 block.PackPos = 0; 654 block.UnpackPos = unpackPos; 655 _blocksArraySize = blockIndex; 656 } 657 } 658 } 659 else 660 { 661 res = SZ_OK; 662 } 663 664 RINOK(SRes_to_Open_HRESULT(res)) 665 666 _stream = inStream; 667 _seqStream = inStream; 668 _isArc = true; 669 return S_OK; 670 } 671 672 673 674 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)) 675 { 676 COM_TRY_BEGIN 677 { 678 Close(); 679 return Open2(inStream, callback); 680 } 681 COM_TRY_END 682 } 683 684 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream)) 685 { 686 Close(); 687 _seqStream = stream; 688 _isArc = true; 689 _needSeekToStart = false; 690 return S_OK; 691 } 692 693 Z7_COM7F_IMF(CHandler::Close()) 694 { 695 XzStatInfo_Clear(&_stat); 696 XzStatInfo_Clear(&_stat2); 697 _stat_defined = false; 698 _stat2_defined = false; 699 _stat2_decode_SRes = SZ_OK; 700 701 _isArc = false; 702 _needSeekToStart = false; 703 _firstBlockWasRead = false; 704 705 _methodsString.Empty(); 706 _stream.Release(); 707 _seqStream.Release(); 708 709 MyFree(_blocks); 710 _blocks = NULL; 711 _blocksArraySize = 0; 712 _maxBlocksSize = 0; 713 714 return S_OK; 715 } 716 717 718 struct CXzUnpackerCPP2 719 { 720 Byte *InBuf; 721 // Byte *OutBuf; 722 CXzUnpacker p; 723 724 CXzUnpackerCPP2(); 725 ~CXzUnpackerCPP2(); 726 }; 727 728 CXzUnpackerCPP2::CXzUnpackerCPP2(): InBuf(NULL) 729 // , OutBuf(NULL) 730 { 731 XzUnpacker_Construct(&p, &g_Alloc); 732 } 733 734 CXzUnpackerCPP2::~CXzUnpackerCPP2() 735 { 736 XzUnpacker_Free(&p); 737 MidFree(InBuf); 738 // MidFree(OutBuf); 739 } 740 741 742 Z7_CLASS_IMP_IInStream( 743 CInStream 744 ) 745 746 UInt64 _virtPos; 747 public: 748 UInt64 Size; 749 UInt64 _cacheStartPos; 750 size_t _cacheSize; 751 CByteBuffer _cache; 752 // UInt64 _startPos; 753 CXzUnpackerCPP2 xz; 754 755 void InitAndSeek() 756 { 757 _virtPos = 0; 758 _cacheStartPos = 0; 759 _cacheSize = 0; 760 // _startPos = startPos; 761 } 762 763 CMyComPtr2<IInArchive, CHandler> _handlerSpec; 764 // ~CInStream(); 765 }; 766 767 /* 768 CInStream::~CInStream() 769 { 770 // _cache.Free(); 771 } 772 */ 773 774 static size_t FindBlock(const CBlockInfo *blocks, size_t numBlocks, UInt64 pos) 775 { 776 size_t left = 0, right = numBlocks; 777 for (;;) 778 { 779 size_t mid = (left + right) / 2; 780 if (mid == left) 781 return left; 782 if (pos < blocks[mid].UnpackPos) 783 right = mid; 784 else 785 left = mid; 786 } 787 } 788 789 790 791 static HRESULT DecodeBlock(CXzUnpackerCPP2 &xzu, 792 ISequentialInStream *seqInStream, 793 unsigned streamFlags, 794 UInt64 packSize, // pure size from Index record, it doesn't include pad zeros 795 size_t unpackSize, Byte *dest 796 // , ICompressProgressInfo *progress 797 ) 798 { 799 const size_t kInBufSize = (size_t)1 << 16; 800 801 XzUnpacker_Init(&xzu.p); 802 803 if (!xzu.InBuf) 804 { 805 xzu.InBuf = (Byte *)MidAlloc(kInBufSize); 806 if (!xzu.InBuf) 807 return E_OUTOFMEMORY; 808 } 809 810 xzu.p.streamFlags = (UInt16)streamFlags; 811 XzUnpacker_PrepareToRandomBlockDecoding(&xzu.p); 812 813 XzUnpacker_SetOutBuf(&xzu.p, dest, unpackSize); 814 815 const UInt64 packSizeAligned = packSize + ((0 - (unsigned)packSize) & 3); 816 UInt64 packRem = packSizeAligned; 817 818 UInt32 inSize = 0; 819 SizeT inPos = 0; 820 SizeT outPos = 0; 821 822 HRESULT readRes = S_OK; 823 824 for (;;) 825 { 826 if (inPos == inSize && readRes == S_OK) 827 { 828 inPos = 0; 829 inSize = 0; 830 UInt32 rem = kInBufSize; 831 if (rem > packRem) 832 rem = (UInt32)packRem; 833 if (rem != 0) 834 readRes = seqInStream->Read(xzu.InBuf, rem, &inSize); 835 } 836 837 SizeT inLen = inSize - inPos; 838 SizeT outLen = unpackSize - outPos; 839 840 ECoderStatus status; 841 842 const SRes res = XzUnpacker_Code(&xzu.p, 843 // dest + outPos, 844 NULL, 845 &outLen, 846 xzu.InBuf + inPos, &inLen, 847 (inLen == 0), // srcFinished 848 CODER_FINISH_END, &status); 849 850 // return E_OUTOFMEMORY; 851 // res = SZ_ERROR_CRC; 852 853 if (res != SZ_OK) 854 { 855 if (res == SZ_ERROR_CRC) 856 return S_FALSE; 857 return SResToHRESULT(res); 858 } 859 860 inPos += inLen; 861 outPos += outLen; 862 863 packRem -= inLen; 864 865 const BoolInt blockFinished = XzUnpacker_IsBlockFinished(&xzu.p); 866 867 if ((inLen == 0 && outLen == 0) || blockFinished) 868 { 869 if (packRem != 0 || !blockFinished || unpackSize != outPos) 870 return S_FALSE; 871 if (XzUnpacker_GetPackSizeForIndex(&xzu.p) != packSize) 872 return S_FALSE; 873 return S_OK; 874 } 875 } 876 } 877 878 879 Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize)) 880 { 881 COM_TRY_BEGIN 882 883 if (processedSize) 884 *processedSize = 0; 885 if (size == 0) 886 return S_OK; 887 888 { 889 if (_virtPos >= Size) 890 return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL; 891 { 892 UInt64 rem = Size - _virtPos; 893 if (size > rem) 894 size = (UInt32)rem; 895 } 896 } 897 898 if (size == 0) 899 return S_OK; 900 901 if (_virtPos < _cacheStartPos || _virtPos >= _cacheStartPos + _cacheSize) 902 { 903 const size_t bi = FindBlock(_handlerSpec->_blocks, _handlerSpec->_blocksArraySize, _virtPos); 904 const CBlockInfo &block = _handlerSpec->_blocks[bi]; 905 const UInt64 unpackSize = _handlerSpec->_blocks[bi + 1].UnpackPos - block.UnpackPos; 906 if (_cache.Size() < unpackSize) 907 return E_FAIL; 908 909 _cacheSize = 0; 910 911 RINOK(_handlerSpec->SeekToPackPos(block.PackPos)) 912 RINOK(DecodeBlock(xz, _handlerSpec->_seqStream, block.StreamFlags, block.PackSize, 913 (size_t)unpackSize, _cache)) 914 _cacheStartPos = block.UnpackPos; 915 _cacheSize = (size_t)unpackSize; 916 } 917 918 { 919 const size_t offset = (size_t)(_virtPos - _cacheStartPos); 920 const size_t rem = _cacheSize - offset; 921 if (size > rem) 922 size = (UInt32)rem; 923 memcpy(data, _cache.ConstData() + offset, size); 924 _virtPos += size; 925 if (processedSize) 926 *processedSize = size; 927 return S_OK; 928 } 929 930 COM_TRY_END 931 } 932 933 934 Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)) 935 { 936 switch (seekOrigin) 937 { 938 case STREAM_SEEK_SET: break; 939 case STREAM_SEEK_CUR: offset += _virtPos; break; 940 case STREAM_SEEK_END: offset += Size; break; 941 default: return STG_E_INVALIDFUNCTION; 942 } 943 if (offset < 0) 944 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; 945 _virtPos = (UInt64)offset; 946 if (newPosition) 947 *newPosition = (UInt64)offset; 948 return S_OK; 949 } 950 951 952 953 static const UInt64 kMaxBlockSize_for_GetStream = (UInt64)1 << 40; 954 955 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) 956 { 957 COM_TRY_BEGIN 958 959 *stream = NULL; 960 961 if (index != 0) 962 return E_INVALIDARG; 963 964 if (!_stat.UnpackSize_Defined 965 || _maxBlocksSize == 0 // 18.02 966 || _maxBlocksSize > kMaxBlockSize_for_GetStream 967 || _maxBlocksSize != (size_t)_maxBlocksSize) 968 return S_FALSE; 969 970 size_t memSize; 971 if (!NSystem::GetRamSize(memSize)) 972 memSize = (size_t)sizeof(size_t) << 28; 973 { 974 if (_maxBlocksSize > memSize / 4) 975 return S_FALSE; 976 } 977 978 CMyComPtr2<ISequentialInStream, CInStream> spec; 979 spec.Create_if_Empty(); 980 spec->_cache.Alloc((size_t)_maxBlocksSize); 981 spec->_handlerSpec.SetFromCls(this); 982 // spec->_handler = (IInArchive *)this; 983 spec->Size = _stat.OutSize; 984 spec->InitAndSeek(); 985 986 *stream = spec.Detach(); 987 return S_OK; 988 989 COM_TRY_END 990 } 991 992 993 static Int32 Get_Extract_OperationResult(const NCompress::NXz::CDecoder &decoder) 994 { 995 Int32 opRes; 996 SRes sres = decoder.MainDecodeSRes; 997 if (sres == SZ_ERROR_NO_ARCHIVE) // (!IsArc) 998 opRes = NExtract::NOperationResult::kIsNotArc; 999 else if (sres == SZ_ERROR_INPUT_EOF) // (UnexpectedEnd) 1000 opRes = NExtract::NOperationResult::kUnexpectedEnd; 1001 else if (decoder.Stat.DataAfterEnd) 1002 opRes = NExtract::NOperationResult::kDataAfterEnd; 1003 else if (sres == SZ_ERROR_CRC) // (CrcError) 1004 opRes = NExtract::NOperationResult::kCRCError; 1005 else if (sres == SZ_ERROR_UNSUPPORTED) // (Unsupported) 1006 opRes = NExtract::NOperationResult::kUnsupportedMethod; 1007 else if (sres == SZ_ERROR_ARCHIVE) // (HeadersError) 1008 opRes = NExtract::NOperationResult::kDataError; 1009 else if (sres == SZ_ERROR_DATA) // (DataError) 1010 opRes = NExtract::NOperationResult::kDataError; 1011 else if (sres != SZ_OK) 1012 opRes = NExtract::NOperationResult::kDataError; 1013 else 1014 opRes = NExtract::NOperationResult::kOK; 1015 return opRes; 1016 } 1017 1018 1019 1020 1021 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, 1022 Int32 testMode, IArchiveExtractCallback *extractCallback)) 1023 { 1024 COM_TRY_BEGIN 1025 if (numItems == 0) 1026 return S_OK; 1027 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) 1028 return E_INVALIDARG; 1029 1030 const CXzStatInfo *stat = GetStat(); 1031 1032 if (stat) 1033 RINOK(extractCallback->SetTotal(stat->InSize)) 1034 1035 UInt64 currentTotalPacked = 0; 1036 RINOK(extractCallback->SetCompleted(¤tTotalPacked)) 1037 Int32 opRes; 1038 { 1039 CMyComPtr<ISequentialOutStream> realOutStream; 1040 const Int32 askMode = testMode ? 1041 NExtract::NAskMode::kTest : 1042 NExtract::NAskMode::kExtract; 1043 1044 RINOK(extractCallback->GetStream(0, &realOutStream, askMode)) 1045 1046 if (!testMode && !realOutStream) 1047 return S_OK; 1048 1049 RINOK(extractCallback->PrepareOperation(askMode)) 1050 1051 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps; 1052 lps->Init(extractCallback, true); 1053 1054 if (_needSeekToStart) 1055 { 1056 if (!_stream) 1057 return E_FAIL; 1058 RINOK(InStream_SeekToBegin(_stream)) 1059 } 1060 else 1061 _needSeekToStart = true; 1062 1063 1064 NCompress::NXz::CDecoder decoder; 1065 1066 const HRESULT hres = Decode(decoder, _seqStream, realOutStream, lps); 1067 1068 if (!decoder.MainDecodeSRes_wasUsed) 1069 return hres == S_OK ? E_FAIL : hres; 1070 1071 opRes = Get_Extract_OperationResult(decoder); 1072 if (opRes == NExtract::NOperationResult::kOK 1073 && hres != S_OK) 1074 opRes = NExtract::NOperationResult::kDataError; 1075 1076 // realOutStream.Release(); 1077 } 1078 return extractCallback->SetOperationResult(opRes); 1079 COM_TRY_END 1080 } 1081 1082 1083 1084 #ifndef Z7_EXTRACT_ONLY 1085 1086 Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType)) 1087 { 1088 *timeType = GET_FileTimeType_NotDefined_for_GetFileTimeType; 1089 // *timeType = NFileTimeType::kUnix; 1090 return S_OK; 1091 } 1092 1093 1094 Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, 1095 IArchiveUpdateCallback *updateCallback)) 1096 { 1097 COM_TRY_BEGIN 1098 1099 if (numItems == 0) 1100 { 1101 CSeqOutStreamWrap seqOutStream; 1102 seqOutStream.Init(outStream); 1103 SRes res = Xz_EncodeEmpty(&seqOutStream.vt); 1104 return SResToHRESULT(res); 1105 } 1106 1107 if (numItems != 1) 1108 return E_INVALIDARG; 1109 1110 { 1111 Z7_DECL_CMyComPtr_QI_FROM( 1112 IStreamSetRestriction, 1113 setRestriction, outStream) 1114 if (setRestriction) 1115 RINOK(setRestriction->SetRestriction(0, 0)) 1116 } 1117 1118 Int32 newData, newProps; 1119 UInt32 indexInArchive; 1120 if (!updateCallback) 1121 return E_FAIL; 1122 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)) 1123 1124 if (IntToBool(newProps)) 1125 { 1126 { 1127 NCOM::CPropVariant prop; 1128 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)) 1129 if (prop.vt != VT_EMPTY) 1130 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) 1131 return E_INVALIDARG; 1132 } 1133 } 1134 1135 if (IntToBool(newData)) 1136 { 1137 UInt64 dataSize; 1138 { 1139 NCOM::CPropVariant prop; 1140 RINOK(updateCallback->GetProperty(0, kpidSize, &prop)) 1141 if (prop.vt != VT_UI8) 1142 return E_INVALIDARG; 1143 dataSize = prop.uhVal.QuadPart; 1144 } 1145 1146 CMyComPtr2_Create<ICompressCoder, NCompress::NXz::CEncoder> encoder; 1147 1148 CXzProps &xzProps = encoder->xzProps; 1149 CLzma2EncProps &lzma2Props = xzProps.lzma2Props; 1150 1151 lzma2Props.lzmaProps.level = GetLevel(); 1152 1153 xzProps.reduceSize = dataSize; 1154 /* 1155 { 1156 NCOM::CPropVariant prop = (UInt64)dataSize; 1157 RINOK(encoder->SetCoderProp(NCoderPropID::kReduceSize, prop)) 1158 } 1159 */ 1160 1161 #ifndef Z7_ST 1162 1163 UInt32 numThreads = _numThreads; 1164 1165 const UInt32 kNumThreads_Max = 1024; 1166 if (numThreads > kNumThreads_Max) 1167 numThreads = kNumThreads_Max; 1168 1169 if (!_numThreads_WasForced 1170 && _numThreads >= 1 1171 && _memUsage_WasSet) 1172 { 1173 COneMethodInfo oneMethodInfo; 1174 if (!_methods.IsEmpty()) 1175 oneMethodInfo = _methods[0]; 1176 1177 SetGlobalLevelTo(oneMethodInfo); 1178 1179 const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0); 1180 if (!numThreads_WasSpecifiedInMethod) 1181 { 1182 // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already 1183 CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads); 1184 } 1185 1186 UInt64 cs = _numSolidBytes; 1187 if (cs != XZ_PROPS_BLOCK_SIZE_AUTO) 1188 oneMethodInfo.AddProp_BlockSize2(cs); 1189 cs = oneMethodInfo.Get_Xz_BlockSize(); 1190 1191 if (cs != XZ_PROPS_BLOCK_SIZE_AUTO && 1192 cs != XZ_PROPS_BLOCK_SIZE_SOLID) 1193 { 1194 const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads(); 1195 const UInt32 numBlockThreads_Original = numThreads / lzmaThreads; 1196 1197 if (numBlockThreads_Original > 1) 1198 { 1199 UInt32 numBlockThreads = numBlockThreads_Original; 1200 { 1201 const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false); 1202 for (; numBlockThreads > 1; numBlockThreads--) 1203 { 1204 UInt64 size = numBlockThreads * (lzmaMemUsage + cs); 1205 UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1; 1206 if (cs < ((UInt32)1 << 26)) numPackChunks++; 1207 if (cs < ((UInt32)1 << 24)) numPackChunks++; 1208 if (cs < ((UInt32)1 << 22)) numPackChunks++; 1209 size += numPackChunks * cs; 1210 // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20)); 1211 if (size <= _memUsage_Compress) 1212 break; 1213 } 1214 } 1215 if (numBlockThreads == 0) 1216 numBlockThreads = 1; 1217 if (numBlockThreads != numBlockThreads_Original) 1218 numThreads = numBlockThreads * lzmaThreads; 1219 } 1220 } 1221 } 1222 xzProps.numTotalThreads = (int)numThreads; 1223 1224 #endif // Z7_ST 1225 1226 1227 xzProps.blockSize = _numSolidBytes; 1228 if (_numSolidBytes == XZ_PROPS_BLOCK_SIZE_SOLID) 1229 { 1230 xzProps.lzma2Props.blockSize = LZMA2_ENC_PROPS_BLOCK_SIZE_SOLID; 1231 } 1232 1233 RINOK(encoder->SetCheckSize(_crcSize)) 1234 1235 { 1236 CXzFilterProps &filter = xzProps.filterProps; 1237 1238 if (_filterId == XZ_ID_Delta) 1239 { 1240 bool deltaDefined = false; 1241 FOR_VECTOR (j, _filterMethod.Props) 1242 { 1243 const CProp &prop = _filterMethod.Props[j]; 1244 if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4) 1245 { 1246 UInt32 delta = (UInt32)prop.Value.ulVal; 1247 if (delta < 1 || delta > 256) 1248 return E_INVALIDARG; 1249 filter.delta = delta; 1250 deltaDefined = true; 1251 } 1252 else 1253 return E_INVALIDARG; 1254 } 1255 if (!deltaDefined) 1256 return E_INVALIDARG; 1257 } 1258 filter.id = _filterId; 1259 } 1260 1261 FOR_VECTOR (i, _methods) 1262 { 1263 COneMethodInfo &m = _methods[i]; 1264 1265 FOR_VECTOR (j, m.Props) 1266 { 1267 const CProp &prop = m.Props[j]; 1268 RINOK(encoder->SetCoderProp(prop.Id, prop.Value)) 1269 } 1270 } 1271 1272 { 1273 CMyComPtr<ISequentialInStream> fileInStream; 1274 RINOK(updateCallback->GetStream(0, &fileInStream)) 1275 if (!fileInStream) 1276 return S_FALSE; 1277 { 1278 CMyComPtr<IStreamGetSize> streamGetSize; 1279 fileInStream.QueryInterface(IID_IStreamGetSize, &streamGetSize); 1280 if (streamGetSize) 1281 { 1282 UInt64 size; 1283 if (streamGetSize->GetSize(&size) == S_OK) 1284 dataSize = size; 1285 } 1286 } 1287 RINOK(updateCallback->SetTotal(dataSize)) 1288 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps; 1289 lps->Init(updateCallback, true); 1290 RINOK(encoder.Interface()->Code(fileInStream, outStream, NULL, NULL, lps)) 1291 } 1292 1293 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); 1294 } 1295 1296 if (indexInArchive != 0) 1297 return E_INVALIDARG; 1298 1299 Z7_DECL_CMyComPtr_QI_FROM( 1300 IArchiveUpdateCallbackFile, 1301 opCallback, updateCallback) 1302 if (opCallback) 1303 { 1304 RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate)) 1305 } 1306 1307 if (_stream) 1308 { 1309 const CXzStatInfo *stat = GetStat(); 1310 if (stat) 1311 { 1312 RINOK(updateCallback->SetTotal(stat->InSize)) 1313 } 1314 RINOK(InStream_SeekToBegin(_stream)) 1315 } 1316 1317 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps; 1318 lps->Init(updateCallback, true); 1319 1320 return NCompress::CopyStream(_stream, outStream, lps); 1321 1322 COM_TRY_END 1323 } 1324 1325 #endif 1326 1327 1328 HRESULT CHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) 1329 { 1330 UString name = nameSpec; 1331 name.MakeLower_Ascii(); 1332 if (name.IsEmpty()) 1333 return E_INVALIDARG; 1334 1335 #ifndef Z7_EXTRACT_ONLY 1336 1337 if (name[0] == L's') 1338 { 1339 const wchar_t *s = name.Ptr(1); 1340 if (*s == 0) 1341 { 1342 bool useStr = false; 1343 bool isSolid; 1344 switch (value.vt) 1345 { 1346 case VT_EMPTY: isSolid = true; break; 1347 case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break; 1348 case VT_BSTR: 1349 if (!StringToBool(value.bstrVal, isSolid)) 1350 useStr = true; 1351 break; 1352 default: return E_INVALIDARG; 1353 } 1354 if (!useStr) 1355 { 1356 _numSolidBytes = (isSolid ? XZ_PROPS_BLOCK_SIZE_SOLID : XZ_PROPS_BLOCK_SIZE_AUTO); 1357 return S_OK; 1358 } 1359 } 1360 return ParseSizeString(s, value, 1361 0, // percentsBase 1362 _numSolidBytes) ? S_OK: E_INVALIDARG; 1363 } 1364 1365 return CMultiMethodProps::SetProperty(name, value); 1366 1367 #else 1368 1369 { 1370 HRESULT hres; 1371 if (SetCommonProperty(name, value, hres)) 1372 return hres; 1373 } 1374 1375 return E_INVALIDARG; 1376 1377 #endif 1378 } 1379 1380 1381 1382 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)) 1383 { 1384 COM_TRY_BEGIN 1385 1386 Init(); 1387 1388 for (UInt32 i = 0; i < numProps; i++) 1389 { 1390 RINOK(SetProperty(names[i], values[i])) 1391 } 1392 1393 #ifndef Z7_EXTRACT_ONLY 1394 1395 if (!_filterMethod.MethodName.IsEmpty()) 1396 { 1397 unsigned k; 1398 for (k = 0; k < Z7_ARRAY_SIZE(g_NamePairs); k++) 1399 { 1400 const CMethodNamePair &pair = g_NamePairs[k]; 1401 if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) 1402 { 1403 _filterId = pair.Id; 1404 break; 1405 } 1406 } 1407 if (k == Z7_ARRAY_SIZE(g_NamePairs)) 1408 return E_INVALIDARG; 1409 } 1410 1411 _methods.DeleteFrontal(GetNumEmptyMethods()); 1412 if (_methods.Size() > 1) 1413 return E_INVALIDARG; 1414 if (_methods.Size() == 1) 1415 { 1416 AString &methodName = _methods[0].MethodName; 1417 if (methodName.IsEmpty()) 1418 methodName = k_LZMA2_Name; 1419 else if ( 1420 !methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name) 1421 && !methodName.IsEqualTo_Ascii_NoCase("xz")) 1422 return E_INVALIDARG; 1423 } 1424 1425 #endif 1426 1427 return S_OK; 1428 1429 COM_TRY_END 1430 } 1431 1432 1433 REGISTER_ARC_IO( 1434 "xz", "xz txz", "* .tar", 0xC, 1435 XZ_SIG, 0 1436 , NArcInfoFlags::kKeepName 1437 , 0 1438 , NULL) 1439 1440 }} 1441