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