Home | History | Annotate | Download | only in Archive
      1 // XzHandler.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../C/Alloc.h"
      6 #include "../../../C/XzCrc64.h"
      7 #include "../../../C/XzEnc.h"
      8 
      9 #include "../../Common/ComTry.h"
     10 #include "../../Common/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 
     35 static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
     36 static void SzFree(void *, void *address) { MyFree(address); }
     37 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
     38 
     39 namespace NArchive {
     40 namespace NXz {
     41 
     42 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 
     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 
     95 struct 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 
    105 struct CVirtProgress_To_LocalProgress: public IDecodeState
    106 {
    107   CLocalProgress *lps;
    108   CMyComPtr<ICompressProgressInfo> progress;
    109 
    110   HRESULT Progress();
    111 };
    112 
    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 
    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 
    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 
    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 
    202 static inline char GetHex(unsigned value)
    203 {
    204   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
    205 }
    206 
    207 static inline void AddHexToString(AString &s, Byte value)
    208 {
    209   s += GetHex(value >> 4);
    210   s += GetHex(value & 0xF);
    211 }
    212 
    213 static void AddUInt32ToString(AString &s, UInt32 value)
    214 {
    215   char temp[16];
    216   ConvertUInt32ToString(value, temp);
    217   s += temp;
    218 }
    219 
    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 
    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 
    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 
    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 
    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 
    375 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
    376 {
    377   *numItems = 1;
    378   return S_OK;
    379 }
    380 
    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 
    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 
    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;
    422   CXzsCPP() { Xzs_Construct(&p); }
    423   ~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 
    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 
    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 
    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 
    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 
    547 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
    548 {
    549   Close();
    550   _seqStream = stream;
    551   _isArc = true;
    552   _needSeekToStart = false;
    553   return S_OK;
    554 }
    555 
    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 
    583 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
    584 {
    585   return Stream->Read(data, size, processedSize);
    586 }
    587 
    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 
    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 
    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 
    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 
    802 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
    803 {
    804   *timeType = NFileTimeType::kUnix;
    805   return S_OK;
    806 }
    807 
    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 
    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