| // XzHandler.cpp |
| |
| #include "StdAfx.h" |
| |
| #include "../../../C/Alloc.h" |
| |
| #include "../../Common/ComTry.h" |
| #include "../../Common/Defs.h" |
| #include "../../Common/IntToString.h" |
| #include "../../Common/MyBuffer.h" |
| #include "../../Common/StringToInt.h" |
| |
| #include "../../Windows/PropVariant.h" |
| #include "../../Windows/System.h" |
| |
| #include "../Common/CWrappers.h" |
| #include "../Common/ProgressUtils.h" |
| #include "../Common/RegisterArc.h" |
| #include "../Common/StreamUtils.h" |
| |
| #include "../Compress/CopyCoder.h" |
| #include "../Compress/XzDecoder.h" |
| #include "../Compress/XzEncoder.h" |
| |
| #include "IArchive.h" |
| |
| #include "Common/HandlerOut.h" |
| |
| using namespace NWindows; |
| |
| namespace NArchive { |
| namespace NXz { |
| |
| #define k_LZMA2_Name "LZMA2" |
| |
| |
| struct CBlockInfo |
| { |
| unsigned StreamFlags; |
| UInt64 PackPos; |
| UInt64 PackSize; // pure value from Index record, it doesn't include pad zeros |
| UInt64 UnpackPos; |
| }; |
| |
| |
| Z7_class_CHandler_final: |
| public IInArchive, |
| public IArchiveOpenSeq, |
| public IInArchiveGetStream, |
| public ISetProperties, |
| #ifndef Z7_EXTRACT_ONLY |
| public IOutArchive, |
| #endif |
| public CMyUnknownImp, |
| #ifndef Z7_EXTRACT_ONLY |
| public CMultiMethodProps |
| #else |
| public CCommonMethodProps |
| #endif |
| { |
| Z7_COM_QI_BEGIN2(IInArchive) |
| Z7_COM_QI_ENTRY(IArchiveOpenSeq) |
| Z7_COM_QI_ENTRY(IInArchiveGetStream) |
| Z7_COM_QI_ENTRY(ISetProperties) |
| #ifndef Z7_EXTRACT_ONLY |
| Z7_COM_QI_ENTRY(IOutArchive) |
| #endif |
| Z7_COM_QI_END |
| Z7_COM_ADDREF_RELEASE |
| |
| Z7_IFACE_COM7_IMP(IInArchive) |
| Z7_IFACE_COM7_IMP(IArchiveOpenSeq) |
| Z7_IFACE_COM7_IMP(IInArchiveGetStream) |
| Z7_IFACE_COM7_IMP(ISetProperties) |
| #ifndef Z7_EXTRACT_ONLY |
| Z7_IFACE_COM7_IMP(IOutArchive) |
| #endif |
| |
| CXzStatInfo _stat; // it's stat from backward parsing |
| CXzStatInfo _stat2; // it's data from forward parsing, if the decoder was called |
| SRes _stat2_decode_SRes; |
| bool _stat_defined; |
| bool _stat2_defined; |
| |
| const CXzStatInfo *GetStat() const |
| { |
| if (_stat_defined) return &_stat; |
| if (_stat2_defined) return &_stat2; |
| return NULL; |
| } |
| |
| bool _isArc; |
| bool _needSeekToStart; |
| bool _firstBlockWasRead; |
| |
| AString _methodsString; |
| |
| |
| #ifndef Z7_EXTRACT_ONLY |
| |
| UInt32 _filterId; |
| UInt64 _numSolidBytes; |
| |
| void InitXz() |
| { |
| _filterId = 0; |
| _numSolidBytes = XZ_PROPS_BLOCK_SIZE_AUTO; |
| } |
| |
| #endif |
| |
| |
| void Init() |
| { |
| #ifndef Z7_EXTRACT_ONLY |
| InitXz(); |
| CMultiMethodProps::Init(); |
| #else |
| CCommonMethodProps::InitCommon(); |
| #endif |
| } |
| |
| HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); |
| |
| HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); |
| |
| HRESULT Decode(NCompress::NXz::CDecoder &decoder, |
| ISequentialInStream *seqInStream, |
| ISequentialOutStream *outStream, |
| ICompressProgressInfo *progress) |
| { |
| #ifndef Z7_ST |
| decoder._numThreads = _numThreads; |
| #endif |
| decoder._memUsage = _memUsage_Decompress; |
| |
| HRESULT hres = decoder.Decode(seqInStream, outStream, |
| NULL, // *outSizeLimit |
| true, // finishStream |
| progress); |
| |
| if (decoder.MainDecodeSRes_wasUsed |
| && decoder.MainDecodeSRes != SZ_ERROR_MEM |
| && decoder.MainDecodeSRes != SZ_ERROR_UNSUPPORTED) |
| { |
| // if (!_stat2_defined) |
| { |
| _stat2_decode_SRes = decoder.MainDecodeSRes; |
| _stat2 = decoder.Stat; |
| _stat2_defined = true; |
| } |
| } |
| |
| return hres; |
| } |
| |
| public: |
| CBlockInfo *_blocks; |
| size_t _blocksArraySize; |
| UInt64 _maxBlocksSize; |
| CMyComPtr<IInStream> _stream; |
| CMyComPtr<ISequentialInStream> _seqStream; |
| |
| CXzBlock _firstBlock; |
| |
| CHandler(); |
| ~CHandler(); |
| |
| HRESULT SeekToPackPos(UInt64 pos) |
| { |
| return InStream_SeekSet(_stream, pos); |
| } |
| }; |
| |
| |
| CHandler::CHandler(): |
| _blocks(NULL), |
| _blocksArraySize(0) |
| { |
| #ifndef Z7_EXTRACT_ONLY |
| InitXz(); |
| #endif |
| } |
| |
| CHandler::~CHandler() |
| { |
| MyFree(_blocks); |
| } |
| |
| |
| static const Byte kProps[] = |
| { |
| kpidSize, |
| kpidPackSize, |
| kpidMethod |
| }; |
| |
| static const Byte kArcProps[] = |
| { |
| kpidMethod, |
| kpidNumStreams, |
| kpidNumBlocks, |
| kpidClusterSize, |
| kpidCharacts |
| }; |
| |
| 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 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'; |
| } |
| } |
| s.Add_UInt32(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_ARM64, "ARM64" }, |
| { XZ_ID_LZMA2, "LZMA2" } |
| }; |
| |
| static void AddMethodString(AString &s, const CXzFilter &f) |
| { |
| const char *p = NULL; |
| for (unsigned i = 0; i < Z7_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; |
| } |
| |
| 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) |
| s.Add_UInt32((UInt32)f.props[0] + 1); |
| else if (f.id == XZ_ID_ARM64 && f.propsSize == 1) |
| s.Add_UInt32((UInt32)f.props[0] + 16 + 2); |
| else |
| { |
| s += '['; |
| for (UInt32 bi = 0; bi < f.propsSize; bi++) |
| AddHexToString(s, f.props[bi]); |
| s += ']'; |
| } |
| } |
| } |
| |
| static const char * const kChecks[] = |
| { |
| "NoCheck" |
| , "CRC32" |
| , NULL |
| , NULL |
| , "CRC64" |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| , "SHA256" |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| , NULL |
| }; |
| |
| static void AddCheckString(AString &s, 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)); |
| for (i = 0; i <= XZ_CHECK_MASK; i++) |
| if (((mask >> i) & 1) != 0) |
| { |
| s.Add_Space_if_NotEmpty(); |
| if (kChecks[i]) |
| s += kChecks[i]; |
| else |
| { |
| s += "Check-"; |
| s.Add_UInt32((UInt32)i); |
| } |
| } |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| NCOM::CPropVariant prop; |
| |
| const CXzStatInfo *stat = GetStat(); |
| |
| switch (propID) |
| { |
| case kpidPhySize: if (stat) prop = stat->InSize; break; |
| case kpidNumStreams: if (stat && stat->NumStreams_Defined) prop = stat->NumStreams; break; |
| case kpidNumBlocks: if (stat && stat->NumBlocks_Defined) prop = stat->NumBlocks; break; |
| case kpidUnpackSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break; |
| case kpidClusterSize: if (_stat_defined && _stat.NumBlocks_Defined && stat->NumBlocks > 1) prop = _maxBlocksSize; break; |
| case kpidCharacts: |
| if (_firstBlockWasRead) |
| { |
| AString s; |
| if (XzBlock_HasPackSize(&_firstBlock)) |
| s.Add_OptSpaced("BlockPackSize"); |
| if (XzBlock_HasUnpackSize(&_firstBlock)) |
| s.Add_OptSpaced("BlockUnpackSize"); |
| if (!s.IsEmpty()) |
| prop = s; |
| } |
| break; |
| |
| |
| case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; |
| case kpidErrorFlags: |
| { |
| UInt32 v = 0; |
| SRes sres = _stat2_decode_SRes; |
| if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; |
| if (sres == SZ_ERROR_INPUT_EOF) v |= kpv_ErrorFlags_UnexpectedEnd; |
| if (_stat2_defined && _stat2.DataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; |
| if (sres == SZ_ERROR_ARCHIVE) v |= kpv_ErrorFlags_HeadersError; |
| if (sres == SZ_ERROR_UNSUPPORTED) v |= kpv_ErrorFlags_UnsupportedMethod; |
| if (sres == SZ_ERROR_DATA) v |= kpv_ErrorFlags_DataError; |
| if (sres == SZ_ERROR_CRC) v |= kpv_ErrorFlags_CrcError; |
| if (v != 0) |
| prop = v; |
| break; |
| } |
| |
| case kpidMainSubfile: |
| { |
| // debug only, comment it: |
| // if (_blocks) prop = (UInt32)0; |
| break; |
| } |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) |
| { |
| *numItems = 1; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value)) |
| { |
| COM_TRY_BEGIN |
| const CXzStatInfo *stat = GetStat(); |
| NCOM::CPropVariant prop; |
| switch (propID) |
| { |
| case kpidSize: if (stat && stat->UnpackSize_Defined) prop = stat->OutSize; break; |
| case kpidPackSize: if (stat) prop = stat->InSize; break; |
| case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; |
| } |
| prop.Detach(value); |
| return S_OK; |
| COM_TRY_END |
| } |
| |
| |
| struct COpenCallbackWrap |
| { |
| ICompressProgress vt; |
| IArchiveOpenCallback *OpenCallback; |
| HRESULT Res; |
| |
| // new clang shows "non-POD" warning for offsetof(), if we use constructor instead of Init() |
| void Init(IArchiveOpenCallback *progress); |
| }; |
| |
| static SRes OpenCallbackProgress(ICompressProgressPtr pp, UInt64 inSize, UInt64 /* outSize */) |
| { |
| Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(COpenCallbackWrap) |
| if (p->OpenCallback) |
| p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); |
| return HRESULT_To_SRes(p->Res, SZ_ERROR_PROGRESS); |
| } |
| |
| void COpenCallbackWrap::Init(IArchiveOpenCallback *callback) |
| { |
| vt.Progress = OpenCallbackProgress; |
| OpenCallback = callback; |
| Res = SZ_OK; |
| } |
| |
| |
| struct CXzsCPP |
| { |
| CXzs p; |
| CXzsCPP() { Xzs_Construct(&p); } |
| ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } |
| }; |
| |
| #define kInputBufSize ((size_t)1 << 10) |
| |
| struct CLookToRead2_CPP: public CLookToRead2 |
| { |
| CLookToRead2_CPP() |
| { |
| buf = NULL; |
| LookToRead2_CreateVTable(this, |
| True // Lookahead ? |
| ); |
| } |
| void Alloc(size_t allocSize) |
| { |
| buf = (Byte *)MyAlloc(allocSize); |
| if (buf) |
| this->bufSize = allocSize; |
| } |
| ~CLookToRead2_CPP() |
| { |
| MyFree(buf); |
| } |
| }; |
| |
| |
| 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; |
| |
| inStreamWrap.Init(inStream); |
| |
| SRes res = Xz_ReadHeader(&st, &inStreamWrap.vt); |
| |
| if (inStreamWrap.Res != S_OK) |
| return inStreamWrap.Res; |
| if (res != SZ_OK) |
| return SRes_to_Open_HRESULT(res); |
| |
| { |
| CXzBlock block; |
| BoolInt isIndex; |
| UInt32 headerSizeRes; |
| |
| SRes res2 = XzBlock_ReadHeader(&block, &inStreamWrap.vt, &isIndex, &headerSizeRes); |
| |
| if (inStreamWrap.Res != S_OK) |
| return inStreamWrap.Res; |
| |
| if (res2 != SZ_OK) |
| { |
| if (res2 == SZ_ERROR_INPUT_EOF) |
| { |
| _stat2_decode_SRes = res2; |
| _stream = inStream; |
| _seqStream = inStream; |
| _isArc = true; |
| return S_OK; |
| } |
| |
| if (res2 == SZ_ERROR_ARCHIVE) |
| return S_FALSE; |
| } |
| else if (!isIndex) |
| { |
| _firstBlockWasRead = true; |
| _firstBlock = block; |
| |
| unsigned numFilters = XzBlock_GetNumFilters(&block); |
| for (unsigned i = 0; i < numFilters; i++) |
| { |
| _methodsString.Add_Space_if_NotEmpty(); |
| AddMethodString(_methodsString, block.filters[i]); |
| } |
| } |
| } |
| } |
| |
| RINOK(InStream_GetSize_SeekToEnd(inStream, _stat.InSize)) |
| if (callback) |
| { |
| RINOK(callback->SetTotal(NULL, &_stat.InSize)) |
| } |
| |
| CSeekInStreamWrap inStreamImp; |
| |
| inStreamImp.Init(inStream); |
| |
| CLookToRead2_CPP lookStream; |
| |
| lookStream.Alloc(kInputBufSize); |
| |
| if (!lookStream.buf) |
| return E_OUTOFMEMORY; |
| |
| lookStream.realStream = &inStreamImp.vt; |
| LookToRead2_INIT(&lookStream) |
| |
| COpenCallbackWrap openWrap; |
| openWrap.Init(callback); |
| |
| CXzsCPP xzs; |
| Int64 startPosition; |
| SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.vt, &startPosition, &openWrap.vt, &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) |
| { |
| _stat_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; |
| |
| AddCheckString(_methodsString, xzs.p); |
| |
| const size_t numBlocks = (size_t)_stat.NumBlocks + 1; |
| const size_t bytesAlloc = numBlocks * sizeof(CBlockInfo); |
| |
| if (bytesAlloc / sizeof(CBlockInfo) == _stat.NumBlocks + 1) |
| { |
| _blocks = (CBlockInfo *)MyAlloc(bytesAlloc); |
| if (_blocks) |
| { |
| unsigned blockIndex = 0; |
| UInt64 unpackPos = 0; |
| |
| for (size_t si = xzs.p.num; si != 0;) |
| { |
| si--; |
| const CXzStream &str = xzs.p.streams[si]; |
| UInt64 packPos = str.startOffset + XZ_STREAM_HEADER_SIZE; |
| |
| for (size_t bi = 0; bi < str.numBlocks; bi++) |
| { |
| const CXzBlockSizes &bs = str.blocks[bi]; |
| const UInt64 packSizeAligned = bs.totalSize + ((0 - (unsigned)bs.totalSize) & 3); |
| |
| if (bs.unpackSize != 0) |
| { |
| if (blockIndex >= _stat.NumBlocks) |
| return E_FAIL; |
| |
| CBlockInfo &block = _blocks[blockIndex++]; |
| block.StreamFlags = str.flags; |
| block.PackSize = bs.totalSize; // packSizeAligned; |
| block.PackPos = packPos; |
| block.UnpackPos = unpackPos; |
| } |
| packPos += packSizeAligned; |
| unpackPos += bs.unpackSize; |
| if (_maxBlocksSize < bs.unpackSize) |
| _maxBlocksSize = bs.unpackSize; |
| } |
| } |
| |
| /* |
| if (blockIndex != _stat.NumBlocks) |
| { |
| // there are Empty blocks; |
| } |
| */ |
| if (_stat.OutSize != unpackPos) |
| return E_FAIL; |
| CBlockInfo &block = _blocks[blockIndex++]; |
| block.StreamFlags = 0; |
| block.PackSize = 0; |
| block.PackPos = 0; |
| block.UnpackPos = unpackPos; |
| _blocksArraySize = blockIndex; |
| } |
| } |
| } |
| else |
| { |
| res = SZ_OK; |
| } |
| |
| RINOK(SRes_to_Open_HRESULT(res)) |
| |
| _stream = inStream; |
| _seqStream = inStream; |
| _isArc = true; |
| return S_OK; |
| } |
| |
| |
| |
| Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback)) |
| { |
| COM_TRY_BEGIN |
| { |
| Close(); |
| return Open2(inStream, callback); |
| } |
| COM_TRY_END |
| } |
| |
| Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream)) |
| { |
| Close(); |
| _seqStream = stream; |
| _isArc = true; |
| _needSeekToStart = false; |
| return S_OK; |
| } |
| |
| Z7_COM7F_IMF(CHandler::Close()) |
| { |
| XzStatInfo_Clear(&_stat); |
| XzStatInfo_Clear(&_stat2); |
| _stat_defined = false; |
| _stat2_defined = false; |
| _stat2_decode_SRes = SZ_OK; |
| |
| _isArc = false; |
| _needSeekToStart = false; |
| _firstBlockWasRead = false; |
| |
| _methodsString.Empty(); |
| _stream.Release(); |
| _seqStream.Release(); |
| |
| MyFree(_blocks); |
| _blocks = NULL; |
| _blocksArraySize = 0; |
| _maxBlocksSize = 0; |
| |
| return S_OK; |
| } |
| |
| |
| struct CXzUnpackerCPP2 |
| { |
| Byte *InBuf; |
| // Byte *OutBuf; |
| CXzUnpacker p; |
| |
| CXzUnpackerCPP2(); |
| ~CXzUnpackerCPP2(); |
| }; |
| |
| CXzUnpackerCPP2::CXzUnpackerCPP2(): InBuf(NULL) |
| // , OutBuf(NULL) |
| { |
| XzUnpacker_Construct(&p, &g_Alloc); |
| } |
| |
| CXzUnpackerCPP2::~CXzUnpackerCPP2() |
| { |
| XzUnpacker_Free(&p); |
| MidFree(InBuf); |
| // MidFree(OutBuf); |
| } |
| |
| |
| Z7_CLASS_IMP_COM_1( |
| CInStream |
| , IInStream |
| ) |
| Z7_IFACE_COM7_IMP(ISequentialInStream) |
| |
| UInt64 _virtPos; |
| public: |
| UInt64 Size; |
| UInt64 _cacheStartPos; |
| size_t _cacheSize; |
| CByteBuffer _cache; |
| // UInt64 _startPos; |
| CXzUnpackerCPP2 xz; |
| |
| void InitAndSeek() |
| { |
| _virtPos = 0; |
| _cacheStartPos = 0; |
| _cacheSize = 0; |
| // _startPos = startPos; |
| } |
| |
| CHandler *_handlerSpec; |
| CMyComPtr<IUnknown> _handler; |
| |
| // ~CInStream(); |
| }; |
| |
| /* |
| CInStream::~CInStream() |
| { |
| // _cache.Free(); |
| } |
| */ |
| |
| static size_t FindBlock(const CBlockInfo *blocks, size_t numBlocks, UInt64 pos) |
| { |
| size_t left = 0, right = numBlocks; |
| for (;;) |
| { |
| size_t mid = (left + right) / 2; |
| if (mid == left) |
| return left; |
| if (pos < blocks[mid].UnpackPos) |
| right = mid; |
| else |
| left = mid; |
| } |
| } |
| |
| |
| |
| static HRESULT DecodeBlock(CXzUnpackerCPP2 &xzu, |
| ISequentialInStream *seqInStream, |
| unsigned streamFlags, |
| UInt64 packSize, // pure size from Index record, it doesn't include pad zeros |
| size_t unpackSize, Byte *dest |
| // , ICompressProgressInfo *progress |
| ) |
| { |
| const size_t kInBufSize = (size_t)1 << 16; |
| |
| XzUnpacker_Init(&xzu.p); |
| |
| if (!xzu.InBuf) |
| { |
| xzu.InBuf = (Byte *)MidAlloc(kInBufSize); |
| if (!xzu.InBuf) |
| return E_OUTOFMEMORY; |
| } |
| |
| xzu.p.streamFlags = (UInt16)streamFlags; |
| XzUnpacker_PrepareToRandomBlockDecoding(&xzu.p); |
| |
| XzUnpacker_SetOutBuf(&xzu.p, dest, unpackSize); |
| |
| const UInt64 packSizeAligned = packSize + ((0 - (unsigned)packSize) & 3); |
| UInt64 packRem = packSizeAligned; |
| |
| UInt32 inSize = 0; |
| SizeT inPos = 0; |
| SizeT outPos = 0; |
| |
| HRESULT readRes = S_OK; |
| |
| for (;;) |
| { |
| if (inPos == inSize && readRes == S_OK) |
| { |
| inPos = 0; |
| inSize = 0; |
| UInt32 rem = kInBufSize; |
| if (rem > packRem) |
| rem = (UInt32)packRem; |
| if (rem != 0) |
| readRes = seqInStream->Read(xzu.InBuf, rem, &inSize); |
| } |
| |
| SizeT inLen = inSize - inPos; |
| SizeT outLen = unpackSize - outPos; |
| |
| ECoderStatus status; |
| |
| const SRes res = XzUnpacker_Code(&xzu.p, |
| // dest + outPos, |
| NULL, |
| &outLen, |
| xzu.InBuf + inPos, &inLen, |
| (inLen == 0), // srcFinished |
| CODER_FINISH_END, &status); |
| |
| // return E_OUTOFMEMORY; |
| // res = SZ_ERROR_CRC; |
| |
| if (res != SZ_OK) |
| { |
| if (res == SZ_ERROR_CRC) |
| return S_FALSE; |
| return SResToHRESULT(res); |
| } |
| |
| inPos += inLen; |
| outPos += outLen; |
| |
| packRem -= inLen; |
| |
| const BoolInt blockFinished = XzUnpacker_IsBlockFinished(&xzu.p); |
| |
| if ((inLen == 0 && outLen == 0) || blockFinished) |
| { |
| if (packRem != 0 || !blockFinished || unpackSize != outPos) |
| return S_FALSE; |
| if (XzUnpacker_GetPackSizeForIndex(&xzu.p) != packSize) |
| return S_FALSE; |
| return S_OK; |
| } |
| } |
| } |
| |
| |
| Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize)) |
| { |
| COM_TRY_BEGIN |
| |
| if (processedSize) |
| *processedSize = 0; |
| if (size == 0) |
| return S_OK; |
| |
| { |
| if (_virtPos >= Size) |
| return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL; |
| { |
| UInt64 rem = Size - _virtPos; |
| if (size > rem) |
| size = (UInt32)rem; |
| } |
| } |
| |
| if (size == 0) |
| return S_OK; |
| |
| if (_virtPos < _cacheStartPos || _virtPos >= _cacheStartPos + _cacheSize) |
| { |
| const size_t bi = FindBlock(_handlerSpec->_blocks, _handlerSpec->_blocksArraySize, _virtPos); |
| const CBlockInfo &block = _handlerSpec->_blocks[bi]; |
| const UInt64 unpackSize = _handlerSpec->_blocks[bi + 1].UnpackPos - block.UnpackPos; |
| if (_cache.Size() < unpackSize) |
| return E_FAIL; |
| |
| _cacheSize = 0; |
| |
| RINOK(_handlerSpec->SeekToPackPos(block.PackPos)) |
| RINOK(DecodeBlock(xz, _handlerSpec->_seqStream, block.StreamFlags, block.PackSize, |
| (size_t)unpackSize, _cache)) |
| _cacheStartPos = block.UnpackPos; |
| _cacheSize = (size_t)unpackSize; |
| } |
| |
| { |
| const size_t offset = (size_t)(_virtPos - _cacheStartPos); |
| const size_t rem = _cacheSize - offset; |
| if (size > rem) |
| size = (UInt32)rem; |
| memcpy(data, _cache + offset, size); |
| _virtPos += size; |
| if (processedSize) |
| *processedSize = size; |
| return S_OK; |
| } |
| |
| COM_TRY_END |
| } |
| |
| |
| Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)) |
| { |
| switch (seekOrigin) |
| { |
| case STREAM_SEEK_SET: break; |
| case STREAM_SEEK_CUR: offset += _virtPos; break; |
| case STREAM_SEEK_END: offset += Size; break; |
| default: return STG_E_INVALIDFUNCTION; |
| } |
| if (offset < 0) |
| return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; |
| _virtPos = (UInt64)offset; |
| if (newPosition) |
| *newPosition = (UInt64)offset; |
| return S_OK; |
| } |
| |
| |
| |
| static const UInt64 kMaxBlockSize_for_GetStream = (UInt64)1 << 40; |
| |
| Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) |
| { |
| COM_TRY_BEGIN |
| |
| *stream = NULL; |
| |
| if (index != 0) |
| return E_INVALIDARG; |
| |
| if (!_stat.UnpackSize_Defined |
| || _maxBlocksSize == 0 // 18.02 |
| || _maxBlocksSize > kMaxBlockSize_for_GetStream |
| || _maxBlocksSize != (size_t)_maxBlocksSize) |
| return S_FALSE; |
| |
| UInt64 memSize; |
| if (!NSystem::GetRamSize(memSize)) |
| memSize = (UInt64)(sizeof(size_t)) << 28; |
| { |
| if (_maxBlocksSize > memSize / 4) |
| return S_FALSE; |
| } |
| |
| CInStream *spec = new CInStream; |
| CMyComPtr<ISequentialInStream> specStream = spec; |
| spec->_cache.Alloc((size_t)_maxBlocksSize); |
| spec->_handlerSpec = this; |
| spec->_handler = (IInArchive *)this; |
| spec->Size = _stat.OutSize; |
| spec->InitAndSeek(); |
| |
| *stream = specStream.Detach(); |
| return S_OK; |
| |
| COM_TRY_END |
| } |
| |
| |
| static Int32 Get_Extract_OperationResult(const NCompress::NXz::CDecoder &decoder) |
| { |
| Int32 opRes; |
| SRes sres = decoder.MainDecodeSRes; |
| if (sres == SZ_ERROR_NO_ARCHIVE) // (!IsArc) |
| opRes = NExtract::NOperationResult::kIsNotArc; |
| else if (sres == SZ_ERROR_INPUT_EOF) // (UnexpectedEnd) |
| opRes = NExtract::NOperationResult::kUnexpectedEnd; |
| else if (decoder.Stat.DataAfterEnd) |
| opRes = NExtract::NOperationResult::kDataAfterEnd; |
| else if (sres == SZ_ERROR_CRC) // (CrcError) |
| opRes = NExtract::NOperationResult::kCRCError; |
| else if (sres == SZ_ERROR_UNSUPPORTED) // (Unsupported) |
| opRes = NExtract::NOperationResult::kUnsupportedMethod; |
| else if (sres == SZ_ERROR_ARCHIVE) // (HeadersError) |
| opRes = NExtract::NOperationResult::kDataError; |
| else if (sres == SZ_ERROR_DATA) // (DataError) |
| opRes = NExtract::NOperationResult::kDataError; |
| else if (sres != SZ_OK) |
| opRes = NExtract::NOperationResult::kDataError; |
| else |
| opRes = NExtract::NOperationResult::kOK; |
| return opRes; |
| } |
| |
| |
| |
| |
| Z7_COM7F_IMF(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; |
| |
| const CXzStatInfo *stat = GetStat(); |
| |
| if (stat) |
| extractCallback->SetTotal(stat->InSize); |
| |
| UInt64 currentTotalPacked = 0; |
| RINOK(extractCallback->SetCompleted(¤tTotalPacked)) |
| CMyComPtr<ISequentialOutStream> realOutStream; |
| const Int32 askMode = testMode ? |
| NExtract::NAskMode::kTest : |
| NExtract::NAskMode::kExtract; |
| |
| RINOK(extractCallback->GetStream(0, &realOutStream, askMode)) |
| |
| if (!testMode && !realOutStream) |
| return S_OK; |
| |
| extractCallback->PrepareOperation(askMode); |
| |
| CLocalProgress *lps = new CLocalProgress; |
| CMyComPtr<ICompressProgressInfo> lpsRef = lps; |
| lps->Init(extractCallback, true); |
| |
| if (_needSeekToStart) |
| { |
| if (!_stream) |
| return E_FAIL; |
| RINOK(InStream_SeekToBegin(_stream)) |
| } |
| else |
| _needSeekToStart = true; |
| |
| |
| NCompress::NXz::CDecoder decoder; |
| |
| HRESULT hres = Decode(decoder, _seqStream, realOutStream, lpsRef); |
| |
| if (!decoder.MainDecodeSRes_wasUsed) |
| return hres == S_OK ? E_FAIL : hres; |
| |
| Int32 opRes = Get_Extract_OperationResult(decoder); |
| if (opRes == NExtract::NOperationResult::kOK |
| && hres != S_OK) |
| opRes = NExtract::NOperationResult::kDataError; |
| |
| realOutStream.Release(); |
| return extractCallback->SetOperationResult(opRes); |
| COM_TRY_END |
| } |
| |
| |
| |
| #ifndef Z7_EXTRACT_ONLY |
| |
| Z7_COM7F_IMF(CHandler::GetFileTimeType(UInt32 *timeType)) |
| { |
| *timeType = GET_FileTimeType_NotDefined_for_GetFileTimeType; |
| // *timeType = NFileTimeType::kUnix; |
| return S_OK; |
| } |
| |
| |
| Z7_COM7F_IMF(CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, |
| IArchiveUpdateCallback *updateCallback)) |
| { |
| COM_TRY_BEGIN |
| |
| if (numItems == 0) |
| { |
| CSeqOutStreamWrap seqOutStream; |
| seqOutStream.Init(outStream); |
| SRes res = Xz_EncodeEmpty(&seqOutStream.vt); |
| return SResToHRESULT(res); |
| } |
| |
| if (numItems != 1) |
| return E_INVALIDARG; |
| |
| { |
| Z7_DECL_CMyComPtr_QI_FROM( |
| IStreamSetRestriction, |
| setRestriction, outStream) |
| if (setRestriction) |
| RINOK(setRestriction->SetRestriction(0, 0)) |
| } |
| |
| 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 dataSize; |
| { |
| NCOM::CPropVariant prop; |
| RINOK(updateCallback->GetProperty(0, kpidSize, &prop)) |
| if (prop.vt != VT_UI8) |
| return E_INVALIDARG; |
| dataSize = prop.uhVal.QuadPart; |
| } |
| |
| NCompress::NXz::CEncoder *encoderSpec = new NCompress::NXz::CEncoder; |
| CMyComPtr<ICompressCoder> encoder = encoderSpec; |
| |
| CXzProps &xzProps = encoderSpec->xzProps; |
| CLzma2EncProps &lzma2Props = xzProps.lzma2Props; |
| |
| lzma2Props.lzmaProps.level = GetLevel(); |
| |
| xzProps.reduceSize = dataSize; |
| /* |
| { |
| NCOM::CPropVariant prop = (UInt64)dataSize; |
| RINOK(encoderSpec->SetCoderProp(NCoderPropID::kReduceSize, prop)) |
| } |
| */ |
| |
| #ifndef Z7_ST |
| |
| UInt32 numThreads = _numThreads; |
| |
| const UInt32 kNumThreads_Max = 1024; |
| if (numThreads > kNumThreads_Max) |
| numThreads = kNumThreads_Max; |
| |
| if (!_numThreads_WasForced |
| && _numThreads >= 1 |
| && _memUsage_WasSet) |
| { |
| COneMethodInfo oneMethodInfo; |
| if (!_methods.IsEmpty()) |
| oneMethodInfo = _methods[0]; |
| |
| SetGlobalLevelTo(oneMethodInfo); |
| |
| const bool numThreads_WasSpecifiedInMethod = (oneMethodInfo.Get_NumThreads() >= 0); |
| if (!numThreads_WasSpecifiedInMethod) |
| { |
| // here we set the (NCoderPropID::kNumThreads) property in each method, only if there is no such property already |
| CMultiMethodProps::SetMethodThreadsTo_IfNotFinded(oneMethodInfo, numThreads); |
| } |
| |
| UInt64 cs = _numSolidBytes; |
| if (cs != XZ_PROPS_BLOCK_SIZE_AUTO) |
| oneMethodInfo.AddProp_BlockSize2(cs); |
| cs = oneMethodInfo.Get_Xz_BlockSize(); |
| |
| if (cs != XZ_PROPS_BLOCK_SIZE_AUTO && |
| cs != XZ_PROPS_BLOCK_SIZE_SOLID) |
| { |
| const UInt32 lzmaThreads = oneMethodInfo.Get_Lzma_NumThreads(); |
| const UInt32 numBlockThreads_Original = numThreads / lzmaThreads; |
| |
| if (numBlockThreads_Original > 1) |
| { |
| UInt32 numBlockThreads = numBlockThreads_Original; |
| { |
| const UInt64 lzmaMemUsage = oneMethodInfo.Get_Lzma_MemUsage(false); |
| for (; numBlockThreads > 1; numBlockThreads--) |
| { |
| UInt64 size = numBlockThreads * (lzmaMemUsage + cs); |
| UInt32 numPackChunks = numBlockThreads + (numBlockThreads / 8) + 1; |
| if (cs < ((UInt32)1 << 26)) numPackChunks++; |
| if (cs < ((UInt32)1 << 24)) numPackChunks++; |
| if (cs < ((UInt32)1 << 22)) numPackChunks++; |
| size += numPackChunks * cs; |
| // printf("\nnumBlockThreads = %d, size = %d\n", (unsigned)(numBlockThreads), (unsigned)(size >> 20)); |
| if (size <= _memUsage_Compress) |
| break; |
| } |
| } |
| if (numBlockThreads == 0) |
| numBlockThreads = 1; |
| if (numBlockThreads != numBlockThreads_Original) |
| numThreads = numBlockThreads * lzmaThreads; |
| } |
| } |
| } |
| xzProps.numTotalThreads = (int)numThreads; |
| |
| #endif // Z7_ST |
| |
| |
| xzProps.blockSize = _numSolidBytes; |
| if (_numSolidBytes == XZ_PROPS_BLOCK_SIZE_SOLID) |
| { |
| xzProps.lzma2Props.blockSize = LZMA2_ENC_PROPS_BLOCK_SIZE_SOLID; |
| } |
| |
| RINOK(encoderSpec->SetCheckSize(_crcSize)) |
| |
| { |
| CXzFilterProps &filter = xzProps.filterProps; |
| |
| 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; |
| } |
| else |
| return E_INVALIDARG; |
| } |
| if (!deltaDefined) |
| return E_INVALIDARG; |
| } |
| filter.id = _filterId; |
| } |
| |
| FOR_VECTOR (i, _methods) |
| { |
| COneMethodInfo &m = _methods[i]; |
| |
| FOR_VECTOR (j, m.Props) |
| { |
| const CProp &prop = m.Props[j]; |
| RINOK(encoderSpec->SetCoderProp(prop.Id, prop.Value)) |
| } |
| } |
| |
| { |
| CMyComPtr<ISequentialInStream> fileInStream; |
| RINOK(updateCallback->GetStream(0, &fileInStream)) |
| if (!fileInStream) |
| return S_FALSE; |
| { |
| CMyComPtr<IStreamGetSize> streamGetSize; |
| fileInStream.QueryInterface(IID_IStreamGetSize, &streamGetSize); |
| if (streamGetSize) |
| { |
| UInt64 size; |
| if (streamGetSize->GetSize(&size) == S_OK) |
| dataSize = size; |
| } |
| } |
| RINOK(updateCallback->SetTotal(dataSize)) |
| CLocalProgress *lps = new CLocalProgress; |
| CMyComPtr<ICompressProgressInfo> progress = lps; |
| lps->Init(updateCallback, true); |
| RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, progress)) |
| } |
| |
| return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); |
| } |
| |
| if (indexInArchive != 0) |
| return E_INVALIDARG; |
| |
| Z7_DECL_CMyComPtr_QI_FROM( |
| IArchiveUpdateCallbackFile, |
| opCallback, updateCallback) |
| if (opCallback) |
| { |
| RINOK(opCallback->ReportOperation(NEventIndexType::kInArcIndex, 0, NUpdateNotifyOp::kReplicate)) |
| } |
| |
| if (_stream) |
| { |
| const CXzStatInfo *stat = GetStat(); |
| if (stat) |
| { |
| RINOK(updateCallback->SetTotal(stat->InSize)) |
| } |
| RINOK(InStream_SeekToBegin(_stream)) |
| } |
| |
| CLocalProgress *lps = new CLocalProgress; |
| CMyComPtr<ICompressProgressInfo> progress = lps; |
| lps->Init(updateCallback, true); |
| |
| return NCompress::CopyStream(_stream, outStream, progress); |
| |
| COM_TRY_END |
| } |
| |
| #endif |
| |
| |
| HRESULT CHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) |
| { |
| UString name = nameSpec; |
| name.MakeLower_Ascii(); |
| if (name.IsEmpty()) |
| return E_INVALIDARG; |
| |
| #ifndef Z7_EXTRACT_ONLY |
| |
| if (name[0] == L's') |
| { |
| const wchar_t *s = name.Ptr(1); |
| if (*s == 0) |
| { |
| bool useStr = false; |
| bool isSolid; |
| switch (value.vt) |
| { |
| case VT_EMPTY: isSolid = true; break; |
| case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break; |
| case VT_BSTR: |
| if (!StringToBool(value.bstrVal, isSolid)) |
| useStr = true; |
| break; |
| default: return E_INVALIDARG; |
| } |
| if (!useStr) |
| { |
| _numSolidBytes = (isSolid ? XZ_PROPS_BLOCK_SIZE_SOLID : XZ_PROPS_BLOCK_SIZE_AUTO); |
| return S_OK; |
| } |
| } |
| return ParseSizeString(s, value, |
| 0, // percentsBase |
| _numSolidBytes) ? S_OK: E_INVALIDARG; |
| } |
| |
| return CMultiMethodProps::SetProperty(name, value); |
| |
| #else |
| |
| { |
| HRESULT hres; |
| if (SetCommonProperty(name, value, hres)) |
| return hres; |
| } |
| |
| return E_INVALIDARG; |
| |
| #endif |
| } |
| |
| |
| |
| Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)) |
| { |
| COM_TRY_BEGIN |
| |
| Init(); |
| |
| for (UInt32 i = 0; i < numProps; i++) |
| { |
| RINOK(SetProperty(names[i], values[i])) |
| } |
| |
| #ifndef Z7_EXTRACT_ONLY |
| |
| if (!_filterMethod.MethodName.IsEmpty()) |
| { |
| unsigned k; |
| for (k = 0; k < Z7_ARRAY_SIZE(g_NamePairs); k++) |
| { |
| const CMethodNamePair &pair = g_NamePairs[k]; |
| if (StringsAreEqualNoCase_Ascii(_filterMethod.MethodName, pair.Name)) |
| { |
| _filterId = pair.Id; |
| break; |
| } |
| } |
| if (k == Z7_ARRAY_SIZE(g_NamePairs)) |
| return E_INVALIDARG; |
| } |
| |
| _methods.DeleteFrontal(GetNumEmptyMethods()); |
| if (_methods.Size() > 1) |
| return E_INVALIDARG; |
| if (_methods.Size() == 1) |
| { |
| AString &methodName = _methods[0].MethodName; |
| if (methodName.IsEmpty()) |
| methodName = k_LZMA2_Name; |
| else if ( |
| !methodName.IsEqualTo_Ascii_NoCase(k_LZMA2_Name) |
| && !methodName.IsEqualTo_Ascii_NoCase("xz")) |
| return E_INVALIDARG; |
| } |
| |
| #endif |
| |
| return S_OK; |
| |
| COM_TRY_END |
| } |
| |
| |
| REGISTER_ARC_IO( |
| "xz", "xz txz", "* .tar", 0xC, |
| XZ_SIG, 0 |
| , NArcInfoFlags::kKeepName |
| , 0 |
| , NULL) |
| |
| }} |