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
26using namespace NWindows;
27
28namespace NCompress {
29namespace NLzma2 {
30
31HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props);
32
33}}
34
35static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
36static void SzFree(void *, void *address) { MyFree(address); }
37static ISzAlloc g_Alloc = { SzAlloc, SzFree };
38
39namespace NArchive {
40namespace NXz {
41
42struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
43
44static const wchar_t *k_LZMA2_Name = L"LZMA2";
45
46struct 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
68  CStatInfo() { Clear();   }
69
70  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
95struct IDecodeState: public CStatInfo
96{
97  SRes DecodeRes;
98
99  IDecodeState(): DecodeRes(SZ_OK) {}
100  virtual HRESULT Progress() = 0;
101
102  HRESULT Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream);
103};
104
105struct CVirtProgress_To_LocalProgress: public IDecodeState
106{
107  CLocalProgress *lps;
108  CMyComPtr<ICompressProgressInfo> progress;
109
110  HRESULT Progress();
111};
112
113HRESULT CVirtProgress_To_LocalProgress::Progress()
114{
115  lps->InSize = InSize;
116  lps->OutSize = OutSize;
117  return lps->SetCur();
118}
119
120
121class 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
143  void Init()
144  {
145    _filterId = 0;
146    CMultiMethodProps::Init();
147  }
148
149  HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback);
150
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
159public:
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
180CHandler::CHandler()
181{
182  Init();
183}
184
185static const Byte kProps[] =
186{
187  kpidSize,
188  kpidPackSize,
189  kpidMethod
190};
191
192static const Byte kArcProps[] =
193{
194  kpidMethod,
195  kpidNumStreams,
196  kpidNumBlocks
197};
198
199IMP_IInArchive_Props
200IMP_IInArchive_ArcProps
201
202static inline char GetHex(unsigned value)
203{
204  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
205}
206
207static inline void AddHexToString(AString &s, Byte value)
208{
209  s += GetHex(value >> 4);
210  s += GetHex(value & 0xF);
211}
212
213static void AddUInt32ToString(AString &s, UInt32 value)
214{
215  char temp[16];
216  ConvertUInt32ToString(value, temp);
217  s += temp;
218}
219
220static 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
241struct CMethodNamePair
242{
243  UInt32 Id;
244  const char *Name;
245};
246
247static 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
260static 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
296static void AddString(AString &dest, const AString &src)
297{
298  if (!dest.IsEmpty())
299    dest += ' ';
300  dest += src;
301}
302
303static 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
323static 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
346STDMETHODIMP 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
375STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
376{
377  *numItems = 1;
378  return S_OK;
379}
380
381STDMETHODIMP 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
397struct COpenCallbackWrap
398{
399  ICompressProgress p;
400  IArchiveOpenCallback *OpenCallback;
401  HRESULT Res;
402  COpenCallbackWrap(IArchiveOpenCallback *progress);
403};
404
405static 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
412COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback)
413{
414  p.Progress = OpenCallbackProgress;
415  OpenCallback = callback;
416  Res = SZ_OK;
417}
418
419struct CXzsCPP
420{
421  CXzs p;
422  CXzsCPP() { Xzs_Construct(&p); }
423  ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
424};
425
426
427struct CVirtProgress_To_OpenProgress: public IDecodeState
428{
429  IArchiveOpenCallback *Callback;
430  UInt64 Offset;
431
432  HRESULT Progress();
433};
434
435HRESULT 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
446static 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
465HRESULT 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
537STDMETHODIMP 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
547STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
548{
549  Close();
550  _seqStream = stream;
551  _isArc = true;
552  _needSeekToStart = false;
553  return S_OK;
554}
555
556STDMETHODIMP 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
571class CSeekToSeqStream:
572  public IInStream,
573  public CMyUnknownImp
574{
575public:
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
583STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
584{
585  return Stream->Read(data, size, processedSize);
586}
587
588STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
589
590struct CXzUnpackerCPP
591{
592  Byte *InBuf;
593  Byte *OutBuf;
594  CXzUnpacker p;
595
596  CXzUnpackerCPP(): InBuf(0), OutBuf(0)
597  {
598    XzUnpacker_Construct(&p, &g_Alloc);
599  }
600  ~CXzUnpackerCPP()
601  {
602    XzUnpacker_Free(&p);
603    MyFree(InBuf);
604    MyFree(OutBuf);
605  }
606};
607
608HRESULT 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
733STDMETHODIMP 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
802STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
803{
804  *timeType = NFileTimeType::kUnix;
805  return S_OK;
806}
807
808STDMETHODIMP 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
937STDMETHODIMP 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
979IMP_CreateArcIn
980IMP_CreateArcOut
981
982static 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
989REGISTER_ARC(xz)
990
991}}
992