5 #include "../../../C/Alloc.h"
\r
6 #include "../../../C/XzCrc64.h"
\r
7 #include "../../../C/XzEnc.h"
\r
9 #include "../../Common/ComTry.h"
\r
10 #include "../../Common/IntToString.h"
\r
12 #include "../ICoder.h"
\r
14 #include "../Common/CWrappers.h"
\r
15 #include "../Common/ProgressUtils.h"
\r
16 #include "../Common/RegisterArc.h"
\r
17 #include "../Common/StreamUtils.h"
\r
19 #include "../Compress/CopyCoder.h"
\r
21 #include "IArchive.h"
\r
23 #include "Common/HandlerOut.h"
\r
25 using namespace NWindows;
\r
27 namespace NCompress {
\r
30 HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props);
\r
34 static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
\r
35 static void SzFree(void *, void *address) { MyFree(address); }
\r
36 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
\r
38 namespace NArchive {
\r
41 struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit;
\r
45 public IArchiveOpenSeq,
\r
46 #ifndef EXTRACT_ONLY
\r
48 public ISetProperties,
\r
51 public CMyUnknownImp
\r
53 Int64 _startPosition;
\r
57 AString _methodsString;
\r
59 UInt64 _unpackSizeDefined;
\r
60 UInt64 _packSizeDefined;
\r
62 CMyComPtr<IInStream> _stream;
\r
63 CMyComPtr<ISequentialInStream> _seqStream;
\r
70 COutHandler::Init();
\r
73 HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback);
\r
76 MY_QUERYINTERFACE_BEGIN2(IInArchive)
\r
77 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq)
\r
78 #ifndef EXTRACT_ONLY
\r
79 MY_QUERYINTERFACE_ENTRY(IOutArchive)
\r
80 MY_QUERYINTERFACE_ENTRY(ISetProperties)
\r
82 MY_QUERYINTERFACE_END
\r
85 INTERFACE_IInArchive(;)
\r
86 STDMETHOD(OpenSeq)(ISequentialInStream *stream);
\r
88 #ifndef EXTRACT_ONLY
\r
89 INTERFACE_IOutArchive(;)
\r
90 STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps);
\r
96 CHandler::CHandler()
\r
101 STATPROPSTG kProps[] =
\r
103 { NULL, kpidSize, VT_UI8},
\r
104 { NULL, kpidPackSize, VT_UI8},
\r
105 { NULL, kpidMethod, VT_BSTR}
\r
108 STATPROPSTG kArcProps[] =
\r
110 { NULL, kpidMethod, VT_BSTR},
\r
111 { NULL, kpidNumBlocks, VT_UI4}
\r
114 IMP_IInArchive_Props
\r
115 IMP_IInArchive_ArcProps
\r
117 static char GetHex(Byte value)
\r
119 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
\r
122 static inline void AddHexToString(AString &res, Byte value)
\r
124 res += GetHex((Byte)(value >> 4));
\r
125 res += GetHex((Byte)(value & 0xF));
\r
128 static AString ConvertUInt32ToString(UInt32 value)
\r
131 ::ConvertUInt32ToString(value, temp);
\r
135 static AString Lzma2PropToString(int prop)
\r
137 if ((prop & 1) == 0)
\r
138 return ConvertUInt32ToString(prop / 2 + 12);
\r
142 UInt32 size = (2 | ((prop) & 1)) << ((prop) / 2 + 1);
\r
146 res = ConvertUInt32ToString(size >> 10);
\r
151 res = ConvertUInt32ToString(size);
\r
157 struct CMethodNamePair
\r
163 static CMethodNamePair g_NamePairs[] =
\r
165 { XZ_ID_Subblock, "SB" },
\r
166 { XZ_ID_Delta, "Delta" },
\r
167 { XZ_ID_X86, "x86" },
\r
168 { XZ_ID_PPC, "PPC" },
\r
169 { XZ_ID_IA64, "IA64" },
\r
170 { XZ_ID_ARM, "ARM" },
\r
171 { XZ_ID_ARMT, "ARMT" },
\r
172 { XZ_ID_SPARC, "SPARC" },
\r
173 { XZ_ID_LZMA2, "LZMA2" }
\r
176 static AString GetMethodString(const CXzFilter &f)
\r
180 for (int i = 0; i < sizeof(g_NamePairs) / sizeof(g_NamePairs[i]); i++)
\r
181 if (g_NamePairs[i].Id == f.id)
\r
182 s = g_NamePairs[i].Name;
\r
186 ::ConvertUInt64ToString(f.id, temp);
\r
190 if (f.propsSize > 0)
\r
193 if (f.id == XZ_ID_LZMA2 && f.propsSize == 1)
\r
194 s += Lzma2PropToString(f.props[0]);
\r
195 else if (f.id == XZ_ID_Delta && f.propsSize == 1)
\r
196 s += ConvertUInt32ToString((UInt32)f.props[0] + 1);
\r
200 for (UInt32 bi = 0; bi < f.propsSize; bi++)
\r
201 AddHexToString(s, f.props[bi]);
\r
208 static void AddString(AString &dest, const AString &src)
\r
210 if (!dest.IsEmpty())
\r
215 static const char *kChecks[] =
\r
235 static AString GetCheckString(const CXzs &xzs)
\r
239 for (i = 0; i < xzs.num; i++)
\r
240 mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags));
\r
242 for (i = 0; i <= XZ_CHECK_MASK; i++)
\r
243 if (((mask >> i) & 1) != 0)
\r
249 s2 = "Check-" + ConvertUInt32ToString((UInt32)i);
\r
255 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
\r
258 NWindows::NCOM::CPropVariant prop;
\r
261 case kpidNumBlocks: if (!_useSeq) prop = _numBlocks; break;
\r
262 case kpidPhySize: if (_packSizeDefined) prop = _packSize; break;
\r
263 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
\r
265 prop.Detach(value);
\r
270 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
276 STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value)
\r
279 NWindows::NCOM::CPropVariant prop;
\r
282 case kpidSize: if (_unpackSizeDefined) prop = _unpackSize; break;
\r
283 case kpidPackSize: if (_packSizeDefined) prop = _packSize; break;
\r
284 case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break;
\r
286 prop.Detach(value);
\r
292 struct COpenCallbackWrap
\r
294 ICompressProgress p;
\r
295 IArchiveOpenCallback *OpenCallback;
\r
297 COpenCallbackWrap(IArchiveOpenCallback *progress);
\r
300 static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */)
\r
302 COpenCallbackWrap *p = (COpenCallbackWrap *)pp;
\r
303 p->Res = p->OpenCallback->SetCompleted(NULL, &inSize);
\r
304 return (SRes)p->Res;
\r
307 COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback)
\r
309 p.Progress = OpenCallbackProgress;
\r
310 OpenCallback = callback;
\r
317 CXzsCPP() { Xzs_Construct(&p); }
\r
318 ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); }
\r
321 HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback)
\r
323 CSeekInStreamWrap inStreamImp(inStream);
\r
325 CLookToRead lookStream;
\r
326 LookToRead_CreateVTable(&lookStream, True);
\r
327 lookStream.realStream = &inStreamImp.p;
\r
328 LookToRead_Init(&lookStream);
\r
330 COpenCallbackWrap openWrap(callback);
\r
331 RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize));
\r
332 RINOK(callback->SetTotal(NULL, &_packSize));
\r
335 SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &_startPosition, &openWrap.p, &g_Alloc);
\r
336 if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0)
\r
340 _packSize -= _startPosition;
\r
341 _unpackSize = Xzs_GetUnpackSize(&xzs.p);
\r
342 _unpackSizeDefined = _packSizeDefined = true;
\r
343 _numBlocks = (UInt64)Xzs_GetNumBlocks(&xzs.p);
\r
345 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
\r
347 CSeqInStreamWrap inStreamWrap(inStream);
\r
348 SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
\r
354 UInt32 headerSizeRes;
\r
355 res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes);
\r
356 if (res2 == SZ_OK && !isIndex)
\r
358 int numFilters = XzBlock_GetNumFilters(&block);
\r
359 for (int i = 0; i < numFilters; i++)
\r
360 AddString(_methodsString, GetMethodString(block.filters[i]));
\r
363 AddString(_methodsString, GetCheckString(xzs.p));
\r
366 if (res != SZ_OK || _startPosition != 0)
\r
368 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
\r
370 CSeqInStreamWrap inStreamWrap(inStream);
\r
371 SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p);
\r
375 _startPosition = 0;
\r
377 _unpackSizeDefined = _packSizeDefined = false;
\r
380 if (res == SZ_ERROR_NO_ARCHIVE)
\r
382 RINOK(SResToHRESULT(res));
\r
383 _stream = inStream;
\r
384 _seqStream = inStream;
\r
388 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)
\r
394 return Open2(inStream, callback);
\r
396 catch(...) { return S_FALSE; }
\r
400 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
\r
403 _seqStream = stream;
\r
407 STDMETHODIMP CHandler::Close()
\r
411 _unpackSizeDefined = _packSizeDefined = false;
\r
412 _methodsString.Empty();
\r
414 _seqStream.Release();
\r
418 class CSeekToSeqStream:
\r
420 public CMyUnknownImp
\r
423 CMyComPtr<ISequentialInStream> Stream;
\r
424 MY_UNKNOWN_IMP1(IInStream)
\r
426 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
\r
427 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
\r
430 STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize)
\r
432 return Stream->Read(data, size, processedSize);
\r
435 STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; }
\r
437 struct CXzUnpackerCPP
\r
442 CXzUnpackerCPP(): InBuf(0), OutBuf(0) {}
\r
445 XzUnpacker_Free(&p);
\r
451 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
452 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
457 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
\r
458 return E_INVALIDARG;
\r
460 extractCallback->SetTotal(_packSize);
\r
461 UInt64 currentTotalPacked = 0;
\r
462 RINOK(extractCallback->SetCompleted(¤tTotalPacked));
\r
463 CMyComPtr<ISequentialOutStream> realOutStream;
\r
464 Int32 askMode = testMode ?
\r
465 NExtract::NAskMode::kTest :
\r
466 NExtract::NAskMode::kExtract;
\r
468 RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
\r
470 if (!testMode && !realOutStream)
\r
473 extractCallback->PrepareOperation(askMode);
\r
477 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
\r
480 CLocalProgress *lps = new CLocalProgress;
\r
481 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
482 lps->Init(extractCallback, true);
\r
484 CCompressProgressWrap progressWrap(progress);
\r
488 const UInt32 kInBufSize = 1 << 15;
\r
489 const UInt32 kOutBufSize = 1 << 21;
\r
494 CXzUnpackerCPP xzu;
\r
495 res = XzUnpacker_Create(&xzu.p, &g_Alloc);
\r
498 xzu.InBuf = (Byte *)MyAlloc(kInBufSize);
\r
499 xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize);
\r
500 if (xzu.InBuf == 0 || xzu.OutBuf == 0)
\r
501 res = SZ_ERROR_MEM;
\r
506 if (inPos == inSize)
\r
508 inPos = inSize = 0;
\r
509 RINOK(_seqStream->Read(xzu.InBuf, kInBufSize, &inSize));
\r
512 SizeT inLen = inSize - inPos;
\r
513 SizeT outLen = kOutBufSize - outPos;
\r
514 ECoderStatus status;
\r
515 res = XzUnpacker_Code(&xzu.p,
\r
516 xzu.OutBuf + outPos, &outLen,
\r
517 xzu.InBuf + inPos, &inLen,
\r
518 (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status);
\r
520 // printf("\n_inPos = %6d inLen = %5d, outLen = %5d", inPos, inLen, outLen);
\r
522 inPos += (UInt32)inLen;
\r
523 outPos += (UInt32)outLen;
\r
524 lps->InSize += inLen;
\r
525 lps->OutSize += outLen;
\r
527 bool finished = (((inLen == 0) && (outLen == 0)) || res != SZ_OK);
\r
529 if (outPos == kOutBufSize || finished)
\r
531 if (realOutStream && outPos > 0)
\r
533 RINOK(WriteStream(realOutStream, xzu.OutBuf, outPos));
\r
539 _packSize = lps->InSize;
\r
540 _unpackSize = lps->OutSize;
\r
541 _packSizeDefined = _unpackSizeDefined = true;
\r
544 if (status == CODER_STATUS_NEEDS_MORE_INPUT)
\r
546 if (XzUnpacker_IsStreamWasFinished(&xzu.p))
\r
547 _packSize -= xzu.p.padSize;
\r
549 res = SZ_ERROR_DATA;
\r
552 res = SZ_ERROR_DATA;
\r
556 RINOK(lps->SetCur());
\r
563 opRes = NExtract::NOperationResult::kOK; break;
\r
564 case SZ_ERROR_UNSUPPORTED:
\r
565 opRes = NExtract::NOperationResult::kUnSupportedMethod; break;
\r
567 opRes = NExtract::NOperationResult::kCRCError; break;
\r
568 case SZ_ERROR_DATA:
\r
569 case SZ_ERROR_ARCHIVE:
\r
570 case SZ_ERROR_NO_ARCHIVE:
\r
571 opRes = NExtract::NOperationResult::kDataError; break;
\r
573 return SResToHRESULT(res);
\r
575 realOutStream.Release();
\r
576 RINOK(extractCallback->SetOperationResult(opRes));
\r
581 #ifndef EXTRACT_ONLY
\r
583 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
\r
585 *timeType = NFileTimeType::kUnix;
\r
589 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
\r
590 IArchiveUpdateCallback *updateCallback)
\r
592 CSeqOutStreamWrap seqOutStream(outStream);
\r
596 SRes res = Xz_EncodeEmpty(&seqOutStream.p);
\r
597 return SResToHRESULT(res);
\r
601 return E_INVALIDARG;
\r
603 Int32 newData, newProps;
\r
604 UInt32 indexInArchive;
\r
605 if (!updateCallback)
\r
607 RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
\r
609 if (IntToBool(newProps))
\r
612 NCOM::CPropVariant prop;
\r
613 RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
\r
614 if (prop.vt != VT_EMPTY)
\r
615 if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
\r
616 return E_INVALIDARG;
\r
620 if (IntToBool(newData))
\r
624 NCOM::CPropVariant prop;
\r
625 RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
\r
626 if (prop.vt != VT_UI8)
\r
627 return E_INVALIDARG;
\r
628 size = prop.uhVal.QuadPart;
\r
629 RINOK(updateCallback->SetTotal(size));
\r
632 CLzma2EncProps lzma2Props;
\r
633 Lzma2EncProps_Init(&lzma2Props);
\r
635 lzma2Props.lzmaProps.level = _level;
\r
637 CMyComPtr<ISequentialInStream> fileInStream;
\r
638 RINOK(updateCallback->GetStream(0, &fileInStream));
\r
640 CSeqInStreamWrap seqInStream(fileInStream);
\r
642 for (int i = 0; i < _methods.Size(); i++)
\r
644 COneMethodInfo &m = _methods[i];
\r
645 SetCompressionMethod2(m
\r
652 for (int j = 0; j < m.Props.Size(); j++)
\r
654 const CProp &prop = m.Props[j];
\r
655 RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props));
\r
661 lzma2Props.numTotalThreads = _numThreads;
\r
664 CLocalProgress *lps = new CLocalProgress;
\r
665 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
666 lps->Init(updateCallback, true);
\r
668 CCompressProgressWrap progressWrap(progress);
\r
669 SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &lzma2Props, False, &progressWrap.p);
\r
671 return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
\r
672 return SResToHRESULT(res);
\r
674 if (indexInArchive != 0)
\r
675 return E_INVALIDARG;
\r
677 RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL));
\r
678 return NCompress::CopyStream(_stream, outStream, 0);
\r
681 STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps)
\r
684 BeforeSetProperty();
\r
685 for (int i = 0; i < numProps; i++)
\r
687 RINOK(SetProperty(names[i], values[i]));
\r
695 static IInArchive *CreateArc() { return new NArchive::NXz::CHandler; }
\r
696 #ifndef EXTRACT_ONLY
\r
697 static IOutArchive *CreateArcOut() { return new NArchive::NXz::CHandler; }
\r
699 #define CreateArcOut 0
\r
702 static CArcInfo g_ArcInfo =
\r
703 { L"xz", L"xz txz", L"* .tar", 0xC, {0xFD, '7' , 'z', 'X', 'Z', '\0'}, 6, true, CreateArc, CreateArcOut };
\r