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