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