mirror of
https://github.com/ip7z/7zip.git
synced 2026-01-20 23:10:17 +01:00
Merge 85e04af223 into 5e96a82794
This commit is contained in:
commit
2fa7fdfc59
140
C/Lz4Dec.c
Normal file
140
C/Lz4Dec.c
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/* Lz4Dec.c -- LZ4 Decoder
|
||||
2025 : Igor Pavlov : Public domain */
|
||||
|
||||
#include "Precomp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
27
C/Lz4Dec.h
Normal file
27
C/Lz4Dec.h
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
546
CPP/7zip/Archive/Lz4Handler.cpp
Normal file
546
CPP/7zip/Archive/Lz4Handler.cpp
Normal file
|
|
@ -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<IInStream> _stream;
|
||||
CMyComPtr<ISequentialInStream> _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<ISequentialOutStream> 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<ISequentialOutStream, CDummyOutStream> outStream;
|
||||
outStream->SetStream(realOutStream);
|
||||
outStream->Init();
|
||||
|
||||
CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> 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)
|
||||
|
||||
}}
|
||||
329
CPP/7zip/Archive/LzipHandler.cpp
Normal file
329
CPP/7zip/Archive/LzipHandler.cpp
Normal file
|
|
@ -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<IInStream> _stream;
|
||||
CMyComPtr<ISequentialInStream> _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<ISequentialOutStream> 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<ICompressCoder, NCompress::NLzma::CDecoder> decoder;
|
||||
|
||||
RINOK(decoder->SetDecoderProperties2(lzmaProps, 5))
|
||||
|
||||
decoder->FinishStream = true;
|
||||
|
||||
CMyComPtr2_Create<ISequentialOutStream, CDummyOutStream> outStream;
|
||||
outStream->SetStream(realOutStream);
|
||||
outStream->Init();
|
||||
|
||||
CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> 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)
|
||||
|
||||
}}
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
Loading…
Reference in a new issue