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