From 85e04af223a4c579dba7bf3a1e90d6000a85afc8 Mon Sep 17 00:00:00 2001 From: yh-sb Date: Wed, 24 Dec 2025 13:05:20 +0200 Subject: [PATCH] feat: add support for .lz and .lz4 compression algorithms --- C/Lz4Dec.c | 140 +++++++ C/Lz4Dec.h | 27 ++ CPP/7zip/7zip_gcc.mak | 6 + CPP/7zip/Archive/Lz4Handler.cpp | 546 +++++++++++++++++++++++++ CPP/7zip/Archive/LzipHandler.cpp | 329 +++++++++++++++ CPP/7zip/Bundles/Format7zF/Arc.mak | 1 + CPP/7zip/Bundles/Format7zF/Arc_gcc.mak | 3 + 7 files changed, 1052 insertions(+) create mode 100644 C/Lz4Dec.c create mode 100644 C/Lz4Dec.h create mode 100644 CPP/7zip/Archive/Lz4Handler.cpp create mode 100644 CPP/7zip/Archive/LzipHandler.cpp diff --git a/C/Lz4Dec.c b/C/Lz4Dec.c new file mode 100644 index 0000000..04ecf26 --- /dev/null +++ b/C/Lz4Dec.c @@ -0,0 +1,140 @@ +/* Lz4Dec.c -- LZ4 Decoder +2025 : Igor Pavlov : Public domain */ + +#include "Precomp.h" + +#include + +#include "Lz4Dec.h" + +/* +LZ4 block format: + A block is composed of sequences. + Each sequence: [token][literals][match] + + Token (1 byte): + - High 4 bits: literal length (0-15, 15 means more bytes follow) + - Low 4 bits: match length - 4 (0-15, 15 means more bytes follow) + + If literal length == 15, read additional bytes until byte < 255 + Literals: raw bytes + + Match: + - Offset: 2 bytes little-endian (must be > 0) + - If match length field == 15, read additional bytes until byte < 255 + - Actual match length = field value + 4 + + Last sequence has no match (ends after literals) +*/ + +SRes Lz4Dec_DecodeBlock( + const Byte *src, SizeT srcLen, + Byte *dest, SizeT *destLen, + SizeT *srcConsumed) +{ + const Byte *srcEnd = src + srcLen; + const Byte *srcStart = src; + Byte *destStart = dest; + Byte *destEnd = dest + *destLen; + + *srcConsumed = 0; + *destLen = 0; + + while (src < srcEnd) + { + unsigned token; + SizeT litLen; + SizeT matchLen; + + // Read token + token = *src++; + litLen = token >> 4; + matchLen = token & 0x0F; + + // Extended literal length + if (litLen == 15) + { + for (;;) + { + unsigned b; + if (src >= srcEnd) + return SZ_ERROR_INPUT_EOF; + b = *src++; + litLen += b; + if (b != 255) + break; + } + } + + // Copy literals (non-overlapping) + if (litLen > 0) + { + if (src + litLen > srcEnd) + return SZ_ERROR_INPUT_EOF; + if (dest + litLen > destEnd) + return SZ_ERROR_OUTPUT_EOF; + + memcpy(dest, src, litLen); + src += litLen; + dest += litLen; + } + + // Is last sequence ? (no match) + if (src >= srcEnd) + break; + + // Read match offset (2 bytes, little-endian) + { + SizeT offset; + const Byte *matchSrc; + + if (src + 2 > srcEnd) + return SZ_ERROR_INPUT_EOF; + + offset = (SizeT)src[0] | ((SizeT)src[1] << 8); + src += 2; + + if (offset == 0) + return SZ_ERROR_DATA; // Offset 0 is invalid + + // Extended match length + matchLen += 4; // Minimum match length is 4 + + if ((token & 0x0F) == 15) + { + for (;;) + { + unsigned b; + if (src >= srcEnd) + return SZ_ERROR_INPUT_EOF; + b = *src++; + matchLen += b; + if (b != 255) + break; + } + } + + // Validate offset + if (offset > (SizeT)(dest - destStart)) + return SZ_ERROR_DATA; // Offset too large + + // Copy match + if (dest + matchLen > destEnd) + return SZ_ERROR_OUTPUT_EOF; + + matchSrc = dest - offset; + + // Handle overlapping copies + { + SizeT i; + for (i = 0; i < matchLen; i++) + dest[i] = matchSrc[i]; + } + dest += matchLen; + } + } + + *srcConsumed = (SizeT)(src - srcStart); + *destLen = (SizeT)(dest - destStart); + return SZ_OK; +} diff --git a/C/Lz4Dec.h b/C/Lz4Dec.h new file mode 100644 index 0000000..aee32b0 --- /dev/null +++ b/C/Lz4Dec.h @@ -0,0 +1,27 @@ +/* Lz4Dec.h -- LZ4 Decoder +2025 : Igor Pavlov : Public domain */ + +#ifndef Z7_LZ4_DEC_H +#define Z7_LZ4_DEC_H + +#include "7zTypes.h" + +EXTERN_C_BEGIN + +/* +LZ4 block decoder +Returns: + SZ_OK - success + SZ_ERROR_DATA - data error + SZ_ERROR_INPUT_EOF - need more input + SZ_ERROR_OUTPUT_EOF - need more output space +*/ + +SRes Lz4Dec_DecodeBlock( + const Byte *src, SizeT srcLen, + Byte *dest, SizeT *destLen, + SizeT *srcConsumed); + +EXTERN_C_END + +#endif diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak index 12f1ef2..3e4dbbe 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -544,6 +544,10 @@ $O/LvmHandler.o: ../../Archive/LvmHandler.cpp $(CXX) $(CXXFLAGS) $< $O/LzhHandler.o: ../../Archive/LzhHandler.cpp $(CXX) $(CXXFLAGS) $< +$O/Lz4Handler.o: ../../Archive/Lz4Handler.cpp + $(CXX) $(CXXFLAGS) $< +$O/LzipHandler.o: ../../Archive/LzipHandler.cpp + $(CXX) $(CXXFLAGS) $< $O/LzmaHandler.o: ../../Archive/LzmaHandler.cpp $(CXX) $(CXXFLAGS) $< $O/MachoHandler.o: ../../Archive/MachoHandler.cpp @@ -1192,6 +1196,8 @@ $O/HuffEnc.o: ../../../../C/HuffEnc.c $(CC) $(CFLAGS) $< $O/LzFind.o: ../../../../C/LzFind.c $(CC) $(CFLAGS) $< +$O/Lz4Dec.o: ../../../../C/Lz4Dec.c + $(CC) $(CFLAGS) $< # ifdef MT_FILES $O/LzFindMt.o: ../../../../C/LzFindMt.c diff --git a/CPP/7zip/Archive/Lz4Handler.cpp b/CPP/7zip/Archive/Lz4Handler.cpp new file mode 100644 index 0000000..ef1fd68 --- /dev/null +++ b/CPP/7zip/Archive/Lz4Handler.cpp @@ -0,0 +1,546 @@ +// Lz4Handler.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/DummyOutStream.h" + +#include "../../../C/CpuArch.h" +#include "../../../C/Alloc.h" +#include "../../../C/Lz4Dec.h" + +using namespace NWindows; + +namespace NArchive { +namespace NLz4 { + +/* +LZ4 Frame format: + Magic: 4 bytes (0x184D2204 little-endian) + Frame Descriptor: + FLG byte: + bits 7-6: Version (must be 01) + bit 5: Block Independence + bit 4: Block Checksum flag + bit 3: Content Size flag + bit 2: Content Checksum flag + bit 1: Reserved (0) + bit 0: DictID flag + BD byte: + bit 7: Reserved (0) + bits 6-4: Block Max Size (4=64KB, 5=256KB, 6=1MB, 7=4MB) + bits 3-0: Reserved (0) + [Content Size: 8 bytes if Content Size flag set] + [DictID: 4 bytes if DictID flag set] + Header Checksum: 1 byte (XXH32 >> 8) + Data Blocks: + Block Size: 4 bytes (bit 31: 1=uncompressed, bits 30-0: size) + Block Data: size bytes + [Block Checksum: 4 bytes if Block Checksum flag set] + ... repeat until Block Size == 0 + End Mark: 4 bytes (0x00000000) + [Content Checksum: 4 bytes if Content Checksum flag set] +*/ + +static const UInt32 kMagic = 0x184D2204; +static const unsigned kMagicSize = 4; +static const unsigned kMinHeaderSize = 7; // magic + FLG + BD + HC +static const unsigned kMaxHeaderSize = 19; // magic + FLG + BD + content_size(8) + dictid(4) + HC + +static const unsigned kBlockSizes[] = { + 0, // 0: reserved + 0, // 1: reserved + 0, // 2: reserved + 0, // 3: reserved + 64 << 10, // 4: 64 KB + 256 << 10, // 5: 256 KB + 1 << 20, // 6: 1 MB + 4 << 20 // 7: 4 MB +}; + +struct CFrameInfo +{ + bool BlockIndependence; + bool BlockChecksum; + bool ContentSizePresent; + bool ContentChecksum; + bool DictIdPresent; + UInt32 BlockMaxSize; + UInt64 ContentSize; + UInt32 DictId; + + unsigned GetHeaderSize() const + { + return kMagicSize + 2 + (ContentSizePresent ? 8 : 0) + (DictIdPresent ? 4 : 0) + 1; + } + + bool Parse(const Byte *p, size_t size) + { + if (size < kMinHeaderSize) + return false; + + // Check magic + if (GetUi32(p) != kMagic) + return false; + p += 4; + + Byte flg = p[0]; + Byte bd = p[1]; + p += 2; + + // Version must be 01 + if ((flg >> 6) != 1) + return false; + + // Reserved bit in FLG must be 0 + if (flg & 2) + return false; + + // Reserved bits in BD must be 0 + if (bd & 0x8F) + return false; + + BlockIndependence = (flg & 0x20) != 0; + BlockChecksum = (flg & 0x10) != 0; + ContentSizePresent = (flg & 0x08) != 0; + ContentChecksum = (flg & 0x04) != 0; + DictIdPresent = (flg & 0x01) != 0; + + unsigned blockMaxSizeCode = (bd >> 4) & 7; + if (blockMaxSizeCode < 4) + return false; // Reserved values + BlockMaxSize = kBlockSizes[blockMaxSizeCode]; + + ContentSize = 0; + DictId = 0; + + unsigned headerSize = kMagicSize + 2; + + if (ContentSizePresent) + { + headerSize += 8; + if (size < headerSize + 1) + return false; + ContentSize = GetUi64(p); + p += 8; + } + + if (DictIdPresent) + { + headerSize += 4; + if (size < headerSize + 1) + return false; + DictId = GetUi32(p); + p += 4; + } + + // Header checksum (we don't verify it, just skip) + headerSize += 1; + + return true; + } +}; + +API_FUNC_static_IsArc IsArc_Lz4(const Byte *p, size_t size) +{ + if (size < kMinHeaderSize) + return k_IsArc_Res_NEED_MORE; + + // Check magic + if (GetUi32(p) != kMagic) + return k_IsArc_Res_NO; + + Byte flg = p[4]; + Byte bd = p[5]; + + // Version must be 01 + if ((flg >> 6) != 1) + return k_IsArc_Res_NO; + + // Reserved bit in FLG must be 0 + if (flg & 2) + return k_IsArc_Res_NO; + + // Reserved bits in BD must be 0 + if (bd & 0x8F) + return k_IsArc_Res_NO; + + // Block max size must be valid + unsigned blockMaxSizeCode = (bd >> 4) & 7; + if (blockMaxSizeCode < 4) + return k_IsArc_Res_NO; + + return k_IsArc_Res_YES; +} +} + +Z7_CLASS_IMP_CHandler_IInArchive_1( + IArchiveOpenSeq +) + CMyComPtr _stream; + CMyComPtr _seqStream; + + bool _isArc; + bool _needSeekToStart; + bool _dataAfterEnd; + bool _needMoreInput; + bool _dataError; + + bool _packSize_Defined; + bool _unpackSize_Defined; + + UInt64 _packSize; + UInt64 _unpackSize; + + CFrameInfo _frameInfo; +}; + +static const Byte kProps[] = +{ + kpidSize, + kpidPackSize +}; + +static const Byte kArcProps[] = +{ + kpidPhySize +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) +{ + NCOM::CPropVariant prop; + switch (propID) + { + case kpidPhySize: if (_packSize_Defined) prop = _packSize; break; + case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; + if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; + if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; + if (_dataError) v |= kpv_ErrorFlags_DataError; + prop = v; + break; + } + default: break; + } + prop.Detach(value); + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) +{ + *numItems = 1; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)) +{ + NCOM::CPropVariant prop; + switch (propID) + { + case kpidPackSize: if (_packSize_Defined) prop = _packSize; break; + case kpidSize: if (_unpackSize_Defined) prop = _unpackSize; break; + default: break; + } + prop.Detach(value); + return S_OK; +} + +Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)) +{ + COM_TRY_BEGIN + Close(); + { + Byte buf[kMaxHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kMinHeaderSize)) + + if (IsArc_Lz4(buf, kMinHeaderSize) == k_IsArc_Res_NO) + return S_FALSE; + + if (!_frameInfo.Parse(buf, kMinHeaderSize)) + return S_FALSE; + + // Read additional header bytes if needed + unsigned headerSize = _frameInfo.GetHeaderSize(); + if (headerSize > kMinHeaderSize) + { + RINOK(ReadStream_FALSE(stream, buf + kMinHeaderSize, headerSize - kMinHeaderSize)) + if (!_frameInfo.Parse(buf, headerSize)) + return S_FALSE; + } + + if (_frameInfo.ContentSizePresent) + { + _unpackSize = _frameInfo.ContentSize; + _unpackSize_Defined = true; + } + + _isArc = true; + _stream = stream; + _seqStream = stream; + _needSeekToStart = true; + } + return S_OK; + COM_TRY_END +} + + +Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream)) +{ + Close(); + _isArc = true; + _seqStream = stream; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::Close()) +{ + _isArc = false; + _needSeekToStart = false; + _dataAfterEnd = false; + _needMoreInput = false; + _dataError = false; + + _packSize_Defined = false; + _unpackSize_Defined = false; + + _packSize = 0; + _unpackSize = 0; + + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + + +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; + + if (_packSize_Defined) + { + RINOK(extractCallback->SetTotal(_packSize)) + } + + Int32 opRes; + { + CMyComPtr realOutStream; + const Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)) + if (!testMode && !realOutStream) + return S_OK; + + RINOK(extractCallback->PrepareOperation(askMode)) + + if (_needSeekToStart) + { + if (!_stream) + return E_FAIL; + RINOK(InStream_SeekToBegin(_stream)) + } + else + _needSeekToStart = true; + + // Read and parse header + Byte header[kMaxHeaderSize]; + RINOK(ReadStream_FALSE(_seqStream, header, kMinHeaderSize)) + + CFrameInfo frameInfo; + if (!frameInfo.Parse(header, kMinHeaderSize)) + { + _isArc = false; + opRes = NExtract::NOperationResult::kIsNotArc; + } + else + { + unsigned headerSize = frameInfo.GetHeaderSize(); + if (headerSize > kMinHeaderSize) + { + RINOK(ReadStream_FALSE(_seqStream, header + kMinHeaderSize, headerSize - kMinHeaderSize)) + } + + CMyComPtr2_Create outStream; + outStream->SetStream(realOutStream); + outStream->Init(); + + CMyComPtr2_Create lps; + lps->Init(extractCallback, true); + + _dataAfterEnd = false; + _needMoreInput = false; + _dataError = false; + + UInt64 inProcessed = headerSize; + UInt64 outProcessed = 0; + bool finished = false; + + // Allocate buffers + Byte *compBuf = NULL; + Byte *decompBuf = NULL; + UInt32 blockMaxSize = frameInfo.BlockMaxSize; + + compBuf = (Byte *)MyAlloc(blockMaxSize); + decompBuf = (Byte *)MyAlloc(blockMaxSize); + + if (!compBuf || !decompBuf) + { + MyFree(compBuf); + MyFree(decompBuf); + return E_OUTOFMEMORY; + } + + opRes = NExtract::NOperationResult::kOK; + + // Process blocks + while (!finished) + { + // Read block size + Byte blockHeader[4]; + HRESULT res = ReadStream_FALSE(_seqStream, blockHeader, 4); + if (res != S_OK) + { + _needMoreInput = true; + opRes = NExtract::NOperationResult::kUnexpectedEnd; + break; + } + inProcessed += 4; + + UInt32 blockSize = GetUi32(blockHeader); + + // End mark + if (blockSize == 0) + { + finished = true; + // Skip content checksum if present + if (frameInfo.ContentChecksum) + { + Byte checksum[4]; + res = ReadStream_FALSE(_seqStream, checksum, 4); + if (res == S_OK) + inProcessed += 4; + } + break; + } + + bool uncompressed = (blockSize & 0x80000000) != 0; + blockSize &= 0x7FFFFFFF; + + if (blockSize > blockMaxSize) + { + _dataError = true; + opRes = NExtract::NOperationResult::kDataError; + break; + } + + // Read block data + res = ReadStream_FALSE(_seqStream, compBuf, blockSize); + if (res != S_OK) + { + _needMoreInput = true; + opRes = NExtract::NOperationResult::kUnexpectedEnd; + break; + } + inProcessed += blockSize; + + // Skip block checksum if present + if (frameInfo.BlockChecksum) + { + Byte checksum[4]; + res = ReadStream_FALSE(_seqStream, checksum, 4); + if (res != S_OK) + { + _needMoreInput = true; + opRes = NExtract::NOperationResult::kUnexpectedEnd; + break; + } + inProcessed += 4; + } + + // Decompress or copy + const Byte *outData; + SizeT outLen; + + if (uncompressed) + { + // Uncompressed block - write directly from compBuf + outData = compBuf; + outLen = blockSize; + } + else + { + // Decompress + outLen = blockMaxSize; + SizeT srcConsumed; + SRes sres = Lz4Dec_DecodeBlock(compBuf, blockSize, decompBuf, &outLen, &srcConsumed); + if (sres != SZ_OK) + { + _dataError = true; + opRes = NExtract::NOperationResult::kDataError; + break; + } + outData = decompBuf; + } + + // Write output + if (outLen > 0) + { + const HRESULT writeRes = WriteStream(outStream, outData, outLen); + if (writeRes != S_OK) + { + MyFree(compBuf); + MyFree(decompBuf); + return writeRes; + } + outProcessed += outLen; + } + + // Progress + lps.Interface()->SetRatioInfo(&inProcessed, &outProcessed); + } + + MyFree(compBuf); + MyFree(decompBuf); + + _packSize = inProcessed; + _unpackSize = outProcessed; + _packSize_Defined = true; + _unpackSize_Defined = true; + } + } + return extractCallback->SetOperationResult(opRes); + + COM_TRY_END +} + + +static const Byte k_Signature[] = { 0x04, 0x22, 0x4D, 0x18 }; + +REGISTER_ARC_I( + "lz4", "lz4 tlz4", "* .tar", 0x11, + k_Signature, + 0, + NArcInfoFlags::kKeepName + , IsArc_Lz4) + +}} diff --git a/CPP/7zip/Archive/LzipHandler.cpp b/CPP/7zip/Archive/LzipHandler.cpp new file mode 100644 index 0000000..4158bce --- /dev/null +++ b/CPP/7zip/Archive/LzipHandler.cpp @@ -0,0 +1,329 @@ +// LzipHandler.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/LzmaDecoder.h" + +#include "Common/DummyOutStream.h" + +#include "../../../C/CpuArch.h" + +using namespace NWindows; + +namespace NArchive { +namespace NLzip { + +// Lzip member structure: +// Header (6 bytes): "LZIP" + version (1 byte) + dictionary_size (1 byte) +// LZMA stream +// Trailer (20 bytes): CRC32 (4 bytes) + data_size (8 bytes) + member_size (8 bytes) + +static const UInt32 kHeaderSize = 6; +static const UInt32 kTrailerSize = 20; + +// Lzip always uses these LZMA parameters: lc=3, lp=0, pb=2 +// Encoded as: lc + lp*9 + pb*9*5 = 3 + 0 + 90 = 93 = 0x5D +static const Byte kLzmaLiteralProps = 0x5D; + +static const Byte kSignature[] = { 'L', 'Z', 'I', 'P' }; +static const Byte kSignatureSize = 4; + +static inline UInt32 GetDictSize(Byte ds) +{ + // Dictionary size formula from lzip specification: + // bits 4-0 (0x1F) contain the exponent (dictionary size = 2^exponent) + // bits 7-5 contain the numerator of the fraction to subtract + // dictionary_size = base_size - fraction * (base_size >> 4) + const unsigned exp = ds & 0x1F; + const UInt32 base = (UInt32)1 << exp; + const unsigned frac = ds >> 5; + return base - (frac * (base >> 4)); +} + +API_FUNC_static_IsArc IsArc_Lzip(const Byte *p, size_t size) +{ + if (size < kHeaderSize) + return k_IsArc_Res_NEED_MORE; + if (memcmp(p, kSignature, kSignatureSize) != 0) + return k_IsArc_Res_NO; + + const Byte version = p[4]; + if (version != 1) + return k_IsArc_Res_NO; // Only version 1 is supported + + const Byte ds = p[5]; + // Valid dictionary size: 4 KiB to 512 MiB + // ds byte value encodes: exponent in bits 4-0, fraction in bits 7-5 + // Minimum valid exponent is 12 (2^12 = 4 KiB) + // Maximum valid exponent is 29 (2^29 = 512 MiB) + const unsigned exp = ds & 0x1F; + if (exp < 12 || exp > 29) + return k_IsArc_Res_NO; + + return k_IsArc_Res_YES; +} +} + +Z7_CLASS_IMP_CHandler_IInArchive_1( + IArchiveOpenSeq +) + CMyComPtr _stream; + CMyComPtr _seqStream; + + bool _isArc; + bool _needSeekToStart; + bool _dataAfterEnd; + bool _needMoreInput; + + bool _packSize_Defined; + bool _unpackSize_Defined; + bool _numMembers_Defined; + + UInt64 _packSize; + UInt64 _unpackSize; + UInt64 _numMembers; + + Byte _version; + UInt32 _dictSize; +}; + +static const Byte kProps[] = +{ + kpidSize, + kpidPackSize +}; + +static const Byte kArcProps[] = +{ + kpidNumStreams +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) +{ + NCOM::CPropVariant prop; + switch (propID) + { + case kpidPhySize: if (_packSize_Defined) prop = _packSize; break; + case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break; + case kpidNumStreams: if (_numMembers_Defined) prop = _numMembers; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc; + if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd; + if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd; + prop = v; + break; + } + default: break; + } + prop.Detach(value); + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) +{ + *numItems = 1; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)) +{ + NCOM::CPropVariant prop; + switch (propID) + { + case kpidPackSize: if (_packSize_Defined) prop = _packSize; break; + case kpidSize: if (_unpackSize_Defined) prop = _unpackSize; break; + default: break; + } + prop.Detach(value); + return S_OK; +} + +Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)) +{ + COM_TRY_BEGIN + Close(); + { + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)) + if (IsArc_Lzip(buf, kHeaderSize) == k_IsArc_Res_NO) + return S_FALSE; + + _version = buf[4]; + _dictSize = GetDictSize(buf[5]); + + _isArc = true; + _stream = stream; + _seqStream = stream; + _needSeekToStart = true; + } + return S_OK; + COM_TRY_END +} + + +Z7_COM7F_IMF(CHandler::OpenSeq(ISequentialInStream *stream)) +{ + Close(); + _isArc = true; + _seqStream = stream; + return S_OK; +} + +Z7_COM7F_IMF(CHandler::Close()) +{ + _isArc = false; + _needSeekToStart = false; + _dataAfterEnd = false; + _needMoreInput = false; + + _packSize_Defined = false; + _unpackSize_Defined = false; + _numMembers_Defined = false; + + _packSize = 0; + _version = 0; + _dictSize = 0; + + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + + +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; + + if (_packSize_Defined) + { + RINOK(extractCallback->SetTotal(_packSize)) + } + + Int32 opRes; + { + CMyComPtr realOutStream; + const Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)) + if (!testMode && !realOutStream) + return S_OK; + + RINOK(extractCallback->PrepareOperation(askMode)) + + if (_needSeekToStart) + { + if (!_stream) + return E_FAIL; + RINOK(InStream_SeekToBegin(_stream)) + } + else + _needSeekToStart = true; + + // Read header + Byte header[kHeaderSize]; + RINOK(ReadStream_FALSE(_seqStream, header, kHeaderSize)) + + if (IsArc_Lzip(header, kHeaderSize) == k_IsArc_Res_NO) + { + _isArc = false; + opRes = NExtract::NOperationResult::kIsNotArc; + } + else + { + _version = header[4]; + _dictSize = GetDictSize(header[5]); + + // Construct LZMA properties (5 bytes): props_byte + dictionary_size (4 bytes LE) + Byte lzmaProps[5]; + lzmaProps[0] = kLzmaLiteralProps; // lc=3, lp=0, pb=2 + SetUi32(lzmaProps + 1, _dictSize); + + CMyComPtr2_Create decoder; + + RINOK(decoder->SetDecoderProperties2(lzmaProps, 5)) + + decoder->FinishStream = true; + + CMyComPtr2_Create outStream; + outStream->SetStream(realOutStream); + outStream->Init(); + + CMyComPtr2_Create lps; + lps->Init(extractCallback, true); + + _dataAfterEnd = false; + _needMoreInput = false; + + HRESULT result = decoder.Interface()->Code(_seqStream, outStream, NULL, NULL, lps); + + if (result != S_FALSE && result != S_OK) + return result; + + const UInt64 inProcessedSize = decoder->GetInputProcessedSize(); + const UInt64 outProcessedSize = decoder->GetOutputProcessedSize(); + + _packSize = kHeaderSize + inProcessedSize + kTrailerSize; + _unpackSize = outProcessedSize; + _numMembers = 1; + + _packSize_Defined = true; + _unpackSize_Defined = true; + _numMembers_Defined = true; + + lps.Interface()->SetRatioInfo(&_packSize, &_unpackSize); + + if (decoder->NeedsMoreInput()) + { + _needMoreInput = true; + opRes = NExtract::NOperationResult::kUnexpectedEnd; + } + else if (result == S_FALSE) + { + opRes = NExtract::NOperationResult::kDataError; + } + else if (!decoder->CheckFinishStatus(true)) // Expect end marker + { + opRes = NExtract::NOperationResult::kDataError; + } + else + { + opRes = NExtract::NOperationResult::kOK; + } + } + } + return extractCallback->SetOperationResult(opRes); + + COM_TRY_END +} + + +static const Byte k_Signature[] = { 'L', 'Z', 'I', 'P', 1 }; + +REGISTER_ARC_I( + "lzip", "lz tlz", "* .tar", 0x10, + k_Signature, + 0, + NArcInfoFlags::kKeepName + , IsArc_Lzip) + +}} diff --git a/CPP/7zip/Bundles/Format7zF/Arc.mak b/CPP/7zip/Bundles/Format7zF/Arc.mak index b1c6fe2..b0f9cc5 100644 --- a/CPP/7zip/Bundles/Format7zF/Arc.mak +++ b/CPP/7zip/Bundles/Format7zF/Arc.mak @@ -81,6 +81,7 @@ AR_OBJS = \ $O\IhexHandler.obj \ $O\LpHandler.obj \ $O\LzhHandler.obj \ + $O\LzipHandler.obj \ $O\LzmaHandler.obj \ $O\MachoHandler.obj \ $O\MbrHandler.obj \ diff --git a/CPP/7zip/Bundles/Format7zF/Arc_gcc.mak b/CPP/7zip/Bundles/Format7zF/Arc_gcc.mak index 746aaff..504c98f 100644 --- a/CPP/7zip/Bundles/Format7zF/Arc_gcc.mak +++ b/CPP/7zip/Bundles/Format7zF/Arc_gcc.mak @@ -118,6 +118,8 @@ AR_OBJS = \ $O/IhexHandler.o \ $O/LpHandler.o \ $O/LzhHandler.o \ + $O/Lz4Handler.o \ + $O/LzipHandler.o \ $O/LzmaHandler.o \ $O/MachoHandler.o \ $O/MbrHandler.o \ @@ -336,6 +338,7 @@ C_OBJS = \ $O/Delta.o \ $O/HuffEnc.o \ $O/LzFind.o \ + $O/Lz4Dec.o \ $O/Lzma2Dec.o \ $O/Lzma2DecMt.o \ $O/Lzma2Enc.o \