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 
CCrc64GenNArchive::NXz::CCrc64Gen44 struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
45 
46 static const char *k_LZMA2_Name = "LZMA2";
47 
Clear()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 
Init()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 
Decode2(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,CDecoder & decoder,ICompressProgressInfo * progress)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 
CHandler()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 
GetHex(unsigned value)161 static inline char GetHex(unsigned value)
162 {
163   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
164 }
165 
AddHexToString(AString & s,Byte value)166 static inline void AddHexToString(AString &s, Byte value)
167 {
168   s += GetHex(value >> 4);
169   s += GetHex(value & 0xF);
170 }
171 
AddUInt32ToString(AString & s,UInt32 value)172 static void AddUInt32ToString(AString &s, UInt32 value)
173 {
174   char temp[16];
175   ConvertUInt32ToString(value, temp);
176   s += temp;
177 }
178 
Lzma2PropToString(AString & s,unsigned prop)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 
GetMethodString(const CXzFilter & f)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 
AddString(AString & dest,const AString & src)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 
GetCheckString(const CXzs & xzs)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 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)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 
GetNumberOfItems(UInt32 * numItems)333 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
334 {
335   *numItems = 1;
336   return S_OK;
337 }
338 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)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 
OpenCallbackProgress(void * pp,UInt64 inSize,UInt64)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 
COpenCallbackWrap(IArchiveOpenCallback * callback)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;
CXzsCPPNArchive::NXz::CXzsCPP381   CXzsCPP() { Xzs_Construct(&p); }
~CXzsCPPNArchive::NXz::CXzsCPP382   ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
383 };
384 
SRes_to_Open_HRESULT(SRes res)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 
Open2(IInStream * inStream,IArchiveOpenCallback * callback)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 
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)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 
OpenSeq(ISequentialInStream * stream)490 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
491 {
492   Close();
493   _seqStream = stream;
494   _isArc = true;
495   _needSeekToStart = false;
496   return S_OK;
497 }
498 
Close()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 
Read(void * data,UInt32 size,UInt32 * processedSize)526 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
527 {
528   return Stream->Read(data, size, processedSize);
529 }
530 
Seek(Int64,UInt32,UInt64 *)531 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
532 
CXzUnpackerCPP()533 CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(0), OutBuf(0)
534 {
535   XzUnpacker_Construct(&p, &g_Alloc);
536 }
537 
~CXzUnpackerCPP()538 CXzUnpackerCPP::~CXzUnpackerCPP()
539 {
540   XzUnpacker_Free(&p);
541   MyFree(InBuf);
542   MyFree(OutBuf);
543 }
544 
Decode(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,ICompressProgressInfo * progress)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 
Get_Extract_OperationResult() const673 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 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)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 
GetFileTimeType(UInt32 * timeType)747 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
748 {
749   *timeType = NFileTimeType::kUnix;
750   return S_OK;
751 }
752 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)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 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)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