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 
SzAlloc(void *,size_t size)34 static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
SzFree(void *,void * address)35 static void SzFree(void *, void *address) { MyFree(address); }
36 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
37 
38 namespace NArchive {
39 namespace NXz {
40 
CCrc64GenNArchive::NXz::CCrc64Gen41 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 
Init()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 
CHandler()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 
GetHex(Byte value)117 static char GetHex(Byte value)
118 {
119   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
120 }
121 
AddHexToString(AString & res,Byte value)122 static inline void AddHexToString(AString &res, Byte value)
123 {
124   res += GetHex((Byte)(value >> 4));
125   res += GetHex((Byte)(value & 0xF));
126 }
127 
ConvertUInt32ToString(UInt32 value)128 static AString ConvertUInt32ToString(UInt32 value)
129 {
130   char temp[32];
131   ::ConvertUInt32ToString(value, temp);
132   return temp;
133 }
134 
Lzma2PropToString(int prop)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 
GetMethodString(const CXzFilter & f)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 
AddString(AString & dest,const AString & src)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 
GetCheckString(const CXzs & xzs)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 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)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 
GetNumberOfItems(UInt32 * numItems)270 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
271 {
272   *numItems = 1;
273   return S_OK;
274 }
275 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)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 
OpenCallbackProgress(void * pp,UInt64 inSize,UInt64)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 
COpenCallbackWrap(IArchiveOpenCallback * callback)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;
CXzsCPPNArchive::NXz::CXzsCPP317   CXzsCPP() { Xzs_Construct(&p); }
~CXzsCPPNArchive::NXz::CXzsCPP318   ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
319 };
320 
Open2(IInStream * inStream,IArchiveOpenCallback * callback)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 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)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 
OpenSeq(ISequentialInStream * stream)400 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
401 {
402   Close();
403   _seqStream = stream;
404   return S_OK;
405 }
406 
Close()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 
Read(void * data,UInt32 size,UInt32 * processedSize)430 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
431 {
432   return Stream->Read(data, size, processedSize);
433 }
434 
Seek(Int64,UInt32,UInt64 *)435 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
436 
437 struct CXzUnpackerCPP
438 {
439   Byte *InBuf;
440   Byte *OutBuf;
441   CXzUnpacker p;
CXzUnpackerCPPNArchive::NXz::CXzUnpackerCPP442   CXzUnpackerCPP(): InBuf(0), OutBuf(0) {}
~CXzUnpackerCPPNArchive::NXz::CXzUnpackerCPP443   ~CXzUnpackerCPP()
444   {
445     XzUnpacker_Free(&p);
446     MyFree(InBuf);
447     MyFree(OutBuf);
448   }
449 };
450 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)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 
GetFileTimeType(UInt32 * timeType)583 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
584 {
585   *timeType = NFileTimeType::kUnix;
586   return S_OK;
587 }
588 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)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 
SetProperties(const wchar_t ** names,const PROPVARIANT * values,Int32 numProps)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 
CreateArc()695 static IInArchive *CreateArc() { return new NArchive::NXz::CHandler; }
696 #ifndef EXTRACT_ONLY
CreateArcOut()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