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