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