diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2014-11-23 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:51 +0300 |
commit | f08f4dcc3c02464c17753b3feafcfe5243b9e236 (patch) | |
tree | b0e1b15bc5368d92dff422e8ec0818564a2b00b8 /CPP/7zip/Archive/MslzHandler.cpp | |
parent | 83f8ddcc5b2161e1e3c49666265257fca8aeb12c (diff) |
9.349.34
Diffstat (limited to 'CPP/7zip/Archive/MslzHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/MslzHandler.cpp | 308 |
1 files changed, 222 insertions, 86 deletions
diff --git a/CPP/7zip/Archive/MslzHandler.cpp b/CPP/7zip/Archive/MslzHandler.cpp index 67495e76..cb124c40 100755..100644 --- a/CPP/7zip/Archive/MslzHandler.cpp +++ b/CPP/7zip/Archive/MslzHandler.cpp @@ -4,10 +4,10 @@ #include "../../../C/CpuArch.h" -#include "Common/ComTry.h" -#include "Common/MyString.h" +#include "../../Common/ComTry.h" +#include "../../Common/MyString.h" -#include "Windows/PropVariant.h" +#include "../../Windows/PropVariant.h" #include "../Common/InBuffer.h" #include "../Common/ProgressUtils.h" @@ -19,28 +19,45 @@ namespace NArchive { namespace NMslz { +static const UInt32 kUnpackSizeMax = 0xFFFFFFE0; + class CHandler: public IInArchive, + public IArchiveOpenSeq, public CMyUnknownImp { - CMyComPtr<IInStream> _stream; - UInt32 _size; + CMyComPtr<IInStream> _inStream; + CMyComPtr<ISequentialInStream> _seqStream; + + bool _isArc; + bool _needSeekToStart; + bool _dataAfterEnd; + bool _needMoreInput; + + bool _packSize_Defined; + bool _unpackSize_Defined; + + UInt32 _unpackSize; UInt64 _packSize; + UInt64 _originalFileSize; UString _name; + + void ParseName(Byte replaceByte, IArchiveOpenCallback *callback); public: - MY_UNKNOWN_IMP1(IInArchive) + MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq) INTERFACE_IInArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); }; -STATPROPSTG kProps[] = +static const Byte kProps[] = { - { NULL, kpidPath, VT_BSTR}, - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, + kpidPath, + kpidSize, + kpidPackSize, }; IMP_IInArchive_Props -IMP_IInArchive_ArcProps_NO +IMP_IInArchive_ArcProps_NO_Table STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { @@ -48,15 +65,39 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) return S_OK; } +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch (propID) + { + case kpidExtension: prop = "mslz"; break; + case kpidIsNotArcType: prop = true; break; + case kpidPhySize: if (_packSize_Defined) prop = _packSize; 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; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; - switch(propID) + switch (propID) { case kpidPath: if (!_name.IsEmpty()) prop = _name; break; - case kpidSize: prop = _size; break; - case kpidPackSize: prop = _packSize; break; + case kpidSize: if (_unpackSize_Defined || _inStream) prop = _unpackSize; break; + case kpidPackSize: if (_packSize_Defined || _inStream) prop = _packSize; break; } prop.Detach(value); return S_OK; @@ -67,69 +108,80 @@ static const unsigned kSignatureSize = 9; static const unsigned kHeaderSize = kSignatureSize + 1 + 4; #define MSLZ_SIGNATURE { 0x53, 0x5A, 0x44, 0x44, 0x88, 0xF0, 0x27, 0x33, 0x41 } // old signature: 53 5A 20 88 F0 27 33 -static const Byte signature[kSignatureSize] = MSLZ_SIGNATURE; +static const Byte kSignature[kSignatureSize] = MSLZ_SIGNATURE; -static const wchar_t *g_Exts[] = +// we support only 3 chars strings here +static const char *g_Exts[] = { - L"dll", - L"exe", - L"kmd", - L"sys" + "dll" + , "exe" + , "kmd" + , "sys" }; +void CHandler::ParseName(Byte replaceByte, IArchiveOpenCallback *callback) +{ + if (!callback) + return; + CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback; + callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback); + if (!volumeCallback) + return; + + NWindows::NCOM::CPropVariant prop; + if (volumeCallback->GetProperty(kpidName, &prop) != S_OK || prop.vt != VT_BSTR) + return; + + UString s = prop.bstrVal; + if (s.IsEmpty() || + s.Back() != L'_') + return; + + s.DeleteBack(); + _name = s; + + if (replaceByte == 0) + { + if (s.Len() < 3 || s[s.Len() - 3] != '.') + return; + for (unsigned i = 0; i < ARRAY_SIZE(g_Exts); i++) + { + const char *ext = g_Exts[i]; + if (s[s.Len() - 2] == ext[0] && + s[s.Len() - 1] == ext[1]) + { + replaceByte = ext[2]; + break; + } + } + } + if (replaceByte >= 0x20 && replaceByte < 0x80) + _name += (wchar_t)replaceByte; +} + STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback) { COM_TRY_BEGIN { Close(); + _needSeekToStart = true; Byte buffer[kHeaderSize]; RINOK(ReadStream_FALSE(stream, buffer, kHeaderSize)); - if (memcmp(buffer, signature, kSignatureSize) != 0) + if (memcmp(buffer, kSignature, kSignatureSize) != 0) return S_FALSE; - _size = GetUi32(buffer + 10); - if (_size > 0xFFFFFFE0) + _unpackSize = GetUi32(buffer + 10); + if (_unpackSize > kUnpackSizeMax) return S_FALSE; - RINOK(stream->Seek(0, STREAM_SEEK_END, &_packSize)); + RINOK(stream->Seek(0, STREAM_SEEK_END, &_originalFileSize)); + _packSize = _originalFileSize; - if (callback) - { - CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; - callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); - if (openVolumeCallback) - { - NWindows::NCOM::CPropVariant prop; - if (openVolumeCallback->GetProperty(kpidName, &prop) == S_OK && prop.vt == VT_BSTR) - { - UString baseName = prop.bstrVal; - if (!baseName.IsEmpty() && baseName.Back() == L'_') - { - baseName.DeleteBack(); - Byte replaceByte = buffer[kSignatureSize]; - if (replaceByte == 0) - { - for (int i = 0; i < sizeof(g_Exts) / sizeof(g_Exts[0]); i++) - { - UString s = g_Exts[i]; - int len = s.Length(); - Byte b = (Byte)s.Back(); - s.DeleteBack(); - if (baseName.Length() >= len && - baseName[baseName.Length() - len] == '.' && - s.CompareNoCase(baseName.Right(len - 1)) == 0) - { - replaceByte = b; - break; - } - } - } - if (replaceByte >= 0x20 && replaceByte < 0x80) - _name = baseName + (wchar_t)replaceByte; - } - } - } - } - _stream = stream; + ParseName(buffer[kSignatureSize], callback); + + _isArc = true; + _unpackSize_Defined = true; + _inStream = stream; + _seqStream = stream; } return S_OK; COM_TRY_END @@ -137,7 +189,20 @@ STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPo STDMETHODIMP CHandler::Close() { - _stream.Release(); + _originalFileSize = 0; + _packSize = 0; + _unpackSize = 0; + + _isArc = false; + _needSeekToStart = false; + _dataAfterEnd = false; + _needMoreInput = false; + + _packSize_Defined = false; + _unpackSize_Defined = false; + + _seqStream.Release(); + _inStream.Release(); _name.Empty(); return S_OK; } @@ -147,12 +212,13 @@ STDMETHODIMP CHandler::Close() // maxLen = 16; MS #define PROGRESS_AND_WRITE \ - if ((dest & kMask) == 0) { RINOK(WriteStream(outStream, buf, kBufSize)); \ + if ((dest & kMask) == 0) { if (outStream) RINOK(WriteStream(outStream, buf, kBufSize)); \ if ((dest & ((1 << 20) - 1)) == 0) \ - { UInt64 inSize = inStream.GetProcessedSize(); UInt64 outSize = dest; \ - RINOK(progress->SetRatioInfo(&inSize, &outSize)); }} + if (progress) \ + { UInt64 inSize = inStream.GetProcessedSize(); UInt64 outSize = dest; \ + RINOK(progress->SetRatioInfo(&inSize, &outSize)); }} -static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, ICompressProgressInfo *progress) +static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, bool &needMoreData, ICompressProgressInfo *progress) { const unsigned kBufSize = (1 << 12); const unsigned kMask = kBufSize - 1; @@ -163,11 +229,17 @@ static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UIn { Byte b; if (!inStream.ReadByte(b)) + { + needMoreData = true; return S_FALSE; + } for (unsigned mask = (unsigned)b | 0x100; mask > 1 && dest < unpackSize; mask >>= 1) { if (!inStream.ReadByte(b)) + { + needMoreData = true; return S_FALSE; + } if (mask & 1) { buf[dest++ & kMask] = b; @@ -177,7 +249,10 @@ static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UIn { Byte b1; if (!inStream.ReadByte(b1)) + { + needMoreData = true; return S_FALSE; + } const unsigned kMaxLen = 16; // 18 in Okumura's code. unsigned src = (((((unsigned)b1 & 0xF0) << 4) | b) + kMaxLen) & kMask; unsigned len = (b1 & 0xF) + 3; @@ -192,7 +267,19 @@ static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UIn } } } - return WriteStream(outStream, buf, dest & kMask); + if (outStream) + RINOK(WriteStream(outStream, buf, dest & kMask)); + return S_OK; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + COM_TRY_BEGIN + Close(); + _isArc = true; + _seqStream = stream; + return S_OK; + COM_TRY_END } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, @@ -201,10 +288,10 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, COM_TRY_BEGIN if (numItems == 0) return S_OK; - if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; - extractCallback->SetTotal(_size); + // extractCallback->SetTotal(_unpackSize); CMyComPtr<ISequentialOutStream> realOutStream; Int32 askMode = testMode ? @@ -225,32 +312,81 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CLocalProgress *lps = new CLocalProgress; CMyComPtr<ICompressProgressInfo> progress = lps; lps->Init(extractCallback, false); - - RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); - CInBuffer s; - if (!s.Create(1 << 20)) - return E_OUTOFMEMORY; - s.SetStream(_stream); - s.Init(); - Byte buffer[kHeaderSize]; + + if (_needSeekToStart) + { + if (!_inStream) + return E_FAIL; + RINOK(_inStream->Seek(0, STREAM_SEEK_SET, NULL)); + } + else + _needSeekToStart = true; + Int32 opRes = NExtract::NOperationResult::kDataError; - if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize) + + bool isArc = false; + bool needMoreInput = false; + try { - HRESULT result = MslzDec(s, outStream, _size, progress); - if (result == S_OK) - opRes = NExtract::NOperationResult::kOK; - else if (result != S_FALSE) - return result; + CInBuffer s; + if (!s.Create(1 << 20)) + return E_OUTOFMEMORY; + s.SetStream(_seqStream); + s.Init(); + + Byte buffer[kHeaderSize]; + if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize) + { + UInt32 unpackSize; + if (memcmp(buffer, kSignature, kSignatureSize) == 0) + { + unpackSize = GetUi32(buffer + 10); + if (unpackSize <= kUnpackSizeMax) + { + HRESULT result = MslzDec(s, outStream, unpackSize, needMoreInput, progress); + if (result == S_OK) + opRes = NExtract::NOperationResult::kOK; + else if (result != S_FALSE) + return result; + _unpackSize = unpackSize; + _unpackSize_Defined = true; + + _packSize = s.GetProcessedSize(); + _packSize_Defined = true; + + if (_inStream && _packSize < _originalFileSize) + _dataAfterEnd = true; + + isArc = true; + } + } + } } + catch (CInBufferException &e) { return e.ErrorCode; } + + _isArc = isArc; + if (isArc) + _needMoreInput = needMoreInput; + if (!_isArc) + opRes = NExtract::NOperationResult::kIsNotArc; + else if (_needMoreInput) + opRes = NExtract::NOperationResult::kUnexpectedEnd; + else if (_dataAfterEnd) + opRes = NExtract::NOperationResult::kDataAfterEnd; + outStream.Release(); return extractCallback->SetOperationResult(opRes); COM_TRY_END } -static IInArchive *CreateArc() { return new CHandler; } +IMP_CreateArcIn static CArcInfo g_ArcInfo = - { L"MsLZ", L"", 0, 0xD5, MSLZ_SIGNATURE, kSignatureSize, false, CreateArc, 0 }; + { "MsLZ", "mslz", 0, 0xD5, + kSignatureSize, MSLZ_SIGNATURE, + 0, + 0, + CreateArc }; REGISTER_ARC(Mslz) |