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