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(¤tTotalPacked));
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