1// XzHandler.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6
7#include "../../Common/ComTry.h"
8#include "../../Common/Defs.h"
9#include "../../Common/IntToString.h"
10#include "../../Common/MyBuffer.h"
11#include "../../Common/StringToInt.h"
12
13#include "../../Windows/PropVariant.h"
14#include "../../Windows/System.h"
15
16#include "../Common/CWrappers.h"
17#include "../Common/ProgressUtils.h"
18#include "../Common/RegisterArc.h"
19#include "../Common/StreamUtils.h"
20
21#include "../Compress/CopyCoder.h"
22#include "../Compress/XzDecoder.h"
23#include "../Compress/XzEncoder.h"
24
25#include "IArchive.h"
26
27#include "Common/HandlerOut.h"
28
29using namespace NWindows;
30
31namespace NArchive {
32namespace NXz {
33
34#define k_LZMA2_Name "LZMA2"
35
36
37struct CBlockInfo
38{
39  unsigned StreamFlags;
40  UInt64 PackPos;
41  UInt64 PackSize; // pure value from Index record, it doesn't include pad zeros
42  UInt64 UnpackPos;
43};
44
45
46Z7_class_CHandler_final:
47  public IInArchive,
48  public IArchiveOpenSeq,
49  public IInArchiveGetStream,
50  public ISetProperties,
51 #ifndef Z7_EXTRACT_ONLY
52  public IOutArchive,
53 #endif
54  public CMyUnknownImp,
55 #ifndef Z7_EXTRACT_ONLY
56  public CMultiMethodProps
57 #else
58  public CCommonMethodProps
59 #endif
60{
61  Z7_COM_QI_BEGIN2(IInArchive)
62  Z7_COM_QI_ENTRY(IArchiveOpenSeq)
63  Z7_COM_QI_ENTRY(IInArchiveGetStream)
64  Z7_COM_QI_ENTRY(ISetProperties)
65 #ifndef Z7_EXTRACT_ONLY
66  Z7_COM_QI_ENTRY(IOutArchive)
67 #endif
68  Z7_COM_QI_END
69  Z7_COM_ADDREF_RELEASE
70
71  Z7_IFACE_COM7_IMP(IInArchive)
72  Z7_IFACE_COM7_IMP(IArchiveOpenSeq)
73  Z7_IFACE_COM7_IMP(IInArchiveGetStream)
74  Z7_IFACE_COM7_IMP(ISetProperties)
75 #ifndef Z7_EXTRACT_ONLY
76  Z7_IFACE_COM7_IMP(IOutArchive)
77 #endif
78
79  CXzStatInfo _stat;    // it's stat from backward parsing
80  CXzStatInfo _stat2;   // it's data from forward parsing, if the decoder was called
81  SRes _stat2_decode_SRes;
82  bool _stat_defined;
83  bool _stat2_defined;
84
85  const CXzStatInfo *GetStat() const
86  {
87    if (_stat_defined) return &_stat;
88    if (_stat2_defined) return &_stat2;
89    return NULL;
90  }
91
92  bool _isArc;
93  bool _needSeekToStart;
94  bool _firstBlockWasRead;
95
96  AString _methodsString;
97
98
99  #ifndef Z7_EXTRACT_ONLY
100
101  UInt32 _filterId;
102  UInt64 _numSolidBytes;
103
104  void InitXz()
105  {
106    _filterId = 0;
107    _numSolidBytes = XZ_PROPS_BLOCK_SIZE_AUTO;
108  }
109
110  #endif
111
112
113  void Init()
114  {
115    #ifndef Z7_EXTRACT_ONLY
116      InitXz();
117      CMultiMethodProps::Init();
118    #else
119      CCommonMethodProps::InitCommon();
120    #endif
121  }
122
123  HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value);
124
125  HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback);
126
127  HRESULT Decode(NCompress::NXz::CDecoder &decoder,
128      ISequentialInStream *seqInStream,
129      ISequentialOutStream *outStream,
130      ICompressProgressInfo *progress)
131  {
132    #ifndef Z7_ST
133    decoder._numThreads = _numThreads;
134    #endif
135    decoder._memUsage = _memUsage_Decompress;
136
137    HRESULT hres = decoder.Decode(seqInStream, outStream,
138        NULL, // *outSizeLimit
139        true, // finishStream
140        progress);
141
142    if (decoder.MainDecodeSRes_wasUsed
143        && decoder.MainDecodeSRes != SZ_ERROR_MEM
144        && decoder.MainDecodeSRes != SZ_ERROR_UNSUPPORTED)
145    {
146      // if (!_stat2_defined)
147      {
148        _stat2_decode_SRes = decoder.MainDecodeSRes;
149        _stat2 = decoder.Stat;
150        _stat2_defined = true;
151      }
152    }
153
154    return hres;
155  }
156
157public:
158  CBlockInfo *_blocks;
159  size_t _blocksArraySize;
160  UInt64 _maxBlocksSize;
161  CMyComPtr<IInStream> _stream;
162  CMyComPtr<ISequentialInStream> _seqStream;
163
164  CXzBlock _firstBlock;
165
166  CHandler();
167  ~CHandler();
168
169  HRESULT SeekToPackPos(UInt64 pos)
170  {
171    return InStream_SeekSet(_stream, pos);
172  }
173};
174
175
176CHandler::CHandler():
177    _blocks(NULL),
178    _blocksArraySize(0)
179{
180  #ifndef Z7_EXTRACT_ONLY
181  InitXz();
182  #endif
183}
184
185CHandler::~CHandler()
186{
187  MyFree(_blocks);
188}
189
190
191static const Byte kProps[] =
192{
193  kpidSize,
194  kpidPackSize,
195  kpidMethod
196};
197
198static const Byte kArcProps[] =
199{
200  kpidMethod,
201  kpidNumStreams,
202  kpidNumBlocks,
203  kpidClusterSize,
204  kpidCharacts
205};
206
207IMP_IInArchive_Props
208IMP_IInArchive_ArcProps
209
210static inline char GetHex(unsigned value)
211{
212  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
213}
214
215static inline void AddHexToString(AString &s, Byte value)
216{
217  s += GetHex(value >> 4);
218  s += GetHex(value & 0xF);
219}
220
221static void Lzma2PropToString(AString &s, unsigned prop)
222{
223  char c = 0;
224  UInt32 size;
225  if ((prop & 1) == 0)
226    size = prop / 2 + 12;
227  else
228  {
229    c = 'k';
230    size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1);
231    if (prop > 17)
232    {
233      size >>= 10;
234      c = 'm';
235    }
236  }
237  s.Add_UInt32(size);
238  if (c != 0)
239    s += c;
240}
241
242struct CMethodNamePair
243{
244  UInt32 Id;
245  const char *Name;
246};
247
248static const CMethodNamePair g_NamePairs[] =
249{
250  { XZ_ID_Subblock, "SB" },
251  { XZ_ID_Delta, "Delta" },
252  { XZ_ID_X86, "BCJ" },
253  { XZ_ID_PPC, "PPC" },
254  { XZ_ID_IA64, "IA64" },
255  { XZ_ID_ARM, "ARM" },
256  { XZ_ID_ARMT, "ARMT" },
257  { XZ_ID_SPARC, "SPARC" },
258  { XZ_ID_ARM64, "ARM64" },
259  { XZ_ID_LZMA2, "LZMA2" }
260};
261
262static void AddMethodString(AString &s, const CXzFilter &f)
263{
264  const char *p = NULL;
265  for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_NamePairs); i++)
266    if (g_NamePairs[i].Id == f.id)
267    {
268      p = g_NamePairs[i].Name;
269      break;
270    }
271  char temp[32];
272  if (!p)
273  {
274    ::ConvertUInt64ToString(f.id, temp);
275    p = temp;
276  }
277
278  s += p;
279
280  if (f.propsSize > 0)
281  {
282    s += ':';
283    if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
284      Lzma2PropToString(s, f.props[0]);
285    else if (f.id == XZ_ID_Delta && f.propsSize == 1)
286      s.Add_UInt32((UInt32)f.props[0] + 1);
287    else if (f.id == XZ_ID_ARM64 && f.propsSize == 1)
288      s.Add_UInt32((UInt32)f.props[0] + 16 + 2);
289    else
290    {
291      s += '[';
292      for (UInt32 bi = 0; bi < f.propsSize; bi++)
293        AddHexToString(s, f.props[bi]);
294      s += ']';
295    }
296  }
297}
298
299static const char * const kChecks[] =
300{
301    "NoCheck"
302  , "CRC32"
303  , NULL
304  , NULL
305  , "CRC64"
306  , NULL
307  , NULL
308  , NULL
309  , NULL
310  , NULL
311  , "SHA256"
312  , NULL
313  , NULL
314  , NULL
315  , NULL
316  , NULL
317};
318
319static void AddCheckString(AString &s, const CXzs &xzs)
320{
321  size_t i;
322  UInt32 mask = 0;
323  for (i = 0; i < xzs.num; i++)
324    mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
325  for (i = 0; i <= XZ_CHECK_MASK; i++)
326    if (((mask >> i) & 1) != 0)
327    {
328      s.Add_Space_if_NotEmpty();
329      if (kChecks[i])
330        s += kChecks[i];
331      else
332      {
333        s += "Check-";
334        s.Add_UInt32((UInt32)i);
335      }
336    }
337}
338
339Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
340{
341  COM_TRY_BEGIN
342  NCOM::CPropVariant prop;
343
344  const CXzStatInfo *stat = GetStat();
345
346  switch (propID)
347  {
348    case kpidPhySize: if (stat) prop = stat->InSize; break;
349    case kpidNumStreams: if (stat && stat->NumStreams_Defined) prop = stat->NumStreams; break;
350    case kpidNumBlocks: if (stat && stat->NumBlocks_Defined) prop = stat->NumBlocks; break;
351    case kpidUnpackSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break;
352    case kpidClusterSize: if (_stat_defined && _stat.NumBlocks_Defined && stat->NumBlocks > 1) prop = _maxBlocksSize; break;
353    case kpidCharacts:
354      if (_firstBlockWasRead)
355      {
356        AString s;
357        if (XzBlock_HasPackSize(&_firstBlock))
358          s.Add_OptSpaced("BlockPackSize");
359        if (XzBlock_HasUnpackSize(&_firstBlock))
360          s.Add_OptSpaced("BlockUnpackSize");
361        if (!s.IsEmpty())
362          prop = s;
363      }
364      break;
365
366
367    case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
368    case kpidErrorFlags:
369    {
370      UInt32 v = 0;
371      SRes sres = _stat2_decode_SRes;
372      if (!_isArc)                      v |= kpv_ErrorFlags_IsNotArc;
373      if (sres == SZ_ERROR_INPUT_EOF)   v |= kpv_ErrorFlags_UnexpectedEnd;
374      if (_stat2_defined && _stat2.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
375      if (sres == SZ_ERROR_ARCHIVE)     v |= kpv_ErrorFlags_HeadersError;
376      if (sres == SZ_ERROR_UNSUPPORTED) v |= kpv_ErrorFlags_UnsupportedMethod;
377      if (sres == SZ_ERROR_DATA)        v |= kpv_ErrorFlags_DataError;
378      if (sres == SZ_ERROR_CRC)         v |= kpv_ErrorFlags_CrcError;
379      if (v != 0)
380        prop = v;
381      break;
382    }
383
384    case kpidMainSubfile:
385    {
386      // debug only, comment it:
387      // if (_blocks) prop = (UInt32)0;
388      break;
389    }
390  }
391  prop.Detach(value);
392  return S_OK;
393  COM_TRY_END
394}
395
396Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
397{
398  *numItems = 1;
399  return S_OK;
400}
401
402Z7_COM7F_IMF(CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value))
403{
404  COM_TRY_BEGIN
405  const CXzStatInfo *stat = GetStat();
406  NCOM::CPropVariant prop;
407  switch (propID)
408  {
409    case kpidSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break;
410    case kpidPackSize: if (stat) prop = stat->InSize; break;
411    case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
412  }
413  prop.Detach(value);
414  return S_OK;
415  COM_TRY_END
416}
417
418
419struct COpenCallbackWrap
420{
421  ICompressProgress vt;
422  IArchiveOpenCallback *OpenCallback;
423  HRESULT Res;
424
425  // new clang shows "non-POD" warning for offsetof(), if we use constructor instead of Init()
426  void Init(IArchiveOpenCallback *progress);
427};
428
429static SRes OpenCallbackProgress(ICompressProgressPtr pp, UInt64 inSize, UInt64 /* outSize */)
430{
431  Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(COpenCallbackWrap)
432  if (p->OpenCallback)
433    p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
434  return HRESULT_To_SRes(p->Res, SZ_ERROR_PROGRESS);
435}
436
437void COpenCallbackWrap::Init(IArchiveOpenCallback *callback)
438{
439  vt.Progress = OpenCallbackProgress;
440  OpenCallback = callback;
441  Res = SZ_OK;
442}
443
444
445struct CXzsCPP
446{
447  CXzs p;
448  CXzsCPP() { Xzs_Construct(&p); }
449  ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
450};
451
452#define kInputBufSize ((size_t)1 << 10)
453
454struct CLookToRead2_CPP: public CLookToRead2
455{
456  CLookToRead2_CPP()
457  {
458    buf = NULL;
459    LookToRead2_CreateVTable(this,
460        True // Lookahead ?
461        );
462  }
463  void Alloc(size_t allocSize)
464  {
465    buf = (Byte *)MyAlloc(allocSize);
466    if (buf)
467      this->bufSize = allocSize;
468  }
469  ~CLookToRead2_CPP()
470  {
471    MyFree(buf);
472  }
473};
474
475
476static HRESULT SRes_to_Open_HRESULT(SRes res)
477{
478  switch (res)
479  {
480    case SZ_OK: return S_OK;
481    case SZ_ERROR_MEM: return E_OUTOFMEMORY;
482    case SZ_ERROR_PROGRESS: return E_ABORT;
483    /*
484    case SZ_ERROR_UNSUPPORTED:
485    case SZ_ERROR_CRC:
486    case SZ_ERROR_DATA:
487    case SZ_ERROR_ARCHIVE:
488    case SZ_ERROR_NO_ARCHIVE:
489      return S_FALSE;
490    */
491  }
492  return S_FALSE;
493}
494
495
496
497HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback)
498{
499  _needSeekToStart = true;
500
501  {
502    CXzStreamFlags st;
503    CSeqInStreamWrap inStreamWrap;
504
505    inStreamWrap.Init(inStream);
506
507    SRes res = Xz_ReadHeader(&st, &inStreamWrap.vt);
508
509    if (inStreamWrap.Res != S_OK)
510      return inStreamWrap.Res;
511    if (res != SZ_OK)
512      return SRes_to_Open_HRESULT(res);
513
514    {
515      CXzBlock block;
516      BoolInt isIndex;
517      UInt32 headerSizeRes;
518
519      SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.vt, &isIndex, &headerSizeRes);
520
521      if (inStreamWrap.Res != S_OK)
522        return inStreamWrap.Res;
523
524      if (res2 != SZ_OK)
525      {
526        if (res2 == SZ_ERROR_INPUT_EOF)
527        {
528          _stat2_decode_SRes = res2;
529          _stream = inStream;
530          _seqStream = inStream;
531          _isArc = true;
532          return S_OK;
533        }
534
535        if (res2 == SZ_ERROR_ARCHIVE)
536          return S_FALSE;
537      }
538      else if (!isIndex)
539      {
540        _firstBlockWasRead = true;
541        _firstBlock = block;
542
543        unsigned numFilters = XzBlock_GetNumFilters(&block);
544        for (unsigned i = 0; i < numFilters; i++)
545        {
546          _methodsString.Add_Space_if_NotEmpty();
547          AddMethodString(_methodsString, block.filters[i]);
548        }
549      }
550    }
551  }
552
553  RINOK(InStream_GetSize_SeekToEnd(inStream, _stat.InSize))
554  if (callback)
555  {
556    RINOK(callback->SetTotal(NULL, &_stat.InSize))
557  }
558
559  CSeekInStreamWrap inStreamImp;
560
561  inStreamImp.Init(inStream);
562
563  CLookToRead2_CPP lookStream;
564
565  lookStream.Alloc(kInputBufSize);
566
567  if (!lookStream.buf)
568    return E_OUTOFMEMORY;
569
570  lookStream.realStream = &inStreamImp.vt;
571  LookToRead2_INIT(&lookStream)
572
573  COpenCallbackWrap openWrap;
574  openWrap.Init(callback);
575
576  CXzsCPP xzs;
577  Int64 startPosition;
578  SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.vt, &startPosition, &openWrap.vt, &g_Alloc);
579  if (res == SZ_ERROR_PROGRESS)
580    return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res;
581  /*
582  if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
583    res = SZ_OK;
584  */
585  if (res == SZ_OK && startPosition == 0)
586  {
587    _stat_defined = true;
588
589    _stat.OutSize = Xzs_GetUnpackSize(&xzs.p);
590    _stat.UnpackSize_Defined = true;
591
592    _stat.NumStreams = xzs.p.num;
593    _stat.NumStreams_Defined = true;
594
595    _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p);
596    _stat.NumBlocks_Defined = true;
597
598    AddCheckString(_methodsString, xzs.p);
599
600    const size_t numBlocks = (size_t)_stat.NumBlocks + 1;
601    const size_t bytesAlloc = numBlocks * sizeof(CBlockInfo);
602
603    if (bytesAlloc / sizeof(CBlockInfo) == _stat.NumBlocks + 1)
604    {
605      _blocks = (CBlockInfo *)MyAlloc(bytesAlloc);
606      if (_blocks)
607      {
608        unsigned blockIndex = 0;
609        UInt64 unpackPos = 0;
610
611        for (size_t si = xzs.p.num; si != 0;)
612        {
613          si--;
614          const CXzStream &str = xzs.p.streams[si];
615          UInt64 packPos = str.startOffset + XZ_STREAM_HEADER_SIZE;
616
617          for (size_t bi = 0; bi < str.numBlocks; bi++)
618          {
619            const CXzBlockSizes &bs = str.blocks[bi];
620            const UInt64 packSizeAligned = bs.totalSize + ((0 - (unsigned)bs.totalSize) & 3);
621
622            if (bs.unpackSize != 0)
623            {
624              if (blockIndex >= _stat.NumBlocks)
625                return E_FAIL;
626
627              CBlockInfo &block = _blocks[blockIndex++];
628              block.StreamFlags = str.flags;
629              block.PackSize = bs.totalSize; // packSizeAligned;
630              block.PackPos = packPos;
631              block.UnpackPos = unpackPos;
632            }
633            packPos += packSizeAligned;
634            unpackPos += bs.unpackSize;
635            if (_maxBlocksSize < bs.unpackSize)
636              _maxBlocksSize = bs.unpackSize;
637          }
638        }
639
640        /*
641        if (blockIndex != _stat.NumBlocks)
642        {
643          // there are Empty blocks;
644        }
645        */
646        if (_stat.OutSize != unpackPos)
647          return E_FAIL;
648        CBlockInfo &block = _blocks[blockIndex++];
649        block.StreamFlags = 0;
650        block.PackSize = 0;
651        block.PackPos = 0;
652        block.UnpackPos = unpackPos;
653        _blocksArraySize = blockIndex;
654      }
655    }
656  }
657  else
658  {
659    res = SZ_OK;
660  }
661
662  RINOK(SRes_to_Open_HRESULT(res))
663
664  _stream = inStream;
665  _seqStream = inStream;
666  _isArc = true;
667  return S_OK;
668}
669
670
671
672Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback))
673{
674  COM_TRY_BEGIN
675  {
676    Close();
677    return Open2(inStream, callback);
678  }
679  COM_TRY_END
680}
681
682Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
683{
684  Close();
685  _seqStream = stream;
686  _isArc = true;
687  _needSeekToStart = false;
688  return S_OK;
689}
690
691Z7_COM7F_IMF(CHandler::Close())
692{
693  XzStatInfo_Clear(&_stat);
694  XzStatInfo_Clear(&_stat2);
695  _stat_defined = false;
696  _stat2_defined = false;
697  _stat2_decode_SRes = SZ_OK;
698
699  _isArc = false;
700  _needSeekToStart = false;
701  _firstBlockWasRead = false;
702
703   _methodsString.Empty();
704  _stream.Release();
705  _seqStream.Release();
706
707  MyFree(_blocks);
708  _blocks = NULL;
709  _blocksArraySize = 0;
710  _maxBlocksSize = 0;
711
712  return S_OK;
713}
714
715
716struct CXzUnpackerCPP2
717{
718  Byte *InBuf;
719  // Byte *OutBuf;
720  CXzUnpacker p;
721
722  CXzUnpackerCPP2();
723  ~CXzUnpackerCPP2();
724};
725
726CXzUnpackerCPP2::CXzUnpackerCPP2(): InBuf(NULL)
727  // , OutBuf(NULL)
728{
729  XzUnpacker_Construct(&p, &g_Alloc);
730}
731
732CXzUnpackerCPP2::~CXzUnpackerCPP2()
733{
734  XzUnpacker_Free(&p);
735  MidFree(InBuf);
736  // MidFree(OutBuf);
737}
738
739
740Z7_CLASS_IMP_COM_1(
741  CInStream
742  , IInStream
743)
744  Z7_IFACE_COM7_IMP(ISequentialInStream)
745
746  UInt64 _virtPos;
747public:
748  UInt64 Size;
749  UInt64 _cacheStartPos;
750  size_t _cacheSize;
751  CByteBuffer _cache;
752  // UInt64 _startPos;
753  CXzUnpackerCPP2 xz;
754
755  void InitAndSeek()
756  {
757    _virtPos = 0;
758    _cacheStartPos = 0;
759    _cacheSize = 0;
760    // _startPos = startPos;
761  }
762
763  CHandler *_handlerSpec;
764  CMyComPtr<IUnknown> _handler;
765
766  // ~CInStream();
767};
768
769/*
770CInStream::~CInStream()
771{
772  // _cache.Free();
773}
774*/
775
776static size_t FindBlock(const CBlockInfo *blocks, size_t numBlocks, UInt64 pos)
777{
778  size_t left = 0, right = numBlocks;
779  for (;;)
780  {
781    size_t mid = (left + right) / 2;
782    if (mid == left)
783      return left;
784    if (pos < blocks[mid].UnpackPos)
785      right = mid;
786    else
787      left = mid;
788  }
789}
790
791
792
793static HRESULT DecodeBlock(CXzUnpackerCPP2 &xzu,
794    ISequentialInStream *seqInStream,
795    unsigned streamFlags,
796    UInt64 packSize, // pure size from Index record, it doesn't include pad zeros
797    size_t unpackSize, Byte *dest
798    // , ICompressProgressInfo *progress
799    )
800{
801  const size_t kInBufSize = (size_t)1 << 16;
802
803  XzUnpacker_Init(&xzu.p);
804
805  if (!xzu.InBuf)
806  {
807    xzu.InBuf = (Byte *)MidAlloc(kInBufSize);
808    if (!xzu.InBuf)
809      return E_OUTOFMEMORY;
810  }
811
812  xzu.p.streamFlags = (UInt16)streamFlags;
813  XzUnpacker_PrepareToRandomBlockDecoding(&xzu.p);
814
815  XzUnpacker_SetOutBuf(&xzu.p, dest, unpackSize);
816
817  const UInt64 packSizeAligned = packSize + ((0 - (unsigned)packSize) & 3);
818  UInt64 packRem = packSizeAligned;
819
820  UInt32 inSize = 0;
821  SizeT inPos = 0;
822  SizeT outPos = 0;
823
824  HRESULT readRes = S_OK;
825
826  for (;;)
827  {
828    if (inPos == inSize && readRes == S_OK)
829    {
830      inPos = 0;
831      inSize = 0;
832      UInt32 rem = kInBufSize;
833      if (rem > packRem)
834        rem = (UInt32)packRem;
835      if (rem != 0)
836        readRes = seqInStream->Read(xzu.InBuf, rem, &inSize);
837    }
838
839    SizeT inLen = inSize - inPos;
840    SizeT outLen = unpackSize - outPos;
841
842    ECoderStatus status;
843
844    const SRes res = XzUnpacker_Code(&xzu.p,
845        // dest + outPos,
846        NULL,
847        &outLen,
848        xzu.InBuf + inPos, &inLen,
849        (inLen == 0), // srcFinished
850        CODER_FINISH_END, &status);
851
852    // return E_OUTOFMEMORY;
853    // res = SZ_ERROR_CRC;
854
855    if (res != SZ_OK)
856    {
857      if (res == SZ_ERROR_CRC)
858        return S_FALSE;
859      return SResToHRESULT(res);
860    }
861
862    inPos += inLen;
863    outPos += outLen;
864
865    packRem -= inLen;
866
867    const BoolInt blockFinished = XzUnpacker_IsBlockFinished(&xzu.p);
868
869    if ((inLen == 0 && outLen == 0) || blockFinished)
870    {
871      if (packRem != 0 || !blockFinished || unpackSize != outPos)
872        return S_FALSE;
873      if (XzUnpacker_GetPackSizeForIndex(&xzu.p) != packSize)
874        return S_FALSE;
875      return S_OK;
876    }
877  }
878}
879
880
881Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
882{
883  COM_TRY_BEGIN
884
885  if (processedSize)
886    *processedSize = 0;
887  if (size == 0)
888    return S_OK;
889
890  {
891    if (_virtPos >= Size)
892      return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL;
893    {
894      UInt64 rem = Size - _virtPos;
895      if (size > rem)
896        size = (UInt32)rem;
897    }
898  }
899
900  if (size == 0)
901    return S_OK;
902
903  if (_virtPos < _cacheStartPos || _virtPos >= _cacheStartPos + _cacheSize)
904  {
905    const size_t bi = FindBlock(_handlerSpec->_blocks, _handlerSpec->_blocksArraySize, _virtPos);
906    const CBlockInfo &block = _handlerSpec->_blocks[bi];
907    const UInt64 unpackSize = _handlerSpec->_blocks[bi + 1].UnpackPos - block.UnpackPos;
908    if (_cache.Size() < unpackSize)
909      return E_FAIL;
910
911    _cacheSize = 0;
912
913    RINOK(_handlerSpec->SeekToPackPos(block.PackPos))
914    RINOK(DecodeBlock(xz, _handlerSpec->_seqStream, block.StreamFlags, block.PackSize,
915        (size_t)unpackSize, _cache))
916    _cacheStartPos = block.UnpackPos;
917    _cacheSize = (size_t)unpackSize;
918  }
919
920  {
921    const size_t offset = (size_t)(_virtPos - _cacheStartPos);
922    const size_t rem = _cacheSize - offset;
923    if (size > rem)
924      size = (UInt32)rem;
925    memcpy(data, _cache + offset, size);
926    _virtPos += size;
927    if (processedSize)
928      *processedSize = size;
929    return S_OK;
930  }
931
932  COM_TRY_END
933}
934
935
936Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
937{
938  switch (seekOrigin)
939  {
940    case STREAM_SEEK_SET: break;
941    case STREAM_SEEK_CUR: offset += _virtPos; break;
942    case STREAM_SEEK_END: offset += Size; break;
943    default: return STG_E_INVALIDFUNCTION;
944  }
945  if (offset < 0)
946    return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
947  _virtPos = (UInt64)offset;
948  if (newPosition)
949    *newPosition = (UInt64)offset;
950  return S_OK;
951}
952
953
954
955static const UInt64 kMaxBlockSize_for_GetStream = (UInt64)1 << 40;
956
957Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
958{
959  COM_TRY_BEGIN
960
961  *stream = NULL;
962
963  if (index != 0)
964    return E_INVALIDARG;
965
966  if (!_stat.UnpackSize_Defined
967      || _maxBlocksSize == 0 // 18.02
968      || _maxBlocksSize > kMaxBlockSize_for_GetStream
969      || _maxBlocksSize != (size_t)_maxBlocksSize)
970    return S_FALSE;
971
972  UInt64 memSize;
973  if (!NSystem::GetRamSize(memSize))
974    memSize = (UInt64)(sizeof(size_t)) << 28;
975  {
976    if (_maxBlocksSize > memSize / 4)
977      return S_FALSE;
978  }
979
980  CInStream *spec = new CInStream;
981  CMyComPtr<ISequentialInStream> specStream = spec;
982  spec->_cache.Alloc((size_t)_maxBlocksSize);
983  spec->_handlerSpec = this;
984  spec->_handler = (IInArchive *)this;
985  spec->Size = _stat.OutSize;
986  spec->InitAndSeek();
987
988  *stream = specStream.Detach();
989  return S_OK;
990
991  COM_TRY_END
992}
993
994
995static Int32 Get_Extract_OperationResult(const NCompress::NXz::CDecoder &decoder)
996{
997  Int32 opRes;
998  SRes sres = decoder.MainDecodeSRes;
999  if (sres == SZ_ERROR_NO_ARCHIVE) // (!IsArc)
1000    opRes = NExtract::NOperationResult::kIsNotArc;
1001  else if (sres == SZ_ERROR_INPUT_EOF) // (UnexpectedEnd)
1002    opRes = NExtract::NOperationResult::kUnexpectedEnd;
1003  else if (decoder.Stat.DataAfterEnd)
1004    opRes = NExtract::NOperationResult::kDataAfterEnd;
1005  else if (sres == SZ_ERROR_CRC) // (CrcError)
1006    opRes = NExtract::NOperationResult::kCRCError;
1007  else if (sres == SZ_ERROR_UNSUPPORTED) // (Unsupported)
1008    opRes = NExtract::NOperationResult::kUnsupportedMethod;
1009  else if (sres == SZ_ERROR_ARCHIVE) //  (HeadersError)
1010    opRes = NExtract::NOperationResult::kDataError;
1011  else if (sres == SZ_ERROR_DATA)  // (DataError)
1012    opRes = NExtract::NOperationResult::kDataError;
1013  else if (sres != SZ_OK)
1014    opRes = NExtract::NOperationResult::kDataError;
1015  else
1016    opRes = NExtract::NOperationResult::kOK;
1017  return opRes;
1018}
1019
1020
1021
1022
1023Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1024    Int32 testMode, IArchiveExtractCallback *extractCallback))
1025{
1026  COM_TRY_BEGIN
1027  if (numItems == 0)
1028    return S_OK;
1029  if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
1030    return E_INVALIDARG;
1031
1032  const CXzStatInfo *stat = GetStat();
1033
1034  if (stat)
1035    extractCallback->SetTotal(stat->InSize);
1036
1037  UInt64 currentTotalPacked = 0;
1038  RINOK(extractCallback->SetCompleted(&currentTotalPacked))
1039  CMyComPtr<ISequentialOutStream> realOutStream;
1040  const Int32 askMode = testMode ?
1041      NExtract::NAskMode::kTest :
1042      NExtract::NAskMode::kExtract;
1043
1044  RINOK(extractCallback->GetStream(0, &realOutStream, askMode))
1045
1046  if (!testMode && !realOutStream)
1047    return S_OK;
1048
1049  extractCallback->PrepareOperation(askMode);
1050
1051  CLocalProgress *lps = new CLocalProgress;
1052  CMyComPtr<ICompressProgressInfo> lpsRef = lps;
1053  lps->Init(extractCallback, true);
1054
1055  if (_needSeekToStart)
1056  {
1057    if (!_stream)
1058      return E_FAIL;
1059    RINOK(InStream_SeekToBegin(_stream))
1060  }
1061  else
1062    _needSeekToStart = true;
1063
1064
1065  NCompress::NXz::CDecoder decoder;
1066
1067  HRESULT hres = Decode(decoder, _seqStream, realOutStream, lpsRef);
1068
1069  if (!decoder.MainDecodeSRes_wasUsed)
1070    return hres == S_OK ? E_FAIL : hres;
1071
1072  Int32 opRes = Get_Extract_OperationResult(decoder);
1073  if (opRes == NExtract::NOperationResult::kOK
1074      && hres != S_OK)
1075    opRes = NExtract::NOperationResult::kDataError;
1076
1077  realOutStream.Release();
1078  return extractCallback->SetOperationResult(opRes);
1079  COM_TRY_END
1080}
1081
1082
1083
1084#ifndef Z7_EXTRACT_ONLY
1085
1086Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType))
1087{
1088  *timeType = GET_FileTimeType_NotDefined_for_GetFileTimeType;
1089  // *timeType = NFileTimeType::kUnix;
1090  return S_OK;
1091}
1092
1093
1094Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
1095    IArchiveUpdateCallback *updateCallback))
1096{
1097  COM_TRY_BEGIN
1098
1099  if (numItems == 0)
1100  {
1101    CSeqOutStreamWrap seqOutStream;
1102    seqOutStream.Init(outStream);
1103    SRes res = Xz_EncodeEmpty(&seqOutStream.vt);
1104    return SResToHRESULT(res);
1105  }
1106
1107  if (numItems != 1)
1108    return E_INVALIDARG;
1109
1110  {
1111    Z7_DECL_CMyComPtr_QI_FROM(
1112        IStreamSetRestriction,
1113        setRestriction, outStream)
1114    if (setRestriction)
1115      RINOK(setRestriction->SetRestriction(0, 0))
1116  }
1117
1118  Int32 newData, newProps;
1119  UInt32 indexInArchive;
1120  if (!updateCallback)
1121    return E_FAIL;
1122  RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive))
1123
1124  if (IntToBool(newProps))
1125  {
1126    {
1127      NCOM::CPropVariant prop;
1128      RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop))
1129      if (prop.vt != VT_EMPTY)
1130        if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
1131          return E_INVALIDARG;
1132    }
1133  }
1134
1135  if (IntToBool(newData))
1136  {
1137    UInt64 dataSize;
1138    {
1139      NCOM::CPropVariant prop;
1140      RINOK(updateCallback->GetProperty(0, kpidSize, &prop))
1141      if (prop.vt != VT_UI8)
1142        return E_INVALIDARG;
1143      dataSize = prop.uhVal.QuadPart;
1144    }
1145
1146    NCompress::NXz::CEncoder *encoderSpec = new NCompress::NXz::CEncoder;
1147    CMyComPtr<ICompressCoder> encoder = encoderSpec;
1148
1149    CXzProps &xzProps = encoderSpec->xzProps;
1150    CLzma2EncProps &lzma2Props = xzProps.lzma2Props;
1151
1152    lzma2Props.lzmaProps.level = GetLevel();
1153
1154    xzProps.reduceSize = dataSize;
1155    /*
1156    {
1157      NCOM::CPropVariant prop = (UInt64)dataSize;
1158      RINOK(encoderSpec->SetCoderProp(NCoderPropID::kReduceSize, prop))
1159    }
1160    */
1161
1162    #ifndef Z7_ST
1163
1164    UInt32 numThreads = _numThreads;
1165
1166    const UInt32 kNumThreads_Max = 1024;
1167    if (numThreads > kNumThreads_Max)
1168      numThreads = kNumThreads_Max;
1169
1170    if (!_numThreads_WasForced
1171        && _numThreads >= 1
1172        && _memUsage_WasSet)
1173    {
1174      COneMethodInfo oneMethodInfo;
1175      if (!_methods.IsEmpty())
1176        oneMethodInfo = _methods[0];
1177
1178      SetGlobalLevelTo(oneMethodInfo);
1179
1180      const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0);
1181      if (!numThreads_WasSpecifiedInMethod)
1182      {
1183        // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already
1184        CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads);
1185      }
1186
1187      UInt64 cs = _numSolidBytes;
1188      if (cs != XZ_PROPS_BLOCK_SIZE_AUTO)
1189        oneMethodInfo.AddProp_BlockSize2(cs);
1190      cs = oneMethodInfo.Get_Xz_BlockSize();
1191
1192      if (cs != XZ_PROPS_BLOCK_SIZE_AUTO &&
1193          cs != XZ_PROPS_BLOCK_SIZE_SOLID)
1194      {
1195        const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads();
1196        const UInt32 numBlockThreads_Original = numThreads / lzmaThreads;
1197
1198        if (numBlockThreads_Original > 1)
1199        {
1200          UInt32 numBlockThreads = numBlockThreads_Original;
1201          {
1202            const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false);
1203            for (; numBlockThreads > 1; numBlockThreads--)
1204            {
1205              UInt64 size = numBlockThreads * (lzmaMemUsage + cs);
1206              UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1;
1207              if (cs < ((UInt32)1 << 26)) numPackChunks++;
1208              if (cs < ((UInt32)1 << 24)) numPackChunks++;
1209              if (cs < ((UInt32)1 << 22)) numPackChunks++;
1210              size += numPackChunks * cs;
1211              // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20));
1212              if (size <= _memUsage_Compress)
1213                break;
1214            }
1215          }
1216          if (numBlockThreads == 0)
1217            numBlockThreads = 1;
1218          if (numBlockThreads != numBlockThreads_Original)
1219            numThreads = numBlockThreads * lzmaThreads;
1220        }
1221      }
1222    }
1223    xzProps.numTotalThreads = (int)numThreads;
1224
1225    #endif // Z7_ST
1226
1227
1228    xzProps.blockSize = _numSolidBytes;
1229    if (_numSolidBytes == XZ_PROPS_BLOCK_SIZE_SOLID)
1230    {
1231      xzProps.lzma2Props.blockSize = LZMA2_ENC_PROPS_BLOCK_SIZE_SOLID;
1232    }
1233
1234    RINOK(encoderSpec->SetCheckSize(_crcSize))
1235
1236    {
1237      CXzFilterProps &filter = xzProps.filterProps;
1238
1239      if (_filterId == XZ_ID_Delta)
1240      {
1241        bool deltaDefined = false;
1242        FOR_VECTOR (j, _filterMethod.Props)
1243        {
1244          const CProp &prop = _filterMethod.Props[j];
1245          if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4)
1246          {
1247            UInt32 delta = (UInt32)prop.Value.ulVal;
1248            if (delta < 1 || delta > 256)
1249              return E_INVALIDARG;
1250            filter.delta = delta;
1251            deltaDefined = true;
1252          }
1253          else
1254            return E_INVALIDARG;
1255        }
1256        if (!deltaDefined)
1257          return E_INVALIDARG;
1258      }
1259      filter.id = _filterId;
1260    }
1261
1262    FOR_VECTOR (i, _methods)
1263    {
1264      COneMethodInfo &m = _methods[i];
1265
1266      FOR_VECTOR (j, m.Props)
1267      {
1268        const CProp &prop = m.Props[j];
1269        RINOK(encoderSpec->SetCoderProp(prop.Id, prop.Value))
1270      }
1271    }
1272
1273    {
1274      CMyComPtr<ISequentialInStream> fileInStream;
1275      RINOK(updateCallback->GetStream(0, &fileInStream))
1276      if (!fileInStream)
1277        return S_FALSE;
1278      {
1279        CMyComPtr<IStreamGetSize> streamGetSize;
1280        fileInStream.QueryInterface(IID_IStreamGetSize, &streamGetSize);
1281        if (streamGetSize)
1282        {
1283          UInt64 size;
1284          if (streamGetSize->GetSize(&size) == S_OK)
1285            dataSize = size;
1286        }
1287      }
1288      RINOK(updateCallback->SetTotal(dataSize))
1289      CLocalProgress *lps = new CLocalProgress;
1290      CMyComPtr<ICompressProgressInfo> progress = lps;
1291      lps->Init(updateCallback, true);
1292      RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, progress))
1293    }
1294
1295    return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
1296  }
1297
1298  if (indexInArchive != 0)
1299    return E_INVALIDARG;
1300
1301  Z7_DECL_CMyComPtr_QI_FROM(
1302      IArchiveUpdateCallbackFile,
1303      opCallback, updateCallback)
1304  if (opCallback)
1305  {
1306    RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate))
1307  }
1308
1309  if (_stream)
1310  {
1311    const CXzStatInfo *stat = GetStat();
1312    if (stat)
1313    {
1314      RINOK(updateCallback->SetTotal(stat->InSize))
1315    }
1316    RINOK(InStream_SeekToBegin(_stream))
1317  }
1318
1319  CLocalProgress *lps = new CLocalProgress;
1320  CMyComPtr<ICompressProgressInfo> progress = lps;
1321  lps->Init(updateCallback, true);
1322
1323  return NCompress::CopyStream(_stream, outStream, progress);
1324
1325  COM_TRY_END
1326}
1327
1328#endif
1329
1330
1331HRESULT CHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
1332{
1333  UString name = nameSpec;
1334  name.MakeLower_Ascii();
1335  if (name.IsEmpty())
1336    return E_INVALIDARG;
1337
1338  #ifndef Z7_EXTRACT_ONLY
1339
1340  if (name[0] == L's')
1341  {
1342    const wchar_t *s = name.Ptr(1);
1343    if (*s == 0)
1344    {
1345      bool useStr = false;
1346      bool isSolid;
1347      switch (value.vt)
1348      {
1349        case VT_EMPTY: isSolid = true; break;
1350        case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
1351        case VT_BSTR:
1352          if (!StringToBool(value.bstrVal, isSolid))
1353            useStr = true;
1354          break;
1355        default: return E_INVALIDARG;
1356      }
1357      if (!useStr)
1358      {
1359        _numSolidBytes = (isSolid ? XZ_PROPS_BLOCK_SIZE_SOLID : XZ_PROPS_BLOCK_SIZE_AUTO);
1360        return S_OK;
1361      }
1362    }
1363    return ParseSizeString(s, value,
1364        0, // percentsBase
1365        _numSolidBytes) ? S_OK: E_INVALIDARG;
1366  }
1367
1368  return CMultiMethodProps::SetProperty(name, value);
1369
1370  #else
1371
1372  {
1373    HRESULT hres;
1374    if (SetCommonProperty(name, value, hres))
1375      return hres;
1376  }
1377
1378  return E_INVALIDARG;
1379
1380  #endif
1381}
1382
1383
1384
1385Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
1386{
1387  COM_TRY_BEGIN
1388
1389  Init();
1390
1391  for (UInt32 i = 0; i < numProps; i++)
1392  {
1393    RINOK(SetProperty(names[i], values[i]))
1394  }
1395
1396  #ifndef Z7_EXTRACT_ONLY
1397
1398  if (!_filterMethod.MethodName.IsEmpty())
1399  {
1400    unsigned k;
1401    for (k = 0; k < Z7_ARRAY_SIZE(g_NamePairs); k++)
1402    {
1403      const CMethodNamePair &pair = g_NamePairs[k];
1404      if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name))
1405      {
1406        _filterId = pair.Id;
1407        break;
1408      }
1409    }
1410    if (k == Z7_ARRAY_SIZE(g_NamePairs))
1411      return E_INVALIDARG;
1412  }
1413
1414  _methods.DeleteFrontal(GetNumEmptyMethods());
1415  if (_methods.Size() > 1)
1416    return E_INVALIDARG;
1417  if (_methods.Size() == 1)
1418  {
1419    AString &methodName = _methods[0].MethodName;
1420    if (methodName.IsEmpty())
1421      methodName = k_LZMA2_Name;
1422    else if (
1423        !methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name)
1424        && !methodName.IsEqualTo_Ascii_NoCase("xz"))
1425      return E_INVALIDARG;
1426  }
1427
1428  #endif
1429
1430  return S_OK;
1431
1432  COM_TRY_END
1433}
1434
1435
1436REGISTER_ARC_IO(
1437  "xz", "xz txz", "* .tar", 0xC,
1438  XZ_SIG, 0
1439  , NArcInfoFlags::kKeepName
1440  , 0
1441  , NULL)
1442
1443}}
1444