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/IntToString.h"
     11 
     12 #include "../ICoder.h"
     13 
     14 #include "../Common/CWrappers.h"
     15 #include "../Common/ProgressUtils.h"
     16 #include "../Common/RegisterArc.h"
     17 #include "../Common/StreamUtils.h"
     18 
     19 #include "../Compress/CopyCoder.h"
     20 
     21 #include "IArchive.h"
     22 
     23 #include "Common/HandlerOut.h"
     24 
     25 using namespace NWindows;
     26 
     27 namespace NCompress {
     28 namespace NLzma2 {
     29 
     30 HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props);
     31 
     32 }}
     33 
     34 static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
     35 static void SzFree(void *, void *address) { MyFree(address); }
     36 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
     37 
     38 namespace NArchive {
     39 namespace NXz {
     40 
     41 struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
     42 
     43 class CHandler:
     44   public IInArchive,
     45   public IArchiveOpenSeq,
     46   #ifndef EXTRACT_ONLY
     47   public IOutArchive,
     48   public ISetProperties,
     49   public COutHandler,
     50   #endif
     51   public CMyUnknownImp
     52 {
     53   Int64 _startPosition;
     54   UInt64 _packSize;
     55   UInt64 _unpackSize;
     56   UInt64 _numBlocks;
     57   AString _methodsString;
     58   bool _useSeq;
     59   UInt64 _unpackSizeDefined;
     60   UInt64 _packSizeDefined;
     61 
     62   CMyComPtr<IInStream> _stream;
     63   CMyComPtr<ISequentialInStream> _seqStream;
     64 
     65   UInt32 _crcSize;
     66 
     67   void Init()
     68   {
     69     _crcSize = 4;
     70     COutHandler::Init();
     71   }
     72 
     73   HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
     74 
     75 public:
     76   MY_QUERYINTERFACE_BEGIN2(IInArchive)
     77   MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq)
     78   #ifndef EXTRACT_ONLY
     79   MY_QUERYINTERFACE_ENTRY(IOutArchive)
     80   MY_QUERYINTERFACE_ENTRY(ISetProperties)
     81   #endif
     82   MY_QUERYINTERFACE_END
     83   MY_ADDREF_RELEASE
     84 
     85   INTERFACE_IInArchive(;)
     86   STDMETHOD(OpenSeq)(ISequentialInStream *stream);
     87 
     88   #ifndef EXTRACT_ONLY
     89   INTERFACE_IOutArchive(;)
     90   STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps);
     91   #endif
     92 
     93   CHandler();
     94 };
     95 
     96 CHandler::CHandler()
     97 {
     98   Init();
     99 }
    100 
    101 STATPROPSTG kProps[] =
    102 {
    103   { NULL, kpidSize, VT_UI8},
    104   { NULL, kpidPackSize, VT_UI8},
    105   { NULL, kpidMethod, VT_BSTR}
    106 };
    107 
    108 STATPROPSTG kArcProps[] =
    109 {
    110   { NULL, kpidMethod, VT_BSTR},
    111   { NULL, kpidNumBlocks, VT_UI4}
    112 };
    113 
    114 IMP_IInArchive_Props
    115 IMP_IInArchive_ArcProps
    116 
    117 static char GetHex(Byte value)
    118 {
    119   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
    120 }
    121 
    122 static inline void AddHexToString(AString &res, Byte value)
    123 {
    124   res += GetHex((Byte)(value >> 4));
    125   res += GetHex((Byte)(value & 0xF));
    126 }
    127 
    128 static AString ConvertUInt32ToString(UInt32 value)
    129 {
    130   char temp[32];
    131   ::ConvertUInt32ToString(value, temp);
    132   return temp;
    133 }
    134 
    135 static AString Lzma2PropToString(int prop)
    136 {
    137   if ((prop & 1) == 0)
    138     return ConvertUInt32ToString(prop / 2 + 12);
    139   AString res;
    140   char c;
    141 
    142   UInt32 size = (2 | ((prop) & 1)) << ((prop) / 2 + 1);
    143 
    144   if (prop > 17)
    145   {
    146     res = ConvertUInt32ToString(size >> 10);
    147     c = 'm';
    148   }
    149   else
    150   {
    151     res = ConvertUInt32ToString(size);
    152     c = 'k';
    153   }
    154   return res + c;
    155 }
    156 
    157 struct CMethodNamePair
    158 {
    159   UInt32 Id;
    160   const char *Name;
    161 };
    162 
    163 static CMethodNamePair g_NamePairs[] =
    164 {
    165   { XZ_ID_Subblock, "SB" },
    166   { XZ_ID_Delta, "Delta" },
    167   { XZ_ID_X86, "x86" },
    168   { XZ_ID_PPC, "PPC" },
    169   { XZ_ID_IA64, "IA64" },
    170   { XZ_ID_ARM, "ARM" },
    171   { XZ_ID_ARMT, "ARMT" },
    172   { XZ_ID_SPARC, "SPARC" },
    173   { XZ_ID_LZMA2, "LZMA2" }
    174 };
    175 
    176 static AString GetMethodString(const CXzFilter &f)
    177 {
    178   AString s;
    179 
    180   for (int i = 0; i < sizeof(g_NamePairs) / sizeof(g_NamePairs[i]); i++)
    181     if (g_NamePairs[i].Id == f.id)
    182       s = g_NamePairs[i].Name;
    183   if (s.IsEmpty())
    184   {
    185     char temp[32];
    186     ::ConvertUInt64ToString(f.id, temp);
    187     s = temp;
    188   }
    189 
    190   if (f.propsSize > 0)
    191   {
    192     s += ':';
    193     if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
    194       s += Lzma2PropToString(f.props[0]);
    195     else if (f.id == XZ_ID_Delta && f.propsSize == 1)
    196       s += ConvertUInt32ToString((UInt32)f.props[0] + 1);
    197     else
    198     {
    199       s += '[';
    200       for (UInt32 bi = 0; bi < f.propsSize; bi++)
    201         AddHexToString(s, f.props[bi]);
    202       s += ']';
    203     }
    204   }
    205   return s;
    206 }
    207 
    208 static void AddString(AString &dest, const AString &src)
    209 {
    210   if (!dest.IsEmpty())
    211     dest += ' ';
    212   dest += src;
    213 }
    214 
    215 static const char *kChecks[] =
    216 {
    217   "NoCheck",
    218   "CRC32",
    219   NULL,
    220   NULL,
    221   "CRC64",
    222   NULL,
    223   NULL,
    224   NULL,
    225   NULL,
    226   NULL,
    227   "SHA256",
    228   NULL,
    229   NULL,
    230   NULL,
    231   NULL,
    232   NULL
    233 };
    234 
    235 static AString GetCheckString(const CXzs &xzs)
    236 {
    237   size_t i;
    238   UInt32 mask = 0;
    239   for (i = 0; i < xzs.num; i++)
    240     mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
    241   AString s;
    242   for (i = 0; i <= XZ_CHECK_MASK; i++)
    243     if (((mask >> i) & 1) != 0)
    244     {
    245       AString s2;
    246       if (kChecks[i])
    247         s2 = kChecks[i];
    248       else
    249         s2 = "Check-" + ConvertUInt32ToString((UInt32)i);
    250       AddString(s, s2);
    251     }
    252   return s;
    253 }
    254 
    255 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
    256 {
    257   COM_TRY_BEGIN
    258   NWindows::NCOM::CPropVariant prop;
    259   switch(propID)
    260   {
    261     case kpidNumBlocks: if (!_useSeq) prop = _numBlocks; break;
    262     case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
    263     case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
    264   }
    265   prop.Detach(value);
    266   return S_OK;
    267   COM_TRY_END
    268 }
    269 
    270 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
    271 {
    272   *numItems = 1;
    273   return S_OK;
    274 }
    275 
    276 STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID,  PROPVARIANT *value)
    277 {
    278   COM_TRY_BEGIN
    279   NWindows::NCOM::CPropVariant prop;
    280   switch(propID)
    281   {
    282     case kpidSize: if (_unpackSizeDefined) prop = _unpackSize; break;
    283     case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
    284     case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
    285   }
    286   prop.Detach(value);
    287   return S_OK;
    288   COM_TRY_END
    289 }
    290 
    291 
    292 struct COpenCallbackWrap
    293 {
    294   ICompressProgress p;
    295   IArchiveOpenCallback *OpenCallback;
    296   HRESULT Res;
    297   COpenCallbackWrap(IArchiveOpenCallback *progress);
    298 };
    299 
    300 static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */)
    301 {
    302   COpenCallbackWrap *p = (COpenCallbackWrap *)pp;
    303   p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
    304   return (SRes)p->Res;
    305 }
    306 
    307 COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback)
    308 {
    309   p.Progress = OpenCallbackProgress;
    310   OpenCallback = callback;
    311   Res = SZ_OK;
    312 }
    313 
    314 struct CXzsCPP
    315 {
    316   CXzs p;
    317   CXzsCPP() { Xzs_Construct(&p); }
    318   ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
    319 };
    320 
    321 HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback)
    322 {
    323   CSeekInStreamWrap inStreamImp(inStream);
    324 
    325   CLookToRead lookStream;
    326   LookToRead_CreateVTable(&lookStream, True);
    327   lookStream.realStream = &inStreamImp.p;
    328   LookToRead_Init(&lookStream);
    329 
    330   COpenCallbackWrap openWrap(callback);
    331   RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize));
    332   RINOK(callback->SetTotal(NULL, &_packSize));
    333 
    334   CXzsCPP xzs;
    335   SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &_startPosition, &openWrap.p, &g_Alloc);
    336   if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
    337     res = SZ_OK;
    338   if (res == SZ_OK)
    339   {
    340     _packSize -= _startPosition;
    341     _unpackSize = Xzs_GetUnpackSize(&xzs.p);
    342     _unpackSizeDefined = _packSizeDefined = true;
    343     _numBlocks = (UInt64)Xzs_GetNumBlocks(&xzs.p);
    344 
    345     RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
    346     CXzStreamFlags st;
    347     CSeqInStreamWrap inStreamWrap(inStream);
    348     SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
    349 
    350     if (res2 == SZ_OK)
    351     {
    352       CXzBlock block;
    353       Bool isIndex;
    354       UInt32 headerSizeRes;
    355       res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes);
    356       if (res2 == SZ_OK && !isIndex)
    357       {
    358         int numFilters = XzBlock_GetNumFilters(&block);
    359         for (int i = 0; i < numFilters; i++)
    360           AddString(_methodsString, GetMethodString(block.filters[i]));
    361       }
    362     }
    363     AddString(_methodsString, GetCheckString(xzs.p));
    364   }
    365 
    366   if (res != SZ_OK || _startPosition != 0)
    367   {
    368     RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
    369     CXzStreamFlags st;
    370     CSeqInStreamWrap inStreamWrap(inStream);
    371     SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
    372     if (res2 == SZ_OK)
    373     {
    374       res = res2;
    375       _startPosition = 0;
    376       _useSeq = True;
    377       _unpackSizeDefined = _packSizeDefined = false;
    378     }
    379   }
    380   if (res == SZ_ERROR_NO_ARCHIVE)
    381     return S_FALSE;
    382   RINOK(SResToHRESULT(res));
    383   _stream = inStream;
    384   _seqStream = inStream;
    385   return S_OK;
    386 }
    387 
    388 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
    389 {
    390   COM_TRY_BEGIN
    391   try
    392   {
    393     Close();
    394     return Open2(inStream, callback);
    395   }
    396   catch(...) { return S_FALSE; }
    397   COM_TRY_END
    398 }
    399 
    400 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
    401 {
    402   Close();
    403   _seqStream = stream;
    404   return S_OK;
    405 }
    406 
    407 STDMETHODIMP CHandler::Close()
    408 {
    409   _numBlocks = 0;
    410   _useSeq = true;
    411   _unpackSizeDefined = _packSizeDefined = false;
    412   _methodsString.Empty();
    413   _stream.Release();
    414   _seqStream.Release();
    415   return S_OK;
    416 }
    417 
    418 class CSeekToSeqStream:
    419   public IInStream,
    420   public CMyUnknownImp
    421 {
    422 public:
    423   CMyComPtr<ISequentialInStream> Stream;
    424   MY_UNKNOWN_IMP1(IInStream)
    425 
    426   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
    427   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
    428 };
    429 
    430 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    431 {
    432   return Stream->Read(data, size, processedSize);
    433 }
    434 
    435 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
    436 
    437 struct CXzUnpackerCPP
    438 {
    439   Byte *InBuf;
    440   Byte *OutBuf;
    441   CXzUnpacker p;
    442   CXzUnpackerCPP(): InBuf(0), OutBuf(0) {}
    443   ~CXzUnpackerCPP()
    444   {
    445     XzUnpacker_Free(&p);
    446     MyFree(InBuf);
    447     MyFree(OutBuf);
    448   }
    449 };
    450 
    451 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    452     Int32 testMode, IArchiveExtractCallback *extractCallback)
    453 {
    454   COM_TRY_BEGIN
    455   if (numItems == 0)
    456     return S_OK;
    457   if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
    458     return E_INVALIDARG;
    459 
    460   extractCallback->SetTotal(_packSize);
    461   UInt64 currentTotalPacked = 0;
    462   RINOK(extractCallback->SetCompleted(&currentTotalPacked));
    463   CMyComPtr<ISequentialOutStream> realOutStream;
    464   Int32 askMode = testMode ?
    465       NExtract::NAskMode::kTest :
    466       NExtract::NAskMode::kExtract;
    467 
    468   RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
    469 
    470   if (!testMode && !realOutStream)
    471     return S_OK;
    472 
    473   extractCallback->PrepareOperation(askMode);
    474 
    475   if (_stream)
    476   {
    477     RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
    478   }
    479 
    480   CLocalProgress *lps = new CLocalProgress;
    481   CMyComPtr<ICompressProgressInfo> progress = lps;
    482   lps->Init(extractCallback, true);
    483 
    484   CCompressProgressWrap progressWrap(progress);
    485 
    486   SRes res;
    487 
    488   const UInt32 kInBufSize = 1 << 15;
    489   const UInt32 kOutBufSize = 1 << 21;
    490 
    491   UInt32 inPos = 0;
    492   UInt32 inSize = 0;
    493   UInt32 outPos = 0;
    494   CXzUnpackerCPP xzu;
    495   res = XzUnpacker_Create(&xzu.p, &g_Alloc);
    496   if (res == SZ_OK)
    497   {
    498     xzu.InBuf = (Byte *)MyAlloc(kInBufSize);
    499     xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize);
    500     if (xzu.InBuf == 0 || xzu.OutBuf == 0)
    501       res = SZ_ERROR_MEM;
    502   }
    503   if (res == SZ_OK)
    504   for (;;)
    505   {
    506     if (inPos == inSize)
    507     {
    508       inPos = inSize = 0;
    509       RINOK(_seqStream->Read(xzu.InBuf, kInBufSize, &inSize));
    510     }
    511 
    512     SizeT inLen = inSize - inPos;
    513     SizeT outLen = kOutBufSize - outPos;
    514     ECoderStatus status;
    515     res = XzUnpacker_Code(&xzu.p,
    516         xzu.OutBuf + outPos, &outLen,
    517         xzu.InBuf + inPos, &inLen,
    518         (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status);
    519 
    520     // printf("\n_inPos = %6d  inLen = %5d, outLen = %5d", inPos, inLen, outLen);
    521 
    522     inPos += (UInt32)inLen;
    523     outPos += (UInt32)outLen;
    524     lps->InSize += inLen;
    525     lps->OutSize += outLen;
    526 
    527     bool finished = (((inLen == 0) && (outLen == 0)) || res != SZ_OK);
    528 
    529     if (outPos == kOutBufSize || finished)
    530     {
    531       if (realOutStream && outPos > 0)
    532       {
    533         RINOK(WriteStream(realOutStream, xzu.OutBuf, outPos));
    534       }
    535       outPos = 0;
    536     }
    537     if (finished)
    538     {
    539       _packSize = lps->InSize;
    540       _unpackSize = lps->OutSize;
    541       _packSizeDefined = _unpackSizeDefined = true;
    542       if (res == SZ_OK)
    543       {
    544         if (status == CODER_STATUS_NEEDS_MORE_INPUT)
    545         {
    546           if (XzUnpacker_IsStreamWasFinished(&xzu.p))
    547             _packSize -= xzu.p.padSize;
    548           else
    549             res = SZ_ERROR_DATA;
    550         }
    551         else
    552           res = SZ_ERROR_DATA;
    553       }
    554       break;
    555     }
    556     RINOK(lps->SetCur());
    557   }
    558 
    559   Int32 opRes;
    560   switch(res)
    561   {
    562     case SZ_OK:
    563       opRes = NExtract::NOperationResult::kOK; break;
    564     case SZ_ERROR_UNSUPPORTED:
    565       opRes = NExtract::NOperationResult::kUnSupportedMethod; break;
    566     case SZ_ERROR_CRC:
    567       opRes = NExtract::NOperationResult::kCRCError; break;
    568     case SZ_ERROR_DATA:
    569     case SZ_ERROR_ARCHIVE:
    570     case SZ_ERROR_NO_ARCHIVE:
    571       opRes = NExtract::NOperationResult::kDataError; break;
    572     default:
    573       return SResToHRESULT(res);
    574   }
    575   realOutStream.Release();
    576   RINOK(extractCallback->SetOperationResult(opRes));
    577   return S_OK;
    578   COM_TRY_END
    579 }
    580 
    581 #ifndef EXTRACT_ONLY
    582 
    583 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
    584 {
    585   *timeType = NFileTimeType::kUnix;
    586   return S_OK;
    587 }
    588 
    589 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
    590     IArchiveUpdateCallback *updateCallback)
    591 {
    592   CSeqOutStreamWrap seqOutStream(outStream);
    593 
    594   if (numItems == 0)
    595   {
    596     SRes res = Xz_EncodeEmpty(&seqOutStream.p);
    597     return SResToHRESULT(res);
    598   }
    599 
    600   if (numItems != 1)
    601     return E_INVALIDARG;
    602 
    603   Int32 newData, newProps;
    604   UInt32 indexInArchive;
    605   if (!updateCallback)
    606     return E_FAIL;
    607   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
    608 
    609   if (IntToBool(newProps))
    610   {
    611     {
    612       NCOM::CPropVariant prop;
    613       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
    614       if (prop.vt != VT_EMPTY)
    615         if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
    616           return E_INVALIDARG;
    617     }
    618   }
    619 
    620   if (IntToBool(newData))
    621   {
    622     {
    623       UInt64 size;
    624       NCOM::CPropVariant prop;
    625       RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
    626       if (prop.vt != VT_UI8)
    627         return E_INVALIDARG;
    628       size = prop.uhVal.QuadPart;
    629       RINOK(updateCallback->SetTotal(size));
    630     }
    631 
    632     CLzma2EncProps lzma2Props;
    633     Lzma2EncProps_Init(&lzma2Props);
    634 
    635     lzma2Props.lzmaProps.level = _level;
    636 
    637     CMyComPtr<ISequentialInStream> fileInStream;
    638     RINOK(updateCallback->GetStream(0, &fileInStream));
    639 
    640     CSeqInStreamWrap seqInStream(fileInStream);
    641 
    642     for (int i = 0; i < _methods.Size(); i++)
    643     {
    644       COneMethodInfo &m = _methods[i];
    645       SetCompressionMethod2(m
    646       #ifndef _7ZIP_ST
    647       , _numThreads
    648       #endif
    649       );
    650       if (m.IsLzma())
    651       {
    652         for (int j = 0; j < m.Props.Size(); j++)
    653         {
    654           const CProp &prop = m.Props[j];
    655           RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props));
    656         }
    657       }
    658     }
    659 
    660     #ifndef _7ZIP_ST
    661     lzma2Props.numTotalThreads = _numThreads;
    662     #endif
    663 
    664     CLocalProgress *lps = new CLocalProgress;
    665     CMyComPtr<ICompressProgressInfo> progress = lps;
    666     lps->Init(updateCallback, true);
    667 
    668     CCompressProgressWrap progressWrap(progress);
    669     SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &lzma2Props, False, &progressWrap.p);
    670     if (res == SZ_OK)
    671       return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
    672     return SResToHRESULT(res);
    673   }
    674   if (indexInArchive != 0)
    675     return E_INVALIDARG;
    676   if (_stream)
    677     RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
    678   return NCompress::CopyStream(_stream, outStream, 0);
    679 }
    680 
    681 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
    682 {
    683   COM_TRY_BEGIN
    684   BeforeSetProperty();
    685   for (int i = 0; i < numProps; i++)
    686   {
    687     RINOK(SetProperty(names[i], values[i]));
    688   }
    689   return S_OK;
    690   COM_TRY_END
    691 }
    692 
    693 #endif
    694 
    695 static IInArchive *CreateArc() { return new NArchive::NXz::CHandler; }
    696 #ifndef EXTRACT_ONLY
    697 static IOutArchive *CreateArcOut() { return new NArchive::NXz::CHandler; }
    698 #else
    699 #define CreateArcOut 0
    700 #endif
    701 
    702 static CArcInfo g_ArcInfo =
    703   { L"xz", L"xz txz", L"* .tar", 0xC, {0xFD, '7' , 'z', 'X', 'Z', '\0'}, 6, true, CreateArc, CreateArcOut };
    704 
    705 REGISTER_ARC(xz)
    706 
    707 }}
    708