// XzHandler.cpp | |
#include "StdAfx.h" | |
#include "../../../C/Alloc.h" | |
#include "../../../C/XzCrc64.h" | |
#include "../../../C/XzEnc.h" | |
#include "../../Common/ComTry.h" | |
#include "../../Common/Defs.h" | |
#include "../../Common/IntToString.h" | |
#include "../ICoder.h" | |
#include "../Common/CWrappers.h" | |
#include "../Common/ProgressUtils.h" | |
#include "../Common/RegisterArc.h" | |
#include "../Common/StreamUtils.h" | |
#include "../Compress/CopyCoder.h" | |
#include "IArchive.h" | |
#include "Common/HandlerOut.h" | |
using namespace NWindows; | |
namespace NCompress { | |
namespace NLzma2 { | |
HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props); | |
}} | |
static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } | |
static void SzFree(void *, void *address) { MyFree(address); } | |
static ISzAlloc g_Alloc = { SzAlloc, SzFree }; | |
namespace NArchive { | |
namespace NXz { | |
struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit; | |
static const wchar_t *k_LZMA2_Name = L"LZMA2"; | |
struct CStatInfo | |
{ | |
UInt64 InSize; | |
UInt64 OutSize; | |
UInt64 PhySize; | |
UInt64 NumStreams; | |
UInt64 NumBlocks; | |
bool UnpackSize_Defined; | |
bool NumStreams_Defined; | |
bool NumBlocks_Defined; | |
bool IsArc; | |
bool UnexpectedEnd; | |
bool DataAfterEnd; | |
bool Unsupported; | |
bool HeadersError; | |
bool DataError; | |
bool CrcError; | |
CStatInfo() { Clear(); } | |
void Clear() | |
{ | |
InSize = 0; | |
OutSize = 0; | |
PhySize = 0; | |
NumStreams = 0; | |
NumBlocks = 0; | |
UnpackSize_Defined = false; | |
NumStreams_Defined = false; | |
NumBlocks_Defined = false; | |
UnexpectedEnd = false; | |
DataAfterEnd = false; | |
Unsupported = false; | |
HeadersError = false; | |
DataError = false; | |
CrcError = false; | |
IsArc = false; | |
} | |
}; | |
struct IDecodeState: public CStatInfo | |
{ | |
SRes DecodeRes; | |
IDecodeState(): DecodeRes(SZ_OK) {} | |
virtual HRESULT Progress() = 0; | |
HRESULT Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream); | |
}; | |
struct CVirtProgress_To_LocalProgress: public IDecodeState | |
{ | |
CLocalProgress *lps; | |
CMyComPtr<ICompressProgressInfo> progress; | |
HRESULT Progress(); | |
}; | |
HRESULT CVirtProgress_To_LocalProgress::Progress() | |
{ | |
lps->InSize = InSize; | |
lps->OutSize = OutSize; | |
return lps->SetCur(); | |
} | |
class CHandler: | |
public IInArchive, | |
public IArchiveOpenSeq, | |
#ifndef EXTRACT_ONLY | |
public IOutArchive, | |
public ISetProperties, | |
public CMultiMethodProps, | |
#endif | |
public CMyUnknownImp | |
{ | |
CStatInfo _stat; | |
bool _isArc; | |
bool _needSeekToStart; | |
bool _phySize_Defined; | |
CMyComPtr<IInStream> _stream; | |
CMyComPtr<ISequentialInStream> _seqStream; | |
UInt32 _filterId; | |
AString _methodsString; | |
void Init() | |
{ | |
_filterId = 0; | |
CMultiMethodProps::Init(); | |
} | |
HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); | |
HRESULT Decode2(ISequentialInStream *seqInStream, ISequentialOutStream *outStream, IDecodeState &progress) | |
{ | |
RINOK(progress.Decode(seqInStream, outStream)); | |
_stat = progress; | |
_phySize_Defined = true; | |
return S_OK; | |
} | |
public: | |
MY_QUERYINTERFACE_BEGIN2(IInArchive) | |
MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) | |
#ifndef EXTRACT_ONLY | |
MY_QUERYINTERFACE_ENTRY(IOutArchive) | |
MY_QUERYINTERFACE_ENTRY(ISetProperties) | |
#endif | |
MY_QUERYINTERFACE_END | |
MY_ADDREF_RELEASE | |
INTERFACE_IInArchive(;) | |
STDMETHOD(OpenSeq)(ISequentialInStream *stream); | |
#ifndef EXTRACT_ONLY | |
INTERFACE_IOutArchive(;) | |
STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps); | |
#endif | |
CHandler(); | |
}; | |
CHandler::CHandler() | |
{ | |
Init(); | |
} | |
static const Byte kProps[] = | |
{ | |
kpidSize, | |
kpidPackSize, | |
kpidMethod | |
}; | |
static const Byte kArcProps[] = | |
{ | |
kpidMethod, | |
kpidNumStreams, | |
kpidNumBlocks | |
}; | |
IMP_IInArchive_Props | |
IMP_IInArchive_ArcProps | |
static inline char GetHex(unsigned value) | |
{ | |
return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); | |
} | |
static inline void AddHexToString(AString &s, Byte value) | |
{ | |
s += GetHex(value >> 4); | |
s += GetHex(value & 0xF); | |
} | |
static void AddUInt32ToString(AString &s, UInt32 value) | |
{ | |
char temp[16]; | |
ConvertUInt32ToString(value, temp); | |
s += temp; | |
} | |
static void Lzma2PropToString(AString &s, unsigned prop) | |
{ | |
char c = 0; | |
UInt32 size; | |
if ((prop & 1) == 0) | |
size = prop / 2 + 12; | |
else | |
{ | |
c = 'k'; | |
size = (UInt32)(2 | (prop & 1)) << (prop / 2 + 1); | |
if (prop > 17) | |
{ | |
size >>= 10; | |
c = 'm'; | |
} | |
} | |
AddUInt32ToString(s, size); | |
if (c != 0) | |
s += c; | |
} | |
struct CMethodNamePair | |
{ | |
UInt32 Id; | |
const char *Name; | |
}; | |
static const CMethodNamePair g_NamePairs[] = | |
{ | |
{ XZ_ID_Subblock, "SB" }, | |
{ XZ_ID_Delta, "Delta" }, | |
{ XZ_ID_X86, "BCJ" }, | |
{ XZ_ID_PPC, "PPC" }, | |
{ XZ_ID_IA64, "IA64" }, | |
{ XZ_ID_ARM, "ARM" }, | |
{ XZ_ID_ARMT, "ARMT" }, | |
{ XZ_ID_SPARC, "SPARC" }, | |
{ XZ_ID_LZMA2, "LZMA2" } | |
}; | |
static AString GetMethodString(const CXzFilter &f) | |
{ | |
const char *p = NULL; | |
for (unsigned i = 0; i < ARRAY_SIZE(g_NamePairs); i++) | |
if (g_NamePairs[i].Id == f.id) | |
{ | |
p = g_NamePairs[i].Name; | |
break; | |
} | |
char temp[32]; | |
if (!p) | |
{ | |
::ConvertUInt64ToString(f.id, temp); | |
p = temp; | |
} | |
AString s = p; | |
if (f.propsSize > 0) | |
{ | |
s += ':'; | |
if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) | |
Lzma2PropToString(s, f.props[0]); | |
else if (f.id == XZ_ID_Delta && f.propsSize == 1) | |
AddUInt32ToString(s, (UInt32)f.props[0] + 1); | |
else | |
{ | |
s += '['; | |
for (UInt32 bi = 0; bi < f.propsSize; bi++) | |
AddHexToString(s, f.props[bi]); | |
s += ']'; | |
} | |
} | |
return s; | |
} | |
static void AddString(AString &dest, const AString &src) | |
{ | |
if (!dest.IsEmpty()) | |
dest += ' '; | |
dest += src; | |
} | |
static const char *kChecks[] = | |
{ | |
"NoCheck" | |
, "CRC32" | |
, NULL | |
, NULL | |
, "CRC64" | |
, NULL | |
, NULL | |
, NULL | |
, NULL | |
, NULL | |
, "SHA256" | |
, NULL | |
, NULL | |
, NULL | |
, NULL | |
, NULL | |
}; | |
static AString GetCheckString(const CXzs &xzs) | |
{ | |
size_t i; | |
UInt32 mask = 0; | |
for (i = 0; i < xzs.num; i++) | |
mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); | |
AString s; | |
for (i = 0; i <= XZ_CHECK_MASK; i++) | |
if (((mask >> i) & 1) != 0) | |
{ | |
AString s2; | |
if (kChecks[i]) | |
s2 = kChecks[i]; | |
else | |
{ | |
s2 = "Check-"; | |
AddUInt32ToString(s2, (UInt32)i); | |
} | |
AddString(s, s2); | |
} | |
return s; | |
} | |
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) | |
{ | |
COM_TRY_BEGIN | |
NCOM::CPropVariant prop; | |
switch (propID) | |
{ | |
case kpidPhySize: if (_phySize_Defined) prop = _stat.PhySize; break; | |
case kpidNumStreams: if (_stat.NumStreams_Defined) prop = _stat.NumStreams; break; | |
case kpidNumBlocks: if (_stat.NumBlocks_Defined) prop = _stat.NumBlocks; break; | |
case kpidUnpackSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; | |
case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; | |
case kpidErrorFlags: | |
{ | |
UInt32 v = 0; | |
if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; | |
if (_stat.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd; | |
if (_stat.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; | |
if (_stat.HeadersError) v |= kpv_ErrorFlags_HeadersError; | |
if (_stat.Unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; | |
if (_stat.DataError) v |= kpv_ErrorFlags_DataError; | |
if (_stat.CrcError) v |= kpv_ErrorFlags_CrcError; | |
prop = v; | |
} | |
} | |
prop.Detach(value); | |
return S_OK; | |
COM_TRY_END | |
} | |
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | |
{ | |
*numItems = 1; | |
return S_OK; | |
} | |
STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value) | |
{ | |
COM_TRY_BEGIN | |
NCOM::CPropVariant prop; | |
switch (propID) | |
{ | |
case kpidSize: if (_stat.UnpackSize_Defined) prop = _stat.OutSize; break; | |
case kpidPackSize: if (_phySize_Defined) prop = _stat.PhySize; break; | |
case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; | |
} | |
prop.Detach(value); | |
return S_OK; | |
COM_TRY_END | |
} | |
struct COpenCallbackWrap | |
{ | |
ICompressProgress p; | |
IArchiveOpenCallback *OpenCallback; | |
HRESULT Res; | |
COpenCallbackWrap(IArchiveOpenCallback *progress); | |
}; | |
static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) | |
{ | |
COpenCallbackWrap *p = (COpenCallbackWrap *)pp; | |
p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); | |
return (SRes)p->Res; | |
} | |
COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback) | |
{ | |
p.Progress = OpenCallbackProgress; | |
OpenCallback = callback; | |
Res = SZ_OK; | |
} | |
struct CXzsCPP | |
{ | |
CXzs p; | |
CXzsCPP() { Xzs_Construct(&p); } | |
~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } | |
}; | |
struct CVirtProgress_To_OpenProgress: public IDecodeState | |
{ | |
IArchiveOpenCallback *Callback; | |
UInt64 Offset; | |
HRESULT Progress(); | |
}; | |
HRESULT CVirtProgress_To_OpenProgress::Progress() | |
{ | |
if (Callback) | |
{ | |
UInt64 files = 0; | |
UInt64 value = Offset + InSize; | |
return Callback->SetCompleted(&files, &value); | |
} | |
return S_OK; | |
} | |
static HRESULT SRes_to_Open_HRESULT(SRes res) | |
{ | |
switch (res) | |
{ | |
case SZ_OK: return S_OK; | |
case SZ_ERROR_MEM: return E_OUTOFMEMORY; | |
case SZ_ERROR_PROGRESS: return E_ABORT; | |
/* | |
case SZ_ERROR_UNSUPPORTED: | |
case SZ_ERROR_CRC: | |
case SZ_ERROR_DATA: | |
case SZ_ERROR_ARCHIVE: | |
case SZ_ERROR_NO_ARCHIVE: | |
return S_FALSE; | |
*/ | |
} | |
return S_FALSE; | |
} | |
HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback) | |
{ | |
_needSeekToStart = true; | |
{ | |
CXzStreamFlags st; | |
CSeqInStreamWrap inStreamWrap(inStream); | |
SRes res = Xz_ReadHeader(&st, &inStreamWrap.p); | |
if (res != SZ_OK) | |
return SRes_to_Open_HRESULT(res); | |
{ | |
CXzBlock block; | |
Bool isIndex; | |
UInt32 headerSizeRes; | |
SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes); | |
if (res2 == SZ_OK && !isIndex) | |
{ | |
unsigned numFilters = XzBlock_GetNumFilters(&block); | |
for (unsigned i = 0; i < numFilters; i++) | |
AddString(_methodsString, GetMethodString(block.filters[i])); | |
} | |
} | |
} | |
RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize)); | |
RINOK(callback->SetTotal(NULL, &_stat.PhySize)); | |
CSeekInStreamWrap inStreamImp(inStream); | |
CLookToRead lookStream; | |
LookToRead_CreateVTable(&lookStream, True); | |
lookStream.realStream = &inStreamImp.p; | |
LookToRead_Init(&lookStream); | |
COpenCallbackWrap openWrap(callback); | |
CXzsCPP xzs; | |
Int64 startPosition; | |
SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &startPosition, &openWrap.p, &g_Alloc); | |
if (res == SZ_ERROR_PROGRESS) | |
return (openWrap.Res == S_OK) ? E_FAIL : openWrap.Res; | |
/* | |
if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) | |
res = SZ_OK; | |
*/ | |
if (res == SZ_OK && startPosition == 0) | |
{ | |
_phySize_Defined = true; | |
_stat.OutSize = Xzs_GetUnpackSize(&xzs.p); | |
_stat.UnpackSize_Defined = true; | |
_stat.NumStreams = xzs.p.num; | |
_stat.NumStreams_Defined = true; | |
_stat.NumBlocks = Xzs_GetNumBlocks(&xzs.p); | |
_stat.NumBlocks_Defined = true; | |
AddString(_methodsString, GetCheckString(xzs.p)); | |
} | |
else | |
{ | |
res = SZ_OK; | |
} | |
RINOK(SRes_to_Open_HRESULT(res)); | |
_stream = inStream; | |
_seqStream = inStream; | |
_isArc = true; | |
return S_OK; | |
} | |
STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) | |
{ | |
COM_TRY_BEGIN | |
{ | |
Close(); | |
return Open2(inStream, /* 0, */ callback); | |
} | |
COM_TRY_END | |
} | |
STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) | |
{ | |
Close(); | |
_seqStream = stream; | |
_isArc = true; | |
_needSeekToStart = false; | |
return S_OK; | |
} | |
STDMETHODIMP CHandler::Close() | |
{ | |
_stat.Clear(); | |
_isArc = false; | |
_needSeekToStart = false; | |
_phySize_Defined = false; | |
_methodsString.Empty(); | |
_stream.Release(); | |
_seqStream.Release(); | |
return S_OK; | |
} | |
class CSeekToSeqStream: | |
public IInStream, | |
public CMyUnknownImp | |
{ | |
public: | |
CMyComPtr<ISequentialInStream> Stream; | |
MY_UNKNOWN_IMP1(IInStream) | |
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); | |
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); | |
}; | |
STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize) | |
{ | |
return Stream->Read(data, size, processedSize); | |
} | |
STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; } | |
struct CXzUnpackerCPP | |
{ | |
Byte *InBuf; | |
Byte *OutBuf; | |
CXzUnpacker p; | |
CXzUnpackerCPP(): InBuf(0), OutBuf(0) | |
{ | |
XzUnpacker_Construct(&p, &g_Alloc); | |
} | |
~CXzUnpackerCPP() | |
{ | |
XzUnpacker_Free(&p); | |
MyFree(InBuf); | |
MyFree(OutBuf); | |
} | |
}; | |
HRESULT IDecodeState::Decode(ISequentialInStream *seqInStream, ISequentialOutStream *outStream) | |
{ | |
const size_t kInBufSize = 1 << 15; | |
const size_t kOutBufSize = 1 << 21; | |
DecodeRes = SZ_OK; | |
CXzUnpackerCPP xzu; | |
XzUnpacker_Init(&xzu.p); | |
xzu.InBuf = (Byte *)MyAlloc(kInBufSize); | |
xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize); | |
if (!xzu.InBuf || !xzu.OutBuf) | |
return E_OUTOFMEMORY; | |
UInt32 inSize = 0; | |
SizeT inPos = 0; | |
SizeT outPos = 0; | |
for (;;) | |
{ | |
if (inPos == inSize) | |
{ | |
inPos = inSize = 0; | |
RINOK(seqInStream->Read(xzu.InBuf, kInBufSize, &inSize)); | |
} | |
SizeT inLen = inSize - inPos; | |
SizeT outLen = kOutBufSize - outPos; | |
ECoderStatus status; | |
SRes res = XzUnpacker_Code(&xzu.p, | |
xzu.OutBuf + outPos, &outLen, | |
xzu.InBuf + inPos, &inLen, | |
(inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status); | |
inPos += inLen; | |
outPos += outLen; | |
InSize += inLen; | |
OutSize += outLen; | |
DecodeRes = res; | |
bool finished = ((inLen == 0 && outLen == 0) || res != SZ_OK); | |
if (outStream) | |
{ | |
if (outPos == kOutBufSize || finished) | |
{ | |
if (outPos != 0) | |
{ | |
RINOK(WriteStream(outStream, xzu.OutBuf, outPos)); | |
outPos = 0; | |
} | |
} | |
} | |
else | |
outPos = 0; | |
RINOK(Progress()); | |
if (finished) | |
{ | |
PhySize = InSize; | |
NumStreams = xzu.p.numStartedStreams; | |
if (NumStreams > 0) | |
IsArc = true; | |
NumBlocks = xzu.p.numTotalBlocks; | |
UnpackSize_Defined = true; | |
NumStreams_Defined = true; | |
NumBlocks_Defined = true; | |
UInt64 extraSize = XzUnpacker_GetExtraSize(&xzu.p); | |
if (res == SZ_OK) | |
{ | |
if (status == CODER_STATUS_NEEDS_MORE_INPUT) | |
{ | |
extraSize = 0; | |
if (!XzUnpacker_IsStreamWasFinished(&xzu.p)) | |
{ | |
// finished at padding bytes, but padding is not aligned for 4 | |
UnexpectedEnd = true; | |
res = SZ_ERROR_DATA; | |
} | |
} | |
else // status == CODER_STATUS_NOT_FINISHED | |
res = SZ_ERROR_DATA; | |
} | |
else if (res == SZ_ERROR_NO_ARCHIVE) | |
{ | |
if (InSize == extraSize) | |
IsArc = false; | |
else | |
{ | |
if (extraSize != 0 || inPos != inSize) | |
{ | |
DataAfterEnd = true; | |
res = SZ_OK; | |
} | |
} | |
} | |
DecodeRes = res; | |
PhySize -= extraSize; | |
switch (res) | |
{ | |
case SZ_OK: break; | |
case SZ_ERROR_NO_ARCHIVE: IsArc = false; break; | |
case SZ_ERROR_ARCHIVE: HeadersError = true; break; | |
case SZ_ERROR_UNSUPPORTED: Unsupported = true; break; | |
case SZ_ERROR_CRC: CrcError = true; break; | |
case SZ_ERROR_DATA: DataError = true; break; | |
default: DataError = true; break; | |
} | |
break; | |
} | |
} | |
return S_OK; | |
} | |
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, | |
Int32 testMode, IArchiveExtractCallback *extractCallback) | |
{ | |
COM_TRY_BEGIN | |
if (numItems == 0) | |
return S_OK; | |
if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) | |
return E_INVALIDARG; | |
extractCallback->SetTotal(_stat.PhySize); | |
UInt64 currentTotalPacked = 0; | |
RINOK(extractCallback->SetCompleted(¤tTotalPacked)); | |
CMyComPtr<ISequentialOutStream> realOutStream; | |
Int32 askMode = testMode ? | |
NExtract::NAskMode::kTest : | |
NExtract::NAskMode::kExtract; | |
RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); | |
if (!testMode && !realOutStream) | |
return S_OK; | |
extractCallback->PrepareOperation(askMode); | |
CVirtProgress_To_LocalProgress vp; | |
vp.lps = new CLocalProgress; | |
vp.progress = vp.lps; | |
vp.lps->Init(extractCallback, true); | |
if (_needSeekToStart) | |
{ | |
if (!_stream) | |
return E_FAIL; | |
RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); | |
} | |
else | |
_needSeekToStart = true; | |
RINOK(Decode2(_seqStream, realOutStream, vp)); | |
Int32 opRes; | |
if (!vp.IsArc) | |
opRes = NExtract::NOperationResult::kIsNotArc; | |
else if (vp.UnexpectedEnd) | |
opRes = NExtract::NOperationResult::kUnexpectedEnd; | |
else if (vp.DataAfterEnd) | |
opRes = NExtract::NOperationResult::kDataAfterEnd; | |
else if (vp.CrcError) | |
opRes = NExtract::NOperationResult::kCRCError; | |
else if (vp.Unsupported) | |
opRes = NExtract::NOperationResult::kUnsupportedMethod; | |
else if (vp.HeadersError) | |
opRes = NExtract::NOperationResult::kDataError; | |
else if (vp.DataError) | |
opRes = NExtract::NOperationResult::kDataError; | |
else if (vp.DecodeRes != SZ_OK) | |
opRes = NExtract::NOperationResult::kDataError; | |
else | |
opRes = NExtract::NOperationResult::kOK; | |
realOutStream.Release(); | |
return extractCallback->SetOperationResult(opRes); | |
COM_TRY_END | |
} | |
#ifndef EXTRACT_ONLY | |
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) | |
{ | |
*timeType = NFileTimeType::kUnix; | |
return S_OK; | |
} | |
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, | |
IArchiveUpdateCallback *updateCallback) | |
{ | |
CSeqOutStreamWrap seqOutStream(outStream); | |
if (numItems == 0) | |
{ | |
SRes res = Xz_EncodeEmpty(&seqOutStream.p); | |
return SResToHRESULT(res); | |
} | |
if (numItems != 1) | |
return E_INVALIDARG; | |
Int32 newData, newProps; | |
UInt32 indexInArchive; | |
if (!updateCallback) | |
return E_FAIL; | |
RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); | |
if (IntToBool(newProps)) | |
{ | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); | |
if (prop.vt != VT_EMPTY) | |
if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) | |
return E_INVALIDARG; | |
} | |
} | |
if (IntToBool(newData)) | |
{ | |
UInt64 size; | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); | |
if (prop.vt != VT_UI8) | |
return E_INVALIDARG; | |
size = prop.uhVal.QuadPart; | |
RINOK(updateCallback->SetTotal(size)); | |
} | |
CLzma2EncProps lzma2Props; | |
Lzma2EncProps_Init(&lzma2Props); | |
lzma2Props.lzmaProps.level = GetLevel(); | |
CMyComPtr<ISequentialInStream> fileInStream; | |
RINOK(updateCallback->GetStream(0, &fileInStream)); | |
CSeqInStreamWrap seqInStream(fileInStream); | |
{ | |
NCOM::CPropVariant prop = (UInt64)size; | |
RINOK(NCompress::NLzma2::SetLzma2Prop(NCoderPropID::kReduceSize, prop, lzma2Props)); | |
} | |
FOR_VECTOR (i, _methods) | |
{ | |
COneMethodInfo &m = _methods[i]; | |
SetGlobalLevelAndThreads(m | |
#ifndef _7ZIP_ST | |
, _numThreads | |
#endif | |
); | |
{ | |
FOR_VECTOR (j, m.Props) | |
{ | |
const CProp &prop = m.Props[j]; | |
RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props)); | |
} | |
} | |
} | |
#ifndef _7ZIP_ST | |
lzma2Props.numTotalThreads = _numThreads; | |
#endif | |
CLocalProgress *lps = new CLocalProgress; | |
CMyComPtr<ICompressProgressInfo> progress = lps; | |
lps->Init(updateCallback, true); | |
CCompressProgressWrap progressWrap(progress); | |
CXzProps xzProps; | |
CXzFilterProps filter; | |
XzProps_Init(&xzProps); | |
XzFilterProps_Init(&filter); | |
xzProps.lzma2Props = &lzma2Props; | |
xzProps.filterProps = (_filterId != 0 ? &filter : NULL); | |
switch (_crcSize) | |
{ | |
case 0: xzProps.checkId = XZ_CHECK_NO; break; | |
case 4: xzProps.checkId = XZ_CHECK_CRC32; break; | |
case 8: xzProps.checkId = XZ_CHECK_CRC64; break; | |
case 32: xzProps.checkId = XZ_CHECK_SHA256; break; | |
default: return E_INVALIDARG; | |
} | |
filter.id = _filterId; | |
if (_filterId == XZ_ID_Delta) | |
{ | |
bool deltaDefined = false; | |
FOR_VECTOR (j, _filterMethod.Props) | |
{ | |
const CProp &prop = _filterMethod.Props[j]; | |
if (prop.Id == NCoderPropID::kDefaultProp && prop.Value.vt == VT_UI4) | |
{ | |
UInt32 delta = (UInt32)prop.Value.ulVal; | |
if (delta < 1 || delta > 256) | |
return E_INVALIDARG; | |
filter.delta = delta; | |
deltaDefined = true; | |
} | |
} | |
if (!deltaDefined) | |
return E_INVALIDARG; | |
} | |
SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &xzProps, &progressWrap.p); | |
if (res == SZ_OK) | |
return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); | |
return SResToHRESULT(res); | |
} | |
if (indexInArchive != 0) | |
return E_INVALIDARG; | |
if (_stream) | |
RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); | |
return NCompress::CopyStream(_stream, outStream, NULL); | |
} | |
STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, UInt32 numProps) | |
{ | |
COM_TRY_BEGIN | |
Init(); | |
for (UInt32 i = 0; i < numProps; i++) | |
{ | |
RINOK(SetProperty(names[i], values[i])); | |
} | |
if (!_filterMethod.MethodName.IsEmpty()) | |
{ | |
unsigned k; | |
for (k = 0; k < ARRAY_SIZE(g_NamePairs); k++) | |
{ | |
const CMethodNamePair &pair = g_NamePairs[k]; | |
if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) | |
{ | |
_filterId = pair.Id; | |
break; | |
} | |
} | |
if (k == ARRAY_SIZE(g_NamePairs)) | |
return E_INVALIDARG; | |
} | |
_methods.DeleteFrontal(GetNumEmptyMethods()); | |
if (_methods.Size() > 1) | |
return E_INVALIDARG; | |
if (_methods.Size() == 1) | |
{ | |
UString &methodName = _methods[0].MethodName; | |
if (methodName.IsEmpty()) | |
methodName = k_LZMA2_Name; | |
else if (!methodName.IsEqualToNoCase(k_LZMA2_Name)) | |
return E_INVALIDARG; | |
} | |
return S_OK; | |
COM_TRY_END | |
} | |
#endif | |
IMP_CreateArcIn | |
IMP_CreateArcOut | |
static CArcInfo g_ArcInfo = | |
{ "xz", "xz txz", "* .tar", 0xC, | |
6, { 0xFD, '7' , 'z', 'X', 'Z', 0 }, | |
0, | |
NArcInfoFlags::kKeepName, | |
REF_CreateArc_Pair }; | |
REGISTER_ARC(xz) | |
}} |