1 // XzHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/Alloc.h"
6 #include "../../../C/XzCrc64.h"
7 #include "../../../C/XzEnc.h"
8
9 #include "../../Common/ComTry.h"
10 #include "../../Common/Defs.h"
11 #include "../../Common/IntToString.h"
12
13 #include "../../Windows/PropVariant.h"
14
15 #include "../ICoder.h"
16
17 #include "../Common/CWrappers.h"
18 #include "../Common/ProgressUtils.h"
19 #include "../Common/RegisterArc.h"
20 #include "../Common/StreamUtils.h"
21
22 #include "../Compress/CopyCoder.h"
23
24 #include "IArchive.h"
25
26 #ifndef EXTRACT_ONLY
27 #include "Common/HandlerOut.h"
28 #endif
29
30 #include "XzHandler.h"
31
32 using namespace NWindows;
33
34 namespace NCompress {
35 namespace NLzma2 {
36
37 HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props);
38
39 }}
40
41 namespace NArchive {
42 namespace NXz {
43
CCrc64GenNArchive::NXz::CCrc64Gen44 struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
45
46 static const char *k_LZMA2_Name = "LZMA2";
47
Clear()48 void CStatInfo::Clear()
49 {
50 InSize = 0;
51 OutSize = 0;
52 PhySize = 0;
53
54 NumStreams = 0;
55 NumBlocks = 0;
56
57 UnpackSize_Defined = false;
58
59 NumStreams_Defined = false;
60 NumBlocks_Defined = false;
61
62 IsArc = false;
63 UnexpectedEnd = false;
64 DataAfterEnd = false;
65 Unsupported = false;
66 HeadersError = false;
67 DataError = false;
68 CrcError = false;
69 }
70
71 class CHandler:
72 public IInArchive,
73 public IArchiveOpenSeq,
74 #ifndef EXTRACT_ONLY
75 public IOutArchive,
76 public ISetProperties,
77 public CMultiMethodProps,
78 #endif
79 public CMyUnknownImp
80 {
81 CStatInfo _stat;
82
83 bool _isArc;
84 bool _needSeekToStart;
85 bool _phySize_Defined;
86
87 CMyComPtr<IInStream> _stream;
88 CMyComPtr<ISequentialInStream> _seqStream;
89
90 AString _methodsString;
91
92 #ifndef EXTRACT_ONLY
93
94 UInt32 _filterId;
95
Init()96 void Init()
97 {
98 _filterId = 0;
99 CMultiMethodProps::Init();
100 }
101
102 #endif
103
104 HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback);
105
Decode2(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,CDecoder & decoder,ICompressProgressInfo * progress)106 HRESULT Decode2(ISequentialInStream *seqInStream, ISequentialOutStream *outStream,
107 CDecoder &decoder, ICompressProgressInfo *progress)
108 {
109 RINOK(decoder.Decode(seqInStream, outStream, progress));
110 _stat = decoder;
111 _phySize_Defined = true;
112 return S_OK;
113 }
114
115 public:
116 MY_QUERYINTERFACE_BEGIN2(IInArchive)
117 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq)
118 #ifndef EXTRACT_ONLY
119 MY_QUERYINTERFACE_ENTRY(IOutArchive)
120 MY_QUERYINTERFACE_ENTRY(ISetProperties)
121 #endif
122 MY_QUERYINTERFACE_END
123 MY_ADDREF_RELEASE
124
125 INTERFACE_IInArchive(;)
126 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
127
128 #ifndef EXTRACT_ONLY
129 INTERFACE_IOutArchive(;)
130 STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
131 #endif
132
133 CHandler();
134 };
135
CHandler()136 CHandler::CHandler()
137 {
138 #ifndef EXTRACT_ONLY
139 Init();
140 #endif
141 }
142
143
144 static const Byte kProps[] =
145 {
146 kpidSize,
147 kpidPackSize,
148 kpidMethod
149 };
150
151 static const Byte kArcProps[] =
152 {
153 kpidMethod,
154 kpidNumStreams,
155 kpidNumBlocks
156 };
157
158 IMP_IInArchive_Props
159 IMP_IInArchive_ArcProps
160
GetHex(unsigned value)161 static inline char GetHex(unsigned value)
162 {
163 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
164 }
165
AddHexToString(AString & s,Byte value)166 static inline void AddHexToString(AString &s, Byte value)
167 {
168 s += GetHex(value >> 4);
169 s += GetHex(value & 0xF);
170 }
171
AddUInt32ToString(AString & s,UInt32 value)172 static void AddUInt32ToString(AString &s, UInt32 value)
173 {
174 char temp[16];
175 ConvertUInt32ToString(value, temp);
176 s += temp;
177 }
178
Lzma2PropToString(AString & s,unsigned prop)179 static void Lzma2PropToString(AString &s, unsigned prop)
180 {
181 char c = 0;
182 UInt32 size;
183 if ((prop & 1) == 0)
184 size = prop / 2 + 12;
185 else
186 {
187 c = 'k';
188 size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1);
189 if (prop > 17)
190 {
191 size >>= 10;
192 c = 'm';
193 }
194 }
195 AddUInt32ToString(s, size);
196 if (c != 0)
197 s += c;
198 }
199
200 struct CMethodNamePair
201 {
202 UInt32 Id;
203 const char *Name;
204 };
205
206 static const CMethodNamePair g_NamePairs[] =
207 {
208 { XZ_ID_Subblock, "SB" },
209 { XZ_ID_Delta, "Delta" },
210 { XZ_ID_X86, "BCJ" },
211 { XZ_ID_PPC, "PPC" },
212 { XZ_ID_IA64, "IA64" },
213 { XZ_ID_ARM, "ARM" },
214 { XZ_ID_ARMT, "ARMT" },
215 { XZ_ID_SPARC, "SPARC" },
216 { XZ_ID_LZMA2, "LZMA2" }
217 };
218
GetMethodString(const CXzFilter & f)219 static AString GetMethodString(const CXzFilter &f)
220 {
221 const char *p = NULL;
222 for (unsigned i = 0; i < ARRAY_SIZE(g_NamePairs); i++)
223 if (g_NamePairs[i].Id == f.id)
224 {
225 p = g_NamePairs[i].Name;
226 break;
227 }
228 char temp[32];
229 if (!p)
230 {
231 ::ConvertUInt64ToString(f.id, temp);
232 p = temp;
233 }
234
235 AString s = p;
236
237 if (f.propsSize > 0)
238 {
239 s += ':';
240 if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
241 Lzma2PropToString(s, f.props[0]);
242 else if (f.id == XZ_ID_Delta && f.propsSize == 1)
243 AddUInt32ToString(s, (UInt32)f.props[0] + 1);
244 else
245 {
246 s += '[';
247 for (UInt32 bi = 0; bi < f.propsSize; bi++)
248 AddHexToString(s, f.props[bi]);
249 s += ']';
250 }
251 }
252 return s;
253 }
254
AddString(AString & dest,const AString & src)255 static void AddString(AString &dest, const AString &src)
256 {
257 dest.Add_Space_if_NotEmpty();
258 dest += src;
259 }
260
261 static const char * const kChecks[] =
262 {
263 "NoCheck"
264 , "CRC32"
265 , NULL
266 , NULL
267 , "CRC64"
268 , NULL
269 , NULL
270 , NULL
271 , NULL
272 , NULL
273 , "SHA256"
274 , NULL
275 , NULL
276 , NULL
277 , NULL
278 , NULL
279 };
280
GetCheckString(const CXzs & xzs)281 static AString GetCheckString(const CXzs &xzs)
282 {
283 size_t i;
284 UInt32 mask = 0;
285 for (i = 0; i < xzs.num; i++)
286 mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
287 AString s;
288 for (i = 0; i <= XZ_CHECK_MASK; i++)
289 if (((mask >> i) & 1) != 0)
290 {
291 AString s2;
292 if (kChecks[i])
293 s2 = kChecks[i];
294 else
295 {
296 s2 = "Check-";
297 AddUInt32ToString(s2, (UInt32)i);
298 }
299 AddString(s, s2);
300 }
301 return s;
302 }
303
GetArchiveProperty(PROPID propID,PROPVARIANT * value)304 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
305 {
306 COM_TRY_BEGIN
307 NCOM::CPropVariant prop;
308 switch (propID)
309 {
310 case kpidPhySize: if (_phySize_Defined) prop = _stat.PhySize; break;
311 case kpidNumStreams: if (_stat.NumStreams_Defined) prop = _stat.NumStreams; break;
312 case kpidNumBlocks: if (_stat.NumBlocks_Defined) prop = _stat.NumBlocks; break;
313 case kpidUnpackSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break;
314 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
315 case kpidErrorFlags:
316 {
317 UInt32 v = 0;
318 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
319 if (_stat.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
320 if (_stat.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
321 if (_stat.HeadersError) v |= kpv_ErrorFlags_HeadersError;
322 if (_stat.Unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
323 if (_stat.DataError) v |= kpv_ErrorFlags_DataError;
324 if (_stat.CrcError) v |= kpv_ErrorFlags_CrcError;
325 prop = v;
326 }
327 }
328 prop.Detach(value);
329 return S_OK;
330 COM_TRY_END
331 }
332
GetNumberOfItems(UInt32 * numItems)333 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
334 {
335 *numItems = 1;
336 return S_OK;
337 }
338
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)339 STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value)
340 {
341 COM_TRY_BEGIN
342 NCOM::CPropVariant prop;
343 switch (propID)
344 {
345 case kpidSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break;
346 case kpidPackSize: if (_phySize_Defined) prop = _stat.PhySize; break;
347 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
348 }
349 prop.Detach(value);
350 return S_OK;
351 COM_TRY_END
352 }
353
354
355 struct COpenCallbackWrap
356 {
357 ICompressProgress p;
358 IArchiveOpenCallback *OpenCallback;
359 HRESULT Res;
360 COpenCallbackWrap(IArchiveOpenCallback *progress);
361 };
362
OpenCallbackProgress(void * pp,UInt64 inSize,UInt64)363 static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */)
364 {
365 COpenCallbackWrap *p = (COpenCallbackWrap *)pp;
366 if (p->OpenCallback)
367 p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
368 return (SRes)p->Res;
369 }
370
COpenCallbackWrap(IArchiveOpenCallback * callback)371 COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback)
372 {
373 p.Progress = OpenCallbackProgress;
374 OpenCallback = callback;
375 Res = SZ_OK;
376 }
377
378 struct CXzsCPP
379 {
380 CXzs p;
CXzsCPPNArchive::NXz::CXzsCPP381 CXzsCPP() { Xzs_Construct(&p); }
~CXzsCPPNArchive::NXz::CXzsCPP382 ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
383 };
384
SRes_to_Open_HRESULT(SRes res)385 static HRESULT SRes_to_Open_HRESULT(SRes res)
386 {
387 switch (res)
388 {
389 case SZ_OK: return S_OK;
390 case SZ_ERROR_MEM: return E_OUTOFMEMORY;
391 case SZ_ERROR_PROGRESS: return E_ABORT;
392 /*
393 case SZ_ERROR_UNSUPPORTED:
394 case SZ_ERROR_CRC:
395 case SZ_ERROR_DATA:
396 case SZ_ERROR_ARCHIVE:
397 case SZ_ERROR_NO_ARCHIVE:
398 return S_FALSE;
399 */
400 }
401 return S_FALSE;
402 }
403
Open2(IInStream * inStream,IArchiveOpenCallback * callback)404 HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback)
405 {
406 _needSeekToStart = true;
407
408 {
409 CXzStreamFlags st;
410 CSeqInStreamWrap inStreamWrap(inStream);
411 SRes res = Xz_ReadHeader(&st, &inStreamWrap.p);
412 if (res != SZ_OK)
413 return SRes_to_Open_HRESULT(res);
414
415 {
416 CXzBlock block;
417 Bool isIndex;
418 UInt32 headerSizeRes;
419 SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes);
420 if (res2 == SZ_OK && !isIndex)
421 {
422 unsigned numFilters = XzBlock_GetNumFilters(&block);
423 for (unsigned i = 0; i < numFilters; i++)
424 AddString(_methodsString, GetMethodString(block.filters[i]));
425 }
426 }
427 }
428
429 RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize));
430 if (callback)
431 {
432 RINOK(callback->SetTotal(NULL, &_stat.PhySize));
433 }
434
435 CSeekInStreamWrap inStreamImp(inStream);
436
437 CLookToRead lookStream;
438 LookToRead_CreateVTable(&lookStream, True);
439 lookStream.realStream = &inStreamImp.p;
440 LookToRead_Init(&lookStream);
441
442 COpenCallbackWrap openWrap(callback);
443
444 CXzsCPP xzs;
445 Int64 startPosition;
446 SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &startPosition, &openWrap.p, &g_Alloc);
447 if (res == SZ_ERROR_PROGRESS)
448 return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res;
449 /*
450 if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
451 res = SZ_OK;
452 */
453 if (res == SZ_OK && startPosition == 0)
454 {
455 _phySize_Defined = true;
456
457 _stat.OutSize = Xzs_GetUnpackSize(&xzs.p);
458 _stat.UnpackSize_Defined = true;
459
460 _stat.NumStreams = xzs.p.num;
461 _stat.NumStreams_Defined = true;
462
463 _stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p);
464 _stat.NumBlocks_Defined = true;
465
466 AddString(_methodsString, GetCheckString(xzs.p));
467 }
468 else
469 {
470 res = SZ_OK;
471 }
472
473 RINOK(SRes_to_Open_HRESULT(res));
474 _stream = inStream;
475 _seqStream = inStream;
476 _isArc = true;
477 return S_OK;
478 }
479
Open(IInStream * inStream,const UInt64 *,IArchiveOpenCallback * callback)480 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
481 {
482 COM_TRY_BEGIN
483 {
484 Close();
485 return Open2(inStream, callback);
486 }
487 COM_TRY_END
488 }
489
OpenSeq(ISequentialInStream * stream)490 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
491 {
492 Close();
493 _seqStream = stream;
494 _isArc = true;
495 _needSeekToStart = false;
496 return S_OK;
497 }
498
Close()499 STDMETHODIMP CHandler::Close()
500 {
501 _stat.Clear();
502
503 _isArc = false;
504 _needSeekToStart = false;
505
506 _phySize_Defined = false;
507
508 _methodsString.Empty();
509 _stream.Release();
510 _seqStream.Release();
511 return S_OK;
512 }
513
514 class CSeekToSeqStream:
515 public IInStream,
516 public CMyUnknownImp
517 {
518 public:
519 CMyComPtr<ISequentialInStream> Stream;
520 MY_UNKNOWN_IMP1(IInStream)
521
522 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
523 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
524 };
525
Read(void * data,UInt32 size,UInt32 * processedSize)526 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
527 {
528 return Stream->Read(data, size, processedSize);
529 }
530
Seek(Int64,UInt32,UInt64 *)531 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
532
CXzUnpackerCPP()533 CXzUnpackerCPP::CXzUnpackerCPP(): InBuf(0), OutBuf(0)
534 {
535 XzUnpacker_Construct(&p, &g_Alloc);
536 }
537
~CXzUnpackerCPP()538 CXzUnpackerCPP::~CXzUnpackerCPP()
539 {
540 XzUnpacker_Free(&p);
541 MyFree(InBuf);
542 MyFree(OutBuf);
543 }
544
Decode(ISequentialInStream * seqInStream,ISequentialOutStream * outStream,ICompressProgressInfo * progress)545 HRESULT CDecoder::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress)
546 {
547 const size_t kInBufSize = 1 << 15;
548 const size_t kOutBufSize = 1 << 21;
549
550 Clear();
551 DecodeRes = SZ_OK;
552
553 XzUnpacker_Init(&xzu.p);
554 if (!xzu.InBuf)
555 xzu.InBuf = (Byte *)MyAlloc(kInBufSize);
556 if (!xzu.OutBuf)
557 xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize);
558
559 UInt32 inSize = 0;
560 SizeT inPos = 0;
561 SizeT outPos = 0;
562
563 for (;;)
564 {
565 if (inPos == inSize)
566 {
567 inPos = inSize = 0;
568 RINOK(seqInStream->Read(xzu.InBuf, kInBufSize, &inSize));
569 }
570
571 SizeT inLen = inSize - inPos;
572 SizeT outLen = kOutBufSize - outPos;
573 ECoderStatus status;
574
575 SRes res = XzUnpacker_Code(&xzu.p,
576 xzu.OutBuf + outPos, &outLen,
577 xzu.InBuf + inPos, &inLen,
578 (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status);
579
580 inPos += inLen;
581 outPos += outLen;
582
583 InSize += inLen;
584 OutSize += outLen;
585
586 DecodeRes = res;
587
588 bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK);
589
590 if (outStream)
591 {
592 if (outPos == kOutBufSize || finished)
593 {
594 if (outPos != 0)
595 {
596 RINOK(WriteStream(outStream, xzu.OutBuf, outPos));
597 outPos = 0;
598 }
599 }
600 }
601 else
602 outPos = 0;
603
604 if (progress)
605 {
606 RINOK(progress->SetRatioInfo(&InSize, &OutSize));
607 }
608
609 if (finished)
610 {
611 PhySize = InSize;
612 NumStreams = xzu.p.numStartedStreams;
613 if (NumStreams > 0)
614 IsArc = true;
615 NumBlocks = xzu.p.numTotalBlocks;
616
617 UnpackSize_Defined = true;
618 NumStreams_Defined = true;
619 NumBlocks_Defined = true;
620
621 UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p);
622
623 if (res == SZ_OK)
624 {
625 if (status == CODER_STATUS_NEEDS_MORE_INPUT)
626 {
627 extraSize = 0;
628 if (!XzUnpacker_IsStreamWasFinished(&xzu.p))
629 {
630 // finished at padding bytes, but padding is not aligned for 4
631 UnexpectedEnd = true;
632 res = SZ_ERROR_DATA;
633 }
634 }
635 else // status == CODER_STATUS_NOT_FINISHED
636 res = SZ_ERROR_DATA;
637 }
638 else if (res == SZ_ERROR_NO_ARCHIVE)
639 {
640 if (InSize == extraSize)
641 IsArc = false;
642 else
643 {
644 if (extraSize != 0 || inPos != inSize)
645 {
646 DataAfterEnd = true;
647 res = SZ_OK;
648 }
649 }
650 }
651
652 DecodeRes = res;
653 PhySize -= extraSize;
654
655 switch (res)
656 {
657 case SZ_OK: break;
658 case SZ_ERROR_NO_ARCHIVE: IsArc = false; break;
659 case SZ_ERROR_ARCHIVE: HeadersError = true; break;
660 case SZ_ERROR_UNSUPPORTED: Unsupported = true; break;
661 case SZ_ERROR_CRC: CrcError = true; break;
662 case SZ_ERROR_DATA: DataError = true; break;
663 default: DataError = true; break;
664 }
665
666 break;
667 }
668 }
669
670 return S_OK;
671 }
672
Get_Extract_OperationResult() const673 Int32 CDecoder::Get_Extract_OperationResult() const
674 {
675 Int32 opRes;
676 if (!IsArc)
677 opRes = NExtract::NOperationResult::kIsNotArc;
678 else if (UnexpectedEnd)
679 opRes = NExtract::NOperationResult::kUnexpectedEnd;
680 else if (DataAfterEnd)
681 opRes = NExtract::NOperationResult::kDataAfterEnd;
682 else if (CrcError)
683 opRes = NExtract::NOperationResult::kCRCError;
684 else if (Unsupported)
685 opRes = NExtract::NOperationResult::kUnsupportedMethod;
686 else if (HeadersError)
687 opRes = NExtract::NOperationResult::kDataError;
688 else if (DataError)
689 opRes = NExtract::NOperationResult::kDataError;
690 else if (DecodeRes != SZ_OK)
691 opRes = NExtract::NOperationResult::kDataError;
692 else
693 opRes = NExtract::NOperationResult::kOK;
694 return opRes;
695 }
696
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)697 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
698 Int32 testMode, IArchiveExtractCallback *extractCallback)
699 {
700 COM_TRY_BEGIN
701 if (numItems == 0)
702 return S_OK;
703 if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
704 return E_INVALIDARG;
705
706 if (_phySize_Defined)
707 extractCallback->SetTotal(_stat.PhySize);
708
709 UInt64 currentTotalPacked = 0;
710 RINOK(extractCallback->SetCompleted(¤tTotalPacked));
711 CMyComPtr<ISequentialOutStream> realOutStream;
712 Int32 askMode = testMode ?
713 NExtract::NAskMode::kTest :
714 NExtract::NAskMode::kExtract;
715
716 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
717
718 if (!testMode && !realOutStream)
719 return S_OK;
720
721 extractCallback->PrepareOperation(askMode);
722
723 CLocalProgress *lps = new CLocalProgress;
724 CMyComPtr<ICompressProgressInfo> lpsRef = lps;
725 lps->Init(extractCallback, true);
726
727 if (_needSeekToStart)
728 {
729 if (!_stream)
730 return E_FAIL;
731 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
732 }
733 else
734 _needSeekToStart = true;
735
736 CDecoder decoder;
737 RINOK(Decode2(_seqStream, realOutStream, decoder, lpsRef));
738 Int32 opRes = decoder.Get_Extract_OperationResult();
739
740 realOutStream.Release();
741 return extractCallback->SetOperationResult(opRes);
742 COM_TRY_END
743 }
744
745 #ifndef EXTRACT_ONLY
746
GetFileTimeType(UInt32 * timeType)747 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
748 {
749 *timeType = NFileTimeType::kUnix;
750 return S_OK;
751 }
752
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)753 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
754 IArchiveUpdateCallback *updateCallback)
755 {
756 COM_TRY_BEGIN
757
758 CSeqOutStreamWrap seqOutStream(outStream);
759
760 if (numItems == 0)
761 {
762 SRes res = Xz_EncodeEmpty(&seqOutStream.p);
763 return SResToHRESULT(res);
764 }
765
766 if (numItems != 1)
767 return E_INVALIDARG;
768
769 Int32 newData, newProps;
770 UInt32 indexInArchive;
771 if (!updateCallback)
772 return E_FAIL;
773 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
774
775 if (IntToBool(newProps))
776 {
777 {
778 NCOM::CPropVariant prop;
779 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
780 if (prop.vt != VT_EMPTY)
781 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
782 return E_INVALIDARG;
783 }
784 }
785
786 if (IntToBool(newData))
787 {
788 UInt64 size;
789 {
790 NCOM::CPropVariant prop;
791 RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
792 if (prop.vt != VT_UI8)
793 return E_INVALIDARG;
794 size = prop.uhVal.QuadPart;
795 RINOK(updateCallback->SetTotal(size));
796 }
797
798 CLzma2EncProps lzma2Props;
799 Lzma2EncProps_Init(&lzma2Props);
800
801 lzma2Props.lzmaProps.level = GetLevel();
802
803 CMyComPtr<ISequentialInStream> fileInStream;
804 RINOK(updateCallback->GetStream(0, &fileInStream));
805
806 CSeqInStreamWrap seqInStream(fileInStream);
807
808 {
809 NCOM::CPropVariant prop = (UInt64)size;
810 RINOK(NCompress::NLzma2::SetLzma2Prop(NCoderPropID::kReduceSize, prop, lzma2Props));
811 }
812
813 FOR_VECTOR (i, _methods)
814 {
815 COneMethodInfo &m = _methods[i];
816 SetGlobalLevelAndThreads(m
817 #ifndef _7ZIP_ST
818 , _numThreads
819 #endif
820 );
821 {
822 FOR_VECTOR (j, m.Props)
823 {
824 const CProp &prop = m.Props[j];
825 RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props));
826 }
827 }
828 }
829
830 #ifndef _7ZIP_ST
831 lzma2Props.numTotalThreads = _numThreads;
832 #endif
833
834 CLocalProgress *lps = new CLocalProgress;
835 CMyComPtr<ICompressProgressInfo> progress = lps;
836 lps->Init(updateCallback, true);
837
838 CCompressProgressWrap progressWrap(progress);
839 CXzProps xzProps;
840 CXzFilterProps filter;
841 XzProps_Init(&xzProps);
842 XzFilterProps_Init(&filter);
843 xzProps.lzma2Props = &lzma2Props;
844 xzProps.filterProps = (_filterId != 0 ? &filter : NULL);
845 switch (_crcSize)
846 {
847 case 0: xzProps.checkId = XZ_CHECK_NO; break;
848 case 4: xzProps.checkId = XZ_CHECK_CRC32; break;
849 case 8: xzProps.checkId = XZ_CHECK_CRC64; break;
850 case 32: xzProps.checkId = XZ_CHECK_SHA256; break;
851 default: return E_INVALIDARG;
852 }
853 filter.id = _filterId;
854 if (_filterId == XZ_ID_Delta)
855 {
856 bool deltaDefined = false;
857 FOR_VECTOR (j, _filterMethod.Props)
858 {
859 const CProp &prop = _filterMethod.Props[j];
860 if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4)
861 {
862 UInt32 delta = (UInt32)prop.Value.ulVal;
863 if (delta < 1 || delta > 256)
864 return E_INVALIDARG;
865 filter.delta = delta;
866 deltaDefined = true;
867 }
868 }
869 if (!deltaDefined)
870 return E_INVALIDARG;
871 }
872 SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &xzProps, &progressWrap.p);
873 if (res == SZ_OK)
874 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
875 return SResToHRESULT(res);
876 }
877
878 if (indexInArchive != 0)
879 return E_INVALIDARG;
880
881 CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
882 updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
883 if (opCallback)
884 {
885 RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate))
886 }
887
888 if (_stream)
889 {
890 if (_phySize_Defined)
891 RINOK(updateCallback->SetTotal(_stat.PhySize));
892 RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
893 }
894
895 CLocalProgress *lps = new CLocalProgress;
896 CMyComPtr<ICompressProgressInfo> progress = lps;
897 lps->Init(updateCallback, true);
898
899 return NCompress::CopyStream(_stream, outStream, progress);
900
901 COM_TRY_END
902 }
903
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)904 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
905 {
906 COM_TRY_BEGIN
907
908 Init();
909 for (UInt32 i = 0; i < numProps; i++)
910 {
911 RINOK(SetProperty(names[i], values[i]));
912 }
913
914 if (!_filterMethod.MethodName.IsEmpty())
915 {
916 unsigned k;
917 for (k = 0; k < ARRAY_SIZE(g_NamePairs); k++)
918 {
919 const CMethodNamePair &pair = g_NamePairs[k];
920 if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name))
921 {
922 _filterId = pair.Id;
923 break;
924 }
925 }
926 if (k == ARRAY_SIZE(g_NamePairs))
927 return E_INVALIDARG;
928 }
929
930 _methods.DeleteFrontal(GetNumEmptyMethods());
931 if (_methods.Size() > 1)
932 return E_INVALIDARG;
933 if (_methods.Size() == 1)
934 {
935 AString &methodName = _methods[0].MethodName;
936 if (methodName.IsEmpty())
937 methodName = k_LZMA2_Name;
938 else if (!methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name))
939 return E_INVALIDARG;
940 }
941
942 return S_OK;
943
944 COM_TRY_END
945 }
946
947 #endif
948
949 REGISTER_ARC_IO(
950 "xz", "xz txz", "* .tar", 0xC,
951 XZ_SIG,
952 0,
953 NArcInfoFlags::kKeepName,
954 NULL)
955
956 }}
957