• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
29 using namespace NWindows;
30 
31 namespace NArchive {
32 namespace NXz {
33 
34 #define k_LZMA2_Name "LZMA2"
35 
36 
37 struct 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 
46 Z7_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 
157 public:
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 
176 CHandler::CHandler():
177     _blocks(NULL),
178     _blocksArraySize(0)
179 {
180   #ifndef Z7_EXTRACT_ONLY
181   InitXz();
182   #endif
183 }
184 
185 CHandler::~CHandler()
186 {
187   MyFree(_blocks);
188 }
189 
190 
191 static const Byte kProps[] =
192 {
193   kpidSize,
194   kpidPackSize,
195   kpidMethod
196 };
197 
198 static const Byte kArcProps[] =
199 {
200   kpidMethod,
201   kpidNumStreams,
202   kpidNumBlocks,
203   kpidClusterSize,
204   kpidCharacts
205 };
206 
207 IMP_IInArchive_Props
208 IMP_IInArchive_ArcProps
209 
210 static inline char GetHex(unsigned value)
211 {
212   return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
213 }
214 
215 static inline void AddHexToString(AString &s, Byte value)
216 {
217   s += GetHex(value >> 4);
218   s += GetHex(value & 0xF);
219 }
220 
221 static 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 
242 struct CMethodNamePair
243 {
244   UInt32 Id;
245   const char *Name;
246 };
247 
248 static 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 
262 static 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 
299 static 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 
319 static 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 
339 Z7_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 
396 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
397 {
398   *numItems = 1;
399   return S_OK;
400 }
401 
402 Z7_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 
419 struct 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 
429 static 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 
437 void COpenCallbackWrap::Init(IArchiveOpenCallback *callback)
438 {
439   vt.Progress = OpenCallbackProgress;
440   OpenCallback = callback;
441   Res = SZ_OK;
442 }
443 
444 
445 struct 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 
454 struct 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 
476 static 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 
497 HRESULT 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 
672 Z7_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 
682 Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream))
683 {
684   Close();
685   _seqStream = stream;
686   _isArc = true;
687   _needSeekToStart = false;
688   return S_OK;
689 }
690 
691 Z7_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 
716 struct CXzUnpackerCPP2
717 {
718   Byte *InBuf;
719   // Byte *OutBuf;
720   CXzUnpacker p;
721 
722   CXzUnpackerCPP2();
723   ~CXzUnpackerCPP2();
724 };
725 
726 CXzUnpackerCPP2::CXzUnpackerCPP2(): InBuf(NULL)
727   // , OutBuf(NULL)
728 {
729   XzUnpacker_Construct(&p, &g_Alloc);
730 }
731 
732 CXzUnpackerCPP2::~CXzUnpackerCPP2()
733 {
734   XzUnpacker_Free(&p);
735   MidFree(InBuf);
736   // MidFree(OutBuf);
737 }
738 
739 
740 Z7_CLASS_IMP_COM_1(
741   CInStream
742   , IInStream
743 )
744   Z7_IFACE_COM7_IMP(ISequentialInStream)
745 
746   UInt64 _virtPos;
747 public:
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 /*
770 CInStream::~CInStream()
771 {
772   // _cache.Free();
773 }
774 */
775 
776 static 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 
793 static 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 
881 Z7_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 
936 Z7_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 
955 static const UInt64 kMaxBlockSize_for_GetStream = (UInt64)1 << 40;
956 
957 Z7_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 
995 static 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 
1023 Z7_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 
1086 Z7_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 
1094 Z7_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 
1331 HRESULT 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 
1385 Z7_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 
1436 REGISTER_ARC_IO(
1437   "xz", "xz txz", "* .tar", 0xC,
1438   XZ_SIG, 0
1439   , NArcInfoFlags::kKeepName
1440   , 0
1441   , NULL)
1442 
1443 }}
1444