diff options
Diffstat (limited to 'src/libs/7zip/unix/CPP/7zip/Archive')
182 files changed, 51945 insertions, 0 deletions
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.cpp new file mode 100644 index 000000000..6774fc482 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.cpp @@ -0,0 +1,3 @@ +// CompressionMethod.cpp + +#include "StdAfx.h" diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h new file mode 100644 index 000000000..55bbc68ee --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h @@ -0,0 +1,50 @@ +// 7zCompressionMode.h + +#ifndef __7Z_COMPRESSION_MODE_H +#define __7Z_COMPRESSION_MODE_H + +#include "../../../Common/MyString.h" + +#include "../../../Windows/PropVariant.h" + +#include "../../Common/MethodProps.h" + +namespace NArchive { +namespace N7z { + +struct CMethodFull: public CMethod +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; + bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } +}; + +struct CBind +{ + UInt32 InCoder; + UInt32 InStream; + UInt32 OutCoder; + UInt32 OutStream; +}; + +struct CCompressionMethodMode +{ + CObjectVector<CMethodFull> Methods; + CRecordVector<CBind> Binds; + #ifndef _7ZIP_ST + UInt32 NumThreads; + #endif + bool PasswordIsDefined; + UString Password; + + bool IsEmpty() const { return (Methods.IsEmpty() && !PasswordIsDefined); } + CCompressionMethodMode(): PasswordIsDefined(false) + #ifndef _7ZIP_ST + , NumThreads(1) + #endif + {} +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp new file mode 100644 index 000000000..425a34157 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp @@ -0,0 +1,332 @@ +// 7zDecode.cpp + +#include "StdAfx.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/LockedStream.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" + +#include "7zDecode.h" + +namespace NArchive { +namespace N7z { + +static void ConvertFolderItemInfoToBindInfo(const CFolder &folder, + CBindInfoEx &bindInfo) +{ + bindInfo.Clear(); + int i; + for (i = 0; i < folder.BindPairs.Size(); i++) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex; + bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex; + bindInfo.BindPairs.Add(bindPair); + } + UInt32 outStreamIndex = 0; + for (i = 0; i < folder.Coders.Size(); i++) + { + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + const CCoderInfo &coderInfo = folder.Coders[i]; + coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams; + coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams; + bindInfo.Coders.Add(coderStreamsInfo); + bindInfo.CoderMethodIDs.Add(coderInfo.MethodID); + for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++) + if (folder.FindBindPairForOutStream(outStreamIndex) < 0) + bindInfo.OutStreams.Add(outStreamIndex); + } + for (i = 0; i < folder.PackStreams.Size(); i++) + bindInfo.InStreams.Add((UInt32)folder.PackStreams[i]); +} + +static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1, + const NCoderMixer::CCoderStreamsInfo &a2) +{ + return (a1.NumInStreams == a2.NumInStreams) && + (a1.NumOutStreams == a2.NumOutStreams); +} + +static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2) +{ + return (a1.InIndex == a2.InIndex) && + (a1.OutIndex == a2.OutIndex); +} + +static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2) +{ + if (a1.Coders.Size() != a2.Coders.Size()) + return false; + int i; + for (i = 0; i < a1.Coders.Size(); i++) + if (!AreCodersEqual(a1.Coders[i], a2.Coders[i])) + return false; + if (a1.BindPairs.Size() != a2.BindPairs.Size()) + return false; + for (i = 0; i < a1.BindPairs.Size(); i++) + if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i])) + return false; + for (i = 0; i < a1.CoderMethodIDs.Size(); i++) + if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i]) + return false; + if (a1.InStreams.Size() != a2.InStreams.Size()) + return false; + if (a1.OutStreams.Size() != a2.OutStreams.Size()) + return false; + return true; +} + +CDecoder::CDecoder(bool multiThread) +{ + #ifndef _ST_MODE + multiThread = true; + #endif + _multiThread = multiThread; + _bindInfoExPrevIsDefined = false; +} + +HRESULT CDecoder::Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + UInt64 startPos, + const UInt64 *packSizes, + const CFolder &folderInfo, + ISequentialOutStream *outStream, + ICompressProgressInfo *compressProgress + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + #if !defined(_7ZIP_ST) && !defined(_SFX) + , bool mtMode, UInt32 numThreads + #endif + ) +{ + if (!folderInfo.CheckStructure()) + return E_NOTIMPL; + #ifndef _NO_CRYPTO + passwordIsDefined = false; + #endif + CObjectVector< CMyComPtr<ISequentialInStream> > inStreams; + + CLockedInStream lockedInStream; + lockedInStream.Init(inStream); + + for (int j = 0; j < folderInfo.PackStreams.Size(); j++) + { + CLockedSequentialInStreamImp *lockedStreamImpSpec = new + CLockedSequentialInStreamImp; + CMyComPtr<ISequentialInStream> lockedStreamImp = lockedStreamImpSpec; + lockedStreamImpSpec->Init(&lockedInStream, startPos); + startPos += packSizes[j]; + + CLimitedSequentialInStream *streamSpec = new + CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream = streamSpec; + streamSpec->SetStream(lockedStreamImp); + streamSpec->Init(packSizes[j]); + inStreams.Add(inStream); + } + + int numCoders = folderInfo.Coders.Size(); + + CBindInfoEx bindInfo; + ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo); + bool createNewCoders; + if (!_bindInfoExPrevIsDefined) + createNewCoders = true; + else + createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev); + if (createNewCoders) + { + int i; + _decoders.Clear(); + // _decoders2.Clear(); + + _mixerCoder.Release(); + + if (_multiThread) + { + _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT; + _mixerCoder = _mixerCoderMTSpec; + _mixerCoderCommon = _mixerCoderMTSpec; + } + else + { + #ifdef _ST_MODE + _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST; + _mixerCoder = _mixerCoderSTSpec; + _mixerCoderCommon = _mixerCoderSTSpec; + #endif + } + RINOK(_mixerCoderCommon->SetBindInfo(bindInfo)); + + for (i = 0; i < numCoders; i++) + { + const CCoderInfo &coderInfo = folderInfo.Coders[i]; + + + CMyComPtr<ICompressCoder> decoder; + CMyComPtr<ICompressCoder2> decoder2; + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + coderInfo.MethodID, decoder, decoder2, false)); + CMyComPtr<IUnknown> decoderUnknown; + if (coderInfo.IsSimpleCoder()) + { + if (decoder == 0) + return E_NOTIMPL; + + decoderUnknown = (IUnknown *)decoder; + + if (_multiThread) + _mixerCoderMTSpec->AddCoder(decoder); + #ifdef _ST_MODE + else + _mixerCoderSTSpec->AddCoder(decoder, false); + #endif + } + else + { + if (decoder2 == 0) + return E_NOTIMPL; + decoderUnknown = (IUnknown *)decoder2; + if (_multiThread) + _mixerCoderMTSpec->AddCoder2(decoder2); + #ifdef _ST_MODE + else + _mixerCoderSTSpec->AddCoder2(decoder2, false); + #endif + } + _decoders.Add(decoderUnknown); + #ifdef EXTERNAL_CODECS + CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; + decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo)); + } + #endif + } + _bindInfoExPrev = bindInfo; + _bindInfoExPrevIsDefined = true; + } + int i; + _mixerCoderCommon->ReInit(); + + UInt32 packStreamIndex = 0, unpackStreamIndex = 0; + UInt32 coderIndex = 0; + // UInt32 coder2Index = 0; + + for (i = 0; i < numCoders; i++) + { + const CCoderInfo &coderInfo = folderInfo.Coders[i]; + CMyComPtr<IUnknown> &decoder = _decoders[coderIndex]; + + { + CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties; + decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); + if (setDecoderProperties) + { + const CByteBuffer &props = coderInfo.Props; + size_t size = props.GetCapacity(); + if (size > 0xFFFFFFFF) + return E_NOTIMPL; + // if (size > 0) + { + RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size)); + } + } + } + + #if !defined(_7ZIP_ST) && !defined(_SFX) + if (mtMode) + { + CMyComPtr<ICompressSetCoderMt> setCoderMt; + decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(numThreads)); + } + } + #endif + + #ifndef _NO_CRYPTO + { + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); + if (cryptoSetPassword) + { + if (getTextPassword == 0) + return E_FAIL; + CMyComBSTR passwordBSTR; + RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR)); + CByteBuffer buffer; + passwordIsDefined = true; + const UString password(passwordBSTR); + const UInt32 sizeInBytes = password.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < password.Length(); i++) + { + wchar_t c = password[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); + } + } + #endif + + coderIndex++; + + UInt32 numInStreams = (UInt32)coderInfo.NumInStreams; + UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams; + CRecordVector<const UInt64 *> packSizesPointers; + CRecordVector<const UInt64 *> unpackSizesPointers; + packSizesPointers.Reserve(numInStreams); + unpackSizesPointers.Reserve(numOutStreams); + UInt32 j; + for (j = 0; j < numOutStreams; j++, unpackStreamIndex++) + unpackSizesPointers.Add(&folderInfo.UnpackSizes[unpackStreamIndex]); + + for (j = 0; j < numInStreams; j++, packStreamIndex++) + { + int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex); + if (bindPairIndex >= 0) + packSizesPointers.Add( + &folderInfo.UnpackSizes[(UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]); + else + { + int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex); + if (index < 0) + return E_FAIL; + packSizesPointers.Add(&packSizes[index]); + } + } + + _mixerCoderCommon->SetCoderInfo(i, + &packSizesPointers.Front(), + &unpackSizesPointers.Front()); + } + UInt32 mainCoder, temp; + bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp); + + if (_multiThread) + _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder); + /* + else + _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);; + */ + + if (numCoders == 0) + return 0; + CRecordVector<ISequentialInStream *> inStreamPointers; + inStreamPointers.Reserve(inStreams.Size()); + for (i = 0; i < inStreams.Size(); i++) + inStreamPointers.Add(inStreams[i]); + ISequentialOutStream *outStreamPointer = outStream; + return _mixerCoder->Code(&inStreamPointers.Front(), NULL, + inStreams.Size(), &outStreamPointer, NULL, 1, compressProgress); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h new file mode 100644 index 000000000..d8a424a36 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h @@ -0,0 +1,68 @@ +// 7zDecode.h + +#ifndef __7Z_DECODE_H +#define __7Z_DECODE_H + +#include "../../IStream.h" +#include "../../IPassword.h" + +#include "../Common/CoderMixer2.h" +#include "../Common/CoderMixer2MT.h" +#ifdef _ST_MODE +#include "../Common/CoderMixer2ST.h" +#endif + +#include "../../Common/CreateCoder.h" + +#include "7zItem.h" + +namespace NArchive { +namespace N7z { + +struct CBindInfoEx: public NCoderMixer::CBindInfo +{ + CRecordVector<CMethodId> CoderMethodIDs; + void Clear() + { + CBindInfo::Clear(); + CoderMethodIDs.Clear(); + } +}; + +class CDecoder +{ + bool _bindInfoExPrevIsDefined; + CBindInfoEx _bindInfoExPrev; + + bool _multiThread; + #ifdef _ST_MODE + NCoderMixer::CCoderMixer2ST *_mixerCoderSTSpec; + #endif + NCoderMixer::CCoderMixer2MT *_mixerCoderMTSpec; + NCoderMixer::CCoderMixer2 *_mixerCoderCommon; + + CMyComPtr<ICompressCoder2> _mixerCoder; + CObjectVector<CMyComPtr<IUnknown> > _decoders; + // CObjectVector<CMyComPtr<ICompressCoder2> > _decoders2; +public: + CDecoder(bool multiThread); + HRESULT Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + UInt64 startPos, + const UInt64 *packSizes, + const CFolder &folder, + ISequentialOutStream *outStream, + ICompressProgressInfo *compressProgress + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPasswordSpec, bool &passwordIsDefined + #endif + #if !defined(_7ZIP_ST) && !defined(_SFX) + , bool mtMode, UInt32 numThreads + #endif + ); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp new file mode 100644 index 000000000..87996bc0e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp @@ -0,0 +1,444 @@ +// 7zEncode.cpp + +#include "StdAfx.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/InOutTempBuffer.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" + +#include "7zEncode.h" +#include "7zSpecStream.h" + +static const UInt64 k_Delta = 0x03; +static const UInt64 k_BCJ = 0x03030103; +static const UInt64 k_BCJ2 = 0x0303011B; + +namespace NArchive { +namespace N7z { + +static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo, + const CRecordVector<CMethodId> decompressionMethods, + CFolder &folder) +{ + folder.Coders.Clear(); + // bindInfo.CoderMethodIDs.Clear(); + // folder.OutStreams.Clear(); + folder.PackStreams.Clear(); + folder.BindPairs.Clear(); + int i; + for (i = 0; i < bindInfo.BindPairs.Size(); i++) + { + CBindPair bindPair; + bindPair.InIndex = bindInfo.BindPairs[i].InIndex; + bindPair.OutIndex = bindInfo.BindPairs[i].OutIndex; + folder.BindPairs.Add(bindPair); + } + for (i = 0; i < bindInfo.Coders.Size(); i++) + { + CCoderInfo coderInfo; + const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i]; + coderInfo.NumInStreams = coderStreamsInfo.NumInStreams; + coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams; + coderInfo.MethodID = decompressionMethods[i]; + folder.Coders.Add(coderInfo); + } + for (i = 0; i < bindInfo.InStreams.Size(); i++) + folder.PackStreams.Add(bindInfo.InStreams[i]); +} + +HRESULT CEncoder::CreateMixerCoder( + DECL_EXTERNAL_CODECS_LOC_VARS + const UInt64 *inSizeForReduce) +{ + _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT; + _mixerCoder = _mixerCoderSpec; + RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo)); + for (int i = 0; i < _options.Methods.Size(); i++) + { + const CMethodFull &methodFull = _options.Methods[i]; + _codersInfo.Add(CCoderInfo()); + CCoderInfo &encodingInfo = _codersInfo.Back(); + encodingInfo.MethodID = methodFull.Id; + CMyComPtr<ICompressCoder> encoder; + CMyComPtr<ICompressCoder2> encoder2; + + + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodFull.Id, encoder, encoder2, true)); + + if (!encoder && !encoder2) + return E_FAIL; + + CMyComPtr<IUnknown> encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2; + + #ifndef _7ZIP_ST + { + CMyComPtr<ICompressSetCoderMt> setCoderMt; + encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads)); + } + } + #endif + + + RINOK(SetMethodProperties(methodFull, inSizeForReduce, encoderCommon)); + + /* + CMyComPtr<ICryptoResetSalt> resetSalt; + encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt); + if (resetSalt != NULL) + { + resetSalt->ResetSalt(); + } + */ + + #ifdef EXTERNAL_CODECS + CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; + encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo)); + } + #endif + + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword); + + if (cryptoSetPassword) + { + CByteBuffer buffer; + const UInt32 sizeInBytes = _options.Password.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < _options.Password.Length(); i++) + { + wchar_t c = _options.Password[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); + } + + if (encoder) + _mixerCoderSpec->AddCoder(encoder); + else + _mixerCoderSpec->AddCoder2(encoder2); + } + return S_OK; +} + +HRESULT CEncoder::Encode( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, + const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, + CFolder &folderItem, + ISequentialOutStream *outStream, + CRecordVector<UInt64> &packSizes, + ICompressProgressInfo *compressProgress) +{ + RINOK(EncoderConstr()); + + if (_mixerCoderSpec == NULL) + { + RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce)); + } + _mixerCoderSpec->ReInit(); + // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress); + + CObjectVector<CInOutTempBuffer> inOutTempBuffers; + CObjectVector<CSequentialOutTempBufferImp *> tempBufferSpecs; + CObjectVector<CMyComPtr<ISequentialOutStream> > tempBuffers; + int numMethods = _bindInfo.Coders.Size(); + int i; + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + inOutTempBuffers.Add(CInOutTempBuffer()); + inOutTempBuffers.Back().Create(); + inOutTempBuffers.Back().InitWriting(); + } + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + CSequentialOutTempBufferImp *tempBufferSpec = new CSequentialOutTempBufferImp; + CMyComPtr<ISequentialOutStream> tempBuffer = tempBufferSpec; + tempBufferSpec->Init(&inOutTempBuffers[i - 1]); + tempBuffers.Add(tempBuffer); + tempBufferSpecs.Add(tempBufferSpec); + } + + for (i = 0; i < numMethods; i++) + _mixerCoderSpec->SetCoderInfo(i, NULL, NULL); + + if (_bindInfo.InStreams.IsEmpty()) + return E_FAIL; + UInt32 mainCoderIndex, mainStreamIndex; + _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex); + + if (inStreamSize != NULL) + { + CRecordVector<const UInt64 *> sizePointers; + for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++) + if (i == mainStreamIndex) + sizePointers.Add(inStreamSize); + else + sizePointers.Add(NULL); + _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL); + } + + + // UInt64 outStreamStartPos; + // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos)); + + CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = new CSequentialInStreamSizeCount2; + CMyComPtr<ISequentialInStream> inStreamSizeCount = inStreamSizeCountSpec; + CSequentialOutStreamSizeCount *outStreamSizeCountSpec = new CSequentialOutStreamSizeCount; + CMyComPtr<ISequentialOutStream> outStreamSizeCount = outStreamSizeCountSpec; + + inStreamSizeCountSpec->Init(inStream); + outStreamSizeCountSpec->SetStream(outStream); + outStreamSizeCountSpec->Init(); + + CRecordVector<ISequentialInStream *> inStreamPointers; + CRecordVector<ISequentialOutStream *> outStreamPointers; + inStreamPointers.Add(inStreamSizeCount); + outStreamPointers.Add(outStreamSizeCount); + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + outStreamPointers.Add(tempBuffers[i - 1]); + + for (i = 0; i < _codersInfo.Size(); i++) + { + CCoderInfo &encodingInfo = _codersInfo[i]; + + CMyComPtr<ICryptoResetInitVector> resetInitVector; + _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector); + if (resetInitVector != NULL) + { + resetInitVector->ResetInitVector(); + } + + CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties; + _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); + if (writeCoderProperties != NULL) + { + CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->Init(); + writeCoderProperties->WriteCoderProperties(outStream); + outStreamSpec->CopyToBuffer(encodingInfo.Props); + } + } + + UInt32 progressIndex = mainCoderIndex; + + for (i = 0; i + 1 < _codersInfo.Size(); i++) + { + UInt64 m = _codersInfo[i].MethodID; + if (m == k_Delta || m == k_BCJ || m == k_BCJ2) + progressIndex = i + 1; + } + + _mixerCoderSpec->SetProgressCoderIndex(progressIndex); + + RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1, + &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress)); + + ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, folderItem); + + packSizes.Add(outStreamSizeCountSpec->GetSize()); + + for (i = 1; i < _bindInfo.OutStreams.Size(); i++) + { + CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1]; + RINOK(inOutTempBuffer.WriteToStream(outStream)); + packSizes.Add(inOutTempBuffer.GetDataSize()); + } + + for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++) + { + int binder = _bindInfo.FindBinderForInStream( + _bindReverseConverter->DestOutToSrcInMap[i]); + UInt64 streamSize; + if (binder < 0) + streamSize = inStreamSizeCountSpec->GetSize(); + else + streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder); + folderItem.UnpackSizes.Add(streamSize); + } + for (i = numMethods - 1; i >= 0; i--) + folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props; + return S_OK; +} + + +CEncoder::CEncoder(const CCompressionMethodMode &options): + _bindReverseConverter(0), + _constructed(false) +{ + if (options.IsEmpty()) + throw 1; + + _options = options; + _mixerCoderSpec = NULL; +} + +HRESULT CEncoder::EncoderConstr() +{ + if (_constructed) + return S_OK; + if (_options.Methods.IsEmpty()) + { + // it has only password method; + if (!_options.PasswordIsDefined) + throw 1; + if (!_options.Binds.IsEmpty()) + throw 1; + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + CMethodFull method; + + method.NumInStreams = 1; + method.NumOutStreams = 1; + coderStreamsInfo.NumInStreams = 1; + coderStreamsInfo.NumOutStreams = 1; + method.Id = k_AES; + + _options.Methods.Add(method); + _bindInfo.Coders.Add(coderStreamsInfo); + + _bindInfo.InStreams.Add(0); + _bindInfo.OutStreams.Add(0); + } + else + { + + UInt32 numInStreams = 0, numOutStreams = 0; + int i; + for (i = 0; i < _options.Methods.Size(); i++) + { + const CMethodFull &methodFull = _options.Methods[i]; + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + coderStreamsInfo.NumInStreams = methodFull.NumOutStreams; + coderStreamsInfo.NumOutStreams = methodFull.NumInStreams; + if (_options.Binds.IsEmpty()) + { + if (i < _options.Methods.Size() - 1) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams; + bindPair.OutIndex = numOutStreams; + _bindInfo.BindPairs.Add(bindPair); + } + else + _bindInfo.OutStreams.Insert(0, numOutStreams); + for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++) + _bindInfo.OutStreams.Add(numOutStreams + j); + } + + numInStreams += coderStreamsInfo.NumInStreams; + numOutStreams += coderStreamsInfo.NumOutStreams; + + _bindInfo.Coders.Add(coderStreamsInfo); + } + + if (!_options.Binds.IsEmpty()) + { + for (i = 0; i < _options.Binds.Size(); i++) + { + NCoderMixer::CBindPair bindPair; + const CBind &bind = _options.Binds[i]; + bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream; + bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream; + _bindInfo.BindPairs.Add(bindPair); + } + for (i = 0; i < (int)numOutStreams; i++) + if (_bindInfo.FindBinderForOutStream(i) == -1) + _bindInfo.OutStreams.Add(i); + } + + for (i = 0; i < (int)numInStreams; i++) + if (_bindInfo.FindBinderForInStream(i) == -1) + _bindInfo.InStreams.Add(i); + + if (_bindInfo.InStreams.IsEmpty()) + throw 1; // this is error + + // Make main stream first in list + int inIndex = _bindInfo.InStreams[0]; + for (;;) + { + UInt32 coderIndex, coderStreamIndex; + _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex); + UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex); + int binder = _bindInfo.FindBinderForOutStream(outIndex); + if (binder >= 0) + { + inIndex = _bindInfo.BindPairs[binder].InIndex; + continue; + } + for (i = 0; i < _bindInfo.OutStreams.Size(); i++) + if (_bindInfo.OutStreams[i] == outIndex) + { + _bindInfo.OutStreams.Delete(i); + _bindInfo.OutStreams.Insert(0, outIndex); + break; + } + break; + } + + if (_options.PasswordIsDefined) + { + int numCryptoStreams = _bindInfo.OutStreams.Size(); + + for (i = 0; i < numCryptoStreams; i++) + { + NCoderMixer::CBindPair bindPair; + bindPair.InIndex = numInStreams + i; + bindPair.OutIndex = _bindInfo.OutStreams[i]; + _bindInfo.BindPairs.Add(bindPair); + } + _bindInfo.OutStreams.Clear(); + + /* + if (numCryptoStreams == 0) + numCryptoStreams = 1; + */ + + for (i = 0; i < numCryptoStreams; i++) + { + NCoderMixer::CCoderStreamsInfo coderStreamsInfo; + CMethodFull method; + method.NumInStreams = 1; + method.NumOutStreams = 1; + coderStreamsInfo.NumInStreams = method.NumOutStreams; + coderStreamsInfo.NumOutStreams = method.NumInStreams; + method.Id = k_AES; + + _options.Methods.Add(method); + _bindInfo.Coders.Add(coderStreamsInfo); + _bindInfo.OutStreams.Add(numOutStreams + i); + } + } + + } + + for (int i = _options.Methods.Size() - 1; i >= 0; i--) + { + const CMethodFull &methodFull = _options.Methods[i]; + _decompressionMethods.Add(methodFull.Id); + } + + _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo); + _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo); + _constructed = true; + return S_OK; +} + +CEncoder::~CEncoder() +{ + delete _bindReverseConverter; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h new file mode 100644 index 000000000..4909a6e89 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h @@ -0,0 +1,55 @@ +// 7zEncode.h + +#ifndef __7Z_ENCODE_H +#define __7Z_ENCODE_H + +// #include "../../Common/StreamObjects.h" + +#include "7zCompressionMode.h" + +#include "../Common/CoderMixer2.h" +#include "../Common/CoderMixer2MT.h" +#ifdef _ST_MODE +#include "../Common/CoderMixer2ST.h" +#endif +#include "7zItem.h" + +#include "../../Common/CreateCoder.h" + +namespace NArchive { +namespace N7z { + +class CEncoder +{ + NCoderMixer::CCoderMixer2MT *_mixerCoderSpec; + CMyComPtr<ICompressCoder2> _mixerCoder; + + CObjectVector<CCoderInfo> _codersInfo; + + CCompressionMethodMode _options; + NCoderMixer::CBindInfo _bindInfo; + NCoderMixer::CBindInfo _decompressBindInfo; + NCoderMixer::CBindReverseConverter *_bindReverseConverter; + CRecordVector<CMethodId> _decompressionMethods; + + HRESULT CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS + const UInt64 *inSizeForReduce); + + bool _constructed; +public: + CEncoder(const CCompressionMethodMode &options); + ~CEncoder(); + HRESULT EncoderConstr(); + HRESULT Encode( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, + const UInt64 *inStreamSize, const UInt64 *inSizeForReduce, + CFolder &folderItem, + ISequentialOutStream *outStream, + CRecordVector<UInt64> &packSizes, + ICompressProgressInfo *compressProgress); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp new file mode 100644 index 000000000..d55f38e13 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp @@ -0,0 +1,270 @@ +// 7zExtract.cpp + +#include "StdAfx.h" + +#include "../../../Common/ComTry.h" + +#include "../../Common/ProgressUtils.h" + +#include "7zDecode.h" +// #include "7z1Decode.h" +#include "7zFolderOutStream.h" +#include "7zHandler.h" + +namespace NArchive { +namespace N7z { + +struct CExtractFolderInfo +{ + #ifdef _7Z_VOL + int VolumeIndex; + #endif + CNum FileIndex; + CNum FolderIndex; + CBoolVector ExtractStatuses; + UInt64 UnpackSize; + CExtractFolderInfo( + #ifdef _7Z_VOL + int volumeIndex, + #endif + CNum fileIndex, CNum folderIndex): + #ifdef _7Z_VOL + VolumeIndex(volumeIndex), + #endif + FileIndex(fileIndex), + FolderIndex(folderIndex), + UnpackSize(0) + { + if (fileIndex != kNumNoIndex) + { + ExtractStatuses.Reserve(1); + ExtractStatuses.Add(true); + } + }; +}; + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) +{ + COM_TRY_BEGIN + bool testMode = (testModeSpec != 0); + CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; + UInt64 importantTotalUnpacked = 0; + + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = + #ifdef _7Z_VOL + _refs.Size(); + #else + _db.Files.Size(); + #endif + + if(numItems == 0) + return S_OK; + + /* + if(_volumes.Size() != 1) + return E_FAIL; + const CVolume &volume = _volumes.Front(); + const CArchiveDatabaseEx &_db = volume.Database; + IInStream *_inStream = volume.Stream; + */ + + CObjectVector<CExtractFolderInfo> extractFolderInfoVector; + for (UInt32 ii = 0; ii < numItems; ii++) + { + // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex]; + UInt32 ref2Index = allFilesMode ? ii : indices[ii]; + // const CRef2 &ref2 = _refs[ref2Index]; + + // for (UInt32 ri = 0; ri < ref2.Refs.Size(); ri++) + { + #ifdef _7Z_VOL + // const CRef &ref = ref2.Refs[ri]; + const CRef &ref = _refs[ref2Index]; + + int volumeIndex = ref.VolumeIndex; + const CVolume &volume = _volumes[volumeIndex]; + const CArchiveDatabaseEx &db = volume.Database; + UInt32 fileIndex = ref.ItemIndex; + #else + const CArchiveDatabaseEx &db = _db; + UInt32 fileIndex = ref2Index; + #endif + + CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex == kNumNoIndex) + { + extractFolderInfoVector.Add(CExtractFolderInfo( + #ifdef _7Z_VOL + volumeIndex, + #endif + fileIndex, kNumNoIndex)); + continue; + } + if (extractFolderInfoVector.IsEmpty() || + folderIndex != extractFolderInfoVector.Back().FolderIndex + #ifdef _7Z_VOL + || volumeIndex != extractFolderInfoVector.Back().VolumeIndex + #endif + ) + { + extractFolderInfoVector.Add(CExtractFolderInfo( + #ifdef _7Z_VOL + volumeIndex, + #endif + kNumNoIndex, folderIndex)); + const CFolder &folderInfo = db.Folders[folderIndex]; + UInt64 unpackSize = folderInfo.GetUnpackSize(); + importantTotalUnpacked += unpackSize; + extractFolderInfoVector.Back().UnpackSize = unpackSize; + } + + CExtractFolderInfo &efi = extractFolderInfoVector.Back(); + + // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex]; + CNum startIndex = db.FolderStartFileIndex[folderIndex]; + for (CNum index = efi.ExtractStatuses.Size(); + index <= fileIndex - startIndex; index++) + { + // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize; + // Count partial_folder_size + // efi.UnpackSize += unpackSize; + // importantTotalUnpacked += unpackSize; + efi.ExtractStatuses.Add(index == fileIndex - startIndex); + } + } + } + + RINOK(extractCallback->SetTotal(importantTotalUnpacked)); + + CDecoder decoder( + #ifdef _ST_MODE + false + #else + true + #endif + ); + // CDecoder1 decoder; + + UInt64 totalPacked = 0; + UInt64 totalUnpacked = 0; + UInt64 curPacked, curUnpacked; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (int i = 0;; i++, totalUnpacked += curUnpacked, totalPacked += curPacked) + { + lps->OutSize = totalUnpacked; + lps->InSize = totalPacked; + RINOK(lps->SetCur()); + + if (i >= extractFolderInfoVector.Size()) + break; + + const CExtractFolderInfo &efi = extractFolderInfoVector[i]; + curUnpacked = efi.UnpackSize; + curPacked = 0; + + CFolderOutStream *folderOutStream = new CFolderOutStream; + CMyComPtr<ISequentialOutStream> outStream(folderOutStream); + + #ifdef _7Z_VOL + const CVolume &volume = _volumes[efi.VolumeIndex]; + const CArchiveDatabaseEx &db = volume.Database; + #else + const CArchiveDatabaseEx &db = _db; + #endif + + CNum startIndex; + if (efi.FileIndex != kNumNoIndex) + startIndex = efi.FileIndex; + else + startIndex = db.FolderStartFileIndex[efi.FolderIndex]; + + HRESULT result = folderOutStream->Init(&db, + #ifdef _7Z_VOL + volume.StartRef2Index, + #else + 0, + #endif + startIndex, + &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0); + + RINOK(result); + + if (efi.FileIndex != kNumNoIndex) + continue; + + CNum folderIndex = efi.FolderIndex; + const CFolder &folderInfo = db.Folders[folderIndex]; + + curPacked = _db.GetFolderFullPackSize(folderIndex); + + CNum packStreamIndex = db.FolderStartPackStreamIndex[folderIndex]; + UInt64 folderStartPackPos = db.GetFolderStreamPos(folderIndex, 0); + + #ifndef _NO_CRYPTO + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + if (extractCallback) + extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); + #endif + + try + { + #ifndef _NO_CRYPTO + bool passwordIsDefined; + #endif + + HRESULT result = decoder.Decode( + EXTERNAL_CODECS_VARS + #ifdef _7Z_VOL + volume.Stream, + #else + _inStream, + #endif + folderStartPackPos, + &db.PackSizes[packStreamIndex], + folderInfo, + outStream, + progress + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + #if !defined(_7ZIP_ST) && !defined(_SFX) + , true, _numThreads + #endif + ); + + if (result == S_FALSE) + { + RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); + continue; + } + if (result == E_NOTIMPL) + { + RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + if (result != S_OK) + return result; + if (folderOutStream->WasWritingFinished() != S_OK) + { + RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); + continue; + } + } + catch(...) + { + RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); + continue; + } + } + return S_OK; + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp new file mode 100644 index 000000000..edd276bc1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp @@ -0,0 +1,123 @@ +// 7zFolderInStream.cpp + +#include "StdAfx.h" + +#include "7zFolderInStream.h" + +namespace NArchive { +namespace N7z { + +CFolderInStream::CFolderInStream() +{ + _inStreamWithHashSpec = new CSequentialInStreamWithCRC; + _inStreamWithHash = _inStreamWithHashSpec; +} + +void CFolderInStream::Init(IArchiveUpdateCallback *updateCallback, + const UInt32 *fileIndices, UInt32 numFiles) +{ + _updateCallback = updateCallback; + _numFiles = numFiles; + _fileIndex = 0; + _fileIndices = fileIndices; + Processed.Clear(); + CRCs.Clear(); + Sizes.Clear(); + _fileIsOpen = false; + _currentSizeIsDefined = false; +} + +HRESULT CFolderInStream::OpenStream() +{ + _filePos = 0; + while (_fileIndex < _numFiles) + { + CMyComPtr<ISequentialInStream> stream; + HRESULT result = _updateCallback->GetStream(_fileIndices[_fileIndex], &stream); + if (result != S_OK && result != S_FALSE) + return result; + _fileIndex++; + _inStreamWithHashSpec->SetStream(stream); + _inStreamWithHashSpec->Init(); + if (stream) + { + _fileIsOpen = true; + CMyComPtr<IStreamGetSize> streamGetSize; + stream.QueryInterface(IID_IStreamGetSize, &streamGetSize); + if (streamGetSize) + { + RINOK(streamGetSize->GetSize(&_currentSize)); + _currentSizeIsDefined = true; + } + return S_OK; + } + RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + Sizes.Add(0); + Processed.Add(result == S_OK); + AddDigest(); + } + return S_OK; +} + +void CFolderInStream::AddDigest() +{ + CRCs.Add(_inStreamWithHashSpec->GetCRC()); +} + +HRESULT CFolderInStream::CloseStream() +{ + RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + _inStreamWithHashSpec->ReleaseStream(); + _fileIsOpen = false; + _currentSizeIsDefined = false; + Processed.Add(true); + Sizes.Add(_filePos); + AddDigest(); + return S_OK; +} + +STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != 0) + *processedSize = 0; + while (size > 0) + { + if (_fileIsOpen) + { + UInt32 processed2; + RINOK(_inStreamWithHash->Read(data, size, &processed2)); + if (processed2 == 0) + { + RINOK(CloseStream()); + continue; + } + if (processedSize != 0) + *processedSize = processed2; + _filePos += processed2; + break; + } + if (_fileIndex >= _numFiles) + break; + RINOK(OpenStream()); + } + return S_OK; +} + +STDMETHODIMP CFolderInStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) +{ + *value = 0; + int index2 = (int)subStream; + if (index2 < 0 || subStream > Sizes.Size()) + return E_FAIL; + if (index2 < Sizes.Size()) + { + *value = Sizes[index2]; + return S_OK; + } + if (!_currentSizeIsDefined) + return S_FALSE; + *value = _currentSize; + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h new file mode 100644 index 000000000..6df3672a1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h @@ -0,0 +1,58 @@ +// 7zFolderInStream.h + +#ifndef __7Z_FOLDER_IN_STREAM_H +#define __7Z_FOLDER_IN_STREAM_H + +#include "../../ICoder.h" +#include "../IArchive.h" +#include "../Common/InStreamWithCRC.h" + +#include "7zItem.h" + +namespace NArchive { +namespace N7z { + +class CFolderInStream: + public ISequentialInStream, + public ICompressGetSubStreamSize, + public CMyUnknownImp +{ + CSequentialInStreamWithCRC *_inStreamWithHashSpec; + CMyComPtr<ISequentialInStream> _inStreamWithHash; + CMyComPtr<IArchiveUpdateCallback> _updateCallback; + + bool _currentSizeIsDefined; + bool _fileIsOpen; + UInt64 _currentSize; + UInt64 _filePos; + const UInt32 *_fileIndices; + UInt32 _numFiles; + UInt32 _fileIndex; + + HRESULT OpenStream(); + HRESULT CloseStream(); + void AddDigest(); + +public: + CRecordVector<bool> Processed; + CRecordVector<UInt32> CRCs; + CRecordVector<UInt64> Sizes; + + MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); + + CFolderInStream(); + void Init(IArchiveUpdateCallback *updateCallback, const UInt32 *fileIndices, UInt32 numFiles); + UInt64 GetFullSize() const + { + UInt64 size = 0; + for (int i = 0; i < Sizes.Size(); i++) + size += Sizes[i]; + return size; + } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp new file mode 100644 index 000000000..22c4600ec --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp @@ -0,0 +1,149 @@ +// 7zFolderOutStream.cpp + +#include "StdAfx.h" + +#include "7zFolderOutStream.h" + +namespace NArchive { +namespace N7z { + +CFolderOutStream::CFolderOutStream() +{ + _crcStreamSpec = new COutStreamWithCRC; + _crcStream = _crcStreamSpec; +} + +HRESULT CFolderOutStream::Init( + const CArchiveDatabaseEx *db, + UInt32 ref2Offset, UInt32 startIndex, + const CBoolVector *extractStatuses, + IArchiveExtractCallback *extractCallback, + bool testMode, bool checkCrc) +{ + _db = db; + _ref2Offset = ref2Offset; + _startIndex = startIndex; + + _extractStatuses = extractStatuses; + _extractCallback = extractCallback; + _testMode = testMode; + _checkCrc = checkCrc; + + _currentIndex = 0; + _fileIsOpen = false; + return ProcessEmptyFiles(); +} + +HRESULT CFolderOutStream::OpenFile() +{ + Int32 askMode = ((*_extractStatuses)[_currentIndex]) ? (_testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract) : + NExtract::NAskMode::kSkip; + CMyComPtr<ISequentialOutStream> realOutStream; + UInt32 index = _startIndex + _currentIndex; + RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode)); + _crcStreamSpec->SetStream(realOutStream); + _crcStreamSpec->Init(_checkCrc); + _fileIsOpen = true; + const CFileItem &fi = _db->Files[index]; + _rem = fi.Size; + if (askMode == NExtract::NAskMode::kExtract && !realOutStream && + !_db->IsItemAnti(index) && !fi.IsDir) + askMode = NExtract::NAskMode::kSkip; + return _extractCallback->PrepareOperation(askMode); +} + +HRESULT CFolderOutStream::CloseFileAndSetResult(Int32 res) +{ + _crcStreamSpec->ReleaseStream(); + _fileIsOpen = false; + _currentIndex++; + return _extractCallback->SetOperationResult(res); +} + +HRESULT CFolderOutStream::CloseFileAndSetResult() +{ + const CFileItem &fi = _db->Files[_startIndex + _currentIndex]; + return CloseFileAndSetResult( + (fi.IsDir || !fi.CrcDefined || !_checkCrc || fi.Crc == _crcStreamSpec->GetCRC()) ? + NExtract::NOperationResult::kOK : + NExtract::NOperationResult::kCRCError); +} + +HRESULT CFolderOutStream::ProcessEmptyFiles() +{ + while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) + { + RINOK(OpenFile()); + RINOK(CloseFileAndSetResult()); + } + return S_OK; +} + +STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + while (size != 0) + { + if (_fileIsOpen) + { + UInt32 cur = size < _rem ? size : (UInt32)_rem; + RINOK(_crcStream->Write(data, cur, &cur)); + if (cur == 0) + break; + data = (const Byte *)data + cur; + size -= cur; + _rem -= cur; + if (processedSize != NULL) + *processedSize += cur; + if (_rem == 0) + { + RINOK(CloseFileAndSetResult()); + RINOK(ProcessEmptyFiles()); + continue; + } + } + else + { + RINOK(ProcessEmptyFiles()); + if (_currentIndex == _extractStatuses->Size()) + { + // we support partial extracting + if (processedSize != NULL) + *processedSize += size; + break; + } + RINOK(OpenFile()); + } + } + return S_OK; +} + +STDMETHODIMP CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value) +{ + *value = 0; + if ((int)subStream >= _extractStatuses->Size()) + return S_FALSE; + *value = _db->Files[_startIndex + (int)subStream].Size; + return S_OK; +} + +HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult) +{ + while (_currentIndex < _extractStatuses->Size()) + { + if (_fileIsOpen) + { + RINOK(CloseFileAndSetResult(resultEOperationResult)); + } + else + { + RINOK(OpenFile()); + } + } + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h new file mode 100644 index 000000000..f9bb1af42 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h @@ -0,0 +1,58 @@ +// 7zFolderOutStream.h + +#ifndef __7Z_FOLDER_OUT_STREAM_H +#define __7Z_FOLDER_OUT_STREAM_H + +#include "../../IStream.h" +#include "../IArchive.h" +#include "../Common/OutStreamWithCRC.h" + +#include "7zIn.h" + +namespace NArchive { +namespace N7z { + +class CFolderOutStream: + public ISequentialOutStream, + public ICompressGetSubStreamSize, + public CMyUnknownImp +{ + COutStreamWithCRC *_crcStreamSpec; + CMyComPtr<ISequentialOutStream> _crcStream; + const CArchiveDatabaseEx *_db; + const CBoolVector *_extractStatuses; + CMyComPtr<IArchiveExtractCallback> _extractCallback; + UInt32 _ref2Offset; + UInt32 _startIndex; + int _currentIndex; + bool _testMode; + bool _checkCrc; + bool _fileIsOpen; + UInt64 _rem; + + HRESULT OpenFile(); + HRESULT CloseFileAndSetResult(Int32 res); + HRESULT CloseFileAndSetResult(); + HRESULT ProcessEmptyFiles(); +public: + MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) + + CFolderOutStream(); + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); + + HRESULT Init( + const CArchiveDatabaseEx *db, + UInt32 ref2Offset, UInt32 startIndex, + const CBoolVector *extractStatuses, + IArchiveExtractCallback *extractCallback, + bool testMode, bool checkCrc); + HRESULT FlushCorrupted(Int32 resultEOperationResult); + HRESULT WasWritingFinished() const + { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp new file mode 100644 index 000000000..4ab7afa87 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp @@ -0,0 +1,482 @@ +// 7zHandler.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" + +#ifndef __7Z_SET_PROPERTIES +#include "../../../Windows/System.h" +#endif + +#include "../Common/ItemNameUtils.h" + +#include "7zHandler.h" +#include "7zProperties.h" + +#ifdef __7Z_SET_PROPERTIES +#ifdef EXTRACT_ONLY +#include "../Common/ParseProperties.h" +#endif +#endif + +using namespace NWindows; + +extern UString ConvertMethodIdToString(UInt64 id); + +namespace NArchive { +namespace N7z { + +CHandler::CHandler() +{ + _crcSize = 4; + + #ifndef _NO_CRYPTO + _passwordIsDefined = false; + #endif + + #ifdef EXTRACT_ONLY + #ifdef __7Z_SET_PROPERTIES + _numThreads = NSystem::GetNumberOfProcessors(); + #endif + #else + Init(); + #endif +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _db.Files.Size(); + return S_OK; +} + +#ifdef _SFX + +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */, + BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */) +{ + return E_NOTIMPL; +} + + +#else + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidSolid, VT_BOOL}, + { NULL, kpidNumBlocks, VT_UI4}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8}, + { NULL, kpidOffset, VT_UI8} +}; + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + UString resString; + CRecordVector<UInt64> ids; + int i; + for (i = 0; i < _db.Folders.Size(); i++) + { + const CFolder &f = _db.Folders[i]; + for (int j = f.Coders.Size() - 1; j >= 0; j--) + ids.AddToUniqueSorted(f.Coders[j].MethodID); + } + + for (i = 0; i < ids.Size(); i++) + { + UInt64 id = ids[i]; + UString methodName; + /* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName); + if (methodName.IsEmpty()) + methodName = ConvertMethodIdToString(id); + if (!resString.IsEmpty()) + resString += L' '; + resString += methodName; + } + prop = resString; + break; + } + case kpidSolid: prop = _db.IsSolid(); break; + case kpidNumBlocks: prop = (UInt32)_db.Folders.Size(); break; + case kpidHeadersSize: prop = _db.HeadersSize; break; + case kpidPhySize: prop = _db.PhySize; break; + case kpidOffset: if (_db.ArchiveInfo.StartPosition != 0) prop = _db.ArchiveInfo.StartPosition; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +IMP_IInArchive_ArcProps + +#endif + +static void SetPropFromUInt64Def(CUInt64DefVector &v, int index, NCOM::CPropVariant &prop) +{ + UInt64 value; + if (v.GetItem(index, value)) + { + FILETIME ft; + ft.dwLowDateTime = (DWORD)value; + ft.dwHighDateTime = (DWORD)(value >> 32); + prop = ft; + } +} + +#ifndef _SFX + +static UString ConvertUInt32ToString(UInt32 value) +{ + wchar_t buffer[32]; + ConvertUInt64ToString(value, buffer); + return buffer; +} + +static UString GetStringForSizeValue(UInt32 value) +{ + for (int i = 31; i >= 0; i--) + if ((UInt32(1) << i) == value) + return ConvertUInt32ToString(i); + UString result; + if (value % (1 << 20) == 0) + { + result += ConvertUInt32ToString(value >> 20); + result += L"m"; + } + else if (value % (1 << 10) == 0) + { + result += ConvertUInt32ToString(value >> 10); + result += L"k"; + } + else + { + result += ConvertUInt32ToString(value); + result += L"b"; + } + return result; +} + +static const UInt64 k_Copy = 0x0; +static const UInt64 k_Delta = 3; +static const UInt64 k_LZMA2 = 0x21; +static const UInt64 k_LZMA = 0x030101; +static const UInt64 k_PPMD = 0x030401; + +static wchar_t GetHex(Byte value) +{ + return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10))); +} +static inline void AddHexToString(UString &res, Byte value) +{ + res += GetHex((Byte)(value >> 4)); + res += GetHex((Byte)(value & 0xF)); +} + +#endif + +bool CHandler::IsEncrypted(UInt32 index2) const +{ + CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + return _db.Folders[folderIndex].IsEncrypted(); + return false; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + /* + const CRef2 &ref2 = _refs[index]; + if (ref2.Refs.IsEmpty()) + return E_FAIL; + const CRef &ref = ref2.Refs.Front(); + */ + + const CFileItem &item = _db.Files[index]; + UInt32 index2 = index; + + switch(propID) + { + case kpidPath: + if (!item.Name.IsEmpty()) + prop = NItemName::GetOSName(item.Name); + break; + case kpidIsDir: prop = item.IsDir; break; + case kpidSize: + { + prop = item.Size; + // prop = ref2.Size; + break; + } + case kpidPackSize: + { + // prop = ref2.PackSize; + { + CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2) + prop = _db.GetFolderFullPackSize(folderIndex); + /* + else + prop = (UInt64)0; + */ + } + else + prop = (UInt64)0; + } + break; + } + case kpidPosition: { UInt64 v; if (_db.StartPos.GetItem(index2, v)) prop = v; break; } + case kpidCTime: SetPropFromUInt64Def(_db.CTime, index2, prop); break; + case kpidATime: SetPropFromUInt64Def(_db.ATime, index2, prop); break; + case kpidMTime: SetPropFromUInt64Def(_db.MTime, index2, prop); break; + case kpidAttrib: if (item.AttribDefined) prop = item.Attrib; break; + case kpidCRC: if (item.CrcDefined) prop = item.Crc; break; + case kpidEncrypted: prop = IsEncrypted(index2); break; + case kpidIsAnti: prop = _db.IsItemAnti(index2); break; + #ifndef _SFX + case kpidMethod: + { + CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + const CFolder &folderInfo = _db.Folders[folderIndex]; + UString methodsString; + for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderInfo &coder = folderInfo.Coders[i]; + if (!methodsString.IsEmpty()) + methodsString += L' '; + + UString methodName, propsString; + bool methodIsKnown = FindMethod( + EXTERNAL_CODECS_VARS + coder.MethodID, methodName); + + if (!methodIsKnown) + methodsString += ConvertMethodIdToString(coder.MethodID); + else + { + methodsString += methodName; + if (coder.MethodID == k_Delta && coder.Props.GetCapacity() == 1) + propsString = ConvertUInt32ToString((UInt32)coder.Props[0] + 1); + else if (coder.MethodID == k_LZMA && coder.Props.GetCapacity() == 5) + { + UInt32 dicSize = GetUi32((const Byte *)coder.Props + 1); + propsString = GetStringForSizeValue(dicSize); + } + else if (coder.MethodID == k_LZMA2 && coder.Props.GetCapacity() == 1) + { + Byte p = coder.Props[0]; + UInt32 dicSize = (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)); + propsString = GetStringForSizeValue(dicSize); + } + else if (coder.MethodID == k_PPMD && coder.Props.GetCapacity() == 5) + { + Byte order = *(const Byte *)coder.Props; + propsString = L'o'; + propsString += ConvertUInt32ToString(order); + propsString += L":mem"; + UInt32 dicSize = GetUi32((const Byte *)coder.Props + 1); + propsString += GetStringForSizeValue(dicSize); + } + else if (coder.MethodID == k_AES && coder.Props.GetCapacity() >= 1) + { + const Byte *data = (const Byte *)coder.Props; + Byte firstByte = *data++; + UInt32 numCyclesPower = firstByte & 0x3F; + propsString = ConvertUInt32ToString(numCyclesPower); + /* + if ((firstByte & 0xC0) != 0) + { + UInt32 saltSize = (firstByte >> 7) & 1; + UInt32 ivSize = (firstByte >> 6) & 1; + if (coder.Props.GetCapacity() >= 2) + { + Byte secondByte = *data++; + saltSize += (secondByte >> 4); + ivSize += (secondByte & 0x0F); + } + } + */ + } + } + if (!propsString.IsEmpty()) + { + methodsString += L':'; + methodsString += propsString; + } + else if (coder.Props.GetCapacity() > 0) + { + methodsString += L":["; + for (size_t bi = 0; bi < coder.Props.GetCapacity(); bi++) + { + if (bi > 5 && bi + 1 < coder.Props.GetCapacity()) + { + methodsString += L".."; + break; + } + else + AddHexToString(methodsString, coder.Props[bi]); + } + methodsString += L']'; + } + } + prop = methodsString; + } + } + break; + case kpidBlock: + { + CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + prop = (UInt32)folderIndex; + } + break; + case kpidPackedSize0: + case kpidPackedSize1: + case kpidPackedSize2: + case kpidPackedSize3: + case kpidPackedSize4: + { + CNum folderIndex = _db.FileIndexToFolderIndexMap[index2]; + if (folderIndex != kNumNoIndex) + { + const CFolder &folderInfo = _db.Folders[folderIndex]; + if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 && + folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0)) + { + prop = _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0); + } + else + prop = (UInt64)0; + } + else + prop = (UInt64)0; + } + break; + #endif + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + Close(); + #ifndef _SFX + _fileInfoPopIDs.Clear(); + #endif + try + { + CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback; + + #ifndef _NO_CRYPTO + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + if (openArchiveCallback) + { + openArchiveCallbackTemp.QueryInterface( + IID_ICryptoGetTextPassword, &getTextPassword); + } + #endif + CInArchive archive; + RINOK(archive.Open(stream, maxCheckStartPosition)); + #ifndef _NO_CRYPTO + _passwordIsDefined = false; + UString password; + #endif + HRESULT result = archive.ReadDatabase( + EXTERNAL_CODECS_VARS + _db + #ifndef _NO_CRYPTO + , getTextPassword, _passwordIsDefined + #endif + ); + RINOK(result); + _db.Fill(); + _inStream = stream; + } + catch(...) + { + Close(); + return S_FALSE; + } + // _inStream = stream; + #ifndef _SFX + FillPopIDs(); + #endif + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + COM_TRY_BEGIN + _inStream.Release(); + _db.Clear(); + return S_OK; + COM_TRY_END +} + +#ifdef __7Z_SET_PROPERTIES +#ifdef EXTRACT_ONLY + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + COM_TRY_BEGIN + const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); + _numThreads = numProcessors; + + for (int i = 0; i < numProperties; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + const PROPVARIANT &value = values[i]; + UInt32 number; + int index = ParseStringToUInt32(name, number); + if (index == 0) + { + if(name.Left(2).CompareNoCase(L"MT") == 0) + { + RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads)); + continue; + } + else + return E_INVALIDARG; + } + } + return S_OK; + COM_TRY_END +} + +#endif +#endif + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h new file mode 100644 index 000000000..56062d464 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h @@ -0,0 +1,119 @@ +// 7z/Handler.h + +#ifndef __7Z_HANDLER_H +#define __7Z_HANDLER_H + +#include "../../ICoder.h" +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#ifndef EXTRACT_ONLY +#include "../Common/HandlerOut.h" +#endif + +#include "7zCompressionMode.h" +#include "7zIn.h" + +namespace NArchive { +namespace N7z { + +#ifndef __7Z_SET_PROPERTIES + +#ifdef EXTRACT_ONLY +#if !defined(_7ZIP_ST) && !defined(_SFX) +#define __7Z_SET_PROPERTIES +#endif +#else +#define __7Z_SET_PROPERTIES +#endif + +#endif + + +class CHandler: + #ifndef EXTRACT_ONLY + public NArchive::COutHandler, + #endif + public IInArchive, + #ifdef __7Z_SET_PROPERTIES + public ISetProperties, + #endif + #ifndef EXTRACT_ONLY + public IOutArchive, + #endif + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + #ifdef __7Z_SET_PROPERTIES + MY_QUERYINTERFACE_ENTRY(ISetProperties) + #endif + #ifndef EXTRACT_ONLY + MY_QUERYINTERFACE_ENTRY(IOutArchive) + #endif + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + + #ifdef __7Z_SET_PROPERTIES + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties); + #endif + + #ifndef EXTRACT_ONLY + INTERFACE_IOutArchive(;) + #endif + + DECL_ISetCompressCodecsInfo + + CHandler(); + +private: + CMyComPtr<IInStream> _inStream; + NArchive::N7z::CArchiveDatabaseEx _db; + #ifndef _NO_CRYPTO + bool _passwordIsDefined; + #endif + + #ifdef EXTRACT_ONLY + + #ifdef __7Z_SET_PROPERTIES + UInt32 _numThreads; + #endif + + UInt32 _crcSize; + + #else + + CRecordVector<CBind> _binds; + + HRESULT SetCompressionMethod(CCompressionMethodMode &method, + CObjectVector<COneMethodInfo> &methodsInfo + #ifndef _7ZIP_ST + , UInt32 numThreads + #endif + ); + + HRESULT SetCompressionMethod( + CCompressionMethodMode &method, + CCompressionMethodMode &headerMethod); + + #endif + + bool IsEncrypted(UInt32 index2) const; + #ifndef _SFX + + CRecordVector<UInt64> _fileInfoPopIDs; + void FillPopIDs(); + + #endif + + DECL_EXTERNAL_CODECS_VARS +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp new file mode 100644 index 000000000..a8ccab6df --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp @@ -0,0 +1,483 @@ +// 7zHandlerOut.cpp + +#include "StdAfx.h" + +#include "../../../Windows/PropVariant.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/StringToInt.h" + +#include "../../ICoder.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/ParseProperties.h" + +#include "7zHandler.h" +#include "7zOut.h" +#include "7zUpdate.h" + +using namespace NWindows; + +namespace NArchive { +namespace N7z { + +static const wchar_t *kLZMAMethodName = L"LZMA"; +static const wchar_t *kCopyMethod = L"Copy"; +static const wchar_t *kDefaultMethodName = kLZMAMethodName; + +static const UInt32 kLzmaAlgorithmX5 = 1; +static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2"; +static const UInt32 kDictionaryForHeaders = + #ifdef UNDER_CE + 1 << 18 + #else + 1 << 20 + #endif +; +static const UInt32 kNumFastBytesForHeaders = 273; +static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5; + +static inline bool IsCopyMethod(const UString &methodName) + { return (methodName.CompareNoCase(kCopyMethod) == 0); } + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) +{ + *type = NFileTimeType::kWindows; + return S_OK; +} + +HRESULT CHandler::SetCompressionMethod( + CCompressionMethodMode &methodMode, + CCompressionMethodMode &headerMethod) +{ + HRESULT res = SetCompressionMethod(methodMode, _methods + #ifndef _7ZIP_ST + , _numThreads + #endif + ); + RINOK(res); + methodMode.Binds = _binds; + + if (_compressHeaders) + { + // headerMethod.Methods.Add(methodMode.Methods.Back()); + + CObjectVector<COneMethodInfo> headerMethodInfoVector; + COneMethodInfo oneMethodInfo; + oneMethodInfo.MethodName = kLZMAMethodName; + { + CProp prop; + prop.Id = NCoderPropID::kMatchFinder; + prop.Value = kLzmaMatchFinderForHeaders; + oneMethodInfo.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kAlgorithm; + prop.Value = kAlgorithmForHeaders; + oneMethodInfo.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kNumFastBytes; + prop.Value = (UInt32)kNumFastBytesForHeaders; + oneMethodInfo.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kDictionarySize; + prop.Value = (UInt32)kDictionaryForHeaders; + oneMethodInfo.Props.Add(prop); + } + headerMethodInfoVector.Add(oneMethodInfo); + HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector + #ifndef _7ZIP_ST + , 1 + #endif + ); + RINOK(res); + } + return S_OK; +} + +HRESULT CHandler::SetCompressionMethod( + CCompressionMethodMode &methodMode, + CObjectVector<COneMethodInfo> &methodsInfo + #ifndef _7ZIP_ST + , UInt32 numThreads + #endif + ) +{ + UInt32 level = _level; + + if (methodsInfo.IsEmpty()) + { + COneMethodInfo oneMethodInfo; + oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName); + methodsInfo.Add(oneMethodInfo); + } + + bool needSolid = false; + for(int i = 0; i < methodsInfo.Size(); i++) + { + COneMethodInfo &oneMethodInfo = methodsInfo[i]; + SetCompressionMethod2(oneMethodInfo + #ifndef _7ZIP_ST + , numThreads + #endif + ); + + if (!IsCopyMethod(oneMethodInfo.MethodName)) + needSolid = true; + + CMethodFull methodFull; + + if (!FindMethod( + EXTERNAL_CODECS_VARS + oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams)) + return E_INVALIDARG; + methodFull.Props = oneMethodInfo.Props; + methodMode.Methods.Add(methodFull); + + if (!_numSolidBytesDefined) + { + for (int j = 0; j < methodFull.Props.Size(); j++) + { + const CProp &prop = methodFull.Props[j]; + if ((prop.Id == NCoderPropID::kDictionarySize || + prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4) + { + _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7; + const UInt64 kMinSize = (1 << 24); + if (_numSolidBytes < kMinSize) + _numSolidBytes = kMinSize; + _numSolidBytesDefined = true; + break; + } + } + } + } + + if (!needSolid && !_numSolidBytesDefined) + { + _numSolidBytesDefined = true; + _numSolidBytes = 0; + } + return S_OK; +} + +static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, bool writeTime, PROPID propID, UInt64 &ft, bool &ftDefined) +{ + ft = 0; + ftDefined = false; + if (!writeTime) + return S_OK; + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(index, propID, &prop)); + if (prop.vt == VT_FILETIME) + { + ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32); + ftDefined = true; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + COM_TRY_BEGIN + + const CArchiveDatabaseEx *db = 0; + #ifdef _7Z_VOL + if (_volumes.Size() > 1) + return E_FAIL; + const CVolume *volume = 0; + if (_volumes.Size() == 1) + { + volume = &_volumes.Front(); + db = &volume->Database; + } + #else + if (_inStream != 0) + db = &_db; + #endif + + CObjectVector<CUpdateItem> updateItems; + + for (UInt32 i = 0; i < numItems; i++) + { + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); + CUpdateItem ui; + ui.NewProps = IntToBool(newProps); + ui.NewData = IntToBool(newData); + ui.IndexInArchive = indexInArchive; + ui.IndexInClient = i; + ui.IsAnti = false; + ui.Size = 0; + + if (ui.IndexInArchive != -1) + { + if (db == 0 || ui.IndexInArchive >= db->Files.Size()) + return E_INVALIDARG; + const CFileItem &fi = db->Files[ui.IndexInArchive]; + ui.Name = fi.Name; + ui.IsDir = fi.IsDir; + ui.Size = fi.Size; + ui.IsAnti = db->IsItemAnti(ui.IndexInArchive); + + ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime); + ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime); + ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime); + } + + if (ui.NewProps) + { + bool nameIsDefined; + bool folderStatusIsDefined; + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop)); + if (prop.vt == VT_EMPTY) + ui.AttribDefined = false; + else if (prop.vt != VT_UI4) + return E_INVALIDARG; + else + { + ui.Attrib = prop.ulVal; + ui.AttribDefined = true; + } + } + + // we need MTime to sort files. + RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined)); + RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined)); + RINOK(GetTime(updateCallback, i, true, kpidMTime, ui.MTime, ui.MTimeDefined)); + + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(i, kpidPath, &prop)); + if (prop.vt == VT_EMPTY) + nameIsDefined = false; + else if (prop.vt != VT_BSTR) + return E_INVALIDARG; + else + { + ui.Name = NItemName::MakeLegalName(prop.bstrVal); + nameIsDefined = true; + } + } + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop)); + if (prop.vt == VT_EMPTY) + folderStatusIsDefined = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + { + ui.IsDir = (prop.boolVal != VARIANT_FALSE); + folderStatusIsDefined = true; + } + } + + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop)); + if (prop.vt == VT_EMPTY) + ui.IsAnti = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + ui.IsAnti = (prop.boolVal != VARIANT_FALSE); + } + + if (ui.IsAnti) + { + ui.AttribDefined = false; + + ui.CTimeDefined = false; + ui.ATimeDefined = false; + ui.MTimeDefined = false; + + ui.Size = 0; + } + + if (!folderStatusIsDefined && ui.AttribDefined) + ui.SetDirStatusFromAttrib(); + } + + if (ui.NewData) + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(i, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + ui.Size = (UInt64)prop.uhVal.QuadPart; + if (ui.Size != 0 && ui.IsAnti) + return E_INVALIDARG; + } + updateItems.Add(ui); + } + + CCompressionMethodMode methodMode, headerMethod; + RINOK(SetCompressionMethod(methodMode, headerMethod)); + #ifndef _7ZIP_ST + methodMode.NumThreads = _numThreads; + headerMethod.NumThreads = 1; + #endif + + CMyComPtr<ICryptoGetTextPassword2> getPassword2; + updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2); + + if (getPassword2) + { + CMyComBSTR password; + Int32 passwordIsDefined; + RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password)); + methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); + if (methodMode.PasswordIsDefined) + methodMode.Password = password; + } + else + methodMode.PasswordIsDefined = false; + + bool compressMainHeader = _compressHeaders; // check it + + bool encryptHeaders = false; + + if (methodMode.PasswordIsDefined) + { + if (_encryptHeadersSpecified) + encryptHeaders = _encryptHeaders; + #ifndef _NO_CRYPTO + else + encryptHeaders = _passwordIsDefined; + #endif + compressMainHeader = true; + if (encryptHeaders) + { + headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined; + headerMethod.Password = methodMode.Password; + } + } + + if (numItems < 2) + compressMainHeader = false; + + CUpdateOptions options; + options.Method = &methodMode; + options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0; + options.UseFilters = _level != 0 && _autoFilter; + options.MaxFilter = _level >= 8; + + options.HeaderOptions.CompressMainHeader = compressMainHeader; + options.HeaderOptions.WriteCTime = WriteCTime; + options.HeaderOptions.WriteATime = WriteATime; + options.HeaderOptions.WriteMTime = WriteMTime; + + options.NumSolidFiles = _numSolidFiles; + options.NumSolidBytes = _numSolidBytes; + options.SolidExtension = _solidExtension; + options.RemoveSfxBlock = _removeSfxBlock; + options.VolumeMode = _volumeMode; + + COutArchive archive; + CArchiveDatabase newDatabase; + + CMyComPtr<ICryptoGetTextPassword> getPassword; + updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword); + + HRESULT res = Update( + EXTERNAL_CODECS_VARS + #ifdef _7Z_VOL + volume ? volume->Stream: 0, + volume ? db : 0, + #else + _inStream, + db, + #endif + updateItems, + archive, newDatabase, outStream, updateCallback, options + #ifndef _NO_CRYPTO + , getPassword + #endif + ); + + RINOK(res); + + updateItems.ClearAndFree(); + + return archive.WriteDatabase(EXTERNAL_CODECS_VARS + newDatabase, options.HeaderMethod, options.HeaderOptions); + + COM_TRY_END +} + +static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream) +{ + stream = 0; + int index = ParseStringToUInt32(srcString, coder); + if (index == 0) + return E_INVALIDARG; + srcString.Delete(0, index); + if (srcString[0] == 'S') + { + srcString.Delete(0); + int index = ParseStringToUInt32(srcString, stream); + if (index == 0) + return E_INVALIDARG; + srcString.Delete(0, index); + } + return S_OK; +} + +static HRESULT GetBindInfo(UString &srcString, CBind &bind) +{ + RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream)); + if (srcString[0] != ':') + return E_INVALIDARG; + srcString.Delete(0); + RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream)); + if (!srcString.IsEmpty()) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + COM_TRY_BEGIN + _binds.Clear(); + BeforeSetProperty(); + + for (int i = 0; i < numProperties; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + + const PROPVARIANT &value = values[i]; + + if (name[0] == 'B') + { + name.Delete(0); + CBind bind; + RINOK(GetBindInfo(name, bind)); + _binds.Add(bind); + continue; + } + + RINOK(SetProperty(name, value)); + } + + return S_OK; + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp new file mode 100644 index 000000000..5b5f2fb37 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp @@ -0,0 +1,14 @@ +// 7zHeader.cpp + +#include "StdAfx.h" +#include "7zHeader.h" + +namespace NArchive { +namespace N7z { + +Byte kSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; +#ifdef _7Z_VOL +Byte kFinishSignature[kSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C + 1}; +#endif + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h new file mode 100644 index 000000000..30622b90e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h @@ -0,0 +1,97 @@ +// 7z/7zHeader.h + +#ifndef __7Z_HEADER_H +#define __7Z_HEADER_H + +#include "../../../Common/Types.h" + +namespace NArchive { +namespace N7z { + +const int kSignatureSize = 6; +extern Byte kSignature[kSignatureSize]; + +// #define _7Z_VOL +// 7z-MultiVolume is not finished yet. +// It can work already, but I still do not like some +// things of that new multivolume format. +// So please keep it commented. + +#ifdef _7Z_VOL +extern Byte kFinishSignature[kSignatureSize]; +#endif + +struct CArchiveVersion +{ + Byte Major; + Byte Minor; +}; + +const Byte kMajorVersion = 0; + +struct CStartHeader +{ + UInt64 NextHeaderOffset; + UInt64 NextHeaderSize; + UInt32 NextHeaderCRC; +}; + +const UInt32 kStartHeaderSize = 20; + +#ifdef _7Z_VOL +struct CFinishHeader: public CStartHeader +{ + UInt64 ArchiveStartOffset; // data offset from end if that struct + UInt64 AdditionalStartBlockSize; // start signature & start header size +}; + +const UInt32 kFinishHeaderSize = kStartHeaderSize + 16; +#endif + +namespace NID +{ + enum EEnum + { + kEnd, + + kHeader, + + kArchiveProperties, + + kAdditionalStreamsInfo, + kMainStreamsInfo, + kFilesInfo, + + kPackInfo, + kUnpackInfo, + kSubStreamsInfo, + + kSize, + kCRC, + + kFolder, + + kCodersUnpackSize, + kNumUnpackStream, + + kEmptyStream, + kEmptyFile, + kAnti, + + kName, + kCTime, + kATime, + kMTime, + kWinAttributes, + kComment, + + kEncodedHeader, + + kStartPos, + kDummy + }; +} + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp new file mode 100644 index 000000000..0feb81d2c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp @@ -0,0 +1,1276 @@ +// 7zIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" +#include "../../../../C/CpuArch.h" + +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "7zDecode.h" +#include "7zIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +// define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader +#ifndef _SFX +#define FORMAT_7Z_RECOVERY +#endif + +namespace NArchive { +namespace N7z { + +static void BoolVector_Fill_False(CBoolVector &v, int size) +{ + v.Clear(); + v.Reserve(size); + for (int i = 0; i < size; i++) + v.Add(false); +} + +static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index) +{ + if (index >= (UInt32)v.Size()) + return true; + bool res = v[index]; + v[index] = true; + return res; +} + +bool CFolder::CheckStructure() const +{ + const int kNumCodersMax = sizeof(UInt32) * 8; // don't change it + const int kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax + const int kNumBindsMax = 32; + + if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) + return false; + + { + CBoolVector v; + BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); + + int i; + for (i = 0; i < BindPairs.Size(); i++) + if (BoolVector_GetAndSet(v, BindPairs[i].InIndex)) + return false; + for (i = 0; i < PackStreams.Size(); i++) + if (BoolVector_GetAndSet(v, PackStreams[i])) + return false; + + BoolVector_Fill_False(v, UnpackSizes.Size()); + for (i = 0; i < BindPairs.Size(); i++) + if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) + return false; + } + + UInt32 mask[kMaskSize]; + int i; + for (i = 0; i < kMaskSize; i++) + mask[i] = 0; + + { + CIntVector inStreamToCoder, outStreamToCoder; + for (i = 0; i < Coders.Size(); i++) + { + CNum j; + const CCoderInfo &coder = Coders[i]; + for (j = 0; j < coder.NumInStreams; j++) + inStreamToCoder.Add(i); + for (j = 0; j < coder.NumOutStreams; j++) + outStreamToCoder.Add(i); + } + + for (i = 0; i < BindPairs.Size(); i++) + { + const CBindPair &bp = BindPairs[i]; + mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]); + } + } + + for (i = 0; i < kMaskSize; i++) + for (int j = 0; j < kMaskSize; j++) + if (((1 << j) & mask[i]) != 0) + mask[i] |= mask[j]; + + for (i = 0; i < kMaskSize; i++) + if (((1 << i) & mask[i]) != 0) + return false; + + return true; +} + +class CInArchiveException {}; + +static void ThrowException() { throw CInArchiveException(); } +static inline void ThrowEndOfData() { ThrowException(); } +static inline void ThrowUnsupported() { ThrowException(); } +static inline void ThrowIncorrect() { ThrowException(); } +static inline void ThrowUnsupportedVersion() { ThrowException(); } + +/* +class CInArchiveException +{ +public: + enum CCauseType + { + kUnsupportedVersion = 0, + kUnsupported, + kIncorrect, + kEndOfData + } Cause; + CInArchiveException(CCauseType cause): Cause(cause) {}; +}; + +static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); } +static void ThrowEndOfData() { ThrowException(CInArchiveException::kEndOfData); } +static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); } +static void ThrowIncorrect() { ThrowException(CInArchiveException::kIncorrect); } +static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); } +*/ + +class CStreamSwitch +{ + CInArchive *_archive; + bool _needRemove; +public: + CStreamSwitch(): _needRemove(false) {} + ~CStreamSwitch() { Remove(); } + void Remove(); + void Set(CInArchive *archive, const Byte *data, size_t size); + void Set(CInArchive *archive, const CByteBuffer &byteBuffer); + void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector); +}; + +void CStreamSwitch::Remove() +{ + if (_needRemove) + { + _archive->DeleteByteStream(); + _needRemove = false; + } +} + +void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size) +{ + Remove(); + _archive = archive; + _archive->AddByteStream(data, size); + _needRemove = true; +} + +void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) +{ + Set(archive, byteBuffer, byteBuffer.GetCapacity()); +} + +void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector) +{ + Remove(); + Byte external = archive->ReadByte(); + if (external != 0) + { + int dataIndex = (int)archive->ReadNum(); + if (dataIndex < 0 || dataIndex >= dataVector->Size()) + ThrowIncorrect(); + Set(archive, (*dataVector)[dataIndex]); + } +} + +Byte CInByte2::ReadByte() +{ + if (_pos >= _size) + ThrowEndOfData(); + return _buffer[_pos++]; +} + +void CInByte2::ReadBytes(Byte *data, size_t size) +{ + if (size > _size - _pos) + ThrowEndOfData(); + for (size_t i = 0; i < size; i++) + data[i] = _buffer[_pos++]; +} + +void CInByte2::SkipData(UInt64 size) +{ + if (size > _size - _pos) + ThrowEndOfData(); + _pos += (size_t)size; +} + +void CInByte2::SkipData() +{ + SkipData(ReadNumber()); +} + +UInt64 CInByte2::ReadNumber() +{ + if (_pos >= _size) + ThrowEndOfData(); + Byte firstByte = _buffer[_pos++]; + Byte mask = 0x80; + UInt64 value = 0; + for (int i = 0; i < 8; i++) + { + if ((firstByte & mask) == 0) + { + UInt64 highPart = firstByte & (mask - 1); + value += (highPart << (i * 8)); + return value; + } + if (_pos >= _size) + ThrowEndOfData(); + value |= ((UInt64)_buffer[_pos++] << (8 * i)); + mask >>= 1; + } + return value; +} + +CNum CInByte2::ReadNum() +{ + UInt64 value = ReadNumber(); + if (value > kNumMax) + ThrowUnsupported(); + return (CNum)value; +} + +UInt32 CInByte2::ReadUInt32() +{ + if (_pos + 4 > _size) + ThrowEndOfData(); + UInt32 res = Get32(_buffer + _pos); + _pos += 4; + return res; +} + +UInt64 CInByte2::ReadUInt64() +{ + if (_pos + 8 > _size) + ThrowEndOfData(); + UInt64 res = Get64(_buffer + _pos); + _pos += 8; + return res; +} + +void CInByte2::ReadString(UString &s) +{ + const Byte *buf = _buffer + _pos; + size_t rem = (_size - _pos) / 2 * 2; + { + size_t i; + for (i = 0; i < rem; i += 2) + if (buf[i] == 0 && buf[i + 1] == 0) + break; + if (i == rem) + ThrowEndOfData(); + rem = i; + } + int len = (int)(rem / 2); + if (len < 0 || (size_t)len * 2 != rem) + ThrowUnsupported(); + wchar_t *p = s.GetBuffer(len); + int i; + for (i = 0; i < len; i++, buf += 2) + p[i] = (wchar_t)Get16(buf); + s.ReleaseBuffer(len); + _pos += rem + 2; +} + +static inline bool TestSignature(const Byte *p) +{ + for (int i = 0; i < kSignatureSize; i++) + if (p[i] != kSignature[i]) + return false; + return CrcCalc(p + 12, 20) == GetUi32(p + 8); +} + +#ifdef FORMAT_7Z_RECOVERY +static inline bool TestSignature2(const Byte *p) +{ + int i; + for (i = 0; i < kSignatureSize; i++) + if (p[i] != kSignature[i]) + return false; + if (CrcCalc(p + 12, 20) == GetUi32(p + 8)) + return true; + for (i = 8; i < kHeaderSize; i++) + if (p[i] != 0) + return false; + return (p[6] != 0 || p[7] != 0); +} +#else +#define TestSignature2(p) TestSignature(p) +#endif + +HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + RINOK(ReadStream_FALSE(stream, _header, kHeaderSize)); + + if (TestSignature2(_header)) + return S_OK; + + CByteBuffer byteBuffer; + const UInt32 kBufferSize = (1 << 16); + byteBuffer.SetCapacity(kBufferSize); + Byte *buffer = byteBuffer; + UInt32 numPrevBytes = kHeaderSize; + memcpy(buffer, _header, kHeaderSize); + UInt64 curTestPos = _arhiveBeginStreamPosition; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit) + break; + do + { + UInt32 numReadBytes = kBufferSize - numPrevBytes; + UInt32 processedSize; + RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); + numPrevBytes += processedSize; + if (processedSize == 0) + return S_FALSE; + } + while (numPrevBytes <= kHeaderSize); + UInt32 numTests = numPrevBytes - kHeaderSize; + for (UInt32 pos = 0; pos < numTests; pos++) + { + for (; buffer[pos] != '7' && pos < numTests; pos++); + if (pos == numTests) + break; + if (TestSignature(buffer + pos)) + { + memcpy(_header, buffer + pos, kHeaderSize); + curTestPos += pos; + _arhiveBeginStreamPosition = curTestPos; + return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL); + } + } + curTestPos += numTests; + numPrevBytes -= numTests; + memmove(buffer, buffer + numTests, numPrevBytes); + } + return S_FALSE; +} + +// S_FALSE means that file is not archive +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + HeadersSize = 0; + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) + RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); + _stream = stream; + return S_OK; +} + +void CInArchive::Close() +{ + _stream.Release(); +} + +void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) +{ + for (;;) + { + if (ReadID() == NID::kEnd) + break; + SkipData(); + } +} + +void CInArchive::GetNextFolderItem(CFolder &folder) +{ + CNum numCoders = ReadNum(); + + folder.Coders.Clear(); + folder.Coders.Reserve((int)numCoders); + CNum numInStreams = 0; + CNum numOutStreams = 0; + CNum i; + for (i = 0; i < numCoders; i++) + { + folder.Coders.Add(CCoderInfo()); + CCoderInfo &coder = folder.Coders.Back(); + + { + Byte mainByte = ReadByte(); + int idSize = (mainByte & 0xF); + Byte longID[15]; + ReadBytes(longID, idSize); + if (idSize > 8) + ThrowUnsupported(); + UInt64 id = 0; + for (int j = 0; j < idSize; j++) + id |= (UInt64)longID[idSize - 1 - j] << (8 * j); + coder.MethodID = id; + + if ((mainByte & 0x10) != 0) + { + coder.NumInStreams = ReadNum(); + coder.NumOutStreams = ReadNum(); + } + else + { + coder.NumInStreams = 1; + coder.NumOutStreams = 1; + } + if ((mainByte & 0x20) != 0) + { + CNum propsSize = ReadNum(); + coder.Props.SetCapacity((size_t)propsSize); + ReadBytes((Byte *)coder.Props, (size_t)propsSize); + } + if ((mainByte & 0x80) != 0) + ThrowUnsupported(); + } + numInStreams += coder.NumInStreams; + numOutStreams += coder.NumOutStreams; + } + + CNum numBindPairs = numOutStreams - 1; + folder.BindPairs.Clear(); + folder.BindPairs.Reserve(numBindPairs); + for (i = 0; i < numBindPairs; i++) + { + CBindPair bp; + bp.InIndex = ReadNum(); + bp.OutIndex = ReadNum(); + folder.BindPairs.Add(bp); + } + + if (numInStreams < numBindPairs) + ThrowUnsupported(); + CNum numPackStreams = numInStreams - numBindPairs; + folder.PackStreams.Reserve(numPackStreams); + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams; i++) + if (folder.FindBindPairForInStream(i) < 0) + { + folder.PackStreams.Add(i); + break; + } + if (folder.PackStreams.Size() != 1) + ThrowUnsupported(); + } + else + for (i = 0; i < numPackStreams; i++) + folder.PackStreams.Add(ReadNum()); +} + +void CInArchive::WaitAttribute(UInt64 attribute) +{ + for (;;) + { + UInt64 type = ReadID(); + if (type == attribute) + return; + if (type == NID::kEnd) + ThrowIncorrect(); + SkipData(); + } +} + +void CInArchive::ReadHashDigests(int numItems, + CBoolVector &digestsDefined, + CRecordVector<UInt32> &digests) +{ + ReadBoolVector2(numItems, digestsDefined); + digests.Clear(); + digests.Reserve(numItems); + for (int i = 0; i < numItems; i++) + { + UInt32 crc = 0; + if (digestsDefined[i]) + crc = ReadUInt32(); + digests.Add(crc); + } +} + +void CInArchive::ReadPackInfo( + UInt64 &dataOffset, + CRecordVector<UInt64> &packSizes, + CBoolVector &packCRCsDefined, + CRecordVector<UInt32> &packCRCs) +{ + dataOffset = ReadNumber(); + CNum numPackStreams = ReadNum(); + + WaitAttribute(NID::kSize); + packSizes.Clear(); + packSizes.Reserve(numPackStreams); + for (CNum i = 0; i < numPackStreams; i++) + packSizes.Add(ReadNumber()); + + UInt64 type; + for (;;) + { + type = ReadID(); + if (type == NID::kEnd) + break; + if (type == NID::kCRC) + { + ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs); + continue; + } + SkipData(); + } + if (packCRCsDefined.IsEmpty()) + { + BoolVector_Fill_False(packCRCsDefined, numPackStreams); + packCRCs.Reserve(numPackStreams); + packCRCs.Clear(); + for (CNum i = 0; i < numPackStreams; i++) + packCRCs.Add(0); + } +} + +void CInArchive::ReadUnpackInfo( + const CObjectVector<CByteBuffer> *dataVector, + CObjectVector<CFolder> &folders) +{ + WaitAttribute(NID::kFolder); + CNum numFolders = ReadNum(); + + { + CStreamSwitch streamSwitch; + streamSwitch.Set(this, dataVector); + folders.Clear(); + folders.Reserve(numFolders); + for (CNum i = 0; i < numFolders; i++) + { + folders.Add(CFolder()); + GetNextFolderItem(folders.Back()); + } + } + + WaitAttribute(NID::kCodersUnpackSize); + + CNum i; + for (i = 0; i < numFolders; i++) + { + CFolder &folder = folders[i]; + CNum numOutStreams = folder.GetNumOutStreams(); + folder.UnpackSizes.Reserve(numOutStreams); + for (CNum j = 0; j < numOutStreams; j++) + folder.UnpackSizes.Add(ReadNumber()); + } + + for (;;) + { + UInt64 type = ReadID(); + if (type == NID::kEnd) + return; + if (type == NID::kCRC) + { + CBoolVector crcsDefined; + CRecordVector<UInt32> crcs; + ReadHashDigests(numFolders, crcsDefined, crcs); + for (i = 0; i < numFolders; i++) + { + CFolder &folder = folders[i]; + folder.UnpackCRCDefined = crcsDefined[i]; + folder.UnpackCRC = crcs[i]; + } + continue; + } + SkipData(); + } +} + +void CInArchive::ReadSubStreamsInfo( + const CObjectVector<CFolder> &folders, + CRecordVector<CNum> &numUnpackStreamsInFolders, + CRecordVector<UInt64> &unpackSizes, + CBoolVector &digestsDefined, + CRecordVector<UInt32> &digests) +{ + numUnpackStreamsInFolders.Clear(); + numUnpackStreamsInFolders.Reserve(folders.Size()); + UInt64 type; + for (;;) + { + type = ReadID(); + if (type == NID::kNumUnpackStream) + { + for (int i = 0; i < folders.Size(); i++) + numUnpackStreamsInFolders.Add(ReadNum()); + continue; + } + if (type == NID::kCRC || type == NID::kSize) + break; + if (type == NID::kEnd) + break; + SkipData(); + } + + if (numUnpackStreamsInFolders.IsEmpty()) + for (int i = 0; i < folders.Size(); i++) + numUnpackStreamsInFolders.Add(1); + + int i; + for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) + { + // v3.13 incorrectly worked with empty folders + // v4.07: we check that folder is empty + CNum numSubstreams = numUnpackStreamsInFolders[i]; + if (numSubstreams == 0) + continue; + UInt64 sum = 0; + for (CNum j = 1; j < numSubstreams; j++) + if (type == NID::kSize) + { + UInt64 size = ReadNumber(); + unpackSizes.Add(size); + sum += size; + } + unpackSizes.Add(folders[i].GetUnpackSize() - sum); + } + if (type == NID::kSize) + type = ReadID(); + + int numDigests = 0; + int numDigestsTotal = 0; + for (i = 0; i < folders.Size(); i++) + { + CNum numSubstreams = numUnpackStreamsInFolders[i]; + if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + numDigests += numSubstreams; + numDigestsTotal += numSubstreams; + } + + for (;;) + { + if (type == NID::kCRC) + { + CBoolVector digestsDefined2; + CRecordVector<UInt32> digests2; + ReadHashDigests(numDigests, digestsDefined2, digests2); + int digestIndex = 0; + for (i = 0; i < folders.Size(); i++) + { + CNum numSubstreams = numUnpackStreamsInFolders[i]; + const CFolder &folder = folders[i]; + if (numSubstreams == 1 && folder.UnpackCRCDefined) + { + digestsDefined.Add(true); + digests.Add(folder.UnpackCRC); + } + else + for (CNum j = 0; j < numSubstreams; j++, digestIndex++) + { + digestsDefined.Add(digestsDefined2[digestIndex]); + digests.Add(digests2[digestIndex]); + } + } + } + else if (type == NID::kEnd) + { + if (digestsDefined.IsEmpty()) + { + BoolVector_Fill_False(digestsDefined, numDigestsTotal); + digests.Clear(); + for (int i = 0; i < numDigestsTotal; i++) + digests.Add(0); + } + return; + } + else + SkipData(); + type = ReadID(); + } +} + +void CInArchive::ReadStreamsInfo( + const CObjectVector<CByteBuffer> *dataVector, + UInt64 &dataOffset, + CRecordVector<UInt64> &packSizes, + CBoolVector &packCRCsDefined, + CRecordVector<UInt32> &packCRCs, + CObjectVector<CFolder> &folders, + CRecordVector<CNum> &numUnpackStreamsInFolders, + CRecordVector<UInt64> &unpackSizes, + CBoolVector &digestsDefined, + CRecordVector<UInt32> &digests) +{ + for (;;) + { + UInt64 type = ReadID(); + if (type > ((UInt32)1 << 30)) + ThrowIncorrect(); + switch((UInt32)type) + { + case NID::kEnd: + return; + case NID::kPackInfo: + { + ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs); + break; + } + case NID::kUnpackInfo: + { + ReadUnpackInfo(dataVector, folders); + break; + } + case NID::kSubStreamsInfo: + { + ReadSubStreamsInfo(folders, numUnpackStreamsInFolders, + unpackSizes, digestsDefined, digests); + break; + } + default: + ThrowIncorrect(); + } + } +} + +void CInArchive::ReadBoolVector(int numItems, CBoolVector &v) +{ + v.Clear(); + v.Reserve(numItems); + Byte b = 0; + Byte mask = 0; + for (int i = 0; i < numItems; i++) + { + if (mask == 0) + { + b = ReadByte(); + mask = 0x80; + } + v.Add((b & mask) != 0); + mask >>= 1; + } +} + +void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v) +{ + Byte allAreDefined = ReadByte(); + if (allAreDefined == 0) + { + ReadBoolVector(numItems, v); + return; + } + v.Clear(); + v.Reserve(numItems); + for (int i = 0; i < numItems; i++) + v.Add(true); +} + +void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector, + CUInt64DefVector &v, int numFiles) +{ + ReadBoolVector2(numFiles, v.Defined); + + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + v.Values.Reserve(numFiles); + + for (int i = 0; i < numFiles; i++) + { + UInt64 t = 0; + if (v.Defined[i]) + t = ReadUInt64(); + v.Values.Add(t); + } +} + +HRESULT CInArchive::ReadAndDecodePackedStreams( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 baseOffset, + UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ) +{ + CRecordVector<UInt64> packSizes; + CBoolVector packCRCsDefined; + CRecordVector<UInt32> packCRCs; + CObjectVector<CFolder> folders; + + CRecordVector<CNum> numUnpackStreamsInFolders; + CRecordVector<UInt64> unpackSizes; + CBoolVector digestsDefined; + CRecordVector<UInt32> digests; + + ReadStreamsInfo(NULL, + dataOffset, + packSizes, + packCRCsDefined, + packCRCs, + folders, + numUnpackStreamsInFolders, + unpackSizes, + digestsDefined, + digests); + + // db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; + + CNum packIndex = 0; + CDecoder decoder( + #ifdef _ST_MODE + false + #else + true + #endif + ); + UInt64 dataStartPos = baseOffset + dataOffset; + for (int i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + dataVector.Add(CByteBuffer()); + CByteBuffer &data = dataVector.Back(); + UInt64 unpackSize64 = folder.GetUnpackSize(); + size_t unpackSize = (size_t)unpackSize64; + if (unpackSize != unpackSize64) + ThrowUnsupported(); + data.SetCapacity(unpackSize); + + CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->Init(data, unpackSize); + + HRESULT result = decoder.Decode( + EXTERNAL_CODECS_LOC_VARS + _stream, dataStartPos, + &packSizes[packIndex], folder, outStream, NULL + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + #if !defined(_7ZIP_ST) && !defined(_SFX) + , false, 1 + #endif + ); + RINOK(result); + + if (folder.UnpackCRCDefined) + if (CrcCalc(data, unpackSize) != folder.UnpackCRC) + ThrowIncorrect(); + for (int j = 0; j < folder.PackStreams.Size(); j++) + { + UInt64 packSize = packSizes[packIndex++]; + dataStartPos += packSize; + HeadersSize += packSize; + } + } + return S_OK; +} + +HRESULT CInArchive::ReadHeader( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ) +{ + UInt64 type = ReadID(); + + if (type == NID::kArchiveProperties) + { + ReadArchiveProperties(db.ArchiveInfo); + type = ReadID(); + } + + CObjectVector<CByteBuffer> dataVector; + + if (type == NID::kAdditionalStreamsInfo) + { + HRESULT result = ReadAndDecodePackedStreams( + EXTERNAL_CODECS_LOC_VARS + db.ArchiveInfo.StartPositionAfterHeader, + db.ArchiveInfo.DataStartPosition2, + dataVector + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + ); + RINOK(result); + db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; + type = ReadID(); + } + + CRecordVector<UInt64> unpackSizes; + CBoolVector digestsDefined; + CRecordVector<UInt32> digests; + + if (type == NID::kMainStreamsInfo) + { + ReadStreamsInfo(&dataVector, + db.ArchiveInfo.DataStartPosition, + db.PackSizes, + db.PackCRCsDefined, + db.PackCRCs, + db.Folders, + db.NumUnpackStreamsVector, + unpackSizes, + digestsDefined, + digests); + db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader; + type = ReadID(); + } + else + { + for (int i = 0; i < db.Folders.Size(); i++) + { + db.NumUnpackStreamsVector.Add(1); + CFolder &folder = db.Folders[i]; + unpackSizes.Add(folder.GetUnpackSize()); + digestsDefined.Add(folder.UnpackCRCDefined); + digests.Add(folder.UnpackCRC); + } + } + + db.Files.Clear(); + + if (type == NID::kEnd) + return S_OK; + if (type != NID::kFilesInfo) + ThrowIncorrect(); + + CNum numFiles = ReadNum(); + db.Files.Reserve(numFiles); + CNum i; + for (i = 0; i < numFiles; i++) + db.Files.Add(CFileItem()); + + db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize); + if (!db.PackSizes.IsEmpty()) + db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo); + if (numFiles > 0 && !digests.IsEmpty()) + db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC); + + CBoolVector emptyStreamVector; + BoolVector_Fill_False(emptyStreamVector, (int)numFiles); + CBoolVector emptyFileVector; + CBoolVector antiFileVector; + CNum numEmptyStreams = 0; + + for (;;) + { + UInt64 type = ReadID(); + if (type == NID::kEnd) + break; + UInt64 size = ReadNumber(); + size_t ppp = _inByteBack->_pos; + bool addPropIdToList = true; + bool isKnownType = true; + if (type > ((UInt32)1 << 30)) + isKnownType = false; + else switch((UInt32)type) + { + case NID::kName: + { + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + for (int i = 0; i < db.Files.Size(); i++) + _inByteBack->ReadString(db.Files[i].Name); + break; + } + case NID::kWinAttributes: + { + CBoolVector boolVector; + ReadBoolVector2(db.Files.Size(), boolVector); + CStreamSwitch streamSwitch; + streamSwitch.Set(this, &dataVector); + for (i = 0; i < numFiles; i++) + { + CFileItem &file = db.Files[i]; + file.AttribDefined = boolVector[i]; + if (file.AttribDefined) + file.Attrib = ReadUInt32(); + } + break; + } + case NID::kEmptyStream: + { + ReadBoolVector(numFiles, emptyStreamVector); + for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) + if (emptyStreamVector[i]) + numEmptyStreams++; + + BoolVector_Fill_False(emptyFileVector, numEmptyStreams); + BoolVector_Fill_False(antiFileVector, numEmptyStreams); + + break; + } + case NID::kEmptyFile: ReadBoolVector(numEmptyStreams, emptyFileVector); break; + case NID::kAnti: ReadBoolVector(numEmptyStreams, antiFileVector); break; + case NID::kStartPos: ReadUInt64DefVector(dataVector, db.StartPos, (int)numFiles); break; + case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break; + case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break; + case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break; + case NID::kDummy: + { + for (UInt64 j = 0; j < size; j++) + if (ReadByte() != 0) + ThrowIncorrect(); + addPropIdToList = false; + break; + } + default: + addPropIdToList = isKnownType = false; + } + if (isKnownType) + { + if(addPropIdToList) + db.ArchiveInfo.FileInfoPopIDs.Add(type); + } + else + SkipData(size); + bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 || + db.ArchiveInfo.Version.Minor > 2); + if (checkRecordsSize && _inByteBack->_pos - ppp != size) + ThrowIncorrect(); + } + + CNum emptyFileIndex = 0; + CNum sizeIndex = 0; + + CNum numAntiItems = 0; + for (i = 0; i < numEmptyStreams; i++) + if (antiFileVector[i]) + numAntiItems++; + + for (i = 0; i < numFiles; i++) + { + CFileItem &file = db.Files[i]; + bool isAnti; + file.HasStream = !emptyStreamVector[i]; + if (file.HasStream) + { + file.IsDir = false; + isAnti = false; + file.Size = unpackSizes[sizeIndex]; + file.Crc = digests[sizeIndex]; + file.CrcDefined = digestsDefined[sizeIndex]; + sizeIndex++; + } + else + { + file.IsDir = !emptyFileVector[emptyFileIndex]; + isAnti = antiFileVector[emptyFileIndex]; + emptyFileIndex++; + file.Size = 0; + file.CrcDefined = false; + } + if (numAntiItems != 0) + db.IsAnti.Add(isAnti); + } + return S_OK; +} + + +void CArchiveDatabaseEx::FillFolderStartPackStream() +{ + FolderStartPackStreamIndex.Clear(); + FolderStartPackStreamIndex.Reserve(Folders.Size()); + CNum startPos = 0; + for (int i = 0; i < Folders.Size(); i++) + { + FolderStartPackStreamIndex.Add(startPos); + startPos += (CNum)Folders[i].PackStreams.Size(); + } +} + +void CArchiveDatabaseEx::FillStartPos() +{ + PackStreamStartPositions.Clear(); + PackStreamStartPositions.Reserve(PackSizes.Size()); + UInt64 startPos = 0; + for (int i = 0; i < PackSizes.Size(); i++) + { + PackStreamStartPositions.Add(startPos); + startPos += PackSizes[i]; + } +} + +void CArchiveDatabaseEx::FillFolderStartFileIndex() +{ + FolderStartFileIndex.Clear(); + FolderStartFileIndex.Reserve(Folders.Size()); + FileIndexToFolderIndexMap.Clear(); + FileIndexToFolderIndexMap.Reserve(Files.Size()); + + int folderIndex = 0; + CNum indexInFolder = 0; + for (int i = 0; i < Files.Size(); i++) + { + const CFileItem &file = Files[i]; + bool emptyStream = !file.HasStream; + if (emptyStream && indexInFolder == 0) + { + FileIndexToFolderIndexMap.Add(kNumNoIndex); + continue; + } + if (indexInFolder == 0) + { + // v3.13 incorrectly worked with empty folders + // v4.07: Loop for skipping empty folders + for (;;) + { + if (folderIndex >= Folders.Size()) + ThrowIncorrect(); + FolderStartFileIndex.Add(i); // check it + if (NumUnpackStreamsVector[folderIndex] != 0) + break; + folderIndex++; + } + } + FileIndexToFolderIndexMap.Add(folderIndex); + if (emptyStream) + continue; + indexInFolder++; + if (indexInFolder >= NumUnpackStreamsVector[folderIndex]) + { + folderIndex++; + indexInFolder = 0; + } + } +} + +HRESULT CInArchive::ReadDatabase2( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ) +{ + db.Clear(); + db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; + + db.ArchiveInfo.Version.Major = _header[6]; + db.ArchiveInfo.Version.Minor = _header[7]; + + if (db.ArchiveInfo.Version.Major != kMajorVersion) + ThrowUnsupportedVersion(); + + UInt32 crcFromArchive = Get32(_header + 8); + UInt64 nextHeaderOffset = Get64(_header + 0xC); + UInt64 nextHeaderSize = Get64(_header + 0x14); + UInt32 nextHeaderCRC = Get32(_header + 0x1C); + UInt32 crc = CrcCalc(_header + 0xC, 20); + + #ifdef FORMAT_7Z_RECOVERY + if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) + { + UInt64 cur, cur2; + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); + const int kCheckSize = 500; + Byte buf[kCheckSize]; + RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2)); + int checkSize = kCheckSize; + if (cur2 - cur < kCheckSize) + checkSize = (int)(cur2 - cur); + RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2)); + + RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); + + int i; + for (i = (int)checkSize - 2; i >= 0; i--) + if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04) + break; + if (i < 0) + return S_FALSE; + nextHeaderSize = checkSize - i; + nextHeaderOffset = cur2 - cur + i; + nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); + RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); + } + else + #endif + { + if (crc != crcFromArchive) + ThrowIncorrect(); + } + + db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; + + if (nextHeaderSize == 0) + return S_OK; + + if (nextHeaderSize > (UInt64)0xFFFFFFFF) + return S_FALSE; + + if ((Int64)nextHeaderOffset < 0) + return S_FALSE; + + RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); + + CByteBuffer buffer2; + buffer2.SetCapacity((size_t)nextHeaderSize); + + RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize)); + HeadersSize += kHeaderSize + nextHeaderSize; + db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; + + if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC) + ThrowIncorrect(); + + CStreamSwitch streamSwitch; + streamSwitch.Set(this, buffer2); + + CObjectVector<CByteBuffer> dataVector; + + UInt64 type = ReadID(); + if (type != NID::kHeader) + { + if (type != NID::kEncodedHeader) + ThrowIncorrect(); + HRESULT result = ReadAndDecodePackedStreams( + EXTERNAL_CODECS_LOC_VARS + db.ArchiveInfo.StartPositionAfterHeader, + db.ArchiveInfo.DataStartPosition2, + dataVector + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + ); + RINOK(result); + if (dataVector.Size() == 0) + return S_OK; + if (dataVector.Size() > 1) + ThrowIncorrect(); + streamSwitch.Remove(); + streamSwitch.Set(this, dataVector.Front()); + if (ReadID() != NID::kHeader) + ThrowIncorrect(); + } + + db.HeadersSize = HeadersSize; + + return ReadHeader( + EXTERNAL_CODECS_LOC_VARS + db + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + ); +} + +HRESULT CInArchive::ReadDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ) +{ + try + { + return ReadDatabase2( + EXTERNAL_CODECS_LOC_VARS db + #ifndef _NO_CRYPTO + , getTextPassword, passwordIsDefined + #endif + ); + } + catch(CInArchiveException &) { return S_FALSE; } +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h new file mode 100644 index 000000000..971f27b2a --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h @@ -0,0 +1,245 @@ +// 7zIn.h + +#ifndef __7Z_IN_H +#define __7Z_IN_H + +#include "../../../Common/MyCom.h" + +#include "../../IPassword.h" +#include "../../IStream.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/InBuffer.h" + +#include "7zItem.h" + +namespace NArchive { +namespace N7z { + +struct CInArchiveInfo +{ + CArchiveVersion Version; + UInt64 StartPosition; + UInt64 StartPositionAfterHeader; + UInt64 DataStartPosition; + UInt64 DataStartPosition2; + CRecordVector<UInt64> FileInfoPopIDs; + void Clear() + { + FileInfoPopIDs.Clear(); + } +}; + +struct CArchiveDatabaseEx: public CArchiveDatabase +{ + CInArchiveInfo ArchiveInfo; + CRecordVector<UInt64> PackStreamStartPositions; + CRecordVector<CNum> FolderStartPackStreamIndex; + CRecordVector<CNum> FolderStartFileIndex; + CRecordVector<CNum> FileIndexToFolderIndexMap; + + UInt64 HeadersSize; + UInt64 PhySize; + + void Clear() + { + CArchiveDatabase::Clear(); + ArchiveInfo.Clear(); + PackStreamStartPositions.Clear(); + FolderStartPackStreamIndex.Clear(); + FolderStartFileIndex.Clear(); + FileIndexToFolderIndexMap.Clear(); + + HeadersSize = 0; + PhySize = 0; + } + + void FillFolderStartPackStream(); + void FillStartPos(); + void FillFolderStartFileIndex(); + + void Fill() + { + FillFolderStartPackStream(); + FillStartPos(); + FillFolderStartFileIndex(); + } + + UInt64 GetFolderStreamPos(int folderIndex, int indexInFolder) const + { + return ArchiveInfo.DataStartPosition + + PackStreamStartPositions[FolderStartPackStreamIndex[folderIndex] + indexInFolder]; + } + + UInt64 GetFolderFullPackSize(int folderIndex) const + { + CNum packStreamIndex = FolderStartPackStreamIndex[folderIndex]; + const CFolder &folder = Folders[folderIndex]; + UInt64 size = 0; + for (int i = 0; i < folder.PackStreams.Size(); i++) + size += PackSizes[packStreamIndex + i]; + return size; + } + + UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const + { + return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex]; + } + + UInt64 GetFilePackSize(CNum fileIndex) const + { + CNum folderIndex = FileIndexToFolderIndexMap[fileIndex]; + if (folderIndex != kNumNoIndex) + if (FolderStartFileIndex[folderIndex] == fileIndex) + return GetFolderFullPackSize(folderIndex); + return 0; + } +}; + +class CInByte2 +{ + const Byte *_buffer; + size_t _size; +public: + size_t _pos; + void Init(const Byte *buffer, size_t size) + { + _buffer = buffer; + _size = size; + _pos = 0; + } + Byte ReadByte(); + void ReadBytes(Byte *data, size_t size); + void SkipData(UInt64 size); + void SkipData(); + UInt64 ReadNumber(); + CNum ReadNum(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + void ReadString(UString &s); +}; + +class CStreamSwitch; + +const UInt32 kHeaderSize = 32; + +class CInArchive +{ + friend class CStreamSwitch; + + CMyComPtr<IInStream> _stream; + + CObjectVector<CInByte2> _inByteVector; + CInByte2 *_inByteBack; + + UInt64 _arhiveBeginStreamPosition; + + Byte _header[kHeaderSize]; + + UInt64 HeadersSize; + + void AddByteStream(const Byte *buffer, size_t size) + { + _inByteVector.Add(CInByte2()); + _inByteBack = &_inByteVector.Back(); + _inByteBack->Init(buffer, size); + } + + void DeleteByteStream() + { + _inByteVector.DeleteBack(); + if (!_inByteVector.IsEmpty()) + _inByteBack = &_inByteVector.Back(); + } + +private: + HRESULT FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + + void ReadBytes(Byte *data, size_t size) { _inByteBack->ReadBytes(data, size); } + Byte ReadByte() { return _inByteBack->ReadByte(); } + UInt64 ReadNumber() { return _inByteBack->ReadNumber(); } + CNum ReadNum() { return _inByteBack->ReadNum(); } + UInt64 ReadID() { return _inByteBack->ReadNumber(); } + UInt32 ReadUInt32() { return _inByteBack->ReadUInt32(); } + UInt64 ReadUInt64() { return _inByteBack->ReadUInt64(); } + void SkipData(UInt64 size) { _inByteBack->SkipData(size); } + void SkipData() { _inByteBack->SkipData(); } + void WaitAttribute(UInt64 attribute); + + void ReadArchiveProperties(CInArchiveInfo &archiveInfo); + void GetNextFolderItem(CFolder &itemInfo); + void ReadHashDigests(int numItems, + CBoolVector &digestsDefined, CRecordVector<UInt32> &digests); + + void ReadPackInfo( + UInt64 &dataOffset, + CRecordVector<UInt64> &packSizes, + CBoolVector &packCRCsDefined, + CRecordVector<UInt32> &packCRCs); + + void ReadUnpackInfo( + const CObjectVector<CByteBuffer> *dataVector, + CObjectVector<CFolder> &folders); + + void ReadSubStreamsInfo( + const CObjectVector<CFolder> &folders, + CRecordVector<CNum> &numUnpackStreamsInFolders, + CRecordVector<UInt64> &unpackSizes, + CBoolVector &digestsDefined, + CRecordVector<UInt32> &digests); + + void ReadStreamsInfo( + const CObjectVector<CByteBuffer> *dataVector, + UInt64 &dataOffset, + CRecordVector<UInt64> &packSizes, + CBoolVector &packCRCsDefined, + CRecordVector<UInt32> &packCRCs, + CObjectVector<CFolder> &folders, + CRecordVector<CNum> &numUnpackStreamsInFolders, + CRecordVector<UInt64> &unpackSizes, + CBoolVector &digestsDefined, + CRecordVector<UInt32> &digests); + + + void ReadBoolVector(int numItems, CBoolVector &v); + void ReadBoolVector2(int numItems, CBoolVector &v); + void ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector, + CUInt64DefVector &v, int numFiles); + HRESULT ReadAndDecodePackedStreams( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 baseOffset, UInt64 &dataOffset, + CObjectVector<CByteBuffer> &dataVector + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ); + HRESULT ReadHeader( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ); + HRESULT ReadDatabase2( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ); +public: + HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); // S_FALSE means is not archive + void Close(); + + HRESULT ReadDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + CArchiveDatabaseEx &db + #ifndef _NO_CRYPTO + ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined + #endif + ); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h new file mode 100644 index 000000000..34f10775c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h @@ -0,0 +1,268 @@ +// 7zItem.h + +#ifndef __7Z_ITEM_H +#define __7Z_ITEM_H + +#include "../../../Common/Buffer.h" +#include "../../../Common/MyString.h" + +#include "../../Common/MethodId.h" + +#include "7zHeader.h" + +namespace NArchive { +namespace N7z { + +const UInt64 k_AES = 0x06F10701; + +typedef UInt32 CNum; +const CNum kNumMax = 0x7FFFFFFF; +const CNum kNumNoIndex = 0xFFFFFFFF; + +struct CCoderInfo +{ + CMethodId MethodID; + CByteBuffer Props; + CNum NumInStreams; + CNum NumOutStreams; + bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); } +}; + +struct CBindPair +{ + CNum InIndex; + CNum OutIndex; +}; + +struct CFolder +{ + CObjectVector<CCoderInfo> Coders; + CRecordVector<CBindPair> BindPairs; + CRecordVector<CNum> PackStreams; + CRecordVector<UInt64> UnpackSizes; + UInt32 UnpackCRC; + bool UnpackCRCDefined; + + CFolder(): UnpackCRCDefined(false) {} + + UInt64 GetUnpackSize() const // test it + { + if (UnpackSizes.IsEmpty()) + return 0; + for (int i = UnpackSizes.Size() - 1; i >= 0; i--) + if (FindBindPairForOutStream(i) < 0) + return UnpackSizes[i]; + throw 1; + } + + CNum GetNumOutStreams() const + { + CNum result = 0; + for (int i = 0; i < Coders.Size(); i++) + result += Coders[i].NumOutStreams; + return result; + } + + int FindBindPairForInStream(CNum inStreamIndex) const + { + for(int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].InIndex == inStreamIndex) + return i; + return -1; + } + int FindBindPairForOutStream(CNum outStreamIndex) const + { + for(int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].OutIndex == outStreamIndex) + return i; + return -1; + } + int FindPackStreamArrayIndex(CNum inStreamIndex) const + { + for(int i = 0; i < PackStreams.Size(); i++) + if (PackStreams[i] == inStreamIndex) + return i; + return -1; + } + + bool IsEncrypted() const + { + for (int i = Coders.Size() - 1; i >= 0; i--) + if (Coders[i].MethodID == k_AES) + return true; + return false; + } + + bool CheckStructure() const; +}; + +struct CUInt64DefVector +{ + CRecordVector<UInt64> Values; + CRecordVector<bool> Defined; + + void Clear() + { + Values.Clear(); + Defined.Clear(); + } + + void ReserveDown() + { + Values.ReserveDown(); + Values.ReserveDown(); + } + + bool GetItem(int index, UInt64 &value) const + { + if (index < Defined.Size() && Defined[index]) + { + value = Values[index]; + return true; + } + value = 0; + return false; + } + + void SetItem(int index, bool defined, UInt64 value) + { + while (index >= Defined.Size()) + Defined.Add(false); + Defined[index] = defined; + if (!defined) + return; + while (index >= Values.Size()) + Values.Add(0); + Values[index] = value; + } + + bool CheckSize(int size) const { return Defined.Size() == size || Defined.Size() == 0; } +}; + +struct CFileItem +{ + UInt64 Size; + UInt32 Attrib; + UInt32 Crc; + UString Name; + + bool HasStream; // Test it !!! it means that there is + // stream in some folder. It can be empty stream + bool IsDir; + bool CrcDefined; + bool AttribDefined; + + CFileItem(): + HasStream(true), + IsDir(false), + CrcDefined(false), + AttribDefined(false) + {} + void SetAttrib(UInt32 attrib) + { + AttribDefined = true; + Attrib = attrib; + } +}; + +struct CFileItem2 +{ + UInt64 CTime; + UInt64 ATime; + UInt64 MTime; + UInt64 StartPos; + bool CTimeDefined; + bool ATimeDefined; + bool MTimeDefined; + bool StartPosDefined; + bool IsAnti; +}; + +struct CArchiveDatabase +{ + CRecordVector<UInt64> PackSizes; + CRecordVector<bool> PackCRCsDefined; + CRecordVector<UInt32> PackCRCs; + CObjectVector<CFolder> Folders; + CRecordVector<CNum> NumUnpackStreamsVector; + CObjectVector<CFileItem> Files; + + CUInt64DefVector CTime; + CUInt64DefVector ATime; + CUInt64DefVector MTime; + CUInt64DefVector StartPos; + CRecordVector<bool> IsAnti; + + void Clear() + { + PackSizes.Clear(); + PackCRCsDefined.Clear(); + PackCRCs.Clear(); + Folders.Clear(); + NumUnpackStreamsVector.Clear(); + Files.Clear(); + CTime.Clear(); + ATime.Clear(); + MTime.Clear(); + StartPos.Clear(); + IsAnti.Clear(); + } + + void ReserveDown() + { + PackSizes.ReserveDown(); + PackCRCsDefined.ReserveDown(); + PackCRCs.ReserveDown(); + Folders.ReserveDown(); + NumUnpackStreamsVector.ReserveDown(); + Files.ReserveDown(); + CTime.ReserveDown(); + ATime.ReserveDown(); + MTime.ReserveDown(); + StartPos.ReserveDown(); + IsAnti.ReserveDown(); + } + + bool IsEmpty() const + { + return (PackSizes.IsEmpty() && + PackCRCsDefined.IsEmpty() && + PackCRCs.IsEmpty() && + Folders.IsEmpty() && + NumUnpackStreamsVector.IsEmpty() && + Files.IsEmpty()); + } + + bool CheckNumFiles() const + { + int size = Files.Size(); + return ( + CTime.CheckSize(size) && + ATime.CheckSize(size) && + MTime.CheckSize(size) && + StartPos.CheckSize(size) && + (size == IsAnti.Size() || IsAnti.Size() == 0)); + } + + bool IsSolid() const + { + for (int i = 0; i < NumUnpackStreamsVector.Size(); i++) + if (NumUnpackStreamsVector[i] > 1) + return true; + return false; + } + bool IsItemAnti(int index) const { return (index < IsAnti.Size() && IsAnti[index]); } + void SetItemAnti(int index, bool isAnti) + { + while (index >= IsAnti.Size()) + IsAnti.Add(false); + IsAnti[index] = isAnti; + } + + void GetFile(int index, CFileItem &file, CFileItem2 &file2) const; + void AddFile(const CFileItem &file, const CFileItem2 &file2); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp new file mode 100644 index 000000000..0c8aa7e8c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp @@ -0,0 +1,866 @@ +// 7zOut.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" + +#include "../../../Common/AutoPtr.h" + +#include "../../Common/StreamObjects.h" + +#include "7zOut.h" + +static HRESULT WriteBytes(ISequentialOutStream *stream, const void *data, size_t size) +{ + while (size > 0) + { + UInt32 curSize = (UInt32)MyMin(size, (size_t)0xFFFFFFFF); + UInt32 processedSize; + RINOK(stream->Write(data, curSize, &processedSize)); + if (processedSize == 0) + return E_FAIL; + data = (const void *)((const Byte *)data + processedSize); + size -= processedSize; + } + return S_OK; +} + +namespace NArchive { +namespace N7z { + +HRESULT COutArchive::WriteDirect(const void *data, UInt32 size) +{ + return ::WriteBytes(SeqStream, data, size); +} + +HRESULT COutArchive::WriteSignature() +{ + Byte buf[8]; + memcpy(buf, kSignature, kSignatureSize); + buf[kSignatureSize] = kMajorVersion; + buf[kSignatureSize + 1] = 3; + return WriteDirect(buf, 8); +} + +#ifdef _7Z_VOL +HRESULT COutArchive::WriteFinishSignature() +{ + RINOK(WriteDirect(kFinishSignature, kSignatureSize)); + CArchiveVersion av; + av.Major = kMajorVersion; + av.Minor = 2; + RINOK(WriteDirectByte(av.Major)); + return WriteDirectByte(av.Minor); +} +#endif + +static void SetUInt32(Byte *p, UInt32 d) +{ + for (int i = 0; i < 4; i++, d >>= 8) + p[i] = (Byte)d; +} + +static void SetUInt64(Byte *p, UInt64 d) +{ + for (int i = 0; i < 8; i++, d >>= 8) + p[i] = (Byte)d; +} + +HRESULT COutArchive::WriteStartHeader(const CStartHeader &h) +{ + Byte buf[24]; + SetUInt64(buf + 4, h.NextHeaderOffset); + SetUInt64(buf + 12, h.NextHeaderSize); + SetUInt32(buf + 20, h.NextHeaderCRC); + SetUInt32(buf, CrcCalc(buf + 4, 20)); + return WriteDirect(buf, 24); +} + +#ifdef _7Z_VOL +HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h) +{ + CCRC crc; + crc.UpdateUInt64(h.NextHeaderOffset); + crc.UpdateUInt64(h.NextHeaderSize); + crc.UpdateUInt32(h.NextHeaderCRC); + crc.UpdateUInt64(h.ArchiveStartOffset); + crc.UpdateUInt64(h.AdditionalStartBlockSize); + RINOK(WriteDirectUInt32(crc.GetDigest())); + RINOK(WriteDirectUInt64(h.NextHeaderOffset)); + RINOK(WriteDirectUInt64(h.NextHeaderSize)); + RINOK(WriteDirectUInt32(h.NextHeaderCRC)); + RINOK(WriteDirectUInt64(h.ArchiveStartOffset)); + return WriteDirectUInt64(h.AdditionalStartBlockSize); +} +#endif + +HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker) +{ + Close(); + #ifdef _7Z_VOL + // endMarker = false; + _endMarker = endMarker; + #endif + SeqStream = stream; + if (!endMarker) + { + SeqStream.QueryInterface(IID_IOutStream, &Stream); + if (!Stream) + { + return E_NOTIMPL; + // endMarker = true; + } + } + #ifdef _7Z_VOL + if (endMarker) + { + /* + CStartHeader sh; + sh.NextHeaderOffset = (UInt32)(Int32)-1; + sh.NextHeaderSize = (UInt32)(Int32)-1; + sh.NextHeaderCRC = 0; + WriteStartHeader(sh); + */ + } + else + #endif + { + if (!Stream) + return E_FAIL; + RINOK(WriteSignature()); + RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos)); + } + return S_OK; +} + +void COutArchive::Close() +{ + SeqStream.Release(); + Stream.Release(); +} + +HRESULT COutArchive::SkipPrefixArchiveHeader() +{ + #ifdef _7Z_VOL + if (_endMarker) + return S_OK; + #endif + return Stream->Seek(24, STREAM_SEEK_CUR, NULL); +} + +UInt64 COutArchive::GetPos() const +{ + if (_countMode) + return _countSize; + if (_writeToStream) + return _outByte.GetProcessedSize(); + return _outByte2.GetPos(); +} + +void COutArchive::WriteBytes(const void *data, size_t size) +{ + if (_countMode) + _countSize += size; + else if (_writeToStream) + { + _outByte.WriteBytes(data, size); + _crc = CrcUpdate(_crc, data, size); + } + else + _outByte2.WriteBytes(data, size); +} + +void COutArchive::WriteByte(Byte b) +{ + if (_countMode) + _countSize++; + else if (_writeToStream) + { + _outByte.WriteByte(b); + _crc = CRC_UPDATE_BYTE(_crc, b); + } + else + _outByte2.WriteByte(b); +} + +void COutArchive::WriteUInt32(UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteUInt64(UInt64 value) +{ + for (int i = 0; i < 8; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteNumber(UInt64 value) +{ + Byte firstByte = 0; + Byte mask = 0x80; + int i; + for (i = 0; i < 8; i++) + { + if (value < ((UInt64(1) << ( 7 * (i + 1))))) + { + firstByte |= Byte(value >> (8 * i)); + break; + } + firstByte |= mask; + mask >>= 1; + } + WriteByte(firstByte); + for (;i > 0; i--) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +static UInt32 GetBigNumberSize(UInt64 value) +{ + int i; + for (i = 1; i < 9; i++) + if (value < (((UInt64)1 << (i * 7)))) + break; + return i; +} + +#ifdef _7Z_VOL +UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props) +{ + UInt32 result = GetBigNumberSize(dataSize) * 2 + 41; + if (nameLength != 0) + { + nameLength = (nameLength + 1) * 2; + result += nameLength + GetBigNumberSize(nameLength) + 2; + } + if (props) + { + result += 20; + } + if (result >= 128) + result++; + result += kSignatureSize + 2 + kFinishHeaderSize; + return result; +} + +UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props) +{ + UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props); + int testSize; + if (volSize > headersSizeBase) + testSize = volSize - headersSizeBase; + else + testSize = 1; + UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props); + UInt64 pureSize = 1; + if (volSize > headersSize) + pureSize = volSize - headersSize; + return pureSize; +} +#endif + +void COutArchive::WriteFolder(const CFolder &folder) +{ + WriteNumber(folder.Coders.Size()); + int i; + for (i = 0; i < folder.Coders.Size(); i++) + { + const CCoderInfo &coder = folder.Coders[i]; + { + size_t propsSize = coder.Props.GetCapacity(); + + UInt64 id = coder.MethodID; + int idSize; + for (idSize = 1; idSize < sizeof(id); idSize++) + if ((id >> (8 * idSize)) == 0) + break; + BYTE longID[15]; + for (int t = idSize - 1; t >= 0 ; t--, id >>= 8) + longID[t] = (Byte)(id & 0xFF); + Byte b; + b = (Byte)(idSize & 0xF); + bool isComplex = !coder.IsSimpleCoder(); + b |= (isComplex ? 0x10 : 0); + b |= ((propsSize != 0) ? 0x20 : 0 ); + WriteByte(b); + WriteBytes(longID, idSize); + if (isComplex) + { + WriteNumber(coder.NumInStreams); + WriteNumber(coder.NumOutStreams); + } + if (propsSize == 0) + continue; + WriteNumber(propsSize); + WriteBytes(coder.Props, propsSize); + } + } + for (i = 0; i < folder.BindPairs.Size(); i++) + { + const CBindPair &bindPair = folder.BindPairs[i]; + WriteNumber(bindPair.InIndex); + WriteNumber(bindPair.OutIndex); + } + if (folder.PackStreams.Size() > 1) + for (i = 0; i < folder.PackStreams.Size(); i++) + { + WriteNumber(folder.PackStreams[i]); + } +} + +void COutArchive::WriteBoolVector(const CBoolVector &boolVector) +{ + Byte b = 0; + Byte mask = 0x80; + for (int i = 0; i < boolVector.Size(); i++) + { + if (boolVector[i]) + b |= mask; + mask >>= 1; + if (mask == 0) + { + WriteByte(b); + mask = 0x80; + b = 0; + } + } + if (mask != 0x80) + WriteByte(b); +} + + +void COutArchive::WriteHashDigests( + const CRecordVector<bool> &digestsDefined, + const CRecordVector<UInt32> &digests) +{ + int numDefined = 0; + int i; + for (i = 0; i < digestsDefined.Size(); i++) + if (digestsDefined[i]) + numDefined++; + if (numDefined == 0) + return; + + WriteByte(NID::kCRC); + if (numDefined == digestsDefined.Size()) + WriteByte(1); + else + { + WriteByte(0); + WriteBoolVector(digestsDefined); + } + for (i = 0; i < digests.Size(); i++) + if (digestsDefined[i]) + WriteUInt32(digests[i]); +} + +void COutArchive::WritePackInfo( + UInt64 dataOffset, + const CRecordVector<UInt64> &packSizes, + const CRecordVector<bool> &packCRCsDefined, + const CRecordVector<UInt32> &packCRCs) +{ + if (packSizes.IsEmpty()) + return; + WriteByte(NID::kPackInfo); + WriteNumber(dataOffset); + WriteNumber(packSizes.Size()); + WriteByte(NID::kSize); + for (int i = 0; i < packSizes.Size(); i++) + WriteNumber(packSizes[i]); + + WriteHashDigests(packCRCsDefined, packCRCs); + + WriteByte(NID::kEnd); +} + +void COutArchive::WriteUnpackInfo(const CObjectVector<CFolder> &folders) +{ + if (folders.IsEmpty()) + return; + + WriteByte(NID::kUnpackInfo); + + WriteByte(NID::kFolder); + WriteNumber(folders.Size()); + { + WriteByte(0); + for (int i = 0; i < folders.Size(); i++) + WriteFolder(folders[i]); + } + + WriteByte(NID::kCodersUnpackSize); + int i; + for (i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + for (int j = 0; j < folder.UnpackSizes.Size(); j++) + WriteNumber(folder.UnpackSizes[j]); + } + + CRecordVector<bool> unpackCRCsDefined; + CRecordVector<UInt32> unpackCRCs; + for (i = 0; i < folders.Size(); i++) + { + const CFolder &folder = folders[i]; + unpackCRCsDefined.Add(folder.UnpackCRCDefined); + unpackCRCs.Add(folder.UnpackCRC); + } + WriteHashDigests(unpackCRCsDefined, unpackCRCs); + + WriteByte(NID::kEnd); +} + +void COutArchive::WriteSubStreamsInfo( + const CObjectVector<CFolder> &folders, + const CRecordVector<CNum> &numUnpackStreamsInFolders, + const CRecordVector<UInt64> &unpackSizes, + const CRecordVector<bool> &digestsDefined, + const CRecordVector<UInt32> &digests) +{ + WriteByte(NID::kSubStreamsInfo); + + int i; + for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) + { + if (numUnpackStreamsInFolders[i] != 1) + { + WriteByte(NID::kNumUnpackStream); + for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) + WriteNumber(numUnpackStreamsInFolders[i]); + break; + } + } + + + bool needFlag = true; + CNum index = 0; + for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) + for (CNum j = 0; j < numUnpackStreamsInFolders[i]; j++) + { + if (j + 1 != numUnpackStreamsInFolders[i]) + { + if (needFlag) + WriteByte(NID::kSize); + needFlag = false; + WriteNumber(unpackSizes[index]); + } + index++; + } + + CRecordVector<bool> digestsDefined2; + CRecordVector<UInt32> digests2; + + int digestIndex = 0; + for (i = 0; i < folders.Size(); i++) + { + int numSubStreams = (int)numUnpackStreamsInFolders[i]; + if (numSubStreams == 1 && folders[i].UnpackCRCDefined) + digestIndex++; + else + for (int j = 0; j < numSubStreams; j++, digestIndex++) + { + digestsDefined2.Add(digestsDefined[digestIndex]); + digests2.Add(digests[digestIndex]); + } + } + WriteHashDigests(digestsDefined2, digests2); + WriteByte(NID::kEnd); +} + +void COutArchive::SkipAlign(unsigned /* pos */, unsigned /* alignSize */) +{ + return; +} + +/* +7-Zip 4.50 - 4.58 contain BUG, so they do not support .7z archives with Unknown field. + +void COutArchive::SkipAlign(unsigned pos, unsigned alignSize) +{ + pos += (unsigned)GetPos(); + pos &= (alignSize - 1); + if (pos == 0) + return; + unsigned skip = alignSize - pos; + if (skip < 2) + skip += alignSize; + skip -= 2; + WriteByte(NID::kDummy); + WriteByte((Byte)skip); + for (unsigned i = 0; i < skip; i++) + WriteByte(0); +} +*/ + +static inline unsigned Bv_GetSizeInBytes(const CBoolVector &v) { return ((unsigned)v.Size() + 7) / 8; } + +void COutArchive::WriteAlignedBoolHeader(const CBoolVector &v, int numDefined, Byte type, unsigned itemSize) +{ + const unsigned bvSize = (numDefined == v.Size()) ? 0 : Bv_GetSizeInBytes(v); + const UInt64 dataSize = (UInt64)numDefined * itemSize + bvSize + 2; + SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize); + + WriteByte(type); + WriteNumber(dataSize); + if (numDefined == v.Size()) + WriteByte(1); + else + { + WriteByte(0); + WriteBoolVector(v); + } + WriteByte(0); +} + +void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type) +{ + int numDefined = 0; + + int i; + for (i = 0; i < v.Defined.Size(); i++) + if (v.Defined[i]) + numDefined++; + + if (numDefined == 0) + return; + + WriteAlignedBoolHeader(v.Defined, numDefined, type, 8); + + for (i = 0; i < v.Defined.Size(); i++) + if (v.Defined[i]) + WriteUInt64(v.Values[i]); +} + +HRESULT COutArchive::EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const CByteBuffer &data, + CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders) +{ + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> stream = streamSpec; + streamSpec->Init(data, data.GetCapacity()); + CFolder folderItem; + folderItem.UnpackCRCDefined = true; + folderItem.UnpackCRC = CrcCalc(data, data.GetCapacity()); + UInt64 dataSize64 = data.GetCapacity(); + RINOK(encoder.Encode( + EXTERNAL_CODECS_LOC_VARS + stream, NULL, &dataSize64, folderItem, SeqStream, packSizes, NULL)) + folders.Add(folderItem); + return S_OK; +} + +void COutArchive::WriteHeader( + const CArchiveDatabase &db, + const CHeaderOptions &headerOptions, + UInt64 &headerOffset) +{ + int i; + + UInt64 packedSize = 0; + for (i = 0; i < db.PackSizes.Size(); i++) + packedSize += db.PackSizes[i]; + + headerOffset = packedSize; + + WriteByte(NID::kHeader); + + // Archive Properties + + if (db.Folders.Size() > 0) + { + WriteByte(NID::kMainStreamsInfo); + WritePackInfo(0, db.PackSizes, + db.PackCRCsDefined, + db.PackCRCs); + + WriteUnpackInfo(db.Folders); + + CRecordVector<UInt64> unpackSizes; + CRecordVector<bool> digestsDefined; + CRecordVector<UInt32> digests; + for (i = 0; i < db.Files.Size(); i++) + { + const CFileItem &file = db.Files[i]; + if (!file.HasStream) + continue; + unpackSizes.Add(file.Size); + digestsDefined.Add(file.CrcDefined); + digests.Add(file.Crc); + } + + WriteSubStreamsInfo( + db.Folders, + db.NumUnpackStreamsVector, + unpackSizes, + digestsDefined, + digests); + WriteByte(NID::kEnd); + } + + if (db.Files.IsEmpty()) + { + WriteByte(NID::kEnd); + return; + } + + WriteByte(NID::kFilesInfo); + WriteNumber(db.Files.Size()); + + { + /* ---------- Empty Streams ---------- */ + CBoolVector emptyStreamVector; + emptyStreamVector.Reserve(db.Files.Size()); + int numEmptyStreams = 0; + for (i = 0; i < db.Files.Size(); i++) + if (db.Files[i].HasStream) + emptyStreamVector.Add(false); + else + { + emptyStreamVector.Add(true); + numEmptyStreams++; + } + if (numEmptyStreams > 0) + { + WriteByte(NID::kEmptyStream); + WriteNumber(Bv_GetSizeInBytes(emptyStreamVector)); + WriteBoolVector(emptyStreamVector); + + CBoolVector emptyFileVector, antiVector; + emptyFileVector.Reserve(numEmptyStreams); + antiVector.Reserve(numEmptyStreams); + CNum numEmptyFiles = 0, numAntiItems = 0; + for (i = 0; i < db.Files.Size(); i++) + { + const CFileItem &file = db.Files[i]; + if (!file.HasStream) + { + emptyFileVector.Add(!file.IsDir); + if (!file.IsDir) + numEmptyFiles++; + bool isAnti = db.IsItemAnti(i); + antiVector.Add(isAnti); + if (isAnti) + numAntiItems++; + } + } + + if (numEmptyFiles > 0) + { + WriteByte(NID::kEmptyFile); + WriteNumber(Bv_GetSizeInBytes(emptyFileVector)); + WriteBoolVector(emptyFileVector); + } + + if (numAntiItems > 0) + { + WriteByte(NID::kAnti); + WriteNumber(Bv_GetSizeInBytes(antiVector)); + WriteBoolVector(antiVector); + } + } + } + + + { + /* ---------- Names ---------- */ + + int numDefined = 0; + size_t namesDataSize = 0; + for (int i = 0; i < db.Files.Size(); i++) + { + const UString &name = db.Files[i].Name; + if (!name.IsEmpty()) + numDefined++; + namesDataSize += (name.Length() + 1) * 2; + } + + if (numDefined > 0) + { + namesDataSize++; + SkipAlign(2 + GetBigNumberSize(namesDataSize), 2); + + WriteByte(NID::kName); + WriteNumber(namesDataSize); + WriteByte(0); + for (int i = 0; i < db.Files.Size(); i++) + { + const UString &name = db.Files[i].Name; + for (int t = 0; t <= name.Length(); t++) + { + wchar_t c = name[t]; + WriteByte((Byte)c); + WriteByte((Byte)(c >> 8)); + } + } + } + } + + if (headerOptions.WriteCTime) WriteUInt64DefVector(db.CTime, NID::kCTime); + if (headerOptions.WriteATime) WriteUInt64DefVector(db.ATime, NID::kATime); + if (headerOptions.WriteMTime) WriteUInt64DefVector(db.MTime, NID::kMTime); + WriteUInt64DefVector(db.StartPos, NID::kStartPos); + + { + /* ---------- Write Attrib ---------- */ + CBoolVector boolVector; + boolVector.Reserve(db.Files.Size()); + int numDefined = 0; + for (i = 0; i < db.Files.Size(); i++) + { + bool defined = db.Files[i].AttribDefined; + boolVector.Add(defined); + if (defined) + numDefined++; + } + if (numDefined > 0) + { + WriteAlignedBoolHeader(boolVector, numDefined, NID::kWinAttributes, 4); + for (i = 0; i < db.Files.Size(); i++) + { + const CFileItem &file = db.Files[i]; + if (file.AttribDefined) + WriteUInt32(file.Attrib); + } + } + } + + WriteByte(NID::kEnd); // for files + WriteByte(NID::kEnd); // for headers +} + +HRESULT COutArchive::WriteDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + const CArchiveDatabase &db, + const CCompressionMethodMode *options, + const CHeaderOptions &headerOptions) +{ + if (!db.CheckNumFiles()) + return E_FAIL; + + UInt64 headerOffset; + UInt32 headerCRC; + UInt64 headerSize; + if (db.IsEmpty()) + { + headerSize = 0; + headerOffset = 0; + headerCRC = CrcCalc(0, 0); + } + else + { + bool encodeHeaders = false; + if (options != 0) + if (options->IsEmpty()) + options = 0; + if (options != 0) + if (options->PasswordIsDefined || headerOptions.CompressMainHeader) + encodeHeaders = true; + + _outByte.SetStream(SeqStream); + _outByte.Init(); + _crc = CRC_INIT_VAL; + _countMode = encodeHeaders; + _writeToStream = true; + _countSize = 0; + WriteHeader(db, headerOptions, headerOffset); + + if (encodeHeaders) + { + CByteBuffer buf; + buf.SetCapacity(_countSize); + _outByte2.Init((Byte *)buf, _countSize); + + _countMode = false; + _writeToStream = false; + WriteHeader(db, headerOptions, headerOffset); + + if (_countSize != _outByte2.GetPos()) + return E_FAIL; + + CCompressionMethodMode encryptOptions; + encryptOptions.PasswordIsDefined = options->PasswordIsDefined; + encryptOptions.Password = options->Password; + CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions); + CRecordVector<UInt64> packSizes; + CObjectVector<CFolder> folders; + RINOK(EncodeStream( + EXTERNAL_CODECS_LOC_VARS + encoder, buf, + packSizes, folders)); + + _writeToStream = true; + + if (folders.Size() == 0) + throw 1; + + WriteID(NID::kEncodedHeader); + WritePackInfo(headerOffset, packSizes, + CRecordVector<bool>(), CRecordVector<UInt32>()); + WriteUnpackInfo(folders); + WriteByte(NID::kEnd); + for (int i = 0; i < packSizes.Size(); i++) + headerOffset += packSizes[i]; + } + RINOK(_outByte.Flush()); + headerCRC = CRC_GET_DIGEST(_crc); + headerSize = _outByte.GetProcessedSize(); + } + #ifdef _7Z_VOL + if (_endMarker) + { + CFinishHeader h; + h.NextHeaderSize = headerSize; + h.NextHeaderCRC = headerCRC; + h.NextHeaderOffset = + UInt64(0) - (headerSize + + 4 + kFinishHeaderSize); + h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset; + h.AdditionalStartBlockSize = 0; + RINOK(WriteFinishHeader(h)); + return WriteFinishSignature(); + } + else + #endif + { + CStartHeader h; + h.NextHeaderSize = headerSize; + h.NextHeaderCRC = headerCRC; + h.NextHeaderOffset = headerOffset; + RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL)); + return WriteStartHeader(h); + } +} + +void CArchiveDatabase::GetFile(int index, CFileItem &file, CFileItem2 &file2) const +{ + file = Files[index]; + file2.CTimeDefined = CTime.GetItem(index, file2.CTime); + file2.ATimeDefined = ATime.GetItem(index, file2.ATime); + file2.MTimeDefined = MTime.GetItem(index, file2.MTime); + file2.StartPosDefined = StartPos.GetItem(index, file2.StartPos); + file2.IsAnti = IsItemAnti(index); +} + +void CArchiveDatabase::AddFile(const CFileItem &file, const CFileItem2 &file2) +{ + int index = Files.Size(); + CTime.SetItem(index, file2.CTimeDefined, file2.CTime); + ATime.SetItem(index, file2.ATimeDefined, file2.ATime); + MTime.SetItem(index, file2.MTimeDefined, file2.MTime); + StartPos.SetItem(index, file2.StartPosDefined, file2.StartPos); + SetItemAnti(index, file2.IsAnti); + Files.Add(file); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h new file mode 100644 index 000000000..7b1b528e6 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h @@ -0,0 +1,152 @@ +// 7zOut.h + +#ifndef __7Z_OUT_H +#define __7Z_OUT_H + +#include "7zCompressionMode.h" +#include "7zEncode.h" +#include "7zHeader.h" +#include "7zItem.h" + +#include "../../Common/OutBuffer.h" + +namespace NArchive { +namespace N7z { + +class CWriteBufferLoc +{ + Byte *_data; + size_t _size; + size_t _pos; +public: + CWriteBufferLoc(): _size(0), _pos(0) {} + void Init(Byte *data, size_t size) + { + _data = data; + _size = size; + _pos = 0; + } + void WriteBytes(const void *data, size_t size) + { + if (size > _size - _pos) + throw 1; + memcpy(_data + _pos, data, size); + _pos += size; + } + void WriteByte(Byte b) + { + if (_size == _pos) + throw 1; + _data[_pos++] = b; + } + size_t GetPos() const { return _pos; } +}; + +struct CHeaderOptions +{ + bool CompressMainHeader; + bool WriteCTime; + bool WriteATime; + bool WriteMTime; + + CHeaderOptions(): + CompressMainHeader(true), + WriteCTime(false), + WriteATime(false), + WriteMTime(true) + {} +}; + +class COutArchive +{ + UInt64 _prefixHeaderPos; + + HRESULT WriteDirect(const void *data, UInt32 size); + + UInt64 GetPos() const; + void WriteBytes(const void *data, size_t size); + void WriteBytes(const CByteBuffer &data) { WriteBytes(data, data.GetCapacity()); } + void WriteByte(Byte b); + void WriteUInt32(UInt32 value); + void WriteUInt64(UInt64 value); + void WriteNumber(UInt64 value); + void WriteID(UInt64 value) { WriteNumber(value); } + + void WriteFolder(const CFolder &folder); + HRESULT WriteFileHeader(const CFileItem &itemInfo); + void WriteBoolVector(const CBoolVector &boolVector); + void WriteHashDigests( + const CRecordVector<bool> &digestsDefined, + const CRecordVector<UInt32> &hashDigests); + + void WritePackInfo( + UInt64 dataOffset, + const CRecordVector<UInt64> &packSizes, + const CRecordVector<bool> &packCRCsDefined, + const CRecordVector<UInt32> &packCRCs); + + void WriteUnpackInfo(const CObjectVector<CFolder> &folders); + + void WriteSubStreamsInfo( + const CObjectVector<CFolder> &folders, + const CRecordVector<CNum> &numUnpackStreamsInFolders, + const CRecordVector<UInt64> &unpackSizes, + const CRecordVector<bool> &digestsDefined, + const CRecordVector<UInt32> &hashDigests); + + void SkipAlign(unsigned pos, unsigned alignSize); + void WriteAlignedBoolHeader(const CBoolVector &v, int numDefined, Byte type, unsigned itemSize); + void WriteUInt64DefVector(const CUInt64DefVector &v, Byte type); + + HRESULT EncodeStream( + DECL_EXTERNAL_CODECS_LOC_VARS + CEncoder &encoder, const CByteBuffer &data, + CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders); + void WriteHeader( + const CArchiveDatabase &db, + const CHeaderOptions &headerOptions, + UInt64 &headerOffset); + + bool _countMode; + bool _writeToStream; + size_t _countSize; + UInt32 _crc; + COutBuffer _outByte; + CWriteBufferLoc _outByte2; + + #ifdef _7Z_VOL + bool _endMarker; + #endif + + HRESULT WriteSignature(); + #ifdef _7Z_VOL + HRESULT WriteFinishSignature(); + #endif + HRESULT WriteStartHeader(const CStartHeader &h); + #ifdef _7Z_VOL + HRESULT WriteFinishHeader(const CFinishHeader &h); + #endif + CMyComPtr<IOutStream> Stream; +public: + + COutArchive() { _outByte.Create(1 << 16); } + CMyComPtr<ISequentialOutStream> SeqStream; + HRESULT Create(ISequentialOutStream *stream, bool endMarker); + void Close(); + HRESULT SkipPrefixArchiveHeader(); + HRESULT WriteDatabase( + DECL_EXTERNAL_CODECS_LOC_VARS + const CArchiveDatabase &db, + const CCompressionMethodMode *options, + const CHeaderOptions &headerOptions); + + #ifdef _7Z_VOL + static UInt32 GetVolHeadersSize(UInt64 dataSize, int nameLength = 0, bool props = false); + static UInt64 GetVolPureSize(UInt64 volSize, int nameLength = 0, bool props = false); + #endif + +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp new file mode 100644 index 000000000..fd4af49c7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp @@ -0,0 +1,164 @@ +// 7zProperties.cpp + +#include "StdAfx.h" + +#include "7zProperties.h" +#include "7zHeader.h" +#include "7zHandler.h" + +// #define _MULTI_PACK + +namespace NArchive { +namespace N7z { + +struct CPropMap +{ + UInt64 FilePropID; + STATPROPSTG StatPROPSTG; +}; + +CPropMap kPropMap[] = +{ + { NID::kName, { NULL, kpidPath, VT_BSTR } }, + { NID::kSize, { NULL, kpidSize, VT_UI8 } }, + { NID::kPackInfo, { NULL, kpidPackSize, VT_UI8 } }, + + #ifdef _MULTI_PACK + { 100, { L"Pack0", kpidPackedSize0, VT_UI8 } }, + { 101, { L"Pack1", kpidPackedSize1, VT_UI8 } }, + { 102, { L"Pack2", kpidPackedSize2, VT_UI8 } }, + { 103, { L"Pack3", kpidPackedSize3, VT_UI8 } }, + { 104, { L"Pack4", kpidPackedSize4, VT_UI8 } }, + #endif + + { NID::kCTime, { NULL, kpidCTime, VT_FILETIME } }, + { NID::kMTime, { NULL, kpidMTime, VT_FILETIME } }, + { NID::kATime, { NULL, kpidATime, VT_FILETIME } }, + { NID::kWinAttributes, { NULL, kpidAttrib, VT_UI4 } }, + { NID::kStartPos, { NULL, kpidPosition, VT_UI4 } }, + + { NID::kCRC, { NULL, kpidCRC, VT_UI4 } }, + + { NID::kAnti, { NULL, kpidIsAnti, VT_BOOL } } + + #ifndef _SFX + , + { 97, { NULL,kpidEncrypted, VT_BOOL } }, + { 98, { NULL,kpidMethod, VT_BSTR } }, + { 99, { NULL,kpidBlock, VT_UI4 } } + #endif +}; + +static const int kPropMapSize = sizeof(kPropMap) / sizeof(kPropMap[0]); + +static int FindPropInMap(UInt64 filePropID) +{ + for (int i = 0; i < kPropMapSize; i++) + if (kPropMap[i].FilePropID == filePropID) + return i; + return -1; +} + +static void CopyOneItem(CRecordVector<UInt64> &src, + CRecordVector<UInt64> &dest, UInt32 item) +{ + for (int i = 0; i < src.Size(); i++) + if (src[i] == item) + { + dest.Add(item); + src.Delete(i); + return; + } +} + +static void RemoveOneItem(CRecordVector<UInt64> &src, UInt32 item) +{ + for (int i = 0; i < src.Size(); i++) + if (src[i] == item) + { + src.Delete(i); + return; + } +} + +static void InsertToHead(CRecordVector<UInt64> &dest, UInt32 item) +{ + for (int i = 0; i < dest.Size(); i++) + if (dest[i] == item) + { + dest.Delete(i); + break; + } + dest.Insert(0, item); +} + +void CHandler::FillPopIDs() +{ + _fileInfoPopIDs.Clear(); + + #ifdef _7Z_VOL + if(_volumes.Size() < 1) + return; + const CVolume &volume = _volumes.Front(); + const CArchiveDatabaseEx &_db = volume.Database; + #endif + + CRecordVector<UInt64> fileInfoPopIDs = _db.ArchiveInfo.FileInfoPopIDs; + + RemoveOneItem(fileInfoPopIDs, NID::kEmptyStream); + RemoveOneItem(fileInfoPopIDs, NID::kEmptyFile); + + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kName); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kAnti); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kSize); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kPackInfo); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCTime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kMTime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kATime); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kWinAttributes); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCRC); + CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kComment); + _fileInfoPopIDs += fileInfoPopIDs; + + #ifndef _SFX + _fileInfoPopIDs.Add(97); + _fileInfoPopIDs.Add(98); + _fileInfoPopIDs.Add(99); + #endif + #ifdef _MULTI_PACK + _fileInfoPopIDs.Add(100); + _fileInfoPopIDs.Add(101); + _fileInfoPopIDs.Add(102); + _fileInfoPopIDs.Add(103); + _fileInfoPopIDs.Add(104); + #endif + + #ifndef _SFX + InsertToHead(_fileInfoPopIDs, NID::kMTime); + InsertToHead(_fileInfoPopIDs, NID::kPackInfo); + InsertToHead(_fileInfoPopIDs, NID::kSize); + InsertToHead(_fileInfoPopIDs, NID::kName); + #endif +} + +STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) +{ + *numProperties = _fileInfoPopIDs.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) +{ + if ((int)index >= _fileInfoPopIDs.Size()) + return E_INVALIDARG; + int indexInMap = FindPropInMap(_fileInfoPopIDs[index]); + if (indexInMap == -1) + return E_INVALIDARG; + const STATPROPSTG &srcItem = kPropMap[indexInMap].StatPROPSTG; + *propID = srcItem.propid; + *varType = srcItem.vt; + *name = 0; + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h new file mode 100644 index 000000000..661817954 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h @@ -0,0 +1,22 @@ +// 7zProperties.h + +#ifndef __7Z_PROPERTIES_H +#define __7Z_PROPERTIES_H + +#include "../../PropID.h" + +namespace NArchive { +namespace N7z { + +enum +{ + kpidPackedSize0 = kpidUserDefined, + kpidPackedSize1, + kpidPackedSize2, + kpidPackedSize3, + kpidPackedSize4 +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp new file mode 100644 index 000000000..6e9bf6b99 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp @@ -0,0 +1,18 @@ +// 7zRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "7zHandler.h" +static IInArchive *CreateArc() { return new NArchive::N7z::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::N7z::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"7z", L"7z", 0, 7, {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}, 6, false, CreateArc, CreateArcOut }; + +REGISTER_ARC(7z) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp new file mode 100644 index 000000000..06969636d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp @@ -0,0 +1,24 @@ +// 7zSpecStream.cpp + +#include "StdAfx.h" + +#include "7zSpecStream.h" + +STDMETHODIMP CSequentialInStreamSizeCount2::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + if (processedSize != 0) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CSequentialInStreamSizeCount2::GetSubStreamSize( + UInt64 subStream, UInt64 *value) +{ + if (_getSubStreamSize == NULL) + return E_NOTIMPL; + return _getSubStreamSize->GetSubStreamSize(subStream, value); +} + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h new file mode 100644 index 000000000..2e26efd5c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h @@ -0,0 +1,35 @@ +// 7zSpecStream.h + +#ifndef __7Z_SPEC_STREAM_H +#define __7Z_SPEC_STREAM_H + +#include "../../IStream.h" +#include "../../ICoder.h" +#include "../../../Common/MyCom.h" + +class CSequentialInStreamSizeCount2: + public ISequentialInStream, + public ICompressGetSubStreamSize, + public CMyUnknownImp +{ + CMyComPtr<ISequentialInStream> _stream; + CMyComPtr<ICompressGetSubStreamSize> _getSubStreamSize; + UInt64 _size; +public: + void Init(ISequentialInStream *stream) + { + _stream = stream; + _getSubStreamSize = 0; + _stream.QueryInterface(IID_ICompressGetSubStreamSize, &_getSubStreamSize); + _size = 0; + } + UInt64 GetSize() const { return _size; } + + MY_UNKNOWN_IMP1(ICompressGetSubStreamSize) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value); +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp new file mode 100644 index 000000000..f07efc178 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -0,0 +1,1216 @@ +// 7zUpdate.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" + +#include "../../Common/CreateCoder.h" + +#include "../../Compress/CopyCoder.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/OutStreamWithCRC.h" + +#include "7zDecode.h" +#include "7zEncode.h" +#include "7zFolderInStream.h" +#include "7zHandler.h" +#include "7zOut.h" +#include "7zUpdate.h" + +#ifndef WIN32 +#include "Windows/FileIO.h" +#endif + +namespace NArchive { +namespace N7z { + +static const UInt64 k_LZMA = 0x030101; +static const UInt64 k_BCJ = 0x03030103; +static const UInt64 k_BCJ2 = 0x0303011B; + +static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2"; +static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20; +static const UInt32 kAlgorithmForBCJ2_LZMA = 1; +static const UInt32 kNumFastBytesForBCJ2_LZMA = 64; + +#ifdef MY_CPU_X86_OR_AMD64 +#define USE_86_FILTER +#endif + +static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream, + UInt64 position, UInt64 size, ICompressProgressInfo *progress) +{ + RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0)); + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(size); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); + return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL); +} + +static int GetReverseSlashPos(const UString &name) +{ + int slashPos = name.ReverseFind(L'/'); + #ifdef _WIN32 + int slash1Pos = name.ReverseFind(L'\\'); + slashPos = MyMax(slashPos, slash1Pos); + #endif + return slashPos; +} + +int CUpdateItem::GetExtensionPos() const +{ + int slashPos = GetReverseSlashPos(Name); + int dotPos = Name.ReverseFind(L'.'); + if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) + return Name.Length(); + return dotPos + 1; +} + +UString CUpdateItem::GetExtension() const +{ + return Name.Mid(GetExtensionPos()); +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +#define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b)) + +static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2) +{ + size_t c1 = a1.GetCapacity(); + size_t c2 = a2.GetCapacity(); + RINOZ_COMP(c1, c2); + for (size_t i = 0; i < c1; i++) + RINOZ_COMP(a1[i], a2[i]); + return 0; +} + +static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2) +{ + RINOZ_COMP(c1.NumInStreams, c2.NumInStreams); + RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams); + RINOZ_COMP(c1.MethodID, c2.MethodID); + return CompareBuffers(c1.Props, c2.Props); +} + +static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2) +{ + RINOZ_COMP(b1.InIndex, b2.InIndex); + return MyCompare(b1.OutIndex, b2.OutIndex); +} + +static int CompareFolders(const CFolder &f1, const CFolder &f2) +{ + int s1 = f1.Coders.Size(); + int s2 = f2.Coders.Size(); + RINOZ_COMP(s1, s2); + int i; + for (i = 0; i < s1; i++) + RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i])); + s1 = f1.BindPairs.Size(); + s2 = f2.BindPairs.Size(); + RINOZ_COMP(s1, s2); + for (i = 0; i < s1; i++) + RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i])); + return 0; +} + +/* +static int CompareFiles(const CFileItem &f1, const CFileItem &f2) +{ + return MyStringCompareNoCase(f1.Name, f2.Name); +} +*/ + +struct CFolderRepack +{ + int FolderIndex; + int Group; + CNum NumCopyFiles; +}; + +static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void *param) +{ + RINOZ_COMP(p1->Group, p2->Group); + int i1 = p1->FolderIndex; + int i2 = p2->FolderIndex; + const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param; + RINOZ(CompareFolders( + db.Folders[i1], + db.Folders[i2])); + return MyCompare(i1, i2); + /* + RINOZ_COMP( + db.NumUnpackStreamsVector[i1], + db.NumUnpackStreamsVector[i2]); + if (db.NumUnpackStreamsVector[i1] == 0) + return 0; + return CompareFiles( + db.Files[db.FolderStartFileIndex[i1]], + db.Files[db.FolderStartFileIndex[i2]]); + */ +} + +//////////////////////////////////////////////////////////// + +static int CompareEmptyItems(const int *p1, const int *p2, void *param) +{ + const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param; + const CUpdateItem &u1 = updateItems[*p1]; + const CUpdateItem &u2 = updateItems[*p2]; + if (u1.IsDir != u2.IsDir) + return (u1.IsDir) ? 1 : -1; + if (u1.IsDir) + { + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + int n = MyStringCompareNoCase(u1.Name, u2.Name); + return -n; + } + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + return MyStringCompareNoCase(u1.Name, u2.Name); +} + +static const char *g_Exts = + " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo" + " zip jar ear war msi" + " 3gp avi mov mpeg mpg mpe wmv" + " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav" + " swf " + " chm hxi hxs" + " gif jpeg jpg jp2 png tiff bmp ico psd psp" + " awg ps eps cgm dxf svg vrml wmf emf ai md" + " cad dwg pps key sxi" + " max 3ds" + " iso bin nrg mdf img pdi tar cpio xpi" + " vfd vhd vud vmc vsv" + " vmdk dsk nvram vmem vmsd vmsn vmss vmtm" + " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def" + " f77 f f90 f95" + " asm sql manifest dep " + " mak clw csproj vcproj sln dsp dsw " + " class " + " bat cmd" + " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml" + " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs" + " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf" + " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf" + " abw afp cwk lwp wpd wps wpt wrf wri" + " abf afm bdf fon mgf otf pcf pfa snf ttf" + " dbf mdb nsf ntf wdb db fdb gdb" + " exe dll ocx vbx sfx sys tlb awx com obj lib out o so " + " pdb pch idb ncb opt"; + +int GetExtIndex(const char *ext) +{ + int extIndex = 1; + const char *p = g_Exts; + for (;;) + { + char c = *p++; + if (c == 0) + return extIndex; + if (c == ' ') + continue; + int pos = 0; + for (;;) + { + char c2 = ext[pos++]; + if (c2 == 0 && (c == 0 || c == ' ')) + return extIndex; + if (c != c2) + break; + c = *p++; + } + extIndex++; + for (;;) + { + if (c == 0) + return extIndex; + if (c == ' ') + break; + c = *p++; + } + } +} + +struct CRefItem +{ + const CUpdateItem *UpdateItem; + UInt32 Index; + UInt32 ExtensionPos; + UInt32 NamePos; + int ExtensionIndex; + CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType): + UpdateItem(&ui), + Index(index), + ExtensionPos(0), + NamePos(0), + ExtensionIndex(0) + { + if (sortByType) + { + int slashPos = GetReverseSlashPos(ui.Name); + NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0); + int dotPos = ui.Name.ReverseFind(L'.'); + if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0)) + ExtensionPos = ui.Name.Length(); + else + { + ExtensionPos = dotPos + 1; + UString us = ui.Name.Mid(ExtensionPos); + if (!us.IsEmpty()) + { + us.MakeLower(); + int i; + AString s; + for (i = 0; i < us.Length(); i++) + { + wchar_t c = us[i]; + if (c >= 0x80) + break; + s += (char)c; + } + if (i == us.Length()) + ExtensionIndex = GetExtIndex(s); + else + ExtensionIndex = 0; + } + } + } + } +}; + +static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param) +{ + const CRefItem &a1 = *p1; + const CRefItem &a2 = *p2; + const CUpdateItem &u1 = *a1.UpdateItem; + const CUpdateItem &u2 = *a2.UpdateItem; + int n; + if (u1.IsDir != u2.IsDir) + return (u1.IsDir) ? 1 : -1; + if (u1.IsDir) + { + if (u1.IsAnti != u2.IsAnti) + return (u1.IsAnti ? 1 : -1); + n = MyStringCompareNoCase(u1.Name, u2.Name); + return -n; + } + bool sortByType = *(bool *)param; + if (sortByType) + { + RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex); + RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos)); + RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos)); + if (!u1.MTimeDefined && u2.MTimeDefined) return 1; + if (u1.MTimeDefined && !u2.MTimeDefined) return -1; + if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime); + RINOZ_COMP(u1.Size, u2.Size); + } + return MyStringCompareNoCase(u1.Name, u2.Name); +} + +struct CSolidGroup +{ + CRecordVector<UInt32> Indices; +}; + +#ifdef _WIN32 +static wchar_t *g_ExeExts[] = +{ + L"dll", + L"exe", + L"ocx", + L"sfx", + L"sys" +}; + +static bool IsExeExt(const UString &ext) +{ + for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++) + if (ext.CompareNoCase(g_ExeExts[i]) == 0) + return true; + return false; +} +#else +static bool IsExeFile(const CUpdateItem &ui) +{ + if (ui.Attrib & FILE_ATTRIBUTE_UNIX_EXTENSION) { + unsigned short st_mode = ui.Attrib >> 16; + if ((st_mode & 00111) && (ui.Size >= 2048)) + { + // file has the execution flag and it's big enought + // try to find if the file is a script + NWindows::NFile::NIO::CInFile file; + if (file.Open(ui.Name)) + { + char buffer[512]; + UINT32 processedSize; + if (file.Read(buffer,sizeof(buffer),processedSize)) + { + for(UInt32 i = 0; i < processedSize ; i++) + { + if (buffer[i] == 0) + { + return true; // this file is not a text (ascii, utf8, ...) ! + } + } + } + } + } + } + return false; +} +#endif + +#ifdef USE_86_FILTER + +static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &methodResult) +{ + methodResult.Id = methodID; + methodResult.NumInStreams = numInStreams; + methodResult.NumOutStreams = 1; +} + +static void MakeExeMethod(const CCompressionMethodMode &method, + bool bcj2Filter, CCompressionMethodMode &exeMethod) +{ + exeMethod = method; + if (bcj2Filter) + { + CMethodFull methodFull; + GetMethodFull(k_BCJ2, 4, methodFull); + exeMethod.Methods.Insert(0, methodFull); + GetMethodFull(k_LZMA, 1, methodFull); + { + CProp prop; + prop.Id = NCoderPropID::kAlgorithm; + prop.Value = kAlgorithmForBCJ2_LZMA; + methodFull.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kMatchFinder; + prop.Value = kMatchFinderForBCJ2_LZMA; + methodFull.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kDictionarySize; + prop.Value = kDictionaryForBCJ2_LZMA; + methodFull.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kNumFastBytes; + prop.Value = kNumFastBytesForBCJ2_LZMA; + methodFull.Props.Add(prop); + } + { + CProp prop; + prop.Id = NCoderPropID::kNumThreads; + prop.Value = (UInt32)1; + methodFull.Props.Add(prop); + } + + exeMethod.Methods.Add(methodFull); + exeMethod.Methods.Add(methodFull); + CBind bind; + + bind.OutCoder = 0; + bind.InStream = 0; + + bind.InCoder = 1; + bind.OutStream = 0; + exeMethod.Binds.Add(bind); + + bind.InCoder = 2; + bind.OutStream = 1; + exeMethod.Binds.Add(bind); + + bind.InCoder = 3; + bind.OutStream = 2; + exeMethod.Binds.Add(bind); + } + else + { + CMethodFull methodFull; + GetMethodFull(k_BCJ, 1, methodFull); + exeMethod.Methods.Insert(0, methodFull); + CBind bind; + bind.OutCoder = 0; + bind.InStream = 0; + bind.InCoder = 1; + bind.OutStream = 0; + exeMethod.Binds.Add(bind); + } +} + +#endif + +static void FromUpdateItemToFileItem(const CUpdateItem &ui, + CFileItem &file, CFileItem2 &file2) +{ + file.Name = NItemName::MakeLegalName(ui.Name); + if (ui.AttribDefined) + file.SetAttrib(ui.Attrib); + + file2.CTime = ui.CTime; file2.CTimeDefined = ui.CTimeDefined; + file2.ATime = ui.ATime; file2.ATimeDefined = ui.ATimeDefined; + file2.MTime = ui.MTime; file2.MTimeDefined = ui.MTimeDefined; + file2.IsAnti = ui.IsAnti; + file2.StartPosDefined = false; + + file.Size = ui.Size; + file.IsDir = ui.IsDir; + file.HasStream = ui.HasStream(); +} + +class CFolderOutStream2: + public ISequentialOutStream, + public CMyUnknownImp +{ + COutStreamWithCRC *_crcStreamSpec; + CMyComPtr<ISequentialOutStream> _crcStream; + const CArchiveDatabaseEx *_db; + const CBoolVector *_extractStatuses; + CMyComPtr<ISequentialOutStream> _outStream; + UInt32 _startIndex; + int _currentIndex; + bool _fileIsOpen; + UInt64 _rem; + + void OpenFile(); + void CloseFile(); + HRESULT CloseFileAndSetResult(); + HRESULT ProcessEmptyFiles(); +public: + MY_UNKNOWN_IMP + + CFolderOutStream2() + { + _crcStreamSpec = new COutStreamWithCRC; + _crcStream = _crcStreamSpec; + } + + HRESULT Init(const CArchiveDatabaseEx *db, UInt32 startIndex, + const CBoolVector *extractStatuses, ISequentialOutStream *outStream); + void ReleaseOutStream(); + HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; } + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +HRESULT CFolderOutStream2::Init(const CArchiveDatabaseEx *db, UInt32 startIndex, + const CBoolVector *extractStatuses, ISequentialOutStream *outStream) +{ + _db = db; + _startIndex = startIndex; + _extractStatuses = extractStatuses; + _outStream = outStream; + + _currentIndex = 0; + _fileIsOpen = false; + return ProcessEmptyFiles(); +} + +void CFolderOutStream2::ReleaseOutStream() +{ + _outStream.Release(); + _crcStreamSpec->ReleaseStream(); +} + +void CFolderOutStream2::OpenFile() +{ + _crcStreamSpec->SetStream((*_extractStatuses)[_currentIndex] ? (ISequentialOutStream *)_outStream : NULL); // FIXED for gcc 2.95 + _crcStreamSpec->Init(true); + _fileIsOpen = true; + _rem = _db->Files[_startIndex + _currentIndex].Size; +} + +void CFolderOutStream2::CloseFile() +{ + _crcStreamSpec->ReleaseStream(); + _fileIsOpen = false; + _currentIndex++; +} + +HRESULT CFolderOutStream2::CloseFileAndSetResult() +{ + const CFileItem &file = _db->Files[_startIndex + _currentIndex]; + CloseFile(); + return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE; +} + +HRESULT CFolderOutStream2::ProcessEmptyFiles() +{ + while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0) + { + OpenFile(); + RINOK(CloseFileAndSetResult()); + } + return S_OK; +} + +STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + while (size != 0) + { + if (_fileIsOpen) + { + UInt32 cur = size < _rem ? size : (UInt32)_rem; + RINOK(_crcStream->Write(data, cur, &cur)); + if (cur == 0) + break; + data = (const Byte *)data + cur; + size -= cur; + _rem -= cur; + if (processedSize != NULL) + *processedSize += cur; + if (_rem == 0) + { + RINOK(CloseFileAndSetResult()); + RINOK(ProcessEmptyFiles()); + continue; + } + } + else + { + RINOK(ProcessEmptyFiles()); + if (_currentIndex == _extractStatuses->Size()) + { + // we don't support partial extracting + return E_FAIL; + } + OpenFile(); + } + } + return S_OK; +} + +class CThreadDecoder: public CVirtThread +{ +public: + HRESULT Result; + CMyComPtr<IInStream> InStream; + + CFolderOutStream2 *FosSpec; + CMyComPtr<ISequentialOutStream> Fos; + + UInt64 StartPos; + const UInt64 *PackSizes; + const CFolder *Folder; + #ifndef _NO_CRYPTO + CMyComPtr<ICryptoGetTextPassword> GetTextPassword; + #endif + + DECL_EXTERNAL_CODECS_VARS + CDecoder Decoder; + + #ifndef _7ZIP_ST + bool MtMode; + UInt32 NumThreads; + #endif + + CThreadDecoder(): + Decoder(true) + { + #ifndef _7ZIP_ST + MtMode = false; + NumThreads = 1; + #endif + FosSpec = new CFolderOutStream2; + Fos = FosSpec; + Result = E_FAIL; + } + virtual void Execute(); +}; + +void CThreadDecoder::Execute() +{ + try + { + #ifndef _NO_CRYPTO + bool passwordIsDefined; + #endif + Result = Decoder.Decode( + EXTERNAL_CODECS_VARS + InStream, + StartPos, + PackSizes, + *Folder, + Fos, + NULL + #ifndef _NO_CRYPTO + , GetTextPassword, passwordIsDefined + #endif + #ifndef _7ZIP_ST + , MtMode, NumThreads + #endif + ); + } + catch(...) + { + Result = E_FAIL; + } + if (Result == S_OK) + Result = FosSpec->CheckFinishedState(); + FosSpec->ReleaseOutStream(); +} + +bool static Is86FilteredFolder(const CFolder &f) +{ + for (int i = 0; i < f.Coders.Size(); i++) + { + CMethodId m = f.Coders[i].MethodID; + if (m == k_BCJ || m == k_BCJ2) + return true; + } + return false; +} + +#ifndef _NO_CRYPTO + +class CCryptoGetTextPassword: + public ICryptoGetTextPassword, + public CMyUnknownImp +{ +public: + UString Password; + + MY_UNKNOWN_IMP + STDMETHOD(CryptoGetTextPassword)(BSTR *password); +}; + +STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password) +{ + return StringToBstr(Password, password); +} + +#endif + +static const int kNumGroupsMax = 4; + +#ifdef USE_86_FILTER +static bool Is86Group(int group) { return (group & 1) != 0; } +#endif +static bool IsEncryptedGroup(int group) { return (group & 2) != 0; } +static int GetGroupIndex(bool encrypted, int bcjFiltered) + { return (encrypted ? 2 : 0) + (bcjFiltered ? 1 : 0); } + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + const CArchiveDatabaseEx *db, + const CObjectVector<CUpdateItem> &updateItems, + COutArchive &archive, + CArchiveDatabase &newDatabase, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getDecoderPassword + #endif + ) +{ + UInt64 numSolidFiles = options.NumSolidFiles; + if (numSolidFiles == 0) + numSolidFiles = 1; + /* + CMyComPtr<IOutStream> outStream; + RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream)); + if (!outStream) + return E_NOTIMPL; + */ + + UInt64 startBlockSize = db != 0 ? db->ArchiveInfo.StartPosition: 0; + if (startBlockSize > 0 && !options.RemoveSfxBlock) + { + RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL)); + } + + CRecordVector<int> fileIndexToUpdateIndexMap; + CRecordVector<CFolderRepack> folderRefs; + UInt64 complexity = 0; + UInt64 inSizeForReduce2 = 0; + bool needEncryptedRepack = false; + if (db != 0) + { + fileIndexToUpdateIndexMap.Reserve(db->Files.Size()); + int i; + for (i = 0; i < db->Files.Size(); i++) + fileIndexToUpdateIndexMap.Add(-1); + + for (i = 0; i < updateItems.Size(); i++) + { + int index = updateItems[i].IndexInArchive; + if (index != -1) + fileIndexToUpdateIndexMap[index] = i; + } + + for (i = 0; i < db->Folders.Size(); i++) + { + CNum indexInFolder = 0; + CNum numCopyItems = 0; + CNum numUnpackStreams = db->NumUnpackStreamsVector[i]; + UInt64 repackSize = 0; + for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++) + { + const CFileItem &file = db->Files[fi]; + if (file.HasStream) + { + indexInFolder++; + int updateIndex = fileIndexToUpdateIndexMap[fi]; + if (updateIndex >= 0 && !updateItems[updateIndex].NewData) + { + numCopyItems++; + repackSize += file.Size; + } + } + } + + if (numCopyItems == 0) + continue; + + CFolderRepack rep; + rep.FolderIndex = i; + rep.NumCopyFiles = numCopyItems; + const CFolder &f = db->Folders[i]; + bool isEncrypted = f.IsEncrypted(); + rep.Group = GetGroupIndex(isEncrypted, Is86FilteredFolder(f)); + folderRefs.Add(rep); + if (numCopyItems == numUnpackStreams) + complexity += db->GetFolderFullPackSize(i); + else + { + complexity += repackSize; + if (repackSize > inSizeForReduce2) + inSizeForReduce2 = repackSize; + if (isEncrypted) + needEncryptedRepack = true; + } + } + folderRefs.Sort(CompareFolderRepacks, (void *)db); + } + + UInt64 inSizeForReduce = 0; + int i; + for (i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (ui.NewData) + { + complexity += ui.Size; + if (numSolidFiles != 1) + inSizeForReduce += ui.Size; + else if (ui.Size > inSizeForReduce) + inSizeForReduce = ui.Size; + } + } + + if (inSizeForReduce2 > inSizeForReduce) + inSizeForReduce = inSizeForReduce2; + + const UInt32 kMinReduceSize = (1 << 16); + if (inSizeForReduce < kMinReduceSize) + inSizeForReduce = kMinReduceSize; + + RINOK(updateCallback->SetTotal(complexity)); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CThreadDecoder threadDecoder; + if (!folderRefs.IsEmpty()) + { + #ifdef EXTERNAL_CODECS + threadDecoder._codecsInfo = codecsInfo; + threadDecoder._externalCodecs = *externalCodecs; + #endif + RINOK(threadDecoder.Create()); + } + + CObjectVector<CSolidGroup> groups; + for (i = 0; i < kNumGroupsMax; i++) + groups.Add(CSolidGroup()); + + { + // ---------- Split files to 2 groups ---------- + + bool useFilters = options.UseFilters; + const CCompressionMethodMode &method = *options.Method; + if (method.Methods.Size() != 1 || method.Binds.Size() != 0) + useFilters = false; + for (i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (!ui.NewData || !ui.HasStream()) + continue; + bool filteredGroup = false; + if (useFilters) + { +#ifdef _WIN32 + int dotPos = ui.Name.ReverseFind(L'.'); + if (dotPos >= 0) + filteredGroup = IsExeExt(ui.Name.Mid(dotPos + 1)); +#else + filteredGroup = IsExeFile(ui); +#endif + } + groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i); + } + } + + #ifndef _NO_CRYPTO + + CCryptoGetTextPassword *getPasswordSpec = NULL; + if (needEncryptedRepack) + { + getPasswordSpec = new CCryptoGetTextPassword; + threadDecoder.GetTextPassword = getPasswordSpec; + + if (options.Method->PasswordIsDefined) + getPasswordSpec->Password = options.Method->Password; + else + { + if (!getDecoderPassword) + return E_NOTIMPL; + CMyComBSTR password; + RINOK(getDecoderPassword->CryptoGetTextPassword(&password)); + getPasswordSpec->Password = password; + } + } + + #endif + + // ---------- Compress ---------- + + RINOK(archive.Create(seqOutStream, false)); + RINOK(archive.SkipPrefixArchiveHeader()); + + int folderRefIndex = 0; + lps->ProgressOffset = 0; + + for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++) + { + const CSolidGroup &group = groups[groupIndex]; + + CCompressionMethodMode method; + #ifdef USE_86_FILTER + if (Is86Group(groupIndex)) + MakeExeMethod(*options.Method, options.MaxFilter, method); + else + #endif + method = *options.Method; + + if (IsEncryptedGroup(groupIndex)) + { + if (!method.PasswordIsDefined) + { + #ifndef _NO_CRYPTO + if (getPasswordSpec) + method.Password = getPasswordSpec->Password; + #endif + method.PasswordIsDefined = true; + } + } + else + { + method.PasswordIsDefined = false; + method.Password.Empty(); + } + + CEncoder encoder(method); + + for (; folderRefIndex < folderRefs.Size(); folderRefIndex++) + { + const CFolderRepack &rep = folderRefs[folderRefIndex]; + if (rep.Group != groupIndex) + break; + int folderIndex = rep.FolderIndex; + + if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex]) + { + UInt64 packSize = db->GetFolderFullPackSize(folderIndex); + RINOK(WriteRange(inStream, archive.SeqStream, + db->GetFolderStreamPos(folderIndex, 0), packSize, progress)); + lps->ProgressOffset += packSize; + + const CFolder &folder = db->Folders[folderIndex]; + CNum startIndex = db->FolderStartPackStreamIndex[folderIndex]; + for (int j = 0; j < folder.PackStreams.Size(); j++) + { + newDatabase.PackSizes.Add(db->PackSizes[startIndex + j]); + // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]); + // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]); + } + newDatabase.Folders.Add(folder); + } + else + { + CStreamBinder sb; + RINOK(sb.CreateEvents()); + CMyComPtr<ISequentialOutStream> sbOutStream; + CMyComPtr<ISequentialInStream> sbInStream; + sb.CreateStreams(&sbInStream, &sbOutStream); + CBoolVector extractStatuses; + + CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; + CNum indexInFolder = 0; + + for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) + { + bool needExtract = false; + if (db->Files[fi].HasStream) + { + indexInFolder++; + int updateIndex = fileIndexToUpdateIndexMap[fi]; + if (updateIndex >= 0 && !updateItems[updateIndex].NewData) + needExtract = true; + } + extractStatuses.Add(needExtract); + } + + RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream)); + sbOutStream.Release(); + + threadDecoder.InStream = inStream; + threadDecoder.Folder = &db->Folders[folderIndex]; + threadDecoder.StartPos = db->GetFolderStreamPos(folderIndex, 0); + threadDecoder.PackSizes = &db->PackSizes[db->FolderStartPackStreamIndex[folderIndex]]; + + threadDecoder.Start(); + + int startPackIndex = newDatabase.PackSizes.Size(); + CFolder newFolder; + RINOK(encoder.Encode( + EXTERNAL_CODECS_LOC_VARS + sbInStream, NULL, &inSizeForReduce, newFolder, + archive.SeqStream, newDatabase.PackSizes, progress)); + + threadDecoder.WaitFinish(); + + RINOK(threadDecoder.Result); + + for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) + lps->OutSize += newDatabase.PackSizes[startPackIndex]; + lps->InSize += newFolder.GetUnpackSize(); + + newDatabase.Folders.Add(newFolder); + } + + newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles); + + CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex]; + + CNum indexInFolder = 0; + for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++) + { + CFileItem file; + CFileItem2 file2; + db->GetFile(fi, file, file2); + if (file.HasStream) + { + indexInFolder++; + int updateIndex = fileIndexToUpdateIndexMap[fi]; + if (updateIndex >= 0) + { + const CUpdateItem &ui = updateItems[updateIndex]; + if (ui.NewData) + continue; + if (ui.NewProps) + { + CFileItem uf; + FromUpdateItemToFileItem(ui, uf, file2); + uf.Size = file.Size; + uf.Crc = file.Crc; + uf.CrcDefined = file.CrcDefined; + uf.HasStream = file.HasStream; + file = uf; + } + newDatabase.AddFile(file, file2); + } + } + } + } + + int numFiles = group.Indices.Size(); + if (numFiles == 0) + continue; + CRecordVector<CRefItem> refItems; + refItems.Reserve(numFiles); + bool sortByType = (numSolidFiles > 1); + for (i = 0; i < numFiles; i++) + refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType)); + refItems.Sort(CompareUpdateItems, (void *)&sortByType); + + CRecordVector<UInt32> indices; + indices.Reserve(numFiles); + + for (i = 0; i < numFiles; i++) + { + UInt32 index = refItems[i].Index; + indices.Add(index); + /* + const CUpdateItem &ui = updateItems[index]; + CFileItem file; + if (ui.NewProps) + FromUpdateItemToFileItem(ui, file); + else + file = db.Files[ui.IndexInArchive]; + if (file.IsAnti || file.IsDir) + return E_FAIL; + newDatabase.Files.Add(file); + */ + } + + for (i = 0; i < numFiles;) + { + UInt64 totalSize = 0; + int numSubFiles; + UString prevExtension; + for (numSubFiles = 0; i + numSubFiles < numFiles && + numSubFiles < numSolidFiles; numSubFiles++) + { + const CUpdateItem &ui = updateItems[indices[i + numSubFiles]]; + totalSize += ui.Size; + if (totalSize > options.NumSolidBytes) + break; + if (options.SolidExtension) + { + UString ext = ui.GetExtension(); + if (numSubFiles == 0) + prevExtension = ext; + else + if (ext.CompareNoCase(prevExtension) != 0) + break; + } + } + if (numSubFiles < 1) + numSubFiles = 1; + + CFolderInStream *inStreamSpec = new CFolderInStream; + CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec); + inStreamSpec->Init(updateCallback, &indices[i], numSubFiles); + + CFolder folderItem; + + int startPackIndex = newDatabase.PackSizes.Size(); + RINOK(encoder.Encode( + EXTERNAL_CODECS_LOC_VARS + solidInStream, NULL, &inSizeForReduce, folderItem, + archive.SeqStream, newDatabase.PackSizes, progress)); + + for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++) + lps->OutSize += newDatabase.PackSizes[startPackIndex]; + + lps->InSize += folderItem.GetUnpackSize(); + // for () + // newDatabase.PackCRCsDefined.Add(false); + // newDatabase.PackCRCs.Add(0); + + newDatabase.Folders.Add(folderItem); + + CNum numUnpackStreams = 0; + for (int subIndex = 0; subIndex < numSubFiles; subIndex++) + { + const CUpdateItem &ui = updateItems[indices[i + subIndex]]; + CFileItem file; + CFileItem2 file2; + if (ui.NewProps) + FromUpdateItemToFileItem(ui, file, file2); + else + db->GetFile(ui.IndexInArchive, file, file2); + if (file2.IsAnti || file.IsDir) + return E_FAIL; + + /* + CFileItem &file = newDatabase.Files[ + startFileIndexInDatabase + i + subIndex]; + */ + if (!inStreamSpec->Processed[subIndex]) + { + continue; + // file.Name += L".locked"; + } + + file.Crc = inStreamSpec->CRCs[subIndex]; + file.Size = inStreamSpec->Sizes[subIndex]; + if (file.Size != 0) + { + file.CrcDefined = true; + file.HasStream = true; + numUnpackStreams++; + } + else + { + file.CrcDefined = false; + file.HasStream = false; + } + newDatabase.AddFile(file, file2); + } + // numUnpackStreams = 0 is very bad case for locked files + // v3.13 doesn't understand it. + newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams); + i += numSubFiles; + } + } + + if (folderRefIndex != folderRefs.Size()) + return E_FAIL; + + /* + folderRefs.ClearAndFree(); + fileIndexToUpdateIndexMap.ClearAndFree(); + groups.ClearAndFree(); + */ + + { + // ---------- Write Folders & Empty Files ---------- + + CRecordVector<int> emptyRefs; + for (i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (ui.NewData) + { + if (ui.HasStream()) + continue; + } + else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream) + continue; + emptyRefs.Add(i); + } + emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems); + for (i = 0; i < emptyRefs.Size(); i++) + { + const CUpdateItem &ui = updateItems[emptyRefs[i]]; + CFileItem file; + CFileItem2 file2; + if (ui.NewProps) + FromUpdateItemToFileItem(ui, file, file2); + else + db->GetFile(ui.IndexInArchive, file, file2); + newDatabase.AddFile(file, file2); + } + } + + newDatabase.ReserveDown(); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h new file mode 100644 index 000000000..31e362246 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h @@ -0,0 +1,88 @@ +// 7zUpdate.h + +#ifndef __7Z_UPDATE_H +#define __7Z_UPDATE_H + +#include "7zCompressionMode.h" +#include "7zIn.h" +#include "7zOut.h" + +#include "../IArchive.h" + +namespace NArchive { +namespace N7z { + +struct CUpdateItem +{ + int IndexInArchive; + int IndexInClient; + + UInt64 CTime; + UInt64 ATime; + UInt64 MTime; + + UInt64 Size; + UString Name; + + UInt32 Attrib; + + bool NewData; + bool NewProps; + + bool IsAnti; + bool IsDir; + + bool AttribDefined; + bool CTimeDefined; + bool ATimeDefined; + bool MTimeDefined; + + bool HasStream() const { return !IsDir && !IsAnti && Size != 0; } + + CUpdateItem(): + IsAnti(false), + IsDir(false), + AttribDefined(false), + CTimeDefined(false), + ATimeDefined(false), + MTimeDefined(false) + {} + void SetDirStatusFromAttrib() { IsDir = ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0); }; + + int GetExtensionPos() const; + UString GetExtension() const; +}; + +struct CUpdateOptions +{ + const CCompressionMethodMode *Method; + const CCompressionMethodMode *HeaderMethod; + bool UseFilters; + bool MaxFilter; + + CHeaderOptions HeaderOptions; + + UInt64 NumSolidFiles; + UInt64 NumSolidBytes; + bool SolidExtension; + bool RemoveSfxBlock; + bool VolumeMode; +}; + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, + const CArchiveDatabaseEx *db, + const CObjectVector<CUpdateItem> &updateItems, + COutArchive &archive, + CArchiveDatabase &newDatabase, + ISequentialOutStream *seqOutStream, + IArchiveUpdateCallback *updateCallback, + const CUpdateOptions &options + #ifndef _NO_CRYPTO + , ICryptoGetTextPassword *getDecoderPassword + #endif + ); +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/ApmHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/ApmHandler.cpp new file mode 100644 index 000000000..a3b5e19b9 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/ApmHandler.cpp @@ -0,0 +1,356 @@ +// ApmHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) + +using namespace NWindows; + +namespace NArchive { +namespace NApm { + +struct CItem +{ + UInt32 StartBlock; + UInt32 NumBlocks; + char Name[32]; + char Type[32]; + /* + UInt32 DataStartBlock; + UInt32 NumDataBlocks; + UInt32 Status; + UInt32 BootStartBlock; + UInt32 BootSize; + UInt32 BootAddr; + UInt32 BootEntry; + UInt32 BootChecksum; + char Processor[16]; + */ + + bool Parse(const Byte *p, UInt32 &numBlocksInMap) + { + if (p[0] != 0x50 || p[1] != 0x4D || p[2] != 0 || p[3] != 0) + return false; + numBlocksInMap = Get32(p + 4); + StartBlock = Get32(p + 8); + NumBlocks = Get32(p + 0xC); + memcpy(Name, p + 0x10, 32); + memcpy(Type, p + 0x30, 32); + /* + DataStartBlock = Get32(p + 0x50); + NumDataBlocks = Get32(p + 0x54); + Status = Get32(p + 0x58); + BootStartBlock = Get32(p + 0x5C); + BootSize = Get32(p + 0x60); + BootAddr = Get32(p + 0x64); + if (Get32(p + 0x68) != 0) + return false; + BootEntry = Get32(p + 0x6C); + if (Get32(p + 0x70) != 0) + return false; + BootChecksum = Get32(p + 0x74); + memcpy(Processor, p + 0x78, 16); + */ + return true; + } +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CRecordVector<CItem> _items; + + int _blockSizeLog; + UInt32 _numBlocks; + + HRESULT ReadTables(IInStream *stream); + UInt64 BlocksToBytes(UInt32 i) const { return (UInt64)i << _blockSizeLog; } + UInt64 GetItemSize(const CItem &item) { return BlocksToBytes(item.NumBlocks); } +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +static inline int GetLog(UInt32 num) +{ + for (int i = 0; i < 31; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + +HRESULT CHandler::ReadTables(IInStream *stream) +{ + const UInt32 kSectorSize = 512; + Byte buf[kSectorSize]; + { + RINOK(ReadStream_FALSE(stream, buf, kSectorSize)); + if (buf[0] != 0x45 || buf[1] != 0x52) + return S_FALSE; + _blockSizeLog = GetLog(Get16(buf + 2)); + if (_blockSizeLog < 9 || _blockSizeLog > 14) + return S_FALSE; + _numBlocks = Get32(buf + 4); + for (int i = 8; i < 16; i++) + if (buf[i] != 0) + return S_FALSE; + } + + unsigned numSkips = (unsigned)1 << (_blockSizeLog - 9); + for (unsigned j = 1; j < numSkips; j++) + { + RINOK(ReadStream_FALSE(stream, buf, kSectorSize)); + } + + UInt32 numBlocksInMap = 0; + for (unsigned i = 0;;) + { + RINOK(ReadStream_FALSE(stream, buf, kSectorSize)); + + CItem item; + + UInt32 numBlocksInMap2; + if (!item.Parse(buf, numBlocksInMap2)) + return S_FALSE; + if (i == 0) + { + numBlocksInMap = numBlocksInMap2; + if (numBlocksInMap > (1 << 8)) + return S_FALSE; + } + else if (numBlocksInMap2 != numBlocksInMap) + return S_FALSE; + + UInt32 finish = item.StartBlock + item.NumBlocks; + if (finish < item.StartBlock) + return S_FALSE; + _numBlocks = MyMax(_numBlocks, finish); + + _items.Add(item); + for (unsigned j = 1; j < numSkips; j++) + { + RINOK(ReadStream_FALSE(stream, buf, kSectorSize)); + } + if (++i == numBlocksInMap) + break; + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(ReadTables(stream)); + _stream = stream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidOffset, VT_UI8} +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidPhySize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static AString GetString(const char *s) +{ + AString res; + for (int i = 0; i < 32 && s[i] != 0; i++) + res += s[i]; + return res; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidMainSubfile: + { + int mainIndex = -1; + for (int i = 0; i < _items.Size(); i++) + { + AString s = GetString(_items[i].Type); + if (s != "Apple_Free" && + s != "Apple_partition_map") + { + if (mainIndex >= 0) + { + mainIndex = -1; + break; + } + mainIndex = i; + } + } + if (mainIndex >= 0) + prop = (UInt32)mainIndex; + break; + } + case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break; + case kpidPhySize: prop = BlocksToBytes(_numBlocks); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CItem &item = _items[index]; + switch(propID) + { + case kpidPath: + { + AString s = GetString(item.Name); + if (s.IsEmpty()) + { + char s2[32]; + ConvertUInt32ToString(index, s2); + s = s2; + } + AString type = GetString(item.Type); + if (type == "Apple_HFS") + type = "hfs"; + if (!type.IsEmpty()) + { + s += '.'; + s += type; + } + prop = s; + break; + } + case kpidSize: + case kpidPackSize: + prop = GetItemSize(item); + break; + case kpidOffset: prop = BlocksToBytes(item.StartBlock); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += GetItemSize(_items[allFilesMode ? i : indices[i]]); + extractCallback->SetTotal(totalSize); + + totalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + UInt64 size = GetItemSize(item); + totalSize += size; + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + RINOK(_stream->Seek(BlocksToBytes(item.StartBlock), STREAM_SEEK_SET, NULL)); + streamSpec->Init(size); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == size ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItem &item = _items[index]; + return CreateLimitedInStream(_stream, BlocksToBytes(item.StartBlock), GetItemSize(item), stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"APM", L"", 0, 0xD4, { 0x50, 0x4D, 0, 0, 0, 0, 0 }, 7, false, CreateArc, 0 }; + +REGISTER_ARC(Apm) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/ArchiveExports.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/ArchiveExports.cpp new file mode 100644 index 000000000..c7908b591 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/ArchiveExports.cpp @@ -0,0 +1,135 @@ +// ArchiveExports.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/RegisterArc.h" + +static const unsigned int kNumArcsMax = 48; +static unsigned int g_NumArcs = 0; +static unsigned int g_DefaultArcIndex = 0; +static const CArcInfo *g_Arcs[kNumArcsMax]; +void RegisterArc(const CArcInfo *arcInfo) +{ + if (g_NumArcs < kNumArcsMax) + { + const wchar_t *p = arcInfo->Name; + if (p[0] == '7' && p[1] == 'z' && p[2] == 0) + g_DefaultArcIndex = g_NumArcs; + g_Arcs[g_NumArcs++] = arcInfo; + } +} + +DEFINE_GUID(CLSID_CArchiveHandler, +0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00); + +#define CLS_ARC_ID_ITEM(cls) ((cls).Data4[5]) + +static inline HRESULT SetPropString(const char *s, unsigned int size, PROPVARIANT *value) +{ + if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0) + value->vt = VT_BSTR; + return S_OK; +} + +static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value) +{ + return SetPropString((const char *)&guid, sizeof(GUID), value); +} + +int FindFormatCalssId(const GUID *clsID) +{ + GUID cls = *clsID; + CLS_ARC_ID_ITEM(cls) = 0; + if (cls != CLSID_CArchiveHandler) + return -1; + Byte id = CLS_ARC_ID_ITEM(*clsID); + for (unsigned i = 0; i < g_NumArcs; i++) + if (g_Arcs[i]->ClassId == id) + return (int)i; + return -1; +} + +STDAPI CreateArchiver(const GUID *clsid, const GUID *iid, void **outObject) +{ + COM_TRY_BEGIN + { + int needIn = (*iid == IID_IInArchive); + int needOut = (*iid == IID_IOutArchive); + if (!needIn && !needOut) + return E_NOINTERFACE; + int formatIndex = FindFormatCalssId(clsid); + if (formatIndex < 0) + return CLASS_E_CLASSNOTAVAILABLE; + + const CArcInfo &arc = *g_Arcs[formatIndex]; + if (needIn) + { + *outObject = arc.CreateInArchive(); + ((IInArchive *)*outObject)->AddRef(); + } + else + { + if (!arc.CreateOutArchive) + return CLASS_E_CLASSNOTAVAILABLE; + *outObject = arc.CreateOutArchive(); + ((IOutArchive *)*outObject)->AddRef(); + } + } + COM_TRY_END + return S_OK; +} + +STDAPI GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + if (formatIndex >= g_NumArcs) + return E_INVALIDARG; + const CArcInfo &arc = *g_Arcs[formatIndex]; + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case NArchive::kName: + prop = arc.Name; + break; + case NArchive::kClassID: + { + GUID clsId = CLSID_CArchiveHandler; + CLS_ARC_ID_ITEM(clsId) = arc.ClassId; + return SetPropGUID(clsId, value); + } + case NArchive::kExtension: + if (arc.Ext != 0) + prop = arc.Ext; + break; + case NArchive::kAddExtension: + if (arc.AddExt != 0) + prop = arc.AddExt; + break; + case NArchive::kUpdate: + prop = (bool)(arc.CreateOutArchive != 0); + break; + case NArchive::kKeepName: + prop = arc.KeepName; + break; + case NArchive::kStartSignature: + return SetPropString((const char *)arc.Signature, arc.SignatureSize, value); + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDAPI GetHandlerProperty(PROPID propID, PROPVARIANT *value) +{ + return GetHandlerProperty2(g_DefaultArcIndex, propID, value); +} + +STDAPI GetNumberOfFormats(UINT32 *numFormats) +{ + *numFormats = g_NumArcs; + return S_OK; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/ArjHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/ArjHandler.cpp new file mode 100644 index 000000000..4dd686ec0 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/ArjHandler.cpp @@ -0,0 +1,798 @@ +// ArjHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/ArjDecoder1.h" +#include "../Compress/ArjDecoder2.h" +#include "../Compress/CopyCoder.h" + +#include "Common/ItemNameUtils.h" +#include "Common/OutStreamWithCRC.h" + +using namespace NWindows; + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) + +namespace NArchive { +namespace NArj { + +const int kBlockSizeMin = 30; +const int kBlockSizeMax = 2600; + +namespace NSignature +{ + const Byte kSig0 = 0x60; + const Byte kSig1 = 0xEA; +} + +namespace NFileHeader +{ + namespace NCompressionMethod + { + enum + { + kStored = 0, + kCompressed1a = 1, + kCompressed1b = 2, + kCompressed1c = 3, + kCompressed2 = 4, + kNoDataNoCRC = 8, + kNoData = 9 + }; + } + + namespace NFileType + { + enum + { + kBinary = 0, + k7BitText = 1, + kArchiveHeader = 2, + kDirectory = 3, + kVolumeLablel = 4, + kChapterLabel = 5 + }; + } + + namespace NFlags + { + const Byte kGarbled = 1; + const Byte kVolume = 4; + const Byte kExtFile = 8; + const Byte kPathSym = 0x10; + const Byte kBackup = 0x20; + } + + namespace NHostOS + { + enum EEnum + { + kMSDOS = 0, // filesystem used by MS-DOS, OS/2, Win32 + // pkarj 2.50 (FAT / VFAT / FAT32 file systems) + kPRIMOS, + kUnix, + kAMIGA, + kMac, + kOS_2, + kAPPLE_GS, + kAtari_ST, + kNext, + kVAX_VMS, + kWIN95 + }; + } +} + +struct CArchiveHeader +{ + // Byte ArchiverVersion; + // Byte ExtractVersion; + Byte HostOS; + // Byte Flags; + // Byte SecuryVersion; + // Byte FileType; + // Byte Reserved; + UInt32 CTime; + UInt32 MTime; + UInt32 ArchiveSize; + // UInt32 SecurityEnvelopeFilePosition; + // UInt16 FilespecPositionInFilename; + // UInt16 LengthOfSecurityEnvelopeSata; + // Byte EncryptionVersion; + // Byte LastChapter; + AString Name; + AString Comment; + + HRESULT Parse(const Byte *p, unsigned size); +}; + +static HRESULT ReadString(const Byte *p, unsigned &size, AString &res) +{ + AString s; + for (unsigned i = 0; i < size;) + { + char c = (char)p[i++]; + if (c == 0) + { + size = i; + res = s; + return S_OK; + } + s += c; + } + return S_FALSE; +} + +HRESULT CArchiveHeader::Parse(const Byte *p, unsigned size) +{ + if (size < kBlockSizeMin) + return S_FALSE; + Byte firstHeaderSize = p[0]; + if (firstHeaderSize > size) + return S_FALSE; + // ArchiverVersion = p[1]; + // ExtractVersion = p[2]; + HostOS = p[3]; + // Flags = p[4]; + // SecuryVersion = p[5]; + if (p[6] != NFileHeader::NFileType::kArchiveHeader) + return S_FALSE; + // Reserved = p[7]; + CTime = Get32(p + 8); + MTime = Get32(p + 12); + ArchiveSize = Get32(p + 16); + // SecurityEnvelopeFilePosition = Get32(p + 20); + // UInt16 filespecPositionInFilename = Get16(p + 24); + // LengthOfSecurityEnvelopeSata = Get16(p + 26); + // EncryptionVersion = p[28]; + // LastChapter = p[29]; + unsigned pos = firstHeaderSize; + unsigned size1 = size - pos; + RINOK(ReadString(p + pos, size1, Name)); + pos += size1; + size1 = size - pos; + RINOK(ReadString(p + pos, size1, Comment)); + pos += size1; + return S_OK; +} + +struct CItem +{ + AString Name; + AString Comment; + + UInt32 MTime; + UInt32 PackSize; + UInt32 Size; + UInt32 FileCRC; + UInt32 SplitPos; + + Byte Version; + Byte ExtractVersion; + Byte HostOS; + Byte Flags; + Byte Method; + Byte FileType; + + // UInt16 FilespecPositionInFilename; + UInt16 FileAccessMode; + // Byte FirstChapter; + // Byte LastChapter; + + UInt64 DataPosition; + + bool IsEncrypted() const { return (Flags & NFileHeader::NFlags::kGarbled) != 0; } + bool IsDir() const { return (FileType == NFileHeader::NFileType::kDirectory); } + bool IsSplitAfter() const { return (Flags & NFileHeader::NFlags::kVolume) != 0; } + bool IsSplitBefore() const { return (Flags & NFileHeader::NFlags::kExtFile) != 0; } + UInt32 GetWinAttributes() const + { + UInt32 winAtrributes; + switch(HostOS) + { + case NFileHeader::NHostOS::kMSDOS: + case NFileHeader::NHostOS::kWIN95: + winAtrributes = FileAccessMode; + break; + default: + winAtrributes = 0; + } + if (IsDir()) + winAtrributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAtrributes; + } + + HRESULT Parse(const Byte *p, unsigned size); +}; + +HRESULT CItem::Parse(const Byte *p, unsigned size) +{ + if (size < kBlockSizeMin) + return S_FALSE; + + Byte firstHeaderSize = p[0]; + + Version = p[1]; + ExtractVersion = p[2]; + HostOS = p[3]; + Flags = p[4]; + Method = p[5]; + FileType = p[6]; + // Reserved = p[7]; + MTime = Get32(p + 8); + PackSize = Get32(p + 12); + Size = Get32(p + 16); + FileCRC = Get32(p + 20); + // FilespecPositionInFilename = Get16(p + 24); + FileAccessMode = Get16(p + 26); + // FirstChapter = p[28]; + // FirstChapter = p[29]; + + SplitPos = 0; + if (IsSplitBefore() && firstHeaderSize >= 34) + SplitPos = Get32(p + 30); + + unsigned pos = firstHeaderSize; + unsigned size1 = size - pos; + RINOK(ReadString(p + pos, size1, Name)); + pos += size1; + size1 = size - pos; + RINOK(ReadString(p + pos, size1, Comment)); + pos += size1; + + return S_OK; +} + +struct CInArchiveException +{ + enum CCauseType + { + kUnexpectedEndOfArchive = 0, + kCRCError, + kIncorrectArchive + } + Cause; + CInArchiveException(CCauseType cause): Cause(cause) {}; +}; + +class CInArchive +{ + UInt32 _blockSize; + Byte _block[kBlockSizeMax + 4]; + + HRESULT ReadBlock(bool &filled); + HRESULT ReadSignatureAndBlock(bool &filled); + HRESULT SkipExtendedHeaders(); + + HRESULT SafeReadBytes(void *data, UInt32 size); + +public: + CArchiveHeader Header; + + IInStream *Stream; + IArchiveOpenCallback *Callback; + UInt64 NumFiles; + UInt64 NumBytes; + + HRESULT Open(const UInt64 *searchHeaderSizeLimit); + HRESULT GetNextItem(bool &filled, CItem &item); +}; + +static inline bool TestMarkerCandidate(const Byte *p, unsigned maxSize) +{ + if (p[0] != NSignature::kSig0 || p[1] != NSignature::kSig1) + return false; + UInt32 blockSize = Get16(p + 2); + p += 4; + if (p[6] != NFileHeader::NFileType::kArchiveHeader || + p[0] > blockSize || + maxSize < 2 + 2 + blockSize + 4 || + blockSize < kBlockSizeMin || blockSize > kBlockSizeMax || + p[28] > 8) // EncryptionVersion + return false; + // return (Get32(p + blockSize) == CrcCalc(p, blockSize)); + return true; +} + +static HRESULT FindAndReadMarker(ISequentialInStream *stream, const UInt64 *searchHeaderSizeLimit, UInt64 &position) +{ + position = 0; + + const int kMarkerSizeMin = 2 + 2 + kBlockSizeMin + 4; + const int kMarkerSizeMax = 2 + 2 + kBlockSizeMax + 4; + + CByteBuffer byteBuffer; + const UInt32 kBufSize = 1 << 16; + byteBuffer.SetCapacity(kBufSize); + Byte *buf = byteBuffer; + + size_t processedSize = kMarkerSizeMax; + RINOK(ReadStream(stream, buf, &processedSize)); + if (processedSize < kMarkerSizeMin) + return S_FALSE; + if (TestMarkerCandidate(buf, (unsigned)processedSize)) + return S_OK; + + UInt32 numBytesPrev = (UInt32)processedSize - 1; + memmove(buf, buf + 1, numBytesPrev); + UInt64 curTestPos = 1; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos > *searchHeaderSizeLimit) + return S_FALSE; + processedSize = kBufSize - numBytesPrev; + RINOK(ReadStream(stream, buf + numBytesPrev, &processedSize)); + UInt32 numBytesInBuffer = numBytesPrev + (UInt32)processedSize; + if (numBytesInBuffer < kMarkerSizeMin) + return S_FALSE; + UInt32 numTests = numBytesInBuffer - kMarkerSizeMin + 1; + UInt32 pos; + for (pos = 0; pos < numTests; pos++) + { + for (; buf[pos] != NSignature::kSig0 && pos < numTests; pos++); + if (pos == numTests) + break; + if (TestMarkerCandidate(buf + pos, numBytesInBuffer - pos)) + { + position = curTestPos + pos; + return S_OK; + } + } + curTestPos += pos; + numBytesPrev = numBytesInBuffer - numTests; + memmove(buf, buf + numTests, numBytesPrev); + } +} + +HRESULT CInArchive::SafeReadBytes(void *data, UInt32 size) +{ + size_t processed = size; + RINOK(ReadStream(Stream, data, &processed)); + if (processed != size) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); + return S_OK; +} + +HRESULT CInArchive::ReadBlock(bool &filled) +{ + filled = false; + Byte buf[2]; + RINOK(SafeReadBytes(buf, 2)); + _blockSize = Get16(buf); + if (_blockSize == 0) + return S_OK; + if (_blockSize > kBlockSizeMax) + throw CInArchiveException(CInArchiveException::kIncorrectArchive); + RINOK(SafeReadBytes(_block, _blockSize + 4)); + NumBytes += _blockSize + 6; + if (Get32(_block + _blockSize) != CrcCalc(_block, _blockSize)) + throw CInArchiveException(CInArchiveException::kCRCError); + filled = true; + return S_OK; +} + +HRESULT CInArchive::ReadSignatureAndBlock(bool &filled) +{ + Byte id[2]; + RINOK(SafeReadBytes(id, 2)); + if (id[0] != NSignature::kSig0 || id[1] != NSignature::kSig1) + throw CInArchiveException(CInArchiveException::kIncorrectArchive); + return ReadBlock(filled); +} + +HRESULT CInArchive::SkipExtendedHeaders() +{ + for (UInt32 i = 0;; i++) + { + bool filled; + RINOK(ReadBlock(filled)); + if (!filled) + return S_OK; + if (Callback && (i & 0xFF) == 0) + RINOK(Callback->SetCompleted(&NumFiles, &NumBytes)); + } +} + +HRESULT CInArchive::Open(const UInt64 *searchHeaderSizeLimit) +{ + UInt64 position = 0; + RINOK(FindAndReadMarker(Stream, searchHeaderSizeLimit, position)); + RINOK(Stream->Seek(position, STREAM_SEEK_SET, NULL)); + bool filled; + RINOK(ReadSignatureAndBlock(filled)); + if (!filled) + return S_FALSE; + RINOK(Header.Parse(_block, _blockSize)); + return SkipExtendedHeaders(); +} + +HRESULT CInArchive::GetNextItem(bool &filled, CItem &item) +{ + RINOK(ReadSignatureAndBlock(filled)); + if (!filled) + return S_OK; + filled = false; + RINOK(item.Parse(_block, _blockSize)); + /* + UInt32 extraData; + if ((header.Flags & NFileHeader::NFlags::kExtFile) != 0) + extraData = GetUi32(_block + pos); + */ + + RINOK(SkipExtendedHeaders()); + filled = true; + return S_OK; +} + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInArchive) + + INTERFACE_IInArchive(;) + + HRESULT Open2(IInStream *inStream, const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *callback); +private: + CInArchive _archive; + CObjectVector<CItem> _items; + CMyComPtr<IInStream> _stream; +}; + +const wchar_t *kHostOS[] = +{ + L"MSDOS", + L"PRIMOS", + L"UNIX", + L"AMIGA", + L"MAC", + L"OS/2", + L"APPLE GS", + L"ATARI ST", + L"NEXT", + L"VAX VMS", + L"WIN95" +}; + +const wchar_t *kUnknownOS = L"Unknown"; + +const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]); + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidName, VT_BSTR}, + { NULL, kpidCTime, VT_BSTR}, + { NULL, kpidMTime, VT_BSTR}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidComment, VT_BSTR} +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI4}, + { NULL, kpidPosition, VT_UI8}, + { NULL, kpidPackSize, VT_UI4}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidEncrypted, VT_BOOL}, + { NULL, kpidCRC, VT_UI4}, + { NULL, kpidMethod, VT_UI1}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidComment, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static void SetTime(UInt32 dosTime, NWindows::NCOM::CPropVariant &prop) +{ + if (dosTime == 0) + return; + FILETIME localFileTime, utc; + if (NTime::DosTimeToFileTime(dosTime, localFileTime)) + { + if (!LocalFileTimeToFileTime(&localFileTime, &utc)) + utc.dwHighDateTime = utc.dwLowDateTime = 0; + } + else + utc.dwHighDateTime = utc.dwLowDateTime = 0; + prop = utc; +} + +static void SetHostOS(Byte hostOS, NWindows::NCOM::CPropVariant &prop) +{ + prop = hostOS < kNumHostOSes ? kHostOS[hostOS] : kUnknownOS; +} + +static void SetUnicodeString(const AString &s, NWindows::NCOM::CPropVariant &prop) +{ + if (!s.IsEmpty()) + prop = MultiByteToUnicodeString(s, CP_OEMCP); +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidName: SetUnicodeString(_archive.Header.Name, prop); break; + case kpidCTime: SetTime(_archive.Header.CTime, prop); break; + case kpidMTime: SetTime(_archive.Header.MTime, prop); break; + case kpidHostOS: SetHostOS(_archive.Header.HostOS, prop); break; + case kpidComment: SetUnicodeString(_archive.Header.Comment, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItem &item = _items[index]; + switch(propID) + { + case kpidPath: prop = NItemName::GetOSName(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.Size; break; + case kpidPackSize: prop = item.PackSize; break; + case kpidPosition: if (item.IsSplitBefore() || item.IsSplitAfter()) prop = (UInt64)item.SplitPos; break; + case kpidAttrib: prop = item.GetWinAttributes(); break; + case kpidEncrypted: prop = item.IsEncrypted(); break; + case kpidCRC: prop = item.FileCRC; break; + case kpidMethod: prop = item.Method; break; + case kpidHostOS: SetHostOS(item.HostOS, prop); break; + case kpidMTime: SetTime(item.MTime, prop); break; + case kpidComment: SetUnicodeString(item.Comment, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Open2(IInStream *inStream, const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *callback) +{ + Close(); + + UInt64 endPos = 0; + if (callback != NULL) + { + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + } + + _archive.Stream = inStream; + _archive.Callback = callback; + _archive.NumFiles = _archive.NumBytes = 0; + + RINOK(_archive.Open(maxCheckStartPosition)); + if (callback != NULL) + RINOK(callback->SetTotal(NULL, &endPos)); + for (;;) + { + CItem item; + bool filled; + + + RINOK(_archive.GetNextItem(filled, item)); + + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &item.DataPosition)); + + if (!filled) + break; + _items.Add(item); + + if (inStream->Seek(item.PackSize, STREAM_SEEK_CUR, NULL) != S_OK) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); + + _archive.NumFiles = _items.Size(); + _archive.NumBytes = item.DataPosition; + + if (callback != NULL && _items.Size() % 100 == 0) + { + RINOK(callback->SetCompleted(&_archive.NumFiles, &_archive.NumBytes)); + } + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + HRESULT res; + try + { + res = Open2(inStream, maxCheckStartPosition, callback); + if (res == S_OK) + { + _stream = inStream; + return S_OK; + } + } + catch(const CInArchiveException &) { res = S_FALSE; } + Close(); + return res; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + UInt64 totalUnpacked = 0, totalPacked = 0; + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CItem &item = _items[allFilesMode ? i : indices[i]]; + totalUnpacked += item.Size; + totalPacked += item.PackSize; + } + extractCallback->SetTotal(totalUnpacked); + + totalUnpacked = totalPacked = 0; + UInt64 curUnpacked, curPacked; + + CMyComPtr<ICompressCoder> arj1Decoder; + CMyComPtr<ICompressCoder> arj2Decoder; + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *inStreamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(inStreamSpec); + inStreamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++, totalUnpacked += curUnpacked, totalPacked += curPacked) + { + lps->InSize = totalPacked; + lps->OutSize = totalUnpacked; + RINOK(lps->SetCur()); + + curUnpacked = curPacked = 0; + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (item.IsDir()) + { + // if (!testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + continue; + } + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + curUnpacked = item.Size; + curPacked = item.PackSize; + + { + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(); + + inStreamSpec->Init(item.PackSize); + + UInt64 pos; + _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos); + + HRESULT result = S_OK; + Int32 opRes = NExtract::NOperationResult::kOK; + + if (item.IsEncrypted()) + opRes = NExtract::NOperationResult::kUnSupportedMethod; + else + { + switch(item.Method) + { + case NFileHeader::NCompressionMethod::kStored: + { + result = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize) + result = S_FALSE; + break; + } + case NFileHeader::NCompressionMethod::kCompressed1a: + case NFileHeader::NCompressionMethod::kCompressed1b: + case NFileHeader::NCompressionMethod::kCompressed1c: + { + if (!arj1Decoder) + arj1Decoder = new NCompress::NArj::NDecoder1::CCoder; + result = arj1Decoder->Code(inStream, outStream, NULL, &curUnpacked, progress); + break; + } + case NFileHeader::NCompressionMethod::kCompressed2: + { + if (!arj2Decoder) + arj2Decoder = new NCompress::NArj::NDecoder2::CCoder; + result = arj2Decoder->Code(inStream, outStream, NULL, &curUnpacked, progress); + break; + } + default: + opRes = NExtract::NOperationResult::kUnSupportedMethod; + } + } + if (opRes == NExtract::NOperationResult::kOK) + { + if (result == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else + { + RINOK(result); + opRes = (outStreamSpec->GetCRC() == item.FileCRC) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError; + } + } + outStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Arj", L"arj", 0, 4, { 0x60, 0xEA }, 2, false, CreateArc, 0 }; + +REGISTER_ARC(Arj) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Bz2Handler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Bz2Handler.cpp new file mode 100644 index 000000000..98cbcc182 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Bz2Handler.cpp @@ -0,0 +1,423 @@ +// Bz2Handler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" + +#ifndef _7ZIP_ST +#include "../../Windows/System.h" +#endif + +#include "../Common/CreateCoder.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/BZip2Decoder.h" +#include "../Compress/BZip2Encoder.h" +#include "../Compress/CopyCoder.h" + +#include "Common/DummyOutStream.h" +#include "Common/ParseProperties.h" + +using namespace NWindows; + +namespace NArchive { +namespace NBz2 { + +static const UInt32 kNumPassesX1 = 1; +static const UInt32 kNumPassesX7 = 2; +static const UInt32 kNumPassesX9 = 7; + +static const UInt32 kDicSizeX1 = 100000; +static const UInt32 kDicSizeX3 = 500000; +static const UInt32 kDicSizeX5 = 900000; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public IOutArchive, + public ISetProperties, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CMyComPtr<ISequentialInStream> _seqStream; + UInt64 _packSize; + UInt64 _startPosition; + bool _packSizeDefined; + + UInt32 _level; + UInt32 _dicSize; + UInt32 _numPasses; + #ifndef _7ZIP_ST + UInt32 _numThreads; + #endif + + void InitMethodProperties() + { + _level = 5; + _dicSize = + _numPasses = 0xFFFFFFFF; + #ifndef _7ZIP_ST + _numThreads = NWindows::NSystem::GetNumberOfProcessors();; + #endif + } + +public: + MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties) + + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); + + CHandler() { InitMethodProperties(); } +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPackSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + try + { + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPosition)); + const int kSignatureSize = 3; + Byte buf[kSignatureSize]; + RINOK(ReadStream_FALSE(stream, buf, kSignatureSize)); + if (buf[0] != 'B' || buf[1] != 'Z' || buf[2] != 'h') + return S_FALSE; + + UInt64 endPosition; + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPosition)); + _packSize = endPosition - _startPosition; + _packSizeDefined = true; + _stream = stream; + _seqStream = stream; + } + catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + _seqStream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + if (_stream) + extractCallback->SetTotal(_packSize); + UInt64 currentTotalPacked = 0; + RINOK(extractCallback->SetCompleted(¤tTotalPacked)); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + NCompress::NBZip2::CDecoder *decoderSpec = new NCompress::NBZip2::CDecoder; + CMyComPtr<ICompressCoder> decoder = decoderSpec; + + if (_stream) + { + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + } + + decoderSpec->SetInStream(_seqStream); + + #ifndef _7ZIP_ST + RINOK(decoderSpec->SetNumberOfThreads(_numThreads)); + #endif + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + HRESULT result = S_OK; + + bool firstItem = true; + for (;;) + { + lps->InSize = currentTotalPacked; + lps->OutSize = outStreamSpec->GetSize(); + + RINOK(lps->SetCur()); + + bool isBz2; + result = decoderSpec->CodeResume(outStream, isBz2, progress); + + if (result != S_OK) + break; + if (!isBz2) + { + if (firstItem) + result = S_FALSE; + break; + } + firstItem = false; + + _packSize = currentTotalPacked = decoderSpec->GetInputProcessedSize(); + _packSizeDefined = true; + } + decoderSpec->ReleaseInStream(); + outStream.Release(); + + Int32 retResult; + if (result == S_OK) + retResult = NExtract::NOperationResult::kOK; + else if (result == S_FALSE) + retResult = NExtract::NOperationResult::kDataError; + else + return result; + return extractCallback->SetOperationResult(retResult); + + COM_TRY_END +} + +static HRESULT UpdateArchive( + UInt64 unpackSize, + ISequentialOutStream *outStream, + int indexInClient, + UInt32 dictionary, + UInt32 numPasses, + #ifndef _7ZIP_ST + UInt32 numThreads, + #endif + IArchiveUpdateCallback *updateCallback) +{ + RINOK(updateCallback->SetTotal(unpackSize)); + UInt64 complexity = 0; + RINOK(updateCallback->SetCompleted(&complexity)); + + CMyComPtr<ISequentialInStream> fileInStream; + + RINOK(updateCallback->GetStream(indexInClient, &fileInStream)); + + CLocalProgress *localProgressSpec = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec; + localProgressSpec->Init(updateCallback, true); + + NCompress::NBZip2::CEncoder *encoderSpec = new NCompress::NBZip2::CEncoder; + CMyComPtr<ICompressCoder> encoder = encoderSpec; + { + NWindows::NCOM::CPropVariant properties[] = + { + dictionary, + numPasses + #ifndef _7ZIP_ST + , numThreads + #endif + }; + PROPID propIDs[] = + { + NCoderPropID::kDictionarySize, + NCoderPropID::kNumPasses + #ifndef _7ZIP_ST + , NCoderPropID::kNumThreads + #endif + }; + RINOK(encoderSpec->SetCoderProperties(propIDs, properties, sizeof(propIDs) / sizeof(propIDs[0]))); + } + + RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress)); + + return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); +} + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) +{ + *type = NFileTimeType::kUnix; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + if (numItems != 1) + return E_INVALIDARG; + + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); + + if (IntToBool(newProps)) + { + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); + if (prop.vt == VT_BOOL) + { + if (prop.boolVal != VARIANT_FALSE) + return E_INVALIDARG; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + } + } + + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + + UInt32 dicSize = _dicSize; + if (dicSize == 0xFFFFFFFF) + dicSize = (_level >= 5 ? kDicSizeX5 : + (_level >= 3 ? kDicSizeX3 : + kDicSizeX1)); + + UInt32 numPasses = _numPasses; + if (numPasses == 0xFFFFFFFF) + numPasses = (_level >= 9 ? kNumPassesX9 : + (_level >= 7 ? kNumPassesX7 : + kNumPassesX1)); + + return UpdateArchive( + size, outStream, 0, dicSize, numPasses, + #ifndef _7ZIP_ST + _numThreads, + #endif + updateCallback); + } + if (indexInArchive != 0) + return E_INVALIDARG; + if (_stream) + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + return NCompress::CopyStream(_stream, outStream, NULL); +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + InitMethodProperties(); + #ifndef _7ZIP_ST + const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); + _numThreads = numProcessors; + #endif + + for (int i = 0; i < numProps; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + const PROPVARIANT &prop = values[i]; + if (name[0] == L'X') + { + UInt32 level = 9; + RINOK(ParsePropValue(name.Mid(1), prop, level)); + _level = level; + } + else if (name[0] == L'D') + { + UInt32 dicSize = kDicSizeX5; + RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize)); + _dicSize = dicSize; + } + else if (name.Left(4) == L"PASS") + { + UInt32 num = kNumPassesX9; + RINOK(ParsePropValue(name.Mid(4), prop, num)); + _numPasses = num; + } + else if (name.Left(2) == L"MT") + { + #ifndef _7ZIP_ST + RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads)); + #endif + } + else + return E_INVALIDARG; + } + return S_OK; +} + +static IInArchive *CreateArc() { return new CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"bzip2", L"bz2 bzip2 tbz2 tbz", L"* * .tar .tar", 2, { 'B', 'Z', 'h' }, 3, true, CreateArc, CreateArcOut }; + +REGISTER_ARC(BZip2) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.cpp new file mode 100644 index 000000000..12c73eb5f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.cpp @@ -0,0 +1,189 @@ +// CabBlockInStream.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" + +#include "Common/Defs.h" + +#include "../../Common/StreamUtils.h" + +#include "CabBlockInStream.h" + +namespace NArchive { +namespace NCab { + +static const UInt32 kBlockSize = (1 << 16); + +bool CCabBlockInStream::Create() +{ + if (!_buffer) + _buffer = (Byte *)::MyAlloc(kBlockSize); + return (_buffer != 0); +} + +CCabBlockInStream::~CCabBlockInStream() +{ + MyFree(_buffer); +} + +class CCheckSum2 +{ + UInt32 m_Value; + int m_Pos; + Byte m_Hist[4]; +public: + CCheckSum2(): m_Value(0){}; + void Init() { m_Value = 0; m_Pos = 0; } + void Update(const void *data, UInt32 size); + void FinishDataUpdate() + { + for (int i = 0; i < m_Pos; i++) + m_Value ^= ((UInt32)(m_Hist[i])) << (8 * (m_Pos - i - 1)); + } + void UpdateUInt32(UInt32 v) { m_Value ^= v; } + UInt32 GetResult() const { return m_Value; } +}; + +void CCheckSum2::Update(const void *data, UInt32 size) +{ + UInt32 checkSum = m_Value; + const Byte *dataPointer = (const Byte *)data; + + while (size != 0 && m_Pos != 0) + { + m_Hist[m_Pos] = *dataPointer++; + m_Pos = (m_Pos + 1) & 3; + size--; + if (m_Pos == 0) + for (int i = 0; i < 4; i++) + checkSum ^= ((UInt32)m_Hist[i]) << (8 * i); + } + + int numWords = size / 4; + + while (numWords-- != 0) + { + UInt32 temp = *dataPointer++; + temp |= ((UInt32)(*dataPointer++)) << 8; + temp |= ((UInt32)(*dataPointer++)) << 16; + temp |= ((UInt32)(*dataPointer++)) << 24; + checkSum ^= temp; + } + m_Value = checkSum; + + size &= 3; + + while (size != 0) + { + m_Hist[m_Pos] = *dataPointer++; + m_Pos = (m_Pos + 1) & 3; + size--; + } +} + +static const UInt32 kDataBlockHeaderSize = 8; + +class CTempCabInBuffer2 +{ +public: + Byte Buffer[kDataBlockHeaderSize]; + UInt32 Pos; + Byte ReadByte() + { + return Buffer[Pos++]; + } + UInt32 ReadUInt32() + { + UInt32 value = 0; + for (int i = 0; i < 4; i++) + value |= (((UInt32)ReadByte()) << (8 * i)); + return value; + } + UInt16 ReadUInt16() + { + UInt16 value = 0; + for (int i = 0; i < 2; i++) + value |= (((UInt16)ReadByte()) << (8 * i)); + return value; + } +}; + +HRESULT CCabBlockInStream::PreRead(UInt32 &packSize, UInt32 &unpackSize) +{ + CTempCabInBuffer2 inBuffer; + inBuffer.Pos = 0; + RINOK(ReadStream_FALSE(_stream, inBuffer.Buffer, kDataBlockHeaderSize)) + + UInt32 checkSum = inBuffer.ReadUInt32(); + packSize = inBuffer.ReadUInt16(); + unpackSize = inBuffer.ReadUInt16(); + if (ReservedSize != 0) + { + RINOK(ReadStream_FALSE(_stream, _buffer, ReservedSize)); + } + _pos = 0; + CCheckSum2 checkSumCalc; + checkSumCalc.Init(); + UInt32 packSize2 = packSize; + if (MsZip && _size == 0) + { + if (packSize < 2) + return S_FALSE; // bad block; + Byte sig[2]; + RINOK(ReadStream_FALSE(_stream, sig, 2)); + if (sig[0] != 0x43 || sig[1] != 0x4B) + return S_FALSE; + packSize2 -= 2; + checkSumCalc.Update(sig, 2); + } + + if (kBlockSize - _size < packSize2) + return S_FALSE; + + UInt32 curSize = packSize2; + if (curSize != 0) + { + size_t processedSizeLoc = curSize; + RINOK(ReadStream(_stream, _buffer + _size, &processedSizeLoc)); + checkSumCalc.Update(_buffer + _size, (UInt32)processedSizeLoc); + _size += (UInt32)processedSizeLoc; + if (processedSizeLoc != curSize) + return S_FALSE; + } + TotalPackSize = _size; + + checkSumCalc.FinishDataUpdate(); + + bool dataError; + if (checkSum == 0) + dataError = false; + else + { + checkSumCalc.UpdateUInt32(packSize | (((UInt32)unpackSize) << 16)); + dataError = (checkSumCalc.GetResult() != checkSum); + } + DataError |= dataError; + return dataError ? S_FALSE : S_OK; +} + +STDMETHODIMP CCabBlockInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != 0) + *processedSize = 0; + if (size == 0) + return S_OK; + if (_size != 0) + { + size = MyMin(_size, size); + memmove(data, _buffer + _pos, size); + _pos += size; + _size -= size; + if (processedSize != 0) + *processedSize = size; + return S_OK; + } + return S_OK; // no blocks data +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.h new file mode 100644 index 000000000..1db3835b4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabBlockInStream.h @@ -0,0 +1,44 @@ +// CabBlockInStream.cpp + +#ifndef __CABBLOCKINSTREAM_H +#define __CABBLOCKINSTREAM_H + +#include "Common/MyCom.h" +#include "../../IStream.h" + +namespace NArchive { +namespace NCab { + +class CCabBlockInStream: + public ISequentialInStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialInStream> _stream; + Byte *_buffer; + UInt32 _pos; + UInt32 _size; + +public: + UInt32 TotalPackSize; + UInt32 ReservedSize; + bool DataError; + bool MsZip; + + CCabBlockInStream(): _buffer(0), ReservedSize(0), MsZip(false), DataError(false), TotalPackSize(0) {} + ~CCabBlockInStream(); + bool Create(); + void SetStream(ISequentialInStream *stream) { _stream = stream; } + + void InitForNewFolder() { TotalPackSize = 0; } + void InitForNewBlock() { _size = 0; } + + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + HRESULT PreRead(UInt32 &packSize, UInt32 &unpackSize); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.cpp new file mode 100644 index 000000000..20f670d35 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.cpp @@ -0,0 +1,929 @@ +// CabHandler.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/Defs.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" +#include "Common/UTFConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" +#include "../../Compress/DeflateDecoder.h" +#include "../../Compress/LzxDecoder.h" +#include "../../Compress/QuantumDecoder.h" + +#include "../Common/ItemNameUtils.h" + +#include "CabBlockInStream.h" +#include "CabHandler.h" + +using namespace NWindows; + +namespace NArchive { +namespace NCab { + +// #define _CAB_DETAILS + +#ifdef _CAB_DETAILS +enum +{ + kpidBlockReal = kpidUserDefined +}; +#endif + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidBlock, VT_I4} + #ifdef _CAB_DETAILS + , + { L"BlockReal", kpidBlockReal, VT_UI4}, + { NULL, kpidOffset, VT_UI4}, + { NULL, kpidVolume, VT_UI4} + #endif +}; + +static const char *kMethods[] = +{ + "None", + "MSZip", + "Quantum", + "LZX" +}; + +static const int kNumMethods = sizeof(kMethods) / sizeof(kMethods[0]); +static const char *kUnknownMethod = "Unknown"; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + // { NULL, kpidSolid, VT_BOOL}, + { NULL, kpidNumBlocks, VT_UI4}, + { NULL, kpidNumVolumes, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + AString resString; + CRecordVector<Byte> ids; + int i; + for (int v = 0; v < m_Database.Volumes.Size(); v++) + { + const CDatabaseEx &de = m_Database.Volumes[v]; + for (i = 0; i < de.Folders.Size(); i++) + ids.AddToUniqueSorted(de.Folders[i].GetCompressionMethod()); + } + for (i = 0; i < ids.Size(); i++) + { + Byte id = ids[i]; + AString method = (id < kNumMethods) ? kMethods[id] : kUnknownMethod; + if (!resString.IsEmpty()) + resString += ' '; + resString += method; + } + prop = resString; + break; + } + // case kpidSolid: prop = _database.IsSolid(); break; + case kpidNumBlocks: + { + UInt32 numFolders = 0; + for (int v = 0; v < m_Database.Volumes.Size(); v++) + numFolders += m_Database.Volumes[v].Folders.Size(); + prop = numFolders; + break; + } + case kpidNumVolumes: + { + prop = (UInt32)m_Database.Volumes.Size(); + break; + } + } + 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; + + const CMvItem &mvItem = m_Database.Items[index]; + const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex]; + int itemIndex = mvItem.ItemIndex; + const CItem &item = db.Items[itemIndex]; + switch(propID) + { + case kpidPath: + { + UString unicodeName; + if (item.IsNameUTF()) + ConvertUTF8ToUnicode(item.Name, unicodeName); + else + unicodeName = MultiByteToUnicodeString(item.Name, CP_ACP); + prop = (const wchar_t *)NItemName::WinNameToOSName(unicodeName); + break; + } + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.Size; break; + case kpidAttrib: prop = item.GetWinAttributes(); break; + + case kpidMTime: + { + FILETIME localFileTime, utcFileTime; + if (NTime::DosTimeToFileTime(item.Time, localFileTime)) + { + if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime)) + utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; + } + else + utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; + prop = utcFileTime; + break; + } + + case kpidMethod: + { + UInt32 realFolderIndex = item.GetFolderIndex(db.Folders.Size()); + const CFolder &folder = db.Folders[realFolderIndex]; + int methodIndex = folder.GetCompressionMethod(); + AString method = (methodIndex < kNumMethods) ? kMethods[methodIndex] : kUnknownMethod; + if (methodIndex == NHeader::NCompressionMethodMajor::kLZX || + methodIndex == NHeader::NCompressionMethodMajor::kQuantum) + { + method += ':'; + char temp[32]; + ConvertUInt64ToString(folder.CompressionTypeMinor, temp); + method += temp; + } + prop = method; + break; + } + case kpidBlock: prop = (Int32)m_Database.GetFolderIndex(&mvItem); break; + + #ifdef _CAB_DETAILS + + case kpidBlockReal: prop = (UInt32)item.FolderIndex; break; + case kpidOffset: prop = (UInt32)item.Offset; break; + case kpidVolume: prop = (UInt32)mvItem.VolumeIndex; break; + + #endif + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +/* +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> m_OpenArchiveCallback; +public: + STDMETHOD(SetTotal)(const UInt64 *numFiles); + STDMETHOD(SetCompleted)(const UInt64 *numFiles); + void Init(IArchiveOpenCallback *openArchiveCallback) + { m_OpenArchiveCallback = openArchiveCallback; } +}; + +STDMETHODIMP CProgressImp::SetTotal(const UInt64 *numFiles) +{ + if (m_OpenArchiveCallback) + return m_OpenArchiveCallback->SetCompleted(numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CProgressImp::SetCompleted(const UInt64 *numFiles) +{ + if (m_OpenArchiveCallback) + return m_OpenArchiveCallback->SetCompleted(numFiles, NULL); + return S_OK; +} +*/ + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + HRESULT res = S_FALSE; + CInArchive archive; + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); + + CMyComPtr<IInStream> nextStream = inStream; + bool prevChecked = false; + UInt64 numItems = 0; + try + { + while (nextStream != 0) + { + CDatabaseEx db; + db.Stream = nextStream; + res = archive.Open(maxCheckStartPosition, db); + if (res == S_OK) + { + if (!m_Database.Volumes.IsEmpty()) + { + const CDatabaseEx &dbPrev = m_Database.Volumes[prevChecked ? m_Database.Volumes.Size() - 1 : 0]; + if (dbPrev.ArchiveInfo.SetID != db.ArchiveInfo.SetID || + dbPrev.ArchiveInfo.CabinetNumber + (prevChecked ? 1: - 1) != + db.ArchiveInfo.CabinetNumber) + res = S_FALSE; + } + } + if (res == S_OK) + m_Database.Volumes.Insert(prevChecked ? m_Database.Volumes.Size() : 0, db); + else if (res != S_FALSE) + return res; + else + { + if (m_Database.Volumes.IsEmpty()) + return S_FALSE; + if (prevChecked) + break; + prevChecked = true; + } + + numItems += db.Items.Size(); + RINOK(callback->SetCompleted(&numItems, NULL)); + + nextStream = 0; + for (;;) + { + const COtherArchive *otherArchive = 0; + if (!prevChecked) + { + const CInArchiveInfo &ai = m_Database.Volumes.Front().ArchiveInfo; + if (ai.IsTherePrev()) + otherArchive = &ai.PrevArc; + else + prevChecked = true; + } + if (otherArchive == 0) + { + const CInArchiveInfo &ai = m_Database.Volumes.Back().ArchiveInfo; + if (ai.IsThereNext()) + otherArchive = &ai.NextArc; + } + if (!otherArchive) + break; + const UString fullName = MultiByteToUnicodeString(otherArchive->FileName, CP_ACP); + if (!openVolumeCallback) + break; + + HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream); + if (result == S_OK) + break; + if (result != S_FALSE) + return result; + if (prevChecked) + break; + prevChecked = true; + } + } + if (res == S_OK) + { + m_Database.FillSortAndShrink(); + if (!m_Database.Check()) + res = S_FALSE; + } + } + catch(...) + { + res = S_FALSE; + } + if (res != S_OK) + { + Close(); + return res; + } + COM_TRY_END + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + m_Database.Clear(); + return S_OK; +} + +class CFolderOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +private: + const CMvDatabaseEx *m_Database; + const CRecordVector<bool> *m_ExtractStatuses; + + Byte *TempBuf; + UInt32 TempBufSize; + int NumIdenticalFiles; + bool TempBufMode; + UInt32 m_BufStartFolderOffset; + + int m_StartIndex; + int m_CurrentIndex; + CMyComPtr<IArchiveExtractCallback> m_ExtractCallback; + bool m_TestMode; + + CMyComPtr<ISequentialOutStream> m_RealOutStream; + + bool m_IsOk; + bool m_FileIsOpen; + UInt32 m_RemainFileSize; + UInt64 m_FolderSize; + UInt64 m_PosInFolder; + + void FreeTempBuf() + { + ::MyFree(TempBuf); + TempBuf = NULL; + } + + HRESULT OpenFile(); + HRESULT CloseFileWithResOp(Int32 resOp); + HRESULT CloseFile(); + HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK); +public: + HRESULT WriteEmptyFiles(); + + CFolderOutStream(): TempBuf(NULL) {} + ~CFolderOutStream() { FreeTempBuf(); } + void Init( + const CMvDatabaseEx *database, + const CRecordVector<bool> *extractStatuses, + int startIndex, + UInt64 folderSize, + IArchiveExtractCallback *extractCallback, + bool testMode); + HRESULT FlushCorrupted(); + HRESULT Unsupported(); + + UInt64 GetRemain() const { return m_FolderSize - m_PosInFolder; } + UInt64 GetPosInFolder() const { return m_PosInFolder; } +}; + +void CFolderOutStream::Init( + const CMvDatabaseEx *database, + const CRecordVector<bool> *extractStatuses, + int startIndex, + UInt64 folderSize, + IArchiveExtractCallback *extractCallback, + bool testMode) +{ + m_Database = database; + m_ExtractStatuses = extractStatuses; + m_StartIndex = startIndex; + m_FolderSize = folderSize; + + m_ExtractCallback = extractCallback; + m_TestMode = testMode; + + m_CurrentIndex = 0; + m_PosInFolder = 0; + m_FileIsOpen = false; + m_IsOk = true; + TempBufMode = false; + NumIdenticalFiles = 0; +} + +HRESULT CFolderOutStream::CloseFileWithResOp(Int32 resOp) +{ + m_RealOutStream.Release(); + m_FileIsOpen = false; + NumIdenticalFiles--; + return m_ExtractCallback->SetOperationResult(resOp); +} + +HRESULT CFolderOutStream::CloseFile() +{ + return CloseFileWithResOp(m_IsOk ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError); +} + +HRESULT CFolderOutStream::OpenFile() +{ + if (NumIdenticalFiles == 0) + { + const CMvItem &mvItem = m_Database->Items[m_StartIndex + m_CurrentIndex]; + const CItem &item = m_Database->Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + int numExtractItems = 0; + int curIndex; + for (curIndex = m_CurrentIndex; curIndex < m_ExtractStatuses->Size(); curIndex++) + { + const CMvItem &mvItem2 = m_Database->Items[m_StartIndex + curIndex]; + const CItem &item2 = m_Database->Volumes[mvItem2.VolumeIndex].Items[mvItem2.ItemIndex]; + if (item.Offset != item2.Offset || + item.Size != item2.Size || + item.Size == 0) + break; + if (!m_TestMode && (*m_ExtractStatuses)[curIndex]) + numExtractItems++; + } + NumIdenticalFiles = (curIndex - m_CurrentIndex); + if (NumIdenticalFiles == 0) + NumIdenticalFiles = 1; + TempBufMode = false; + if (numExtractItems > 1) + { + if (!TempBuf || item.Size > TempBufSize) + { + FreeTempBuf(); + TempBuf = (Byte *)MyAlloc(item.Size); + TempBufSize = item.Size; + if (TempBuf == NULL) + return E_OUTOFMEMORY; + } + TempBufMode = true; + m_BufStartFolderOffset = item.Offset; + } + else if (numExtractItems == 1) + { + while (NumIdenticalFiles && !(*m_ExtractStatuses)[m_CurrentIndex]) + { + CMyComPtr<ISequentialOutStream> stream; + RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &stream, NExtract::NAskMode::kSkip)); + if (stream) + return E_FAIL; + RINOK(m_ExtractCallback->PrepareOperation(NExtract::NAskMode::kSkip)); + m_CurrentIndex++; + m_FileIsOpen = true; + CloseFile(); + } + } + } + + Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract) : + NExtract::NAskMode::kSkip; + RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode)); + if (!m_RealOutStream && !m_TestMode) + askMode = NExtract::NAskMode::kSkip; + return m_ExtractCallback->PrepareOperation(askMode); +} + +HRESULT CFolderOutStream::WriteEmptyFiles() +{ + if (m_FileIsOpen) + return S_OK; + for (; m_CurrentIndex < m_ExtractStatuses->Size(); m_CurrentIndex++) + { + const CMvItem &mvItem = m_Database->Items[m_StartIndex + m_CurrentIndex]; + const CItem &item = m_Database->Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + UInt64 fileSize = item.Size; + if (fileSize != 0) + return S_OK; + HRESULT result = OpenFile(); + m_RealOutStream.Release(); + RINOK(result); + RINOK(m_ExtractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + return S_OK; +} + +// This is Write function +HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK) +{ + COM_TRY_BEGIN + UInt32 realProcessed = 0; + if (processedSize != NULL) + *processedSize = 0; + while (size != 0) + { + if (m_FileIsOpen) + { + UInt32 numBytesToWrite = MyMin(m_RemainFileSize, size); + HRESULT res = S_OK; + if (numBytesToWrite > 0) + { + if (!isOK) + m_IsOk = false; + if (m_RealOutStream) + { + UInt32 processedSizeLocal = 0; + res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal); + numBytesToWrite = processedSizeLocal; + } + if (TempBufMode && TempBuf) + memcpy(TempBuf + (m_PosInFolder - m_BufStartFolderOffset), data, numBytesToWrite); + } + realProcessed += numBytesToWrite; + if (processedSize != NULL) + *processedSize = realProcessed; + data = (const void *)((const Byte *)data + numBytesToWrite); + size -= numBytesToWrite; + m_RemainFileSize -= numBytesToWrite; + m_PosInFolder += numBytesToWrite; + if (res != S_OK) + return res; + if (m_RemainFileSize == 0) + { + RINOK(CloseFile()); + + while (NumIdenticalFiles) + { + HRESULT result = OpenFile(); + m_FileIsOpen = true; + m_CurrentIndex++; + if (result == S_OK && m_RealOutStream && TempBuf) + result = WriteStream(m_RealOutStream, TempBuf, (size_t)(m_PosInFolder - m_BufStartFolderOffset)); + + if (!TempBuf && TempBufMode && m_RealOutStream) + { + RINOK(CloseFileWithResOp(NExtract::NOperationResult::kUnSupportedMethod)); + } + else + { + RINOK(CloseFile()); + } + RINOK(result); + } + TempBufMode = false; + } + if (realProcessed > 0) + break; // with this break this function works as Write-Part + } + else + { + if (m_CurrentIndex >= m_ExtractStatuses->Size()) + return E_FAIL; + + const CMvItem &mvItem = m_Database->Items[m_StartIndex + m_CurrentIndex]; + const CItem &item = m_Database->Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + + m_RemainFileSize = item.Size; + + UInt32 fileOffset = item.Offset; + if (fileOffset < m_PosInFolder) + return E_FAIL; + if (fileOffset > m_PosInFolder) + { + UInt32 numBytesToWrite = MyMin(fileOffset - (UInt32)m_PosInFolder, size); + realProcessed += numBytesToWrite; + if (processedSize != NULL) + *processedSize = realProcessed; + data = (const void *)((const Byte *)data + numBytesToWrite); + size -= numBytesToWrite; + m_PosInFolder += numBytesToWrite; + } + if (fileOffset == m_PosInFolder) + { + RINOK(OpenFile()); + m_FileIsOpen = true; + m_CurrentIndex++; + m_IsOk = true; + } + } + } + return WriteEmptyFiles(); + COM_TRY_END +} + +STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + return Write2(data, size, processedSize, true); +} + +HRESULT CFolderOutStream::FlushCorrupted() +{ + const UInt32 kBufferSize = (1 << 10); + Byte buffer[kBufferSize]; + for (int i = 0; i < kBufferSize; i++) + buffer[i] = 0; + for (;;) + { + UInt64 remain = GetRemain(); + if (remain == 0) + return S_OK; + UInt32 size = (UInt32)MyMin(remain, (UInt64)kBufferSize); + UInt32 processedSizeLocal = 0; + RINOK(Write2(buffer, size, &processedSizeLocal, false)); + } +} + +HRESULT CFolderOutStream::Unsupported() +{ + while(m_CurrentIndex < m_ExtractStatuses->Size()) + { + HRESULT result = OpenFile(); + if (result != S_FALSE && result != S_OK) + return result; + m_RealOutStream.Release(); + RINOK(m_ExtractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + m_CurrentIndex++; + } + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testModeSpec, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = m_Database.Items.Size(); + if(numItems == 0) + return S_OK; + bool testMode = (testModeSpec != 0); + UInt64 totalUnPacked = 0; + + UInt32 i; + int lastFolder = -2; + UInt64 lastFolderSize = 0; + for(i = 0; i < numItems; i++) + { + int index = allFilesMode ? i : indices[i]; + const CMvItem &mvItem = m_Database.Items[index]; + const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + if (item.IsDir()) + continue; + int folderIndex = m_Database.GetFolderIndex(&mvItem); + if (folderIndex != lastFolder) + totalUnPacked += lastFolderSize; + lastFolder = folderIndex; + lastFolderSize = item.GetEndOffset(); + } + totalUnPacked += lastFolderSize; + + extractCallback->SetTotal(totalUnPacked); + + totalUnPacked = 0; + + UInt64 totalPacked = 0; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + NCompress::NDeflate::NDecoder::CCOMCoder *deflateDecoderSpec = NULL; + CMyComPtr<ICompressCoder> deflateDecoder; + + NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL; + CMyComPtr<ICompressCoder> lzxDecoder; + + NCompress::NQuantum::CDecoder *quantumDecoderSpec = NULL; + CMyComPtr<ICompressCoder> quantumDecoder; + + CCabBlockInStream *cabBlockInStreamSpec = new CCabBlockInStream(); + CMyComPtr<ISequentialInStream> cabBlockInStream = cabBlockInStreamSpec; + if (!cabBlockInStreamSpec->Create()) + return E_OUTOFMEMORY; + + CRecordVector<bool> extractStatuses; + for(i = 0; i < numItems;) + { + int index = allFilesMode ? i : indices[i]; + + const CMvItem &mvItem = m_Database.Items[index]; + const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex]; + int itemIndex = mvItem.ItemIndex; + const CItem &item = db.Items[itemIndex]; + + i++; + if (item.IsDir()) + { + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + int folderIndex = m_Database.GetFolderIndex(&mvItem); + if (folderIndex < 0) + { + // If we need previous archive + Int32 askMode= testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kDataError)); + continue; + } + int startIndex2 = m_Database.FolderStartFileIndex[folderIndex]; + int startIndex = startIndex2; + extractStatuses.Clear(); + for (; startIndex < index; startIndex++) + extractStatuses.Add(false); + extractStatuses.Add(true); + startIndex++; + UInt64 curUnpack = item.GetEndOffset(); + for(;i < numItems; i++) + { + int indexNext = allFilesMode ? i : indices[i]; + const CMvItem &mvItem = m_Database.Items[indexNext]; + const CItem &item = m_Database.Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + if (item.IsDir()) + continue; + int newFolderIndex = m_Database.GetFolderIndex(&mvItem); + + if (newFolderIndex != folderIndex) + break; + for (; startIndex < indexNext; startIndex++) + extractStatuses.Add(false); + extractStatuses.Add(true); + startIndex++; + curUnpack = item.GetEndOffset(); + } + + lps->OutSize = totalUnPacked; + lps->InSize = totalPacked; + RINOK(lps->SetCur()); + + CFolderOutStream *cabFolderOutStream = new CFolderOutStream; + CMyComPtr<ISequentialOutStream> outStream(cabFolderOutStream); + + const CFolder &folder = db.Folders[item.GetFolderIndex(db.Folders.Size())]; + + cabFolderOutStream->Init(&m_Database, &extractStatuses, startIndex2, + curUnpack, extractCallback, testMode); + + cabBlockInStreamSpec->MsZip = false; + switch(folder.GetCompressionMethod()) + { + case NHeader::NCompressionMethodMajor::kNone: + break; + case NHeader::NCompressionMethodMajor::kMSZip: + if(deflateDecoderSpec == NULL) + { + deflateDecoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder; + deflateDecoder = deflateDecoderSpec; + } + cabBlockInStreamSpec->MsZip = true; + break; + case NHeader::NCompressionMethodMajor::kLZX: + if(lzxDecoderSpec == NULL) + { + lzxDecoderSpec = new NCompress::NLzx::CDecoder; + lzxDecoder = lzxDecoderSpec; + } + RINOK(lzxDecoderSpec->SetParams(folder.CompressionTypeMinor)); + break; + case NHeader::NCompressionMethodMajor::kQuantum: + if(quantumDecoderSpec == NULL) + { + quantumDecoderSpec = new NCompress::NQuantum::CDecoder; + quantumDecoder = quantumDecoderSpec; + } + quantumDecoderSpec->SetParams(folder.CompressionTypeMinor); + break; + default: + { + RINOK(cabFolderOutStream->Unsupported()); + totalUnPacked += curUnpack; + continue; + } + } + + cabBlockInStreamSpec->InitForNewFolder(); + + HRESULT res = S_OK; + + { + int volIndex = mvItem.VolumeIndex; + int locFolderIndex = item.GetFolderIndex(db.Folders.Size()); + bool keepHistory = false; + bool keepInputBuffer = false; + for (UInt32 f = 0; cabFolderOutStream->GetRemain() != 0;) + { + if (volIndex >= m_Database.Volumes.Size()) + { + res = S_FALSE; + break; + } + + const CDatabaseEx &db = m_Database.Volumes[volIndex]; + const CFolder &folder = db.Folders[locFolderIndex]; + if (f == 0) + { + cabBlockInStreamSpec->SetStream(db.Stream); + cabBlockInStreamSpec->ReservedSize = db.ArchiveInfo.GetDataBlockReserveSize(); + RINOK(db.Stream->Seek(db.StartPosition + folder.DataStart, STREAM_SEEK_SET, NULL)); + } + if (f == folder.NumDataBlocks) + { + volIndex++; + locFolderIndex = 0; + f = 0; + continue; + } + f++; + + cabBlockInStreamSpec->DataError = false; + + if (!keepInputBuffer) + cabBlockInStreamSpec->InitForNewBlock(); + + UInt32 packSize, unpackSize; + res = cabBlockInStreamSpec->PreRead(packSize, unpackSize); + if (res == S_FALSE) + break; + RINOK(res); + keepInputBuffer = (unpackSize == 0); + if (keepInputBuffer) + continue; + + UInt64 totalUnPacked2 = totalUnPacked + cabFolderOutStream->GetPosInFolder(); + totalPacked += packSize; + + lps->OutSize = totalUnPacked2; + lps->InSize = totalPacked; + RINOK(lps->SetCur()); + + UInt64 unpackRemain = cabFolderOutStream->GetRemain(); + + const UInt32 kBlockSizeMax = (1 << 15); + if (unpackRemain > kBlockSizeMax) + unpackRemain = kBlockSizeMax; + if (unpackRemain > unpackSize) + unpackRemain = unpackSize; + + switch(folder.GetCompressionMethod()) + { + case NHeader::NCompressionMethodMajor::kNone: + res = copyCoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + break; + case NHeader::NCompressionMethodMajor::kMSZip: + deflateDecoderSpec->SetKeepHistory(keepHistory); + res = deflateDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + break; + case NHeader::NCompressionMethodMajor::kLZX: + lzxDecoderSpec->SetKeepHistory(keepHistory); + res = lzxDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + break; + case NHeader::NCompressionMethodMajor::kQuantum: + quantumDecoderSpec->SetKeepHistory(keepHistory); + res = quantumDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + break; + } + if (res != S_OK) + { + if (res != S_FALSE) + RINOK(res); + break; + } + keepHistory = true; + } + if (res == S_OK) + { + RINOK(cabFolderOutStream->WriteEmptyFiles()); + } + } + if (res != S_OK || cabFolderOutStream->GetRemain() != 0) + { + RINOK(cabFolderOutStream->FlushCorrupted()); + } + totalUnPacked += curUnpack; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = m_Database.Items.Size(); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.h new file mode 100644 index 000000000..1edcd11e2 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHandler.h @@ -0,0 +1,28 @@ +// CabHandler.h + +#ifndef __CAB_HANDLER_H +#define __CAB_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" +#include "CabIn.h" + +namespace NArchive { +namespace NCab { + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInArchive) + + INTERFACE_IInArchive(;) + +private: + CMvDatabaseEx m_Database; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.cpp new file mode 100644 index 000000000..0cba1b0b7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.cpp @@ -0,0 +1,15 @@ +// CabHeader.cpp + +#include "StdAfx.h" + +#include "CabHeader.h" + +namespace NArchive { +namespace NCab { +namespace NHeader { + +Byte kMarker[kMarkerSize] = {'M', 'S', 'C', 'F', 0, 0, 0, 0 }; + +// struct CSignatureInitializer { CSignatureInitializer() { kMarker[0]--; }; } g_SignatureInitializer; + +}}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.h new file mode 100644 index 000000000..0f0d2af35 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabHeader.h @@ -0,0 +1,44 @@ +// Archive/Cab/Header.h + +#ifndef __ARCHIVE_CAB_HEADER_H +#define __ARCHIVE_CAB_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NCab { +namespace NHeader { + +const unsigned kMarkerSize = 8; +extern Byte kMarker[kMarkerSize]; + +namespace NArchive +{ + namespace NFlags + { + const int kPrevCabinet = 0x0001; + const int kNextCabinet = 0x0002; + const int kReservePresent = 0x0004; + } +} + +namespace NCompressionMethodMajor +{ + const Byte kNone = 0; + const Byte kMSZip = 1; + const Byte kQuantum = 2; + const Byte kLZX = 3; +} + +const int kFileNameIsUTFAttributeMask = 0x80; + +namespace NFolderIndex +{ + const int kContinuedFromPrev = 0xFFFD; + const int kContinuedToNext = 0xFFFE; + const int kContinuedPrevAndNext = 0xFFFF; +} + +}}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.cpp new file mode 100644 index 000000000..c0bffa2d2 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.cpp @@ -0,0 +1,272 @@ +// Archive/CabIn.cpp + +#include "StdAfx.h" + +#include "../Common/FindSignature.h" + +#include "CabIn.h" + +namespace NArchive { +namespace NCab { + +Byte CInArchive::Read8() +{ + Byte b; + if (!inBuffer.ReadByte(b)) + throw CInArchiveException(CInArchiveException::kUnsupported); + return b; +} + +UInt16 CInArchive::Read16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = Read8(); + value |= (UInt16(b) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::Read32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + Byte b = Read8(); + value |= (UInt32(b) << (8 * i)); + } + return value; +} + +AString CInArchive::SafeReadName() +{ + AString name; + for (;;) + { + Byte b = Read8(); + if (b == 0) + return name; + name += (char)b; + } +} + +void CInArchive::ReadOtherArchive(COtherArchive &oa) +{ + oa.FileName = SafeReadName(); + oa.DiskName = SafeReadName(); +} + +void CInArchive::Skip(UInt32 size) +{ + while (size-- != 0) + Read8(); +} + +HRESULT CInArchive::Open(const UInt64 *searchHeaderSizeLimit, CDatabaseEx &db) +{ + IInStream *stream = db.Stream; + db.Clear(); + RINOK(stream->Seek(0, STREAM_SEEK_SET, &db.StartPosition)); + + RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize, + searchHeaderSizeLimit, db.StartPosition)); + + RINOK(stream->Seek(db.StartPosition + NHeader::kMarkerSize, STREAM_SEEK_SET, NULL)); + if (!inBuffer.Create(1 << 17)) + return E_OUTOFMEMORY; + inBuffer.SetStream(stream); + inBuffer.Init(); + + CInArchiveInfo &ai = db.ArchiveInfo; + + ai.Size = Read32(); + if (Read32() != 0) + return S_FALSE; + ai.FileHeadersOffset = Read32(); + if (Read32() != 0) + return S_FALSE; + + ai.VersionMinor = Read8(); + ai.VersionMajor = Read8(); + ai.NumFolders = Read16(); + ai.NumFiles = Read16(); + ai.Flags = Read16(); + if (ai.Flags > 7) + return S_FALSE; + ai.SetID = Read16(); + ai.CabinetNumber = Read16(); + + if (ai.ReserveBlockPresent()) + { + ai.PerCabinetAreaSize = Read16(); + ai.PerFolderAreaSize = Read8(); + ai.PerDataBlockAreaSize = Read8(); + + Skip(ai.PerCabinetAreaSize); + } + + { + if (ai.IsTherePrev()) + ReadOtherArchive(ai.PrevArc); + if (ai.IsThereNext()) + ReadOtherArchive(ai.NextArc); + } + + int i; + for (i = 0; i < ai.NumFolders; i++) + { + CFolder folder; + + folder.DataStart = Read32(); + folder.NumDataBlocks = Read16(); + folder.CompressionTypeMajor = Read8(); + folder.CompressionTypeMinor = Read8(); + + Skip(ai.PerFolderAreaSize); + db.Folders.Add(folder); + } + + RINOK(stream->Seek(db.StartPosition + ai.FileHeadersOffset, STREAM_SEEK_SET, NULL)); + + inBuffer.SetStream(stream); + inBuffer.Init(); + for (i = 0; i < ai.NumFiles; i++) + { + CItem item; + item.Size = Read32(); + item.Offset = Read32(); + item.FolderIndex = Read16(); + UInt16 pureDate = Read16(); + UInt16 pureTime = Read16(); + item.Time = ((UInt32(pureDate) << 16)) | pureTime; + item.Attributes = Read16(); + item.Name = SafeReadName(); + int folderIndex = item.GetFolderIndex(db.Folders.Size()); + if (folderIndex >= db.Folders.Size()) + return S_FALSE; + db.Items.Add(item); + } + return S_OK; +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param) +{ + const CMvDatabaseEx &mvDb = *(const CMvDatabaseEx *)param; + const CDatabaseEx &db1 = mvDb.Volumes[p1->VolumeIndex]; + const CDatabaseEx &db2 = mvDb.Volumes[p2->VolumeIndex]; + const CItem &item1 = db1.Items[p1->ItemIndex]; + const CItem &item2 = db2.Items[p2->ItemIndex];; + bool isDir1 = item1.IsDir(); + bool isDir2 = item2.IsDir(); + if (isDir1 && !isDir2) + return -1; + if (isDir2 && !isDir1) + return 1; + int f1 = mvDb.GetFolderIndex(p1); + int f2 = mvDb.GetFolderIndex(p2); + RINOZ(MyCompare(f1, f2)); + RINOZ(MyCompare(item1.Offset, item2.Offset)); + RINOZ(MyCompare(item1.Size, item2.Size)); + RINOZ(MyCompare(p1->VolumeIndex, p2->VolumeIndex)); + return MyCompare(p1->ItemIndex, p2->ItemIndex); +} + +bool CMvDatabaseEx::AreItemsEqual(int i1, int i2) +{ + const CMvItem *p1 = &Items[i1]; + const CMvItem *p2 = &Items[i2]; + const CDatabaseEx &db1 = Volumes[p1->VolumeIndex]; + const CDatabaseEx &db2 = Volumes[p2->VolumeIndex]; + const CItem &item1 = db1.Items[p1->ItemIndex]; + const CItem &item2 = db2.Items[p2->ItemIndex];; + return GetFolderIndex(p1) == GetFolderIndex(p2) && + item1.Offset == item2.Offset && + item1.Size == item2.Size && + item1.Name == item2.Name; +} + +void CMvDatabaseEx::FillSortAndShrink() +{ + Items.Clear(); + StartFolderOfVol.Clear(); + FolderStartFileIndex.Clear(); + int offset = 0; + for (int v = 0; v < Volumes.Size(); v++) + { + const CDatabaseEx &db = Volumes[v]; + int curOffset = offset; + if (db.IsTherePrevFolder()) + curOffset--; + StartFolderOfVol.Add(curOffset); + offset += db.GetNumberOfNewFolders(); + + CMvItem mvItem; + mvItem.VolumeIndex = v; + for (int i = 0 ; i < db.Items.Size(); i++) + { + mvItem.ItemIndex = i; + Items.Add(mvItem); + } + } + + Items.Sort(CompareMvItems, (void *)this); + int j = 1; + int i; + for (i = 1; i < Items.Size(); i++) + if (!AreItemsEqual(i, i -1)) + Items[j++] = Items[i]; + Items.DeleteFrom(j); + + for (i = 0; i < Items.Size(); i++) + { + int folderIndex = GetFolderIndex(&Items[i]); + if (folderIndex >= FolderStartFileIndex.Size()) + FolderStartFileIndex.Add(i); + } +} + +bool CMvDatabaseEx::Check() +{ + for (int v = 1; v < Volumes.Size(); v++) + { + const CDatabaseEx &db1 = Volumes[v]; + if (db1.IsTherePrevFolder()) + { + const CDatabaseEx &db0 = Volumes[v - 1]; + if (db0.Folders.IsEmpty() || db1.Folders.IsEmpty()) + return false; + const CFolder &f0 = db0.Folders.Back(); + const CFolder &f1 = db1.Folders.Front(); + if (f0.CompressionTypeMajor != f1.CompressionTypeMajor || + f0.CompressionTypeMinor != f1.CompressionTypeMinor) + return false; + } + } + UInt32 beginPos = 0; + UInt64 endPos = 0; + int prevFolder = -2; + for (int i = 0; i < Items.Size(); i++) + { + const CMvItem &mvItem = Items[i]; + int fIndex = GetFolderIndex(&mvItem); + if (fIndex >= FolderStartFileIndex.Size()) + return false; + const CItem &item = Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; + if (item.IsDir()) + continue; + int folderIndex = GetFolderIndex(&mvItem); + if (folderIndex != prevFolder) + prevFolder = folderIndex; + else if (item.Offset < endPos && + (item.Offset != beginPos || item.GetEndOffset() != endPos)) + return false; + beginPos = item.Offset; + endPos = item.GetEndOffset(); + } + return true; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.h new file mode 100644 index 000000000..1e9b188b5 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabIn.h @@ -0,0 +1,161 @@ +// Archive/CabIn.h + +#ifndef __ARCHIVE_CAB_IN_H +#define __ARCHIVE_CAB_IN_H + +#include "../../IStream.h" +#include "../../Common/InBuffer.h" +#include "CabHeader.h" +#include "CabItem.h" + +namespace NArchive { +namespace NCab { + +class CInArchiveException +{ +public: + enum CCauseType + { + kUnexpectedEndOfArchive = 0, + kIncorrectArchive, + kUnsupported + } Cause; + CInArchiveException(CCauseType cause) : Cause(cause) {} +}; + +struct COtherArchive +{ + AString FileName; + AString DiskName; +}; + +struct CArchiveInfo +{ + Byte VersionMinor; /* cabinet file format version, minor */ + Byte VersionMajor; /* cabinet file format version, major */ + UInt16 NumFolders; /* number of CFFOLDER entries in this cabinet */ + UInt16 NumFiles; /* number of CFFILE entries in this cabinet */ + UInt16 Flags; /* cabinet file option indicators */ + UInt16 SetID; /* must be the same for all cabinets in a set */ + UInt16 CabinetNumber; /* number of this cabinet file in a set */ + + bool ReserveBlockPresent() const { return (Flags & NHeader::NArchive::NFlags::kReservePresent) != 0; } + + bool IsTherePrev() const { return (Flags & NHeader::NArchive::NFlags::kPrevCabinet) != 0; } + bool IsThereNext() const { return (Flags & NHeader::NArchive::NFlags::kNextCabinet) != 0; } + + UInt16 PerCabinetAreaSize; // (optional) size of per-cabinet reserved area + Byte PerFolderAreaSize; // (optional) size of per-folder reserved area + Byte PerDataBlockAreaSize; // (optional) size of per-datablock reserved area + + Byte GetDataBlockReserveSize() const { return (Byte)(ReserveBlockPresent() ? PerDataBlockAreaSize : 0); } + + COtherArchive PrevArc; + COtherArchive NextArc; + + CArchiveInfo() + { + Clear(); + } + + void Clear() + { + PerCabinetAreaSize = 0; + PerFolderAreaSize = 0; + PerDataBlockAreaSize = 0; + } +}; + +struct CInArchiveInfo: public CArchiveInfo +{ + UInt32 Size; /* size of this cabinet file in bytes */ + UInt32 FileHeadersOffset; // offset of the first CFFILE entry +}; + + +struct CDatabase +{ + UInt64 StartPosition; + CInArchiveInfo ArchiveInfo; + CObjectVector<CFolder> Folders; + CObjectVector<CItem> Items; + + void Clear() + { + ArchiveInfo.Clear(); + Folders.Clear(); + Items.Clear(); + } + bool IsTherePrevFolder() const + { + for (int i = 0; i < Items.Size(); i++) + if (Items[i].ContinuedFromPrev()) + return true; + return false; + } + int GetNumberOfNewFolders() const + { + int res = Folders.Size(); + if (IsTherePrevFolder()) + res--; + return res; + } + UInt32 GetFileOffset(int index) const { return Items[index].Offset; } + UInt32 GetFileSize(int index) const { return Items[index].Size; } +}; + +struct CDatabaseEx: public CDatabase +{ + CMyComPtr<IInStream> Stream; +}; + +struct CMvItem +{ + int VolumeIndex; + int ItemIndex; +}; + +class CMvDatabaseEx +{ + bool AreItemsEqual(int i1, int i2); +public: + CObjectVector<CDatabaseEx> Volumes; + CRecordVector<CMvItem> Items; + CRecordVector<int> StartFolderOfVol; + CRecordVector<int> FolderStartFileIndex; + + int GetFolderIndex(const CMvItem *mvi) const + { + const CDatabaseEx &db = Volumes[mvi->VolumeIndex]; + return StartFolderOfVol[mvi->VolumeIndex] + + db.Items[mvi->ItemIndex].GetFolderIndex(db.Folders.Size()); + } + void Clear() + { + Volumes.Clear(); + Items.Clear(); + StartFolderOfVol.Clear(); + FolderStartFileIndex.Clear(); + } + void FillSortAndShrink(); + bool Check(); +}; + +class CInArchive +{ + CInBuffer inBuffer; + + Byte Read8(); + UInt16 Read16(); + UInt32 Read32(); + AString SafeReadName(); + void Skip(UInt32 size); + void ReadOtherArchive(COtherArchive &oa); + +public: + HRESULT Open(const UInt64 *searchHeaderSizeLimit, CDatabaseEx &db); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabItem.h new file mode 100644 index 000000000..63a1e856c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabItem.h @@ -0,0 +1,63 @@ +// Archive/CabItem.h + +#ifndef __ARCHIVE_CAB_ITEM_H +#define __ARCHIVE_CAB_ITEM_H + +#include "Common/Types.h" +#include "Common/MyString.h" +#include "CabHeader.h" + +namespace NArchive { +namespace NCab { + +struct CFolder +{ + UInt32 DataStart; // offset of the first CFDATA block in this folder + UInt16 NumDataBlocks; // number of CFDATA blocks in this folder + Byte CompressionTypeMajor; + Byte CompressionTypeMinor; + Byte GetCompressionMethod() const { return (Byte)(CompressionTypeMajor & 0xF); } +}; + +struct CItem +{ + AString Name; + UInt32 Offset; + UInt32 Size; + UInt32 Time; + UInt16 FolderIndex; + UInt16 Flags; + UInt16 Attributes; + + UInt64 GetEndOffset() const { return (UInt64)Offset + Size; } + UInt32 GetWinAttributes() const { return (Attributes & ~NHeader::kFileNameIsUTFAttributeMask); } + bool IsNameUTF() const { return (Attributes & NHeader::kFileNameIsUTFAttributeMask) != 0; } + bool IsDir() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } + + bool ContinuedFromPrev() const + { + return + (FolderIndex == NHeader::NFolderIndex::kContinuedFromPrev) || + (FolderIndex == NHeader::NFolderIndex::kContinuedPrevAndNext); + } + + bool ContinuedToNext() const + { + return + (FolderIndex == NHeader::NFolderIndex::kContinuedToNext) || + (FolderIndex == NHeader::NFolderIndex::kContinuedPrevAndNext); + } + + int GetFolderIndex(int numFolders) const + { + if (ContinuedFromPrev()) + return 0; + if (ContinuedToNext()) + return (numFolders - 1); + return FolderIndex; + } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabRegister.cpp new file mode 100644 index 000000000..15fe4099f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Cab/CabRegister.cpp @@ -0,0 +1,13 @@ +// CabRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "CabHandler.h" +static IInArchive *CreateArc() { return new NArchive::NCab::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Cab", L"cab", 0, 8, { 0x4D, 0x53, 0x43, 0x46 }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Cab) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.cpp new file mode 100644 index 000000000..a9e334b03 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.cpp @@ -0,0 +1,721 @@ +// ChmHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/Defs.h" +#include "Common/StringConvert.h" +#include "Common/UTFConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" +#include "../../Compress/LzxDecoder.h" + +#include "../Common/ItemNameUtils.h" + +#include "ChmHandler.h" + +using namespace NWindows; +using namespace NTime; + +namespace NArchive { +namespace NChm { + +// #define _CHM_DETAILS + +#ifdef _CHM_DETAILS + +enum +{ + kpidSection = kpidUserDefined +}; + +#endif + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidBlock, VT_UI4} + + #ifdef _CHM_DETAILS + , + { L"Section", kpidSection, VT_UI4}, + { NULL, kpidOffset, VT_UI4} + #endif +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidNumBlocks, VT_UI8} +}; + +IMP_IInArchive_Props + +IMP_IInArchive_ArcProps_NO +/* +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidNumBlocks: + { + UInt64 numBlocks = 0; + for (int i = 0; i < m_Database.Sections.Size(); i++) + { + const CSectionInfo &s = m_Database.Sections[i]; + for (int j = 0; j < s.Methods.Size(); j++) + { + const CMethodInfo &m = s.Methods[j]; + if (m.IsLzx()) + numBlocks += m.LzxInfo.ResetTable.GetNumBlocks(); + } + } + prop = numBlocks; + break; + } + } + 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; + if (m_Database.NewFormat) + { + switch(propID) + { + case kpidSize: + prop = (UInt64)m_Database.NewFormatString.Length(); + break; + } + prop.Detach(value); + return S_OK; + } + int entryIndex; + if (m_Database.LowLevel) + entryIndex = index; + else + entryIndex = m_Database.Indices[index]; + const CItem &item = m_Database.Items[entryIndex]; + switch(propID) + { + case kpidPath: + { + UString us; + if (ConvertUTF8ToUnicode(item.Name, us)) + { + if (!m_Database.LowLevel) + { + if (us.Length() > 1) + if (us[0] == L'/') + us.Delete(0); + } + prop = NItemName::GetOSName2(us); + } + break; + } + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.Size; break; + case kpidMethod: + { + if (!item.IsDir()) + if (item.Section == 0) + prop = L"Copy"; + else if (item.Section < m_Database.Sections.Size()) + prop = m_Database.Sections[(int)item.Section].GetMethodName(); + break; + } + case kpidBlock: + if (m_Database.LowLevel) + prop = item.Section; + else if (item.Section != 0) + prop = m_Database.GetFolder(index); + break; + + #ifdef _CHM_DETAILS + + case kpidSection: prop = (UInt32)item.Section; break; + case kpidOffset: prop = (UInt32)item.Offset; break; + + #endif + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> _callback; +public: + STDMETHOD(SetTotal)(const UInt64 *numFiles); + STDMETHOD(SetCompleted)(const UInt64 *numFiles); + CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}; +}; + +STDMETHODIMP CProgressImp::SetTotal(const UInt64 *numFiles) +{ + if (_callback) + return _callback->SetCompleted(numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CProgressImp::SetCompleted(const UInt64 *numFiles) +{ + if (_callback) + return _callback->SetCompleted(numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + m_Stream.Release(); + try + { + CInArchive archive; + // CProgressImp progressImp(openArchiveCallback); + RINOK(archive.Open(inStream, maxCheckStartPosition, m_Database)); + /* + if (m_Database.LowLevel) + return S_FALSE; + */ + m_Stream = inStream; + } + catch(...) + { + return S_FALSE; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + m_Database.Clear(); + m_Stream.Release(); + return S_OK; +} + +class CChmFolderOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK); + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + + UInt64 m_FolderSize; + UInt64 m_PosInFolder; + UInt64 m_PosInSection; + const CRecordVector<bool> *m_ExtractStatuses; + int m_StartIndex; + int m_CurrentIndex; + int m_NumFiles; + +private: + const CFilesDatabase *m_Database; + CMyComPtr<IArchiveExtractCallback> m_ExtractCallback; + bool m_TestMode; + + bool m_IsOk; + bool m_FileIsOpen; + UInt64 m_RemainFileSize; + CMyComPtr<ISequentialOutStream> m_RealOutStream; + + HRESULT OpenFile(); + HRESULT WriteEmptyFiles(); +public: + void Init( + const CFilesDatabase *database, + IArchiveExtractCallback *extractCallback, + bool testMode); + HRESULT FlushCorrupted(UInt64 maxSize); +}; + +void CChmFolderOutStream::Init( + const CFilesDatabase *database, + IArchiveExtractCallback *extractCallback, + bool testMode) +{ + m_Database = database; + m_ExtractCallback = extractCallback; + m_TestMode = testMode; + + m_CurrentIndex = 0; + m_FileIsOpen = false; +} + +HRESULT CChmFolderOutStream::OpenFile() +{ + Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract) : + NExtract::NAskMode::kSkip; + m_RealOutStream.Release(); + RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode)); + if (!m_RealOutStream && !m_TestMode) + askMode = NExtract::NAskMode::kSkip; + return m_ExtractCallback->PrepareOperation(askMode); +} + +HRESULT CChmFolderOutStream::WriteEmptyFiles() +{ + if (m_FileIsOpen) + return S_OK; + for (;m_CurrentIndex < m_NumFiles; m_CurrentIndex++) + { + UInt64 fileSize = m_Database->GetFileSize(m_StartIndex + m_CurrentIndex); + if (fileSize != 0) + return S_OK; + HRESULT result = OpenFile(); + m_RealOutStream.Release(); + RINOK(result); + RINOK(m_ExtractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + return S_OK; +} + +// This is WritePart function +HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK) +{ + UInt32 realProcessed = 0; + if (processedSize != NULL) + *processedSize = 0; + while(size != 0) + { + if (m_FileIsOpen) + { + UInt32 numBytesToWrite = (UInt32)MyMin(m_RemainFileSize, (UInt64)(size)); + HRESULT res = S_OK; + if (numBytesToWrite > 0) + { + if (!isOK) + m_IsOk = false; + if (m_RealOutStream) + { + UInt32 processedSizeLocal = 0; + res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal); + numBytesToWrite = processedSizeLocal; + } + } + realProcessed += numBytesToWrite; + if (processedSize != NULL) + *processedSize = realProcessed; + data = (const void *)((const Byte *)data + numBytesToWrite); + size -= numBytesToWrite; + m_RemainFileSize -= numBytesToWrite; + m_PosInSection += numBytesToWrite; + m_PosInFolder += numBytesToWrite; + if (res != S_OK) + return res; + if (m_RemainFileSize == 0) + { + m_RealOutStream.Release(); + RINOK(m_ExtractCallback->SetOperationResult( + m_IsOk ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + m_FileIsOpen = false; + } + if (realProcessed > 0) + break; // with this break this function works as write part + } + else + { + if (m_CurrentIndex >= m_NumFiles) + return E_FAIL; + int fullIndex = m_StartIndex + m_CurrentIndex; + m_RemainFileSize = m_Database->GetFileSize(fullIndex); + UInt64 fileOffset = m_Database->GetFileOffset(fullIndex); + if (fileOffset < m_PosInSection) + return E_FAIL; + if (fileOffset > m_PosInSection) + { + UInt32 numBytesToWrite = (UInt32)MyMin(fileOffset - m_PosInSection, UInt64(size)); + realProcessed += numBytesToWrite; + if (processedSize != NULL) + *processedSize = realProcessed; + data = (const void *)((const Byte *)data + numBytesToWrite); + size -= numBytesToWrite; + m_PosInSection += numBytesToWrite; + m_PosInFolder += numBytesToWrite; + } + if (fileOffset == m_PosInSection) + { + RINOK(OpenFile()); + m_FileIsOpen = true; + m_CurrentIndex++; + m_IsOk = true; + } + } + } + return WriteEmptyFiles(); +} + +STDMETHODIMP CChmFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + return Write2(data, size, processedSize, true); +} + +HRESULT CChmFolderOutStream::FlushCorrupted(UInt64 maxSize) +{ + const UInt32 kBufferSize = (1 << 10); + Byte buffer[kBufferSize]; + for (int i = 0; i < kBufferSize; i++) + buffer[i] = 0; + if (maxSize > m_FolderSize) + maxSize = m_FolderSize; + while (m_PosInFolder < maxSize) + { + UInt32 size = (UInt32)MyMin(maxSize - m_PosInFolder, (UInt64)kBufferSize); + UInt32 processedSizeLocal = 0; + RINOK(Write2(buffer, size, &processedSizeLocal, false)); + if (processedSizeLocal == 0) + return S_OK; + } + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testModeSpec, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + + if (allFilesMode) + numItems = m_Database.NewFormat ? 1: + (m_Database.LowLevel ? + m_Database.Items.Size(): + m_Database.Indices.Size()); + if (numItems == 0) + return S_OK; + bool testMode = (testModeSpec != 0); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + UInt32 i; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(m_Stream); + + if (m_Database.LowLevel) + { + UInt64 currentItemSize = 0; + UInt64 totalSize = 0; + if (m_Database.NewFormat) + totalSize = m_Database.NewFormatString.Length(); + else + for (i = 0; i < numItems; i++) + totalSize += m_Database.Items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + currentItemSize = 0; + lps->InSize = currentTotalSize; // Change it + lps->OutSize = currentTotalSize; + + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode= testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (m_Database.NewFormat) + { + if (index != 0) + return E_FAIL; + if (!testMode && !realOutStream) + continue; + if (!testMode) + { + UInt32 size = m_Database.NewFormatString.Length(); + RINOK(WriteStream(realOutStream, (const char *)m_Database.NewFormatString, size)); + } + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + const CItem &item = m_Database.Items[index]; + + currentItemSize = item.Size; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (item.Section != 0) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + + if (testMode) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + + RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + } + + UInt64 lastFolderIndex = ((UInt64)0 - 1); + for (i = 0; i < numItems; i++) + { + UInt32 index = allFilesMode ? i : indices[i]; + int entryIndex = m_Database.Indices[index]; + const CItem &item = m_Database.Items[entryIndex]; + UInt64 sectionIndex = item.Section; + if (item.IsDir() || item.Size == 0) + continue; + if (sectionIndex == 0) + { + currentTotalSize += item.Size; + continue; + } + const CSectionInfo §ion = m_Database.Sections[(int)item.Section]; + if (section.IsLzx()) + { + const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo; + UInt64 folderIndex = m_Database.GetFolder(index); + if (lastFolderIndex == folderIndex) + folderIndex++; + lastFolderIndex = m_Database.GetLastFolder(index); + for (; folderIndex <= lastFolderIndex; folderIndex++) + currentTotalSize += lzxInfo.GetFolderSize(); + } + } + + RINOK(extractCallback->SetTotal(currentTotalSize)); + + NCompress::NLzx::CDecoder *lzxDecoderSpec = 0; + CMyComPtr<ICompressCoder> lzxDecoder; + CChmFolderOutStream *chmFolderOutStream = 0; + CMyComPtr<ISequentialOutStream> outStream; + + currentTotalSize = 0; + + CRecordVector<bool> extractStatuses; + for (i = 0; i < numItems;) + { + RINOK(extractCallback->SetCompleted(¤tTotalSize)); + UInt32 index = allFilesMode ? i : indices[i]; + i++; + int entryIndex = m_Database.Indices[index]; + const CItem &item = m_Database.Items[entryIndex]; + UInt64 sectionIndex = item.Section; + Int32 askMode= testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + if (item.IsDir()) + { + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + lps->InSize = currentTotalSize; // Change it + lps->OutSize = currentTotalSize; + + if (item.Size == 0 || sectionIndex == 0) + { + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + Int32 opRes = NExtract::NOperationResult::kOK; + if (!testMode && item.Size != 0) + { + RINOK(m_Stream->Seek(m_Database.ContentOffset + item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize != item.Size) + opRes = NExtract::NOperationResult::kDataError; + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + currentTotalSize += item.Size; + continue; + } + + const CSectionInfo §ion = m_Database.Sections[(int)sectionIndex]; + + if (!section.IsLzx()) + { + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + + const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo; + + if (chmFolderOutStream == 0) + { + chmFolderOutStream = new CChmFolderOutStream; + outStream = chmFolderOutStream; + } + + chmFolderOutStream->Init(&m_Database, extractCallback, testMode); + + if (lzxDecoderSpec == NULL) + { + lzxDecoderSpec = new NCompress::NLzx::CDecoder; + lzxDecoder = lzxDecoderSpec; + } + + UInt64 folderIndex = m_Database.GetFolder(index); + + UInt64 compressedPos = m_Database.ContentOffset + section.Offset; + UInt32 numDictBits = lzxInfo.GetNumDictBits(); + RINOK(lzxDecoderSpec->SetParams(numDictBits)); + + const CItem *lastItem = &item; + extractStatuses.Clear(); + extractStatuses.Add(true); + + for (;; folderIndex++) + { + RINOK(extractCallback->SetCompleted(¤tTotalSize)); + + UInt64 startPos = lzxInfo.GetFolderPos(folderIndex); + UInt64 finishPos = lastItem->Offset + lastItem->Size; + UInt64 limitFolderIndex = lzxInfo.GetFolder(finishPos); + + lastFolderIndex = m_Database.GetLastFolder(index); + UInt64 folderSize = lzxInfo.GetFolderSize(); + UInt64 unPackSize = folderSize; + if (extractStatuses.IsEmpty()) + chmFolderOutStream->m_StartIndex = index + 1; + else + chmFolderOutStream->m_StartIndex = index; + if (limitFolderIndex == folderIndex) + { + for (; i < numItems; i++) + { + UInt32 nextIndex = allFilesMode ? i : indices[i]; + int entryIndex = m_Database.Indices[nextIndex]; + const CItem &nextItem = m_Database.Items[entryIndex]; + if (nextItem.Section != sectionIndex) + break; + UInt64 nextFolderIndex = m_Database.GetFolder(nextIndex); + if (nextFolderIndex != folderIndex) + break; + for (index++; index < nextIndex; index++) + extractStatuses.Add(false); + extractStatuses.Add(true); + index = nextIndex; + lastItem = &nextItem; + if (nextItem.Size != 0) + finishPos = nextItem.Offset + nextItem.Size; + lastFolderIndex = m_Database.GetLastFolder(index); + } + } + unPackSize = MyMin(finishPos - startPos, unPackSize); + + chmFolderOutStream->m_FolderSize = folderSize; + chmFolderOutStream->m_PosInFolder = 0; + chmFolderOutStream->m_PosInSection = startPos; + chmFolderOutStream->m_ExtractStatuses = &extractStatuses; + chmFolderOutStream->m_NumFiles = extractStatuses.Size(); + chmFolderOutStream->m_CurrentIndex = 0; + try + { + UInt64 startBlock = lzxInfo.GetBlockIndexFromFolderIndex(folderIndex); + const CResetTable &rt = lzxInfo.ResetTable; + UInt32 numBlocks = (UInt32)rt.GetNumBlocks(unPackSize); + for (UInt32 b = 0; b < numBlocks; b++) + { + UInt64 completedSize = currentTotalSize + chmFolderOutStream->m_PosInSection - startPos; + RINOK(extractCallback->SetCompleted(&completedSize)); + UInt64 bCur = startBlock + b; + if (bCur >= rt.ResetOffsets.Size()) + return E_FAIL; + UInt64 offset = rt.ResetOffsets[(int)bCur]; + UInt64 compressedSize; + rt.GetCompressedSizeOfBlock(bCur, compressedSize); + UInt64 rem = finishPos - chmFolderOutStream->m_PosInSection; + if (rem > rt.BlockSize) + rem = rt.BlockSize; + RINOK(m_Stream->Seek(compressedPos + offset, STREAM_SEEK_SET, NULL)); + streamSpec->SetStream(m_Stream); + streamSpec->Init(compressedSize); + lzxDecoderSpec->SetKeepHistory(b > 0); + HRESULT res = lzxDecoder->Code(inStream, outStream, NULL, &rem, NULL); + if (res != S_OK) + { + if (res != S_FALSE) + return res; + throw 1; + } + } + } + catch(...) + { + RINOK(chmFolderOutStream->FlushCorrupted(unPackSize)); + } + currentTotalSize += folderSize; + if (folderIndex == lastFolderIndex) + break; + extractStatuses.Clear(); + } + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = m_Database.NewFormat ? 1: + (m_Database.LowLevel ? + m_Database.Items.Size(): + m_Database.Indices.Size()); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.h new file mode 100644 index 000000000..440c50f11 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHandler.h @@ -0,0 +1,29 @@ +// ChmHandler.h + +#ifndef __ARCHIVE_CHM_HANDLER_H +#define __ARCHIVE_CHM_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" +#include "ChmIn.h" + +namespace NArchive { +namespace NChm { + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInArchive) + + INTERFACE_IInArchive(;) + +private: + CFilesDatabase m_Database; + CMyComPtr<IInStream> m_Stream; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.cpp new file mode 100644 index 000000000..e8dc9f3e8 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.cpp @@ -0,0 +1,24 @@ +// Archive/Chm/Header.h + +#include "StdAfx.h" + +#include "ChmHeader.h" + +namespace NArchive{ +namespace NChm{ +namespace NHeader{ + +UInt32 kItsfSignature = 0x46535449 + 1; +UInt32 kItolSignature = 0x4C4F5449 + 1; +static class CSignatureInitializer +{ +public: + CSignatureInitializer() + { + kItsfSignature--; + kItolSignature--; + } +}g_SignatureInitializer; + + +}}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.h new file mode 100644 index 000000000..9f1bd42b6 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmHeader.h @@ -0,0 +1,28 @@ +// Archive/Chm/Header.h + +#ifndef __ARCHIVE_CHM_HEADER_H +#define __ARCHIVE_CHM_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NChm { +namespace NHeader{ + +const UInt32 kItspSignature = 0x50535449; +const UInt32 kPmglSignature = 0x4C474D50; +const UInt32 kLzxcSignature = 0x43585A4C; + +const UInt32 kIfcmSignature = 0x4D434649; +const UInt32 kAollSignature = 0x4C4C4F41; +const UInt32 kCaolSignature = 0x4C4F4143; + +extern UInt32 kItsfSignature; + +extern UInt32 kItolSignature; +const UInt32 kItlsSignature = 0x534C5449; +UInt64 inline GetHxsSignature() { return ((UInt64)kItlsSignature << 32) | kItolSignature; } + +}}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.cpp new file mode 100644 index 000000000..d52b9ba6c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.cpp @@ -0,0 +1,937 @@ +// Archive/ChmIn.cpp + +#include "StdAfx.h" + +#include "Common/IntToString.h" +#include "Common/UTFConvert.h" + +#include "../../Common/LimitedStreams.h" + +#include "ChmIn.h" + +namespace NArchive { +namespace NChm { + +// define CHM_LOW, if you want to see low level items +// #define CHM_LOW + +static const GUID kChmLzxGuid = { 0x7FC28940, 0x9D31, 0x11D0, { 0x9B, 0x27, 0x00, 0xA0, 0xC9, 0x1E, 0x9C, 0x7C } }; +static const GUID kHelp2LzxGuid = { 0x0A9007C6, 0x4076, 0x11D3, { 0x87, 0x89, 0x00, 0x00, 0xF8, 0x10, 0x57, 0x54 } }; +static const GUID kDesGuid = { 0x67F6E4A2, 0x60BF, 0x11D3, { 0x85, 0x40, 0x00, 0xC0, 0x4F, 0x58, 0xC3, 0xCF } }; + +static bool AreGuidsEqual(REFGUID g1, REFGUID g2) +{ + if (g1.Data1 != g2.Data1 || + g1.Data2 != g2.Data2 || + g1.Data3 != g2.Data3) + return false; + for (int i = 0; i < 8; i++) + if (g1.Data4[i] != g2.Data4[i]) + return false; + return true; +} + +static char GetHex(Byte value) +{ + return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); +} + +static void PrintByte(Byte b, AString &s) +{ + s += GetHex(b >> 4); + s += GetHex(b & 0xF); +} + +static void PrintUInt16(UInt16 v, AString &s) +{ + PrintByte((Byte)(v >> 8), s); + PrintByte((Byte)v, s); +} + +static void PrintUInt32(UInt32 v, AString &s) +{ + PrintUInt16((UInt16)(v >> 16), s); + PrintUInt16((UInt16)v, s); +} + +AString CMethodInfo::GetGuidString() const +{ + AString s; + s += '{'; + PrintUInt32(Guid.Data1, s); + s += '-'; + PrintUInt16(Guid.Data2, s); + s += '-'; + PrintUInt16(Guid.Data3, s); + s += '-'; + PrintByte(Guid.Data4[0], s); + PrintByte(Guid.Data4[1], s); + s += '-'; + for (int i = 2; i < 8; i++) + PrintByte(Guid.Data4[i], s); + s += '}'; + return s; +} + +bool CMethodInfo::IsLzx() const +{ + if (AreGuidsEqual(Guid, kChmLzxGuid)) + return true; + return AreGuidsEqual(Guid, kHelp2LzxGuid); +} + +bool CMethodInfo::IsDes() const +{ + return AreGuidsEqual(Guid, kDesGuid); +} + +UString CMethodInfo::GetName() const +{ + UString s; + if (IsLzx()) + { + s = L"LZX:"; + wchar_t temp[16]; + ConvertUInt32ToString(LzxInfo.GetNumDictBits(), temp); + s += temp; + } + else + { + AString s2; + if (IsDes()) + s2 = "DES"; + else + { + s2 = GetGuidString(); + if (ControlData.GetCapacity() > 0) + { + s2 += ':'; + for (size_t i = 0; i < ControlData.GetCapacity(); i++) + PrintByte(ControlData[i], s2); + } + } + ConvertUTF8ToUnicode(s2, s); + } + return s; +} + +bool CSectionInfo::IsLzx() const +{ + if (Methods.Size() != 1) + return false; + return Methods[0].IsLzx(); +} + +UString CSectionInfo::GetMethodName() const +{ + UString s; + if (!IsLzx()) + { + UString temp; + if (ConvertUTF8ToUnicode(Name, temp)) + s += temp; + s += L": "; + } + for (int i = 0; i < Methods.Size(); i++) + { + if (i != 0) + s += L' '; + s += Methods[i].GetName(); + } + return s; +} + +Byte CInArchive::ReadByte() +{ + Byte b; + if (!_inBuffer.ReadByte(b)) + throw 1; + return b; +} + +void CInArchive::Skip(size_t size) +{ + while (size-- != 0) + ReadByte(); +} + +void CInArchive::ReadBytes(Byte *data, UInt32 size) +{ + for (UInt32 i = 0; i < size; i++) + data[i] = ReadByte(); +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + value |= ((UInt16)(ReadByte()) << (8 * i)); + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + value |= ((UInt32)(ReadByte()) << (8 * i)); + return value; +} + +UInt64 CInArchive::ReadUInt64() +{ + UInt64 value = 0; + for (int i = 0; i < 8; i++) + value |= ((UInt64)(ReadByte()) << (8 * i)); + return value; +} + +UInt64 CInArchive::ReadEncInt() +{ + UInt64 val = 0;; + for (int i = 0; i < 10; i++) + { + Byte b = ReadByte(); + val |= (b & 0x7F); + if (b < 0x80) + return val; + val <<= 7; + } + throw 1; +} + +void CInArchive::ReadGUID(GUID &g) +{ + g.Data1 = ReadUInt32(); + g.Data2 = ReadUInt16(); + g.Data3 = ReadUInt16(); + ReadBytes(g.Data4, 8); +} + +void CInArchive::ReadString(int size, AString &s) +{ + s.Empty(); + while(size-- != 0) + { + char c = (char)ReadByte(); + if (c == 0) + { + Skip(size); + return; + } + s += c; + } +} + +void CInArchive::ReadUString(int size, UString &s) +{ + s.Empty(); + while(size-- != 0) + { + wchar_t c = ReadUInt16(); + if (c == 0) + { + Skip(2 * size); + return; + } + s += c; + } +} + +HRESULT CInArchive::ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size) +{ + RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL)); + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> limitedStream(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(size); + _inBuffer.SetStream(limitedStream); + _inBuffer.Init(); + return S_OK; +} + +HRESULT CInArchive::ReadDirEntry(CDatabase &database) +{ + CItem item; + UInt64 nameLength = ReadEncInt(); + if (nameLength == 0 || nameLength >= 0x10000000) + return S_FALSE; + ReadString((int)nameLength, item.Name); + item.Section = ReadEncInt(); + item.Offset = ReadEncInt(); + item.Size = ReadEncInt(); + database.Items.Add(item); + return S_OK; +} + +HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database) +{ + UInt32 headerSize = ReadUInt32(); + if (headerSize != 0x60) + return S_FALSE; + UInt32 unknown1 = ReadUInt32(); + if (unknown1 != 0 && unknown1 != 1) // it's 0 in one .sll file + return S_FALSE; + /* UInt32 timeStamp = */ ReadUInt32(); + // Considered as a big-endian DWORD, it appears to contain seconds (MSB) and + // fractional seconds (second byte). + // The third and fourth bytes may contain even more fractional bits. + // The 4 least significant bits in the last byte are constant. + /* UInt32 lang = */ ReadUInt32(); + GUID g; + ReadGUID(g); // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC} + ReadGUID(g); // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC} + const int kNumSections = 2; + UInt64 sectionOffsets[kNumSections]; + UInt64 sectionSizes[kNumSections]; + int i; + for (i = 0; i < kNumSections; i++) + { + sectionOffsets[i] = ReadUInt64(); + sectionSizes[i] = ReadUInt64(); + } + // if (chmVersion == 3) + database.ContentOffset = ReadUInt64(); + /* + else + database.ContentOffset = _startPosition + 0x58 + */ + + /* + // Section 0 + ReadChunk(inStream, sectionOffsets[0], sectionSizes[0]); + if (sectionSizes[0] != 0x18) + return S_FALSE; + ReadUInt32(); // unknown: 01FE + ReadUInt32(); // unknown: 0 + UInt64 fileSize = ReadUInt64(); + ReadUInt32(); // unknown: 0 + ReadUInt32(); // unknown: 0 + */ + + // Section 1: The Directory Listing + ReadChunk(inStream, sectionOffsets[1], sectionSizes[1]); + if (ReadUInt32() != NHeader::kItspSignature) + return S_FALSE; + if (ReadUInt32() != 1) // version + return S_FALSE; + /* UInt32 dirHeaderSize = */ ReadUInt32(); + ReadUInt32(); // 0x0A (unknown) + UInt32 dirChunkSize = ReadUInt32(); // $1000 + if (dirChunkSize < 32) + return S_FALSE; + /* UInt32 density = */ ReadUInt32(); // "Density" of quickref section, usually 2. + /* UInt32 depth = */ ReadUInt32(); // Depth of the index tree: 1 there is no index, + // 2 if there is one level of PMGI chunks. + + /* UInt32 chunkNumber = */ ReadUInt32(); // Chunk number of root index chunk, -1 if there is none + // (though at least one file has 0 despite there being no + // index chunk, probably a bug.) + /* UInt32 firstPmglChunkNumber = */ ReadUInt32(); // Chunk number of first PMGL (listing) chunk + /* UInt32 lastPmglChunkNumber = */ ReadUInt32(); // Chunk number of last PMGL (listing) chunk + ReadUInt32(); // -1 (unknown) + UInt32 numDirChunks = ReadUInt32(); // Number of directory chunks (total) + /* UInt32 windowsLangId = */ ReadUInt32(); + ReadGUID(g); // {5D02926A-212E-11D0-9DF9-00A0C922E6EC} + ReadUInt32(); // 0x54 (This is the length again) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + + for (UInt32 ci = 0; ci < numDirChunks; ci++) + { + UInt64 chunkPos = _inBuffer.GetProcessedSize(); + if (ReadUInt32() == NHeader::kPmglSignature) + { + // The quickref area is written backwards from the end of the chunk. + // One quickref entry exists for every n entries in the file, where n + // is calculated as 1 + (1 << quickref density). So for density = 2, n = 5. + + UInt32 quickrefLength = ReadUInt32(); // Length of free space and/or quickref area at end of directory chunk + if (quickrefLength > dirChunkSize || quickrefLength < 2) + return S_FALSE; + ReadUInt32(); // Always 0 + ReadUInt32(); // Chunk number of previous listing chunk when reading + // directory in sequence (-1 if this is the first listing chunk) + ReadUInt32(); // Chunk number of next listing chunk when reading + // directory in sequence (-1 if this is the last listing chunk) + int numItems = 0; + for (;;) + { + UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos; + UInt32 offsetLimit = dirChunkSize - quickrefLength; + if (offset > offsetLimit) + return S_FALSE; + if (offset == offsetLimit) + break; + RINOK(ReadDirEntry(database)); + numItems++; + } + Skip(quickrefLength - 2); + if (ReadUInt16() != numItems) + return S_FALSE; + } + else + Skip(dirChunkSize - 4); + } + return S_OK; +} + +HRESULT CInArchive::OpenHelp2(IInStream *inStream, CDatabase &database) +{ + if (ReadUInt32() != 1) // version + return S_FALSE; + if (ReadUInt32() != 0x28) // Location of header section table + return S_FALSE; + UInt32 numHeaderSections = ReadUInt32(); + const int kNumHeaderSectionsMax = 5; + if (numHeaderSections != kNumHeaderSectionsMax) + return S_FALSE; + ReadUInt32(); // Length of post-header table + GUID g; + ReadGUID(g); // {0A9007C1-4076-11D3-8789-0000F8105754} + + // header section table + UInt64 sectionOffsets[kNumHeaderSectionsMax]; + UInt64 sectionSizes[kNumHeaderSectionsMax]; + UInt32 i; + for (i = 0; i < numHeaderSections; i++) + { + sectionOffsets[i] = ReadUInt64(); + sectionSizes[i] = ReadUInt64(); + } + + // Post-Header + ReadUInt32(); // 2 + ReadUInt32(); // 0x98: offset to CAOL from beginning of post-header) + // ----- Directory information + ReadUInt64(); // Chunk number of top-level AOLI chunk in directory, or -1 + ReadUInt64(); // Chunk number of first AOLL chunk in directory + ReadUInt64(); // Chunk number of last AOLL chunk in directory + ReadUInt64(); // 0 (unknown) + ReadUInt32(); // $2000 (Directory chunk size of directory) + ReadUInt32(); // Quickref density for main directory, usually 2 + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // Depth of main directory index tree + // 1 there is no index, 2 if there is one level of AOLI chunks. + ReadUInt64(); // 0 (unknown) + UInt64 numDirEntries = ReadUInt64(); // Number of directory entries + // ----- Directory Index Information + ReadUInt64(); // -1 (unknown, probably chunk number of top-level AOLI in directory index) + ReadUInt64(); // Chunk number of first AOLL chunk in directory index + ReadUInt64(); // Chunk number of last AOLL chunk in directory index + ReadUInt64(); // 0 (unknown) + ReadUInt32(); // $200 (Directory chunk size of directory index) + ReadUInt32(); // Quickref density for directory index, usually 2 + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // Depth of directory index index tree. + ReadUInt64(); // Possibly flags -- sometimes 1, sometimes 0. + ReadUInt64(); // Number of directory index entries (same as number of AOLL + // chunks in main directory) + + // (The obvious guess for the following two fields, which recur in a number + // of places, is they are maximum sizes for the directory and directory index. + // However, I have seen no direct evidence that this is the case.) + + ReadUInt32(); // $100000 (Same as field following chunk size in directory) + ReadUInt32(); // $20000 (Same as field following chunk size in directory index) + + ReadUInt64(); // 0 (unknown) + if (ReadUInt32() != NHeader::kCaolSignature) + return S_FALSE; + if (ReadUInt32() != 2) // (Most likely a version number) + return S_FALSE; + UInt32 caolLength = ReadUInt32(); // $50 (Length of the CAOL section, which includes the ITSF section) + if (caolLength >= 0x2C) + { + /* UInt32 c7 = */ ReadUInt16(); // Unknown. Remains the same when identical files are built. + // Does not appear to be a checksum. Many files have + // 'HH' (HTML Help?) here, indicating this may be a compiler ID + // field. But at least one ITOL/ITLS compiler does not set this + // field to a constant value. + ReadUInt16(); // 0 (Unknown. Possibly part of 00A4 field) + ReadUInt32(); // Unknown. Two values have been seen -- $43ED, and 0. + ReadUInt32(); // $2000 (Directory chunk size of directory) + ReadUInt32(); // $200 (Directory chunk size of directory index) + ReadUInt32(); // $100000 (Same as field following chunk size in directory) + ReadUInt32(); // $20000 (Same as field following chunk size in directory index) + ReadUInt32(); // 0 (unknown) + ReadUInt32(); // 0 (Unknown) + if (caolLength == 0x2C) + { + database.ContentOffset = 0; + database.NewFormat = true; + } + else if (caolLength == 0x50) + { + ReadUInt32(); // 0 (Unknown) + if (ReadUInt32() != NHeader::kItsfSignature) + return S_FALSE; + if (ReadUInt32() != 4) // $4 (Version number -- CHM uses 3) + return S_FALSE; + if (ReadUInt32() != 0x20) // $20 (length of ITSF) + return S_FALSE; + UInt32 unknown = ReadUInt32(); + if (unknown != 0 && unknown != 1) // = 0 for some HxW files, 1 in other cases; + return S_FALSE; + database.ContentOffset = _startPosition + ReadUInt64(); + /* UInt32 timeStamp = */ ReadUInt32(); + // A timestamp of some sort. + // Considered as a big-endian DWORD, it appears to contain + // seconds (MSB) and fractional seconds (second byte). + // The third and fourth bytes may contain even more fractional + // bits. The 4 least significant bits in the last byte are constant. + /* UInt32 lang = */ ReadUInt32(); // BE? + } + else + return S_FALSE; + } + + /* + // Section 0 + ReadChunk(inStream, _startPosition + sectionOffsets[0], sectionSizes[0]); + if (sectionSizes[0] != 0x18) + return S_FALSE; + ReadUInt32(); // unknown: 01FE + ReadUInt32(); // unknown: 0 + UInt64 fileSize = ReadUInt64(); + ReadUInt32(); // unknown: 0 + ReadUInt32(); // unknown: 0 + */ + + // Section 1: The Directory Listing + ReadChunk(inStream, _startPosition + sectionOffsets[1], sectionSizes[1]); + if (ReadUInt32() != NHeader::kIfcmSignature) + return S_FALSE; + if (ReadUInt32() != 1) // (probably a version number) + return S_FALSE; + UInt32 dirChunkSize = ReadUInt32(); // $2000 + if (dirChunkSize < 64) + return S_FALSE; + ReadUInt32(); // $100000 (unknown) + ReadUInt32(); // -1 (unknown) + ReadUInt32(); // -1 (unknown) + UInt32 numDirChunks = ReadUInt32(); + ReadUInt32(); // 0 (unknown, probably high word of above) + + for (UInt32 ci = 0; ci < numDirChunks; ci++) + { + UInt64 chunkPos = _inBuffer.GetProcessedSize(); + if (ReadUInt32() == NHeader::kAollSignature) + { + UInt32 quickrefLength = ReadUInt32(); // Length of quickref area at end of directory chunk + if (quickrefLength > dirChunkSize || quickrefLength < 2) + return S_FALSE; + ReadUInt64(); // Directory chunk number + // This must match physical position in file, that is + // the chunk size times the chunk number must be the + // offset from the end of the directory header. + ReadUInt64(); // Chunk number of previous listing chunk when reading + // directory in sequence (-1 if first listing chunk) + ReadUInt64(); // Chunk number of next listing chunk when reading + // directory in sequence (-1 if last listing chunk) + ReadUInt64(); // Number of first listing entry in this chunk + ReadUInt32(); // 1 (unknown -- other values have also been seen here) + ReadUInt32(); // 0 (unknown) + + int numItems = 0; + for (;;) + { + UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos; + UInt32 offsetLimit = dirChunkSize - quickrefLength; + if (offset > offsetLimit) + return S_FALSE; + if (offset == offsetLimit) + break; + if (database.NewFormat) + { + UInt16 nameLength = ReadUInt16(); + if (nameLength == 0) + return S_FALSE; + UString name; + ReadUString((int)nameLength, name); + AString s; + ConvertUnicodeToUTF8(name, s); + Byte b = ReadByte(); + s += ' '; + PrintByte(b, s); + s += ' '; + UInt64 len = ReadEncInt(); + // then number of items ? + // then length ? + // then some data (binary encoding?) + while (len-- != 0) + { + b = ReadByte(); + PrintByte(b, s); + } + database.NewFormatString += s; + database.NewFormatString += "\r\n"; + } + else + { + RINOK(ReadDirEntry(database)); + } + numItems++; + } + Skip(quickrefLength - 2); + if (ReadUInt16() != numItems) + return S_FALSE; + if (numItems > numDirEntries) + return S_FALSE; + numDirEntries -= numItems; + } + else + Skip(dirChunkSize - 4); + } + return numDirEntries == 0 ? S_OK : S_FALSE; +} + +HRESULT CInArchive::DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name) +{ + int index = database.FindItem(name); + if (index < 0) + return S_FALSE; + const CItem &item = database.Items[index]; + _chunkSize = item.Size; + return ReadChunk(inStream, database.ContentOffset + item.Offset, item.Size); +} + + +#define DATA_SPACE "::DataSpace/" +static const char *kNameList = DATA_SPACE "NameList"; +static const char *kStorage = DATA_SPACE "Storage/"; +static const char *kContent = "Content"; +static const char *kControlData = "ControlData"; +static const char *kSpanInfo = "SpanInfo"; +static const char *kTransform = "Transform/"; +static const char *kResetTable = "/InstanceData/ResetTable"; +static const char *kTransformList = "List"; + +static AString GetSectionPrefix(const AString &name) +{ + return AString(kStorage) + name + AString("/"); +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareFiles(const int *p1, const int *p2, void *param) +{ + const CObjectVector<CItem> &items = *(const CObjectVector<CItem> *)param; + const CItem &item1 = items[*p1]; + const CItem &item2 = items[*p2]; + bool isDir1 = item1.IsDir(); + bool isDir2 = item2.IsDir(); + if (isDir1 && !isDir2) + return -1; + if (isDir2) + { + if (isDir1) + return MyCompare(*p1, *p2); + return 1; + } + RINOZ(MyCompare(item1.Section, item2.Section)); + RINOZ(MyCompare(item1.Offset, item2.Offset)); + RINOZ(MyCompare(item1.Size, item2.Size)); + return MyCompare(*p1, *p2); +} + +void CFilesDatabase::SetIndices() +{ + for (int i = 0; i < Items.Size(); i++) + { + const CItem &item = Items[i]; + if (item.IsUserItem() && item.Name.Length() != 1) + Indices.Add(i); + } +} + +void CFilesDatabase::Sort() +{ + Indices.Sort(CompareFiles, (void *)&Items); +} + +bool CFilesDatabase::Check() +{ + UInt64 maxPos = 0; + UInt64 prevSection = 0; + for(int i = 0; i < Indices.Size(); i++) + { + const CItem &item = Items[Indices[i]]; + if (item.Section == 0 || item.IsDir()) + continue; + if (item.Section != prevSection) + { + prevSection = item.Section; + maxPos = 0; + continue; + } + if (item.Offset < maxPos) + return false; + maxPos = item.Offset + item.Size; + if (maxPos < item.Offset) + return false; + } + return true; +} + +HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) +{ + { + // The NameList file + RINOK(DecompressStream(inStream, database, kNameList)); + /* UInt16 length = */ ReadUInt16(); + UInt16 numSections = ReadUInt16(); + for (int i = 0; i < numSections; i++) + { + CSectionInfo section; + UInt16 nameLength = ReadUInt16(); + UString name; + ReadUString(nameLength, name); + if (ReadUInt16() != 0) + return S_FALSE; + if (!ConvertUnicodeToUTF8(name, section.Name)) + return S_FALSE; + database.Sections.Add(section); + } + } + + int i; + for (i = 1; i < database.Sections.Size(); i++) + { + CSectionInfo §ion = database.Sections[i]; + AString sectionPrefix = GetSectionPrefix(section.Name); + { + // Content + int index = database.FindItem(sectionPrefix + kContent); + if (index < 0) + return S_FALSE; + const CItem &item = database.Items[index]; + section.Offset = item.Offset; + section.CompressedSize = item.Size; + } + AString transformPrefix = sectionPrefix + kTransform; + if (database.Help2Format) + { + // Transform List + RINOK(DecompressStream(inStream, database, transformPrefix + kTransformList)); + if ((_chunkSize & 0xF) != 0) + return S_FALSE; + int numGuids = (int)(_chunkSize / 0x10); + if (numGuids < 1) + return S_FALSE; + for (int i = 0; i < numGuids; i++) + { + CMethodInfo method; + ReadGUID(method.Guid); + section.Methods.Add(method); + } + } + else + { + CMethodInfo method; + method.Guid = kChmLzxGuid; + section.Methods.Add(method); + } + + { + // Control Data + RINOK(DecompressStream(inStream, database, sectionPrefix + kControlData)); + for (int mi = 0; mi < section.Methods.Size(); mi++) + { + CMethodInfo &method = section.Methods[mi]; + UInt32 numDWORDS = ReadUInt32(); + if (method.IsLzx()) + { + if (numDWORDS < 5) + return S_FALSE; + if (ReadUInt32() != NHeader::kLzxcSignature) + return S_FALSE; + CLzxInfo &li = method.LzxInfo; + li.Version = ReadUInt32(); + if (li.Version != 2 && li.Version != 3) + return S_FALSE; + li.ResetInterval = ReadUInt32(); + li.WindowSize = ReadUInt32(); + li.CacheSize = ReadUInt32(); + if ( + li.ResetInterval != 1 && + li.ResetInterval != 2 && + li.ResetInterval != 4 && + li.ResetInterval != 8 && + li.ResetInterval != 16 && + li.ResetInterval != 32 && + li.ResetInterval != 64) + return S_FALSE; + if ( + li.WindowSize != 1 && + li.WindowSize != 2 && + li.WindowSize != 4 && + li.WindowSize != 8 && + li.WindowSize != 16 && + li.WindowSize != 32 && + li.WindowSize != 64) + return S_FALSE; + numDWORDS -= 5; + while (numDWORDS-- != 0) + ReadUInt32(); + } + else + { + UInt32 numBytes = numDWORDS * 4; + method.ControlData.SetCapacity(numBytes); + ReadBytes(method.ControlData, numBytes); + } + } + } + + { + // SpanInfo + RINOK(DecompressStream(inStream, database, sectionPrefix + kSpanInfo)); + section.UncompressedSize = ReadUInt64(); + } + + // read ResetTable for LZX + for (int mi = 0; mi < section.Methods.Size(); mi++) + { + CMethodInfo &method = section.Methods[mi]; + if (method.IsLzx()) + { + // ResetTable; + RINOK(DecompressStream(inStream, database, transformPrefix + + method.GetGuidString() + kResetTable)); + CResetTable &rt = method.LzxInfo.ResetTable; + if (_chunkSize < 4) + { + if (_chunkSize != 0) + return S_FALSE; + // ResetTable is empty in .chw files + if (section.UncompressedSize != 0) + return S_FALSE; + rt.UncompressedSize = 0; + rt.CompressedSize = 0; + rt.BlockSize = 0; + } + else + { + UInt32 ver = ReadUInt32(); // 2 unknown (possibly a version number) + if (ver != 2 && ver != 3) + return S_FALSE; + UInt32 numEntries = ReadUInt32(); + if (ReadUInt32() != 8) // Size of table entry (bytes) + return S_FALSE; + if (ReadUInt32() != 0x28) // Length of table header + return S_FALSE; + rt.UncompressedSize = ReadUInt64(); + rt.CompressedSize = ReadUInt64(); + rt.BlockSize = ReadUInt64(); // 0x8000 block size for locations below + if (rt.BlockSize != 0x8000) + return S_FALSE; + rt.ResetOffsets.Reserve(numEntries); + for (UInt32 i = 0; i < numEntries; i++) + rt.ResetOffsets.Add(ReadUInt64()); + } + } + } + } + + database.SetIndices(); + database.Sort(); + return database.Check() ? S_OK : S_FALSE; +} + +HRESULT CInArchive::Open2(IInStream *inStream, + const UInt64 *searchHeaderSizeLimit, + CFilesDatabase &database) +{ + database.Clear(); + + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition)); + + database.Help2Format = false; + const UInt32 chmVersion = 3; + { + if (!_inBuffer.Create(1 << 14)) + return E_OUTOFMEMORY; + _inBuffer.SetStream(inStream); + _inBuffer.Init(); + UInt64 value = 0; + const int kSignatureSize = 8; + UInt64 hxsSignature = NHeader::GetHxsSignature(); + UInt64 chmSignature = ((UInt64)chmVersion << 32)| NHeader::kItsfSignature; + UInt64 limit = 1 << 18; + if (searchHeaderSizeLimit) + if (limit > *searchHeaderSizeLimit) + limit = *searchHeaderSizeLimit; + + for (;;) + { + Byte b; + if (!_inBuffer.ReadByte(b)) + return S_FALSE; + value >>= 8; + value |= ((UInt64)b) << ((kSignatureSize - 1) * 8); + if (_inBuffer.GetProcessedSize() >= kSignatureSize) + { + if (value == chmSignature) + break; + if (value == hxsSignature) + { + database.Help2Format = true; + break; + } + if (_inBuffer.GetProcessedSize() > limit) + return S_FALSE; + } + } + _startPosition += _inBuffer.GetProcessedSize() - kSignatureSize; + } + + if (database.Help2Format) + { + RINOK(OpenHelp2(inStream, database)); + if (database.NewFormat) + return S_OK; + } + else + { + RINOK(OpenChm(inStream, database)); + } + + #ifndef CHM_LOW + try + { + HRESULT res = OpenHighLevel(inStream, database); + if (res == S_FALSE) + { + database.HighLevelClear(); + return S_OK; + } + RINOK(res); + database.LowLevel = false; + } + catch(...) + { + return S_OK; + } + #endif + return S_OK; +} + +HRESULT CInArchive::Open(IInStream *inStream, + const UInt64 *searchHeaderSizeLimit, + CFilesDatabase &database) +{ + try + { + HRESULT res = Open2(inStream, searchHeaderSizeLimit, database); + _inBuffer.ReleaseStream(); + return res; + } + catch(...) + { + _inBuffer.ReleaseStream(); + throw; + } +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.h new file mode 100644 index 000000000..4719a484d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmIn.h @@ -0,0 +1,244 @@ +// Archive/ChmIn.h + +#ifndef __ARCHIVE_CHM_IN_H +#define __ARCHIVE_CHM_IN_H + +#include "Common/Buffer.h" +#include "Common/MyString.h" + +#include "../../IStream.h" +#include "../../Common/InBuffer.h" + +#include "ChmHeader.h" + +namespace NArchive { +namespace NChm { + +struct CItem +{ + UInt64 Section; + UInt64 Offset; + UInt64 Size; + AString Name; + + bool IsFormatRelatedItem() const + { + if (Name.Length() < 2) + return false; + return Name[0] == ':' && Name[1] == ':'; + } + + bool IsUserItem() const + { + if (Name.Length() < 2) + return false; + return Name[0] == '/'; + } + + bool IsDir() const + { + if (Name.Length() == 0) + return false; + return (Name[Name.Length() - 1] == '/'); + } +}; + +struct CDatabase +{ + UInt64 ContentOffset; + CObjectVector<CItem> Items; + AString NewFormatString; + bool Help2Format; + bool NewFormat; + + int FindItem(const AString &name) const + { + for (int i = 0; i < Items.Size(); i++) + if (Items[i].Name == name) + return i; + return -1; + } + + void Clear() + { + NewFormat = false; + NewFormatString.Empty(); + Help2Format = false; + Items.Clear(); + } +}; + +struct CResetTable +{ + UInt64 UncompressedSize; + UInt64 CompressedSize; + UInt64 BlockSize; + CRecordVector<UInt64> ResetOffsets; + bool GetCompressedSizeOfBlocks(UInt64 blockIndex, UInt32 numBlocks, UInt64 &size) const + { + if (blockIndex >= ResetOffsets.Size()) + return false; + UInt64 startPos = ResetOffsets[(int)blockIndex]; + if (blockIndex + numBlocks >= ResetOffsets.Size()) + size = CompressedSize - startPos; + else + size = ResetOffsets[(int)(blockIndex + numBlocks)] - startPos; + return true; + } + bool GetCompressedSizeOfBlock(UInt64 blockIndex, UInt64 &size) const + { + return GetCompressedSizeOfBlocks(blockIndex, 1, size); + } + UInt64 GetNumBlocks(UInt64 size) const + { + return (size + BlockSize - 1) / BlockSize; + } +}; + +struct CLzxInfo +{ + UInt32 Version; + UInt32 ResetInterval; + UInt32 WindowSize; + UInt32 CacheSize; + CResetTable ResetTable; + + UInt32 GetNumDictBits() const + { + if (Version == 2 || Version == 3) + { + for (int i = 0; i <= 31; i++) + if (((UInt32)1 << i) >= WindowSize) + return 15 + i; + } + return 0; + } + + UInt64 GetFolderSize() const { return ResetTable.BlockSize * ResetInterval; }; + UInt64 GetFolder(UInt64 offset) const { return offset / GetFolderSize(); }; + UInt64 GetFolderPos(UInt64 folderIndex) const { return folderIndex * GetFolderSize(); }; + UInt64 GetBlockIndexFromFolderIndex(UInt64 folderIndex) const { return folderIndex * ResetInterval; }; + bool GetOffsetOfFolder(UInt64 folderIndex, UInt64 &offset) const + { + UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); + if (blockIndex >= ResetTable.ResetOffsets.Size()) + return false; + offset = ResetTable.ResetOffsets[(int)blockIndex]; + return true; + } + bool GetCompressedSizeOfFolder(UInt64 folderIndex, UInt64 &size) const + { + UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); + return ResetTable.GetCompressedSizeOfBlocks(blockIndex, ResetInterval, size); + } +}; + +struct CMethodInfo +{ + GUID Guid; + CByteBuffer ControlData; + CLzxInfo LzxInfo; + bool IsLzx() const; + bool IsDes() const; + AString GetGuidString() const; + UString GetName() const; +}; + +struct CSectionInfo +{ + UInt64 Offset; + UInt64 CompressedSize; + UInt64 UncompressedSize; + + AString Name; + CObjectVector<CMethodInfo> Methods; + + bool IsLzx() const; + UString GetMethodName() const; +}; + +class CFilesDatabase: public CDatabase +{ +public: + bool LowLevel; + CRecordVector<int> Indices; + CObjectVector<CSectionInfo> Sections; + + UInt64 GetFileSize(int fileIndex) const { return Items[Indices[fileIndex]].Size; } + UInt64 GetFileOffset(int fileIndex) const { return Items[Indices[fileIndex]].Offset; } + + UInt64 GetFolder(int fileIndex) const + { + const CItem &item = Items[Indices[fileIndex]]; + const CSectionInfo §ion = Sections[(int)item.Section]; + if (section.IsLzx()) + return section.Methods[0].LzxInfo.GetFolder(item.Offset); + return 0; + } + + UInt64 GetLastFolder(int fileIndex) const + { + const CItem &item = Items[Indices[fileIndex]]; + const CSectionInfo §ion = Sections[(int)item.Section]; + if (section.IsLzx()) + return section.Methods[0].LzxInfo.GetFolder(item.Offset + item.Size - 1); + return 0; + } + + void HighLevelClear() + { + LowLevel = true; + Indices.Clear(); + Sections.Clear(); + } + + void Clear() + { + CDatabase::Clear(); + HighLevelClear(); + } + void SetIndices(); + void Sort(); + bool Check(); +}; + +class CProgressVirt +{ +public: + STDMETHOD(SetTotal)(const UInt64 *numFiles) PURE; + STDMETHOD(SetCompleted)(const UInt64 *numFiles) PURE; +}; + +class CInArchive +{ + UInt64 _startPosition; + ::CInBuffer _inBuffer; + UInt64 _chunkSize; + + Byte ReadByte(); + void ReadBytes(Byte *data, UInt32 size); + void Skip(size_t size); + UInt16 ReadUInt16(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + UInt64 ReadEncInt(); + void ReadString(int size, AString &s); + void ReadUString(int size, UString &s); + void ReadGUID(GUID &g); + + HRESULT ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size); + + HRESULT ReadDirEntry(CDatabase &database); + HRESULT DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name); + +public: + HRESULT OpenChm(IInStream *inStream, CDatabase &database); + HRESULT OpenHelp2(IInStream *inStream, CDatabase &database); + HRESULT OpenHighLevel(IInStream *inStream, CFilesDatabase &database); + HRESULT Open2(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database); + HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, CFilesDatabase &database); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmRegister.cpp new file mode 100644 index 000000000..e5f38afa4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Chm/ChmRegister.cpp @@ -0,0 +1,13 @@ +// ChmRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "ChmHandler.h" +static IInArchive *CreateArc() { return new NArchive::NChm::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Chm", L"chm chi chq chw hxs hxi hxr hxq hxw lit", 0, 0xE9, { 'I', 'T', 'S', 'F' }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Chm) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.cpp new file mode 100644 index 000000000..58f76439f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.cpp @@ -0,0 +1,239 @@ +// ComHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "ComHandler.h" + +namespace NArchive { +namespace NCom { + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidMTime, VT_FILETIME} +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidSectorSize, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidClusterSize: prop = (UInt32)1 << _db.SectorSizeBits; break; + case kpidSectorSize: prop = (UInt32)1 << _db.MiniSectorSizeBits; break; + case kpidMainSubfile: if (_db.MainSubfile >= 0) prop = (UInt32)_db.MainSubfile; break; + } + 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; + const CRef &ref = _db.Refs[index]; + const CItem &item = _db.Items[ref.Did]; + + switch(propID) + { + case kpidPath: prop = _db.GetItemPath(index); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidCTime: prop = item.CTime; break; + case kpidMTime: prop = item.MTime; break; + case kpidPackSize: if (!item.IsDir()) prop = _db.GetItemPackSize(item.Size); break; + case kpidSize: if (!item.IsDir()) prop = item.Size; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + try + { + if (_db.Open(inStream) != S_OK) + return S_FALSE; + _stream = inStream; + } + catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _db.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _db.Refs.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + UInt64 totalSize = 0; + for(i = 0; i < numItems; i++) + { + const CItem &item = _db.Items[_db.Refs[allFilesMode ? i : indices[i]].Did]; + if (!item.IsDir()) + totalSize += item.Size; + } + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _db.Items[_db.Refs[index].Did]; + + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + totalPackSize += _db.GetItemPackSize(item.Size); + totalSize += item.Size; + + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + Int32 res = NExtract::NOperationResult::kDataError; + CMyComPtr<ISequentialInStream> inStream; + HRESULT hres = GetStream(index, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kDataError; + else if (hres == E_NOTIMPL) + res = NExtract::NOperationResult::kUnSupportedMethod; + else + { + RINOK(hres); + if (inStream) + { + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } + } + outStream.Release(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _db.Refs.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + const CItem &item = _db.Items[_db.Refs[index].Did]; + CClusterInStream *streamSpec = new CClusterInStream; + CMyComPtr<ISequentialInStream> streamTemp = streamSpec; + streamSpec->Stream = _stream; + streamSpec->StartOffset = 0; + + bool isLargeStream = _db.IsLargeStream(item.Size); + int bsLog = isLargeStream ? _db.SectorSizeBits : _db.MiniSectorSizeBits; + streamSpec->BlockSizeLog = bsLog; + streamSpec->Size = item.Size; + + UInt32 clusterSize = (UInt32)1 << bsLog; + UInt64 numClusters64 = (item.Size + clusterSize - 1) >> bsLog; + if (numClusters64 >= ((UInt32)1 << 31)) + return E_NOTIMPL; + streamSpec->Vector.Reserve((int)numClusters64); + UInt32 sid = item.Sid; + UInt64 size = item.Size; + + if (size != 0) + { + for (;; size -= clusterSize) + { + if (isLargeStream) + { + if (sid >= _db.FatSize) + return S_FALSE; + streamSpec->Vector.Add(sid + 1); + sid = _db.Fat[sid]; + } + else + { + UInt64 val; + if (sid >= _db.MatSize || !_db.GetMiniCluster(sid, val) || val >= (UInt64)1 << 32) + return S_FALSE; + streamSpec->Vector.Add((UInt32)val); + sid = _db.Mat[sid]; + } + if (size <= clusterSize) + break; + } + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + RINOK(streamSpec->InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.h new file mode 100644 index 000000000..f2b7de96d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComHandler.h @@ -0,0 +1,28 @@ +// ComHandler.h + +#ifndef __ARCHIVE_COM_HANDLER_H +#define __ARCHIVE_COM_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" +#include "ComIn.h" + +namespace NArchive { +namespace NCom { + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CDatabase _db; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.cpp new file mode 100644 index 000000000..2203ca531 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.cpp @@ -0,0 +1,389 @@ +// Archive/ComIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" +#include "../../../../C/CpuArch.h" + +#include "Common/IntToString.h" +#include "Common/MyCom.h" + +#include "../../Common/StreamUtils.h" + +#include "ComIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) + +namespace NArchive{ +namespace NCom{ + +static const UInt32 kSignatureSize = 8; +static const Byte kSignature[kSignatureSize] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; + +void CUInt32Buf::Free() +{ + MyFree(_buf); + _buf = 0; +} + +bool CUInt32Buf::Allocate(UInt32 numItems) +{ + Free(); + if (numItems == 0) + return true; + size_t newSize = (size_t)numItems * sizeof(UInt32); + if (newSize / sizeof(UInt32) != numItems) + return false; + _buf = (UInt32 *)MyAlloc(newSize); + return (_buf != 0); +} + +static HRESULT ReadSector(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid) +{ + RINOK(inStream->Seek((((UInt64)sid + 1) << sectorSizeBits), STREAM_SEEK_SET, NULL)); + return ReadStream_FALSE(inStream, buf, (UInt32)1 << sectorSizeBits); +} + +static HRESULT ReadIDs(IInStream *inStream, Byte *buf, int sectorSizeBits, UInt32 sid, UInt32 *dest) +{ + RINOK(ReadSector(inStream, buf, sectorSizeBits, sid)); + UInt32 sectorSize = (UInt32)1 << sectorSizeBits; + for (UInt32 t = 0; t < sectorSize; t += 4) + *dest++ = Get32(buf + t); + return S_OK; +} + +static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) +{ + ft->dwLowDateTime = Get32(p); + ft->dwHighDateTime = Get32(p + 4); +} + +void CItem::Parse(const Byte *p, bool mode64bit) +{ + memcpy(Name, p, kNameSizeMax); + // NameSize = Get16(p + 64); + Type = p[66]; + LeftDid = Get32(p + 68); + RightDid = Get32(p + 72); + SonDid = Get32(p + 76); + // Flags = Get32(p + 96); + GetFileTimeFromMem(p + 100, &CTime); + GetFileTimeFromMem(p + 108, &MTime); + Sid = Get32(p + 116); + Size = Get32(p + 120); + if (mode64bit) + Size |= ((UInt64)Get32(p + 124) << 32); +} + +void CDatabase::Clear() +{ + Fat.Free(); + MiniSids.Free(); + Mat.Free(); + Items.Clear(); + Refs.Clear(); +} + +static const UInt32 kNoDid = 0xFFFFFFFF; + +HRESULT CDatabase::AddNode(int parent, UInt32 did) +{ + if (did == kNoDid) + return S_OK; + if (did >= (UInt32)Items.Size()) + return S_FALSE; + const CItem &item = Items[did]; + if (item.IsEmpty()) + return S_FALSE; + CRef ref; + ref.Parent = parent; + ref.Did = did; + int index = Refs.Add(ref); + if (Refs.Size() > Items.Size()) + return S_FALSE; + RINOK(AddNode(parent, item.LeftDid)); + RINOK(AddNode(parent, item.RightDid)); + if (item.IsDir()) + { + RINOK(AddNode(index, item.SonDid)); + } + return S_OK; +} + +static const char kCharOpenBracket = '['; +static const char kCharCloseBracket = ']'; + +static UString CompoundNameToFileName(const UString &s) +{ + UString res; + for (int i = 0; i < s.Length(); i++) + { + wchar_t c = s[i]; + if (c < 0x20) + { + res += kCharOpenBracket; + wchar_t buf[32]; + ConvertUInt32ToString(c, buf); + res += buf; + res += kCharCloseBracket; + } + else + res += c; + } + return res; +} + +static char g_MsiChars[] = +"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._"; + +static const wchar_t *kMsi_ID = L""; // L"{msi}"; + +static const int kMsiNumBits = 6; +static const UInt32 kMsiNumChars = 1 << kMsiNumBits; +static const UInt32 kMsiCharMask = kMsiNumChars - 1; +static const UInt32 kMsiStartUnicodeChar = 0x3800; +static const UInt32 kMsiUnicodeRange = kMsiNumChars * (kMsiNumChars + 1); + +bool CompoundMsiNameToFileName(const UString &name, UString &resultName) +{ + resultName.Empty(); + for (int i = 0; i < name.Length(); i++) + { + wchar_t c = name[i]; + if (c < kMsiStartUnicodeChar || c > kMsiStartUnicodeChar + kMsiUnicodeRange) + return false; + if (i == 0) + resultName += kMsi_ID; + c -= kMsiStartUnicodeChar; + + UInt32 c0 = c & kMsiCharMask; + UInt32 c1 = c >> kMsiNumBits; + + if (c1 <= kMsiNumChars) + { + resultName += (wchar_t)g_MsiChars[c0]; + if (c1 == kMsiNumChars) + break; + resultName += (wchar_t)g_MsiChars[c1]; + } + else + resultName += L'!'; + } + return true; +} + +static UString ConvertName(const Byte *p, bool &isMsi) +{ + isMsi = false; + UString s; + for (int i = 0; i < kNameSizeMax; i += 2) + { + wchar_t c = (p[i] | (wchar_t)p[i + 1] << 8); + if (c == 0) + break; + s += c; + } + UString msiName; + if (CompoundMsiNameToFileName(s, msiName)) + { + isMsi = true; + return msiName; + } + return CompoundNameToFileName(s); +} + +static UString ConvertName(const Byte *p) +{ + bool isMsi; + return ConvertName(p, isMsi); +} + +UString CDatabase::GetItemPath(UInt32 index) const +{ + UString s; + while (index != kNoDid) + { + const CRef &ref = Refs[index]; + const CItem &item = Items[ref.Did]; + if (!s.IsEmpty()) + s = (UString)WCHAR_PATH_SEPARATOR + s; + s = ConvertName(item.Name) + s; + index = ref.Parent; + } + return s; +} + +HRESULT CDatabase::Open(IInStream *inStream) +{ + MainSubfile = -1; + static const UInt32 kHeaderSize = 512; + Byte p[kHeaderSize]; + RINOK(ReadStream_FALSE(inStream, p, kHeaderSize)); + if (memcmp(p, kSignature, kSignatureSize) != 0) + return S_FALSE; + if (Get16(p + 0x1A) > 4) // majorVer + return S_FALSE; + if (Get16(p + 0x1C) != 0xFFFE) + return S_FALSE; + int sectorSizeBits = Get16(p + 0x1E); + bool mode64bit = (sectorSizeBits >= 12); + int miniSectorSizeBits = Get16(p + 0x20); + SectorSizeBits = sectorSizeBits; + MiniSectorSizeBits = miniSectorSizeBits; + + if (sectorSizeBits > 28 || miniSectorSizeBits > 28 || + sectorSizeBits < 7 || miniSectorSizeBits < 2 || miniSectorSizeBits > sectorSizeBits) + return S_FALSE; + UInt32 numSectorsForFAT = Get32(p + 0x2C); + LongStreamMinSize = Get32(p + 0x38); + + UInt32 sectSize = (UInt32)1 << (int)sectorSizeBits; + + CByteBuffer sect; + sect.SetCapacity(sectSize); + + int ssb2 = (int)(sectorSizeBits - 2); + UInt32 numSidsInSec = (UInt32)1 << ssb2; + UInt32 numFatItems = numSectorsForFAT << ssb2; + if ((numFatItems >> ssb2) != numSectorsForFAT) + return S_FALSE; + FatSize = numFatItems; + + { + CUInt32Buf bat; + UInt32 numSectorsForBat = Get32(p + 0x48); + const UInt32 kNumHeaderBatItems = 109; + UInt32 numBatItems = kNumHeaderBatItems + (numSectorsForBat << ssb2); + if (numBatItems < kNumHeaderBatItems || ((numBatItems - kNumHeaderBatItems) >> ssb2) != numSectorsForBat) + return S_FALSE; + if (!bat.Allocate(numBatItems)) + return S_FALSE; + UInt32 i; + for (i = 0; i < kNumHeaderBatItems; i++) + bat[i] = Get32(p + 0x4c + i * 4); + UInt32 sid = Get32(p + 0x44); + for (UInt32 s = 0; s < numSectorsForBat; s++) + { + RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, bat + i)); + i += numSidsInSec - 1; + sid = bat[i]; + } + numBatItems = i; + + if (!Fat.Allocate(numFatItems)) + return S_FALSE; + UInt32 j = 0; + + for (i = 0; i < numFatItems; j++, i += numSidsInSec) + { + if (j >= numBatItems) + return S_FALSE; + RINOK(ReadIDs(inStream, sect, sectorSizeBits, bat[j], Fat + i)); + } + } + + UInt32 numMatItems; + { + UInt32 numSectorsForMat = Get32(p + 0x40); + numMatItems = (UInt32)numSectorsForMat << ssb2; + if ((numMatItems >> ssb2) != numSectorsForMat) + return S_FALSE; + if (!Mat.Allocate(numMatItems)) + return S_FALSE; + UInt32 i; + UInt32 sid = Get32(p + 0x3C); + for (i = 0; i < numMatItems; i += numSidsInSec) + { + RINOK(ReadIDs(inStream, sect, sectorSizeBits, sid, Mat + i)); + if (sid >= numFatItems) + return S_FALSE; + sid = Fat[sid]; + } + if (sid != NFatID::kEndOfChain) + return S_FALSE; + } + + { + UInt32 sid = Get32(p + 0x30); + for (;;) + { + if (sid >= numFatItems) + return S_FALSE; + RINOK(ReadSector(inStream, sect, sectorSizeBits, sid)); + for (UInt32 i = 0; i < sectSize; i += 128) + { + CItem item; + item.Parse(sect + i, mode64bit); + Items.Add(item); + } + sid = Fat[sid]; + if (sid == NFatID::kEndOfChain) + break; + } + } + + CItem root = Items[0]; + + { + UInt32 numSectorsInMiniStream; + { + UInt64 numSatSects64 = (root.Size + sectSize - 1) >> sectorSizeBits; + if (numSatSects64 > NFatID::kMaxValue) + return S_FALSE; + numSectorsInMiniStream = (UInt32)numSatSects64; + } + NumSectorsInMiniStream = numSectorsInMiniStream; + if (!MiniSids.Allocate(numSectorsInMiniStream)) + return S_FALSE; + { + UInt64 matSize64 = (root.Size + ((UInt64)1 << miniSectorSizeBits) - 1) >> miniSectorSizeBits; + if (matSize64 > NFatID::kMaxValue) + return S_FALSE; + MatSize = (UInt32)matSize64; + if (numMatItems < MatSize) + return S_FALSE; + } + + UInt32 sid = root.Sid; + for (UInt32 i = 0; ; i++) + { + if (sid == NFatID::kEndOfChain) + { + if (i != numSectorsInMiniStream) + return S_FALSE; + break; + } + if (i >= numSectorsInMiniStream) + return S_FALSE; + MiniSids[i] = sid; + if (sid >= numFatItems) + return S_FALSE; + sid = Fat[sid]; + } + } + + RINOK(AddNode(-1, root.SonDid)); + + unsigned numCabs = 0; + for (int i = 0; i < Refs.Size(); i++) + { + const CItem &item = Items[Refs[i].Did]; + if (item.IsDir() || numCabs > 1) + continue; + bool isMsiName; + UString msiName = ConvertName(item.Name, isMsiName); + if (isMsiName && msiName.Right(4).CompareNoCase(L".cab") == 0) + { + numCabs++; + MainSubfile = i; + } + } + if (numCabs > 1) + MainSubfile = -1; + + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.h new file mode 100644 index 000000000..429d3796e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComIn.h @@ -0,0 +1,119 @@ +// Archive/ComIn.h + +#ifndef __ARCHIVE_COM_IN_H +#define __ARCHIVE_COM_IN_H + +#include "Common/MyString.h" +#include "Common/Buffer.h" + +namespace NArchive { +namespace NCom { + +struct CUInt32Buf +{ + UInt32 *_buf; +public: + CUInt32Buf(): _buf(0) {} + ~CUInt32Buf() { Free(); } + void Free(); + bool Allocate(UInt32 numItems); + operator UInt32 *() const { return _buf; }; +}; + +namespace NFatID +{ + const UInt32 kFree = 0xFFFFFFFF; + const UInt32 kEndOfChain = 0xFFFFFFFE; + const UInt32 kFatSector = 0xFFFFFFFD; + const UInt32 kMatSector = 0xFFFFFFFC; + const UInt32 kMaxValue = 0xFFFFFFFA; +} + +namespace NItemType +{ + const Byte kEmpty = 0; + const Byte kStorage = 1; + const Byte kStream = 2; + const Byte kLockBytes = 3; + const Byte kProperty = 4; + const Byte kRootStorage = 5; +} + +const UInt32 kNameSizeMax = 64; + +struct CItem +{ + Byte Name[kNameSizeMax]; + // UInt16 NameSize; + // UInt32 Flags; + FILETIME CTime; + FILETIME MTime; + UInt64 Size; + UInt32 LeftDid; + UInt32 RightDid; + UInt32 SonDid; + UInt32 Sid; + Byte Type; + + bool IsEmpty() const { return Type == NItemType::kEmpty; } + bool IsDir() const { return Type == NItemType::kStorage || Type == NItemType::kRootStorage; } + + void Parse(const Byte *p, bool mode64bit); +}; + +struct CRef +{ + int Parent; + UInt32 Did; +}; + +class CDatabase +{ + UInt32 NumSectorsInMiniStream; + CUInt32Buf MiniSids; + + HRESULT AddNode(int parent, UInt32 did); +public: + + CUInt32Buf Fat; + UInt32 FatSize; + + CUInt32Buf Mat; + UInt32 MatSize; + + CObjectVector<CItem> Items; + CRecordVector<CRef> Refs; + + UInt32 LongStreamMinSize; + int SectorSizeBits; + int MiniSectorSizeBits; + + Int32 MainSubfile; + + void Clear(); + bool IsLargeStream(UInt64 size) const { return size >= LongStreamMinSize; } + UString GetItemPath(UInt32 index) const; + + UInt64 GetItemPackSize(UInt64 size) const + { + UInt64 mask = ((UInt64)1 << (IsLargeStream(size) ? SectorSizeBits : MiniSectorSizeBits)) - 1; + return (size + mask) & ~mask; + } + + bool GetMiniCluster(UInt32 sid, UInt64 &res) const + { + int subBits = SectorSizeBits - MiniSectorSizeBits; + UInt32 fid = sid >> subBits; + if (fid >= NumSectorsInMiniStream) + return false; + res = (((UInt64)MiniSids[fid] + 1) << subBits) + (sid & ((1 << subBits) - 1)); + return true; + } + + HRESULT Open(IInStream *inStream); +}; + + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComRegister.cpp new file mode 100644 index 000000000..6712b890f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Com/ComRegister.cpp @@ -0,0 +1,13 @@ +// ComRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "ComHandler.h" +static IInArchive *CreateArc() { return new NArchive::NCom::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Compound", L"msi msp doc xls ppt", 0, 0xE5, { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }, 8, false, CreateArc, 0 }; + +REGISTER_ARC(Com) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp new file mode 100644 index 000000000..0b06a489f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp @@ -0,0 +1,121 @@ +// CoderMixer2.cpp + +#include "StdAfx.h" + +#include "CoderMixer2.h" + +namespace NCoderMixer { + +CBindReverseConverter::CBindReverseConverter(const CBindInfo &srcBindInfo): + _srcBindInfo(srcBindInfo) +{ + srcBindInfo.GetNumStreams(NumSrcInStreams, _numSrcOutStreams); + + UInt32 j; + for (j = 0; j < NumSrcInStreams; j++) + { + _srcInToDestOutMap.Add(0); + DestOutToSrcInMap.Add(0); + } + for (j = 0; j < _numSrcOutStreams; j++) + { + _srcOutToDestInMap.Add(0); + _destInToSrcOutMap.Add(0); + } + + UInt32 destInOffset = 0; + UInt32 destOutOffset = 0; + UInt32 srcInOffset = NumSrcInStreams; + UInt32 srcOutOffset = _numSrcOutStreams; + + for (int i = srcBindInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderStreamsInfo &srcCoderInfo = srcBindInfo.Coders[i]; + + srcInOffset -= srcCoderInfo.NumInStreams; + srcOutOffset -= srcCoderInfo.NumOutStreams; + + UInt32 j; + for (j = 0; j < srcCoderInfo.NumInStreams; j++, destOutOffset++) + { + UInt32 index = srcInOffset + j; + _srcInToDestOutMap[index] = destOutOffset; + DestOutToSrcInMap[destOutOffset] = index; + } + for (j = 0; j < srcCoderInfo.NumOutStreams; j++, destInOffset++) + { + UInt32 index = srcOutOffset + j; + _srcOutToDestInMap[index] = destInOffset; + _destInToSrcOutMap[destInOffset] = index; + } + } +} + +void CBindReverseConverter::CreateReverseBindInfo(CBindInfo &destBindInfo) +{ + destBindInfo.Coders.Clear(); + destBindInfo.BindPairs.Clear(); + destBindInfo.InStreams.Clear(); + destBindInfo.OutStreams.Clear(); + + int i; + for (i = _srcBindInfo.Coders.Size() - 1; i >= 0; i--) + { + const CCoderStreamsInfo &srcCoderInfo = _srcBindInfo.Coders[i]; + CCoderStreamsInfo destCoderInfo; + destCoderInfo.NumInStreams = srcCoderInfo.NumOutStreams; + destCoderInfo.NumOutStreams = srcCoderInfo.NumInStreams; + destBindInfo.Coders.Add(destCoderInfo); + } + for (i = _srcBindInfo.BindPairs.Size() - 1; i >= 0; i--) + { + const CBindPair &srcBindPair = _srcBindInfo.BindPairs[i]; + CBindPair destBindPair; + destBindPair.InIndex = _srcOutToDestInMap[srcBindPair.OutIndex]; + destBindPair.OutIndex = _srcInToDestOutMap[srcBindPair.InIndex]; + destBindInfo.BindPairs.Add(destBindPair); + } + for (i = 0; i < _srcBindInfo.InStreams.Size(); i++) + destBindInfo.OutStreams.Add(_srcInToDestOutMap[_srcBindInfo.InStreams[i]]); + for (i = 0; i < _srcBindInfo.OutStreams.Size(); i++) + destBindInfo.InStreams.Add(_srcOutToDestInMap[_srcBindInfo.OutStreams[i]]); +} + +CCoderInfo2::CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams): + NumInStreams(numInStreams), + NumOutStreams(numOutStreams) +{ + InSizes.Reserve(NumInStreams); + InSizePointers.Reserve(NumInStreams); + OutSizes.Reserve(NumOutStreams); + OutSizePointers.Reserve(NumOutStreams); +} + +static void SetSizes(const UInt64 **srcSizes, CRecordVector<UInt64> &sizes, + CRecordVector<const UInt64 *> &sizePointers, UInt32 numItems) +{ + sizes.Clear(); + sizePointers.Clear(); + for(UInt32 i = 0; i < numItems; i++) + { + if (srcSizes == 0 || srcSizes[i] == NULL) + { + sizes.Add(0); + sizePointers.Add(NULL); + } + else + { + sizes.Add(*srcSizes[i]); + sizePointers.Add(&sizes.Back()); + } + } +} + +void CCoderInfo2::SetCoderInfo(const UInt64 **inSizes, + const UInt64 **outSizes) +{ + SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); + SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); +} + +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h new file mode 100644 index 000000000..a03722d61 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h @@ -0,0 +1,174 @@ +// CoderMixer2.h + +#ifndef __CODER_MIXER2_H +#define __CODER_MIXER2_H + +#include "../../../Common/MyVector.h" +#include "../../../Common/Types.h" +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" + +namespace NCoderMixer { + +struct CBindPair +{ + UInt32 InIndex; + UInt32 OutIndex; +}; + +struct CCoderStreamsInfo +{ + UInt32 NumInStreams; + UInt32 NumOutStreams; +}; + +struct CBindInfo +{ + CRecordVector<CCoderStreamsInfo> Coders; + CRecordVector<CBindPair> BindPairs; + CRecordVector<UInt32> InStreams; + CRecordVector<UInt32> OutStreams; + + void Clear() + { + Coders.Clear(); + BindPairs.Clear(); + InStreams.Clear(); + OutStreams.Clear(); + } + + /* + UInt32 GetCoderStartOutStream(UInt32 coderIndex) const + { + UInt32 numOutStreams = 0; + for (UInt32 i = 0; i < coderIndex; i++) + numOutStreams += Coders[i].NumOutStreams; + return numOutStreams; + } + */ + + + void GetNumStreams(UInt32 &numInStreams, UInt32 &numOutStreams) const + { + numInStreams = 0; + numOutStreams = 0; + for (int i = 0; i < Coders.Size(); i++) + { + const CCoderStreamsInfo &coderStreamsInfo = Coders[i]; + numInStreams += coderStreamsInfo.NumInStreams; + numOutStreams += coderStreamsInfo.NumOutStreams; + } + } + + int FindBinderForInStream(UInt32 inStream) const + { + for (int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].InIndex == inStream) + return i; + return -1; + } + int FindBinderForOutStream(UInt32 outStream) const + { + for (int i = 0; i < BindPairs.Size(); i++) + if (BindPairs[i].OutIndex == outStream) + return i; + return -1; + } + + UInt32 GetCoderInStreamIndex(UInt32 coderIndex) const + { + UInt32 streamIndex = 0; + for (UInt32 i = 0; i < coderIndex; i++) + streamIndex += Coders[i].NumInStreams; + return streamIndex; + } + + UInt32 GetCoderOutStreamIndex(UInt32 coderIndex) const + { + UInt32 streamIndex = 0; + for (UInt32 i = 0; i < coderIndex; i++) + streamIndex += Coders[i].NumOutStreams; + return streamIndex; + } + + + void FindInStream(UInt32 streamIndex, UInt32 &coderIndex, + UInt32 &coderStreamIndex) const + { + for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) + { + UInt32 curSize = Coders[coderIndex].NumInStreams; + if (streamIndex < curSize) + { + coderStreamIndex = streamIndex; + return; + } + streamIndex -= curSize; + } + throw 1; + } + void FindOutStream(UInt32 streamIndex, UInt32 &coderIndex, + UInt32 &coderStreamIndex) const + { + for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++) + { + UInt32 curSize = Coders[coderIndex].NumOutStreams; + if (streamIndex < curSize) + { + coderStreamIndex = streamIndex; + return; + } + streamIndex -= curSize; + } + throw 1; + } +}; + +class CBindReverseConverter +{ + UInt32 _numSrcOutStreams; + NCoderMixer::CBindInfo _srcBindInfo; + CRecordVector<UInt32> _srcInToDestOutMap; + CRecordVector<UInt32> _srcOutToDestInMap; + CRecordVector<UInt32> _destInToSrcOutMap; +public: + UInt32 NumSrcInStreams; + CRecordVector<UInt32> DestOutToSrcInMap; + + CBindReverseConverter(const NCoderMixer::CBindInfo &srcBindInfo); + void CreateReverseBindInfo(NCoderMixer::CBindInfo &destBindInfo); +}; + +struct CCoderInfo2 +{ + CMyComPtr<ICompressCoder> Coder; + CMyComPtr<ICompressCoder2> Coder2; + UInt32 NumInStreams; + UInt32 NumOutStreams; + + CRecordVector<UInt64> InSizes; + CRecordVector<UInt64> OutSizes; + CRecordVector<const UInt64 *> InSizePointers; + CRecordVector<const UInt64 *> OutSizePointers; + + CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams); + void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); + + HRESULT QueryInterface(REFGUID iid, void** pp) const + { + IUnknown *p = Coder ? (IUnknown *)Coder : (IUnknown *)Coder2; + return p->QueryInterface(iid, pp); + } +}; + +class CCoderMixer2 +{ +public: + virtual HRESULT SetBindInfo(const CBindInfo &bindInfo) = 0; + virtual void ReInit() = 0; + virtual void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) = 0; +}; + +} +#endif + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp new file mode 100644 index 000000000..d76450bd1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp @@ -0,0 +1,240 @@ +// CoderMixer2MT.cpp + +#include "StdAfx.h" + +#include "CoderMixer2MT.h" + +namespace NCoderMixer { + +CCoder2::CCoder2(UInt32 numInStreams, UInt32 numOutStreams): + CCoderInfo2(numInStreams, numOutStreams) +{ + InStreams.Reserve(NumInStreams); + InStreamPointers.Reserve(NumInStreams); + OutStreams.Reserve(NumOutStreams); + OutStreamPointers.Reserve(NumOutStreams); +} + +void CCoder2::Execute() { Code(NULL); } + +void CCoder2::Code(ICompressProgressInfo *progress) +{ + InStreamPointers.Clear(); + OutStreamPointers.Clear(); + UInt32 i; + for (i = 0; i < NumInStreams; i++) + { + if (InSizePointers[i] != NULL) + InSizePointers[i] = &InSizes[i]; + InStreamPointers.Add((ISequentialInStream *)InStreams[i]); + } + for (i = 0; i < NumOutStreams; i++) + { + if (OutSizePointers[i] != NULL) + OutSizePointers[i] = &OutSizes[i]; + OutStreamPointers.Add((ISequentialOutStream *)OutStreams[i]); + } + if (Coder) + Result = Coder->Code(InStreamPointers[0], OutStreamPointers[0], + InSizePointers[0], OutSizePointers[0], progress); + else + Result = Coder2->Code(&InStreamPointers.Front(), &InSizePointers.Front(), NumInStreams, + &OutStreamPointers.Front(), &OutSizePointers.Front(), NumOutStreams, progress); + { + int i; + for (i = 0; i < InStreams.Size(); i++) + InStreams[i].Release(); + for (i = 0; i < OutStreams.Size(); i++) + OutStreams[i].Release(); + } +} + +static void SetSizes(const UInt64 **srcSizes, CRecordVector<UInt64> &sizes, + CRecordVector<const UInt64 *> &sizePointers, UInt32 numItems) +{ + sizes.Clear(); + sizePointers.Clear(); + for (UInt32 i = 0; i < numItems; i++) + { + if (srcSizes == 0 || srcSizes[i] == NULL) + { + sizes.Add(0); + sizePointers.Add(NULL); + } + else + { + sizes.Add(*srcSizes[i]); + sizePointers.Add(&sizes.Back()); + } + } +} + + +void CCoder2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes) +{ + SetSizes(inSizes, InSizes, InSizePointers, NumInStreams); + SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams); +} + +////////////////////////////////////// +// CCoderMixer2MT + +HRESULT CCoderMixer2MT::SetBindInfo(const CBindInfo &bindInfo) +{ + _bindInfo = bindInfo; + _streamBinders.Clear(); + for (int i = 0; i < _bindInfo.BindPairs.Size(); i++) + { + _streamBinders.Add(CStreamBinder()); + RINOK(_streamBinders.Back().CreateEvents()); + } + return S_OK; +} + +void CCoderMixer2MT::AddCoderCommon() +{ + const CCoderStreamsInfo &c = _bindInfo.Coders[_coders.Size()]; + CCoder2 threadCoderInfo(c.NumInStreams, c.NumOutStreams); + _coders.Add(threadCoderInfo); +} + +void CCoderMixer2MT::AddCoder(ICompressCoder *coder) +{ + AddCoderCommon(); + _coders.Back().Coder = coder; +} + +void CCoderMixer2MT::AddCoder2(ICompressCoder2 *coder) +{ + AddCoderCommon(); + _coders.Back().Coder2 = coder; +} + + +void CCoderMixer2MT::ReInit() +{ + for (int i = 0; i < _streamBinders.Size(); i++) + _streamBinders[i].ReInit(); +} + + +HRESULT CCoderMixer2MT::Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams) +{ + /* + if (_coders.Size() != _bindInfo.Coders.Size()) + throw 0; + */ + int i; + for (i = 0; i < _coders.Size(); i++) + { + CCoder2 &coderInfo = _coders[i]; + const CCoderStreamsInfo &coderStreamsInfo = _bindInfo.Coders[i]; + coderInfo.InStreams.Clear(); + UInt32 j; + for (j = 0; j < coderStreamsInfo.NumInStreams; j++) + coderInfo.InStreams.Add(NULL); + coderInfo.OutStreams.Clear(); + for (j = 0; j < coderStreamsInfo.NumOutStreams; j++) + coderInfo.OutStreams.Add(NULL); + } + + for (i = 0; i < _bindInfo.BindPairs.Size(); i++) + { + const CBindPair &bindPair = _bindInfo.BindPairs[i]; + UInt32 inCoderIndex, inCoderStreamIndex; + UInt32 outCoderIndex, outCoderStreamIndex; + _bindInfo.FindInStream(bindPair.InIndex, inCoderIndex, inCoderStreamIndex); + _bindInfo.FindOutStream(bindPair.OutIndex, outCoderIndex, outCoderStreamIndex); + + _streamBinders[i].CreateStreams( + &_coders[inCoderIndex].InStreams[inCoderStreamIndex], + &_coders[outCoderIndex].OutStreams[outCoderStreamIndex]); + + CMyComPtr<ICompressSetBufSize> inSetSize, outSetSize; + _coders[inCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&inSetSize); + _coders[outCoderIndex].QueryInterface(IID_ICompressSetBufSize, (void **)&outSetSize); + if (inSetSize && outSetSize) + { + const UInt32 kBufSize = 1 << 19; + inSetSize->SetInBufSize(inCoderStreamIndex, kBufSize); + outSetSize->SetOutBufSize(outCoderStreamIndex, kBufSize); + } + } + + for (i = 0; i < _bindInfo.InStreams.Size(); i++) + { + UInt32 inCoderIndex, inCoderStreamIndex; + _bindInfo.FindInStream(_bindInfo.InStreams[i], inCoderIndex, inCoderStreamIndex); + _coders[inCoderIndex].InStreams[inCoderStreamIndex] = inStreams[i]; + } + + for (i = 0; i < _bindInfo.OutStreams.Size(); i++) + { + UInt32 outCoderIndex, outCoderStreamIndex; + _bindInfo.FindOutStream(_bindInfo.OutStreams[i], outCoderIndex, outCoderStreamIndex); + _coders[outCoderIndex].OutStreams[outCoderStreamIndex] = outStreams[i]; + } + return S_OK; +} + +HRESULT CCoderMixer2MT::ReturnIfError(HRESULT code) +{ + for (int i = 0; i < _coders.Size(); i++) + if (_coders[i].Result == code) + return code; + return S_OK; +} + +STDMETHODIMP CCoderMixer2MT::Code(ISequentialInStream **inStreams, + const UInt64 ** /* inSizes */, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 ** /* outSizes */, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + if (numInStreams != (UInt32)_bindInfo.InStreams.Size() || + numOutStreams != (UInt32)_bindInfo.OutStreams.Size()) + return E_INVALIDARG; + + Init(inStreams, outStreams); + + int i; + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + { + RINOK(_coders[i].Create()); + } + + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + _coders[i].Start(); + + _coders[_progressCoderIndex].Code(progress); + + for (i = 0; i < _coders.Size(); i++) + if (i != _progressCoderIndex) + _coders[i].WaitFinish(); + + RINOK(ReturnIfError(E_ABORT)); + RINOK(ReturnIfError(E_OUTOFMEMORY)); + + for (i = 0; i < _coders.Size(); i++) + { + HRESULT result = _coders[i].Result; + if (result != S_OK && result != E_FAIL && result != S_FALSE) + return result; + } + + RINOK(ReturnIfError(S_FALSE)); + + for (i = 0; i < _coders.Size(); i++) + { + HRESULT result = _coders[i].Result; + if (result != S_OK) + return result; + } + return S_OK; +} + +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h new file mode 100644 index 000000000..d1c7f4d07 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h @@ -0,0 +1,80 @@ +// CoderMixer2MT.h + +#ifndef __CODER_MIXER2_MT_H +#define __CODER_MIXER2_MT_H + +#include "CoderMixer2.h" +#include "../../../Common/MyCom.h" +#include "../../Common/StreamBinder.h" +#include "../../Common/VirtThread.h" + +namespace NCoderMixer { + +struct CCoder2: public CCoderInfo2, public CVirtThread +{ + HRESULT Result; + CObjectVector< CMyComPtr<ISequentialInStream> > InStreams; + CObjectVector< CMyComPtr<ISequentialOutStream> > OutStreams; + CRecordVector<ISequentialInStream*> InStreamPointers; + CRecordVector<ISequentialOutStream*> OutStreamPointers; + + CCoder2(UInt32 numInStreams, UInt32 numOutStreams); + void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes); + virtual void Execute(); + void Code(ICompressProgressInfo *progress); +}; + + +/* + SetBindInfo() + for each coder + AddCoder[2]() + SetProgressIndex(UInt32 coderIndex); + + for each file + { + ReInit() + for each coder + SetCoderInfo + Code + } +*/ + +class CCoderMixer2MT: + public ICompressCoder2, + public CCoderMixer2, + public CMyUnknownImp +{ + CBindInfo _bindInfo; + CObjectVector<CStreamBinder> _streamBinders; + int _progressCoderIndex; + + void AddCoderCommon(); + HRESULT Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams); + HRESULT ReturnIfError(HRESULT code); +public: + CObjectVector<CCoder2> _coders; + MY_UNKNOWN_IMP + + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); + + HRESULT SetBindInfo(const CBindInfo &bindInfo); + void AddCoder(ICompressCoder *coder); + void AddCoder2(ICompressCoder2 *coder); + void SetProgressCoderIndex(int coderIndex) { _progressCoderIndex = coderIndex; } + + void ReInit(); + void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) + { _coders[coderIndex].SetCoderInfo(inSizes, outSizes); } + UInt64 GetWriteProcessedSize(UInt32 binderIndex) const + { return _streamBinders[binderIndex].ProcessedSize; } +}; + +} +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.cpp new file mode 100644 index 000000000..a59ce5fc0 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.cpp @@ -0,0 +1,239 @@ +// CoderMixer2ST.cpp + +#include "StdAfx.h" + +#include "CoderMixer2ST.h" + +namespace NCoderMixer2 { + +CCoderMixer2ST::CCoderMixer2ST() {} + +CCoderMixer2ST::~CCoderMixer2ST(){ } + +HRESULT CCoderMixer2ST::SetBindInfo(const CBindInfo &bindInfo) +{ + _bindInfo = bindInfo; + return S_OK; +} + +void CCoderMixer2ST::AddCoderCommon(bool isMain) +{ + const CCoderStreamsInfo &csi = _bindInfo.Coders[_coders.Size()]; + _coders.Add(CSTCoderInfo(csi.NumInStreams, csi.NumOutStreams, isMain)); +} + +void CCoderMixer2ST::AddCoder(ICompressCoder *coder, bool isMain) +{ + AddCoderCommon(isMain); + _coders.Back().Coder = coder; +} + +void CCoderMixer2ST::AddCoder2(ICompressCoder2 *coder, bool isMain) +{ + AddCoderCommon(isMain); + _coders.Back().Coder2 = coder; +} + +void CCoderMixer2ST::ReInit() { } + +HRESULT CCoderMixer2ST::GetInStream( + ISequentialInStream **inStreams, const UInt64 **inSizes, + UInt32 streamIndex, ISequentialInStream **inStreamRes) +{ + CMyComPtr<ISequentialInStream> seqInStream; + int i; + for(i = 0; i < _bindInfo.InStreams.Size(); i++) + if (_bindInfo.InStreams[i] == streamIndex) + { + seqInStream = inStreams[i]; + *inStreamRes = seqInStream.Detach(); + return S_OK; + } + int binderIndex = _bindInfo.FindBinderForInStream(streamIndex); + if (binderIndex < 0) + return E_INVALIDARG; + + UInt32 coderIndex, coderStreamIndex; + _bindInfo.FindOutStream(_bindInfo.BindPairs[binderIndex].OutIndex, + coderIndex, coderStreamIndex); + + CCoderInfo &coder = _coders[coderIndex]; + if (!coder.Coder) + return E_NOTIMPL; + coder.Coder.QueryInterface(IID_ISequentialInStream, &seqInStream); + if (!seqInStream) + return E_NOTIMPL; + + UInt32 startIndex = _bindInfo.GetCoderInStreamIndex(coderIndex); + + CMyComPtr<ICompressSetInStream> setInStream; + if (!coder.Coder) + return E_NOTIMPL; + coder.Coder.QueryInterface(IID_ICompressSetInStream, &setInStream); + if (!setInStream) + return E_NOTIMPL; + + if (coder.NumInStreams > 1) + return E_NOTIMPL; + for (i = 0; i < (int)coder.NumInStreams; i++) + { + CMyComPtr<ISequentialInStream> seqInStream2; + RINOK(GetInStream(inStreams, inSizes, startIndex + i, &seqInStream2)); + RINOK(setInStream->SetInStream(seqInStream2)); + } + *inStreamRes = seqInStream.Detach(); + return S_OK; +} + +HRESULT CCoderMixer2ST::GetOutStream( + ISequentialOutStream **outStreams, const UInt64 **outSizes, + UInt32 streamIndex, ISequentialOutStream **outStreamRes) +{ + CMyComPtr<ISequentialOutStream> seqOutStream; + int i; + for(i = 0; i < _bindInfo.OutStreams.Size(); i++) + if (_bindInfo.OutStreams[i] == streamIndex) + { + seqOutStream = outStreams[i]; + *outStreamRes = seqOutStream.Detach(); + return S_OK; + } + int binderIndex = _bindInfo.FindBinderForOutStream(streamIndex); + if (binderIndex < 0) + return E_INVALIDARG; + + UInt32 coderIndex, coderStreamIndex; + _bindInfo.FindInStream(_bindInfo.BindPairs[binderIndex].InIndex, + coderIndex, coderStreamIndex); + + CCoderInfo &coder = _coders[coderIndex]; + if (!coder.Coder) + return E_NOTIMPL; + coder.Coder.QueryInterface(IID_ISequentialOutStream, &seqOutStream); + if (!seqOutStream) + return E_NOTIMPL; + + UInt32 startIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex); + + CMyComPtr<ICompressSetOutStream> setOutStream; + if (!coder.Coder) + return E_NOTIMPL; + coder.Coder.QueryInterface(IID_ICompressSetOutStream, &setOutStream); + if (!setOutStream) + return E_NOTIMPL; + + if (coder.NumOutStreams > 1) + return E_NOTIMPL; + for (i = 0; i < (int)coder.NumOutStreams; i++) + { + CMyComPtr<ISequentialOutStream> seqOutStream2; + RINOK(GetOutStream(outStreams, outSizes, startIndex + i, &seqOutStream2)); + RINOK(setOutStream->SetOutStream(seqOutStream2)); + } + *outStreamRes = seqOutStream.Detach(); + return S_OK; +} + + +STDMETHODIMP CCoderMixer2ST::Code(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress) +{ + if (numInStreams != (UInt32)_bindInfo.InStreams.Size() || + numOutStreams != (UInt32)_bindInfo.OutStreams.Size()) + return E_INVALIDARG; + + // Find main coder + int _mainCoderIndex = -1; + int i; + for (i = 0; i < _coders.Size(); i++) + if (_coders[i].IsMain) + { + _mainCoderIndex = i; + break; + } + if (_mainCoderIndex < 0) + for (i = 0; i < _coders.Size(); i++) + if (_coders[i].NumInStreams > 1) + { + if (_mainCoderIndex >= 0) + return E_NOTIMPL; + _mainCoderIndex = i; + } + if (_mainCoderIndex < 0) + _mainCoderIndex = 0; + + // _mainCoderIndex = 0; + // _mainCoderIndex = _coders.Size() - 1; + CCoderInfo &mainCoder = _coders[_mainCoderIndex]; + + CObjectVector< CMyComPtr<ISequentialInStream> > seqInStreams; + CObjectVector< CMyComPtr<ISequentialOutStream> > seqOutStreams; + UInt32 startInIndex = _bindInfo.GetCoderInStreamIndex(_mainCoderIndex); + UInt32 startOutIndex = _bindInfo.GetCoderOutStreamIndex(_mainCoderIndex); + for (i = 0; i < (int)mainCoder.NumInStreams; i++) + { + CMyComPtr<ISequentialInStream> seqInStream; + RINOK(GetInStream(inStreams, inSizes, startInIndex + i, &seqInStream)); + seqInStreams.Add(seqInStream); + } + for (i = 0; i < (int)mainCoder.NumOutStreams; i++) + { + CMyComPtr<ISequentialOutStream> seqOutStream; + RINOK(GetOutStream(outStreams, outSizes, startOutIndex + i, &seqOutStream)); + seqOutStreams.Add(seqOutStream); + } + CRecordVector< ISequentialInStream * > seqInStreamsSpec; + CRecordVector< ISequentialOutStream * > seqOutStreamsSpec; + for (i = 0; i < (int)mainCoder.NumInStreams; i++) + seqInStreamsSpec.Add(seqInStreams[i]); + for (i = 0; i < (int)mainCoder.NumOutStreams; i++) + seqOutStreamsSpec.Add(seqOutStreams[i]); + + for (i = 0; i < _coders.Size(); i++) + { + if (i == _mainCoderIndex) + continue; + CCoderInfo &coder = _coders[i]; + CMyComPtr<ICompressSetOutStreamSize> setOutStreamSize; + coder.Coder.QueryInterface(IID_ICompressSetOutStreamSize, &setOutStreamSize); + if (setOutStreamSize) + { + RINOK(setOutStreamSize->SetOutStreamSize(coder.OutSizePointers[0])); + } + } + if (mainCoder.Coder) + { + RINOK(mainCoder.Coder->Code( + seqInStreamsSpec[0], seqOutStreamsSpec[0], + mainCoder.InSizePointers[0], mainCoder.OutSizePointers[0], + progress)); + } + else + { + RINOK(mainCoder.Coder2->Code( + &seqInStreamsSpec.Front(), + &mainCoder.InSizePointers.Front(), mainCoder.NumInStreams, + &seqOutStreamsSpec.Front(), + &mainCoder.OutSizePointers.Front(), mainCoder.NumOutStreams, + progress)); + } + CMyComPtr<IOutStreamFlush> flush; + seqOutStreams.Front().QueryInterface(IID_IOutStreamFlush, &flush); + if (flush) + return flush->Flush(); + return S_OK; +} + +/* +UInt64 CCoderMixer2ST::GetWriteProcessedSize(UInt32 binderIndex) const +{ + return _streamBinders[binderIndex].ProcessedSize; +} +*/ + +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.h new file mode 100644 index 000000000..a4ea7e80d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2ST.h @@ -0,0 +1,88 @@ +// CoderMixer2ST.h + +#ifndef __CODER_MIXER2_ST_H +#define __CODER_MIXER2_ST_H + +#include "CoderMixer2.h" +#include "../../../Common/MyCom.h" +#include "../../ICoder.h" + +namespace NCoderMixer2 { + +// SetBindInfo() +// for each coder +// { +// AddCoder[2]() +// } +// +// for each file +// { +// ReInit() +// for each coder +// { +// SetCoderInfo +// } +// SetProgressIndex(UInt32 coderIndex); +// Code +// } + +struct CSTCoderInfo: public CCoderInfo +{ + bool IsMain; + CSTCoderInfo(UInt32 numInStreams, UInt32 numOutStreams, bool isMain): + CCoderInfo(numInStreams, numOutStreams),IsMain(isMain) {} +}; + +class CCoderMixer2ST: + public ICompressCoder2, + public CCoderMixer2, + public CMyUnknownImp +{ + MY_UNKNOWN_IMP + + HRESULT GetInStream( + ISequentialInStream **inStreams, const UInt64 **inSizes, + UInt32 streamIndex, ISequentialInStream **inStreamRes); + HRESULT GetOutStream( + ISequentialOutStream **outStreams, const UInt64 **outSizes, + UInt32 streamIndex, ISequentialOutStream **outStreamRes); +public: + STDMETHOD(Code)(ISequentialInStream **inStreams, + const UInt64 **inSizes, + UInt32 numInStreams, + ISequentialOutStream **outStreams, + const UInt64 **outSizes, + UInt32 numOutStreams, + ICompressProgressInfo *progress); + + CCoderMixer2ST(); + ~CCoderMixer2ST(); + void AddCoderCommon(bool isMain); + void AddCoder(ICompressCoder *coder, bool isMain); + void AddCoder2(ICompressCoder2 *coder, bool isMain); + + void ReInit(); + void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) + { + { _coders[coderIndex].SetCoderInfo(inSizes, outSizes); } + } + + void SetProgressCoderIndex(UInt32 /*coderIndex*/) + { + // _progressCoderIndex = coderIndex; + } + + // UInt64 GetWriteProcessedSize(UInt32 binderIndex) const; + +private: + CBindInfo _bindInfo; + CObjectVector<CSTCoderInfo> _coders; + int _mainCoderIndex; +public: + HRESULT SetBindInfo(const CBindInfo &bindInfo); + +}; + +} +#endif + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.cpp new file mode 100644 index 000000000..a974b54c7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.cpp @@ -0,0 +1,15 @@ +// CrossThreadProgress.cpp + +#include "StdAfx.h" + +#include "CrossThreadProgress.h" + +STDMETHODIMP CCrossThreadProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + InSize = inSize; + OutSize = outSize; + ProgressEvent.Set(); + WaitEvent.Lock(); + return Result; +} + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.h new file mode 100644 index 000000000..7e0b10538 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/CrossThreadProgress.h @@ -0,0 +1,37 @@ +// CrossThreadProgress.h + +#ifndef __CROSSTHREADPROGRESS_H +#define __CROSSTHREADPROGRESS_H + +#include "../../ICoder.h" +#include "../../../Windows/Synchronization.h" +#include "../../../Common/MyCom.h" + +class CCrossThreadProgress: + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + const UInt64 *InSize; + const UInt64 *OutSize; + HRESULT Result; + NWindows::NSynchronization::CAutoResetEvent ProgressEvent; + NWindows::NSynchronization::CAutoResetEvent WaitEvent; + + HRes Create() + { + RINOK(ProgressEvent.CreateIfNotCreated()); + return WaitEvent.CreateIfNotCreated(); + } + void Init() + { + ProgressEvent.Reset(); + WaitEvent.Reset(); + } + + MY_UNKNOWN_IMP + + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp new file mode 100644 index 000000000..54bcfec11 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp @@ -0,0 +1,22 @@ +// DummyOutStream.cpp + +#include "StdAfx.h" + +#include "DummyOutStream.h" + +STDMETHODIMP CDummyOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result; + if(!_stream) + { + realProcessedSize = size; + result = S_OK; + } + else + result = _stream->Write(data, size, &realProcessedSize); + _size += realProcessedSize; + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h new file mode 100644 index 000000000..13d5b62ca --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h @@ -0,0 +1,24 @@ +// DummyOutStream.h + +#ifndef __DUMMYOUTSTREAM_H +#define __DUMMYOUTSTREAM_H + +#include "../../IStream.h" +#include "Common/MyCom.h" + +class CDummyOutStream: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialOutStream> _stream; + UInt64 _size; +public: + void SetStream(ISequentialOutStream *outStream) { _stream = outStream; } + void ReleaseStream() { _stream.Release(); } + void Init() { _size = 0; } + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + UInt64 GetSize() const { return _size; } +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.cpp new file mode 100644 index 000000000..15aa6ceae --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.cpp @@ -0,0 +1,62 @@ +// FindSignature.cpp + +#include "StdAfx.h" + +#include "Common/Buffer.h" + +#include "FindSignature.h" + +#include "../../Common/StreamUtils.h" + +HRESULT FindSignatureInStream(ISequentialInStream *stream, + const Byte *signature, unsigned signatureSize, + const UInt64 *limit, UInt64 &resPos) +{ + resPos = 0; + CByteBuffer byteBuffer2; + byteBuffer2.SetCapacity(signatureSize); + RINOK(ReadStream_FALSE(stream, byteBuffer2, signatureSize)); + + if (memcmp(byteBuffer2, signature, signatureSize) == 0) + return S_OK; + + const UInt32 kBufferSize = (1 << 16); + CByteBuffer byteBuffer; + byteBuffer.SetCapacity(kBufferSize); + Byte *buffer = byteBuffer; + UInt32 numPrevBytes = signatureSize - 1; + memcpy(buffer, (const Byte *)byteBuffer2 + 1, numPrevBytes); + resPos = 1; + for (;;) + { + if (limit != NULL) + if (resPos > *limit) + return S_FALSE; + do + { + UInt32 numReadBytes = kBufferSize - numPrevBytes; + UInt32 processedSize; + RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); + numPrevBytes += processedSize; + if (processedSize == 0) + return S_FALSE; + } + while (numPrevBytes < signatureSize); + UInt32 numTests = numPrevBytes - signatureSize + 1; + for (UInt32 pos = 0; pos < numTests; pos++) + { + Byte b = signature[0]; + for (; buffer[pos] != b && pos < numTests; pos++); + if (pos == numTests) + break; + if (memcmp(buffer + pos, signature, signatureSize) == 0) + { + resPos += pos; + return S_OK; + } + } + resPos += numTests; + numPrevBytes -= numTests; + memmove(buffer, buffer + numTests, numPrevBytes); + } +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.h new file mode 100644 index 000000000..e15af5732 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/FindSignature.h @@ -0,0 +1,12 @@ +// FindSignature.h + +#ifndef __FINDSIGNATURE_H +#define __FINDSIGNATURE_H + +#include "../../IStream.h" + +HRESULT FindSignatureInStream(ISequentialInStream *stream, + const Byte *signature, unsigned signatureSize, + const UInt64 *limit, UInt64 &resPos); + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp new file mode 100644 index 000000000..70ad47aad --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp @@ -0,0 +1,623 @@ +// HandlerOut.cpp + +#include "StdAfx.h" + +#include "../../../Common/StringToInt.h" + +#include "../../../Windows/PropVariant.h" + +#ifndef _7ZIP_ST +#include "../../../Windows/System.h" +#endif + +#include "../../ICoder.h" + +#include "../Common/ParseProperties.h" + +#include "HandlerOut.h" + +using namespace NWindows; + +namespace NArchive { + +static const wchar_t *kCopyMethod = L"Copy"; +static const wchar_t *kLZMAMethodName = L"LZMA"; +static const wchar_t *kLZMA2MethodName = L"LZMA2"; +static const wchar_t *kBZip2MethodName = L"BZip2"; +static const wchar_t *kPpmdMethodName = L"PPMd"; +static const wchar_t *kDeflateMethodName = L"Deflate"; +static const wchar_t *kDeflate64MethodName = L"Deflate64"; + +static const wchar_t *kLzmaMatchFinderX1 = L"HC4"; +static const wchar_t *kLzmaMatchFinderX5 = L"BT4"; + +static const UInt32 kLzmaAlgoX1 = 0; +static const UInt32 kLzmaAlgoX5 = 1; + +static const UInt32 kLzmaDicSizeX1 = 1 << 16; +static const UInt32 kLzmaDicSizeX3 = 1 << 20; +static const UInt32 kLzmaDicSizeX5 = 1 << 24; +static const UInt32 kLzmaDicSizeX7 = 1 << 25; +static const UInt32 kLzmaDicSizeX9 = 1 << 26; + +static const UInt32 kLzmaFastBytesX1 = 32; +static const UInt32 kLzmaFastBytesX7 = 64; + +static const UInt32 kPpmdMemSizeX1 = (1 << 22); +static const UInt32 kPpmdMemSizeX5 = (1 << 24); +static const UInt32 kPpmdMemSizeX7 = (1 << 26); +static const UInt32 kPpmdMemSizeX9 = (192 << 20); + +static const UInt32 kPpmdOrderX1 = 4; +static const UInt32 kPpmdOrderX5 = 6; +static const UInt32 kPpmdOrderX7 = 16; +static const UInt32 kPpmdOrderX9 = 32; + +static const UInt32 kDeflateAlgoX1 = 0; +static const UInt32 kDeflateAlgoX5 = 1; + +static const UInt32 kDeflateFastBytesX1 = 32; +static const UInt32 kDeflateFastBytesX7 = 64; +static const UInt32 kDeflateFastBytesX9 = 128; + +static const UInt32 kDeflatePassesX1 = 1; +static const UInt32 kDeflatePassesX7 = 3; +static const UInt32 kDeflatePassesX9 = 10; + +static const UInt32 kBZip2NumPassesX1 = 1; +static const UInt32 kBZip2NumPassesX7 = 2; +static const UInt32 kBZip2NumPassesX9 = 7; + +static const UInt32 kBZip2DicSizeX1 = 100000; +static const UInt32 kBZip2DicSizeX3 = 500000; +static const UInt32 kBZip2DicSizeX5 = 900000; + +static const wchar_t *kDefaultMethodName = kLZMAMethodName; + +static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2"; +static const UInt32 kDictionaryForHeaders = 1 << 20; +static const UInt32 kNumFastBytesForHeaders = 273; +static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5; + +static bool AreEqual(const UString &methodName, const wchar_t *s) + { return (methodName.CompareNoCase(s) == 0); } + +bool COneMethodInfo::IsLzma() const +{ + return + AreEqual(MethodName, kLZMAMethodName) || + AreEqual(MethodName, kLZMA2MethodName); +} + +static inline bool IsBZip2Method(const UString &methodName) + { return AreEqual(methodName, kBZip2MethodName); } + +static inline bool IsPpmdMethod(const UString &methodName) + { return AreEqual(methodName, kPpmdMethodName); } + +static inline bool IsDeflateMethod(const UString &methodName) +{ + return + AreEqual(methodName, kDeflateMethodName) || + AreEqual(methodName, kDeflate64MethodName); +} + +struct CNameToPropID +{ + PROPID PropID; + VARTYPE VarType; + const wchar_t *Name; +}; + +static CNameToPropID g_NameToPropID[] = +{ + { NCoderPropID::kBlockSize, VT_UI4, L"C" }, + { NCoderPropID::kDictionarySize, VT_UI4, L"D" }, + { NCoderPropID::kUsedMemorySize, VT_UI4, L"MEM" }, + + { NCoderPropID::kOrder, VT_UI4, L"O" }, + { NCoderPropID::kPosStateBits, VT_UI4, L"PB" }, + { NCoderPropID::kLitContextBits, VT_UI4, L"LC" }, + { NCoderPropID::kLitPosBits, VT_UI4, L"LP" }, + { NCoderPropID::kEndMarker, VT_BOOL, L"eos" }, + + { NCoderPropID::kNumPasses, VT_UI4, L"Pass" }, + { NCoderPropID::kNumFastBytes, VT_UI4, L"fb" }, + { NCoderPropID::kMatchFinderCycles, VT_UI4, L"mc" }, + { NCoderPropID::kAlgorithm, VT_UI4, L"a" }, + { NCoderPropID::kMatchFinder, VT_BSTR, L"mf" }, + { NCoderPropID::kNumThreads, VT_UI4, L"mt" }, + { NCoderPropID::kDefaultProp, VT_UI4, L"" } +}; + +static bool ConvertProperty(PROPVARIANT srcProp, VARTYPE varType, NCOM::CPropVariant &destProp) +{ + if (varType == srcProp.vt) + { + destProp = srcProp; + return true; + } + if (varType == VT_UI1) + { + if (srcProp.vt == VT_UI4) + { + UInt32 value = srcProp.ulVal; + if (value > 0xFF) + return false; + destProp = (Byte)value; + return true; + } + } + else if (varType == VT_BOOL) + { + bool res; + if (SetBoolProperty(res, srcProp) != S_OK) + return false; + destProp = res; + return true; + } + return false; +} + +static int FindPropIdExact(const UString &name) +{ + for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++) + if (name.CompareNoCase(g_NameToPropID[i].Name) == 0) + return i; + return -1; +} + +static int FindPropIdStart(const UString &name) +{ + for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++) + { + UString t = g_NameToPropID[i].Name; + if (t.CompareNoCase(name.Left(t.Length())) == 0) + return i; + } + return -1; +} + +static void SetMethodProp(COneMethodInfo &m, PROPID propID, const NCOM::CPropVariant &value) +{ + for (int j = 0; j < m.Props.Size(); j++) + if (m.Props[j].Id == propID) + return; + CProp prop; + prop.Id = propID; + prop.Value = value; + m.Props.Add(prop); +} + +void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo + #ifndef _7ZIP_ST + , UInt32 numThreads + #endif + ) +{ + UInt32 level = _level; + if (oneMethodInfo.MethodName.IsEmpty()) + oneMethodInfo.MethodName = kDefaultMethodName; + + if (oneMethodInfo.IsLzma()) + { + UInt32 dicSize = + (level >= 9 ? kLzmaDicSizeX9 : + (level >= 7 ? kLzmaDicSizeX7 : + (level >= 5 ? kLzmaDicSizeX5 : + (level >= 3 ? kLzmaDicSizeX3 : + kLzmaDicSizeX1)))); + + UInt32 algo = + (level >= 5 ? kLzmaAlgoX5 : + kLzmaAlgoX1); + + UInt32 fastBytes = + (level >= 7 ? kLzmaFastBytesX7 : + kLzmaFastBytesX1); + + const wchar_t *matchFinder = + (level >= 5 ? kLzmaMatchFinderX5 : + kLzmaMatchFinderX1); + + SetMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize); + SetMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo); + SetMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes); + SetMethodProp(oneMethodInfo, NCoderPropID::kMatchFinder, matchFinder); + #ifndef _7ZIP_ST + SetMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); + #endif + } + else if (IsDeflateMethod(oneMethodInfo.MethodName)) + { + UInt32 fastBytes = + (level >= 9 ? kDeflateFastBytesX9 : + (level >= 7 ? kDeflateFastBytesX7 : + kDeflateFastBytesX1)); + + UInt32 numPasses = + (level >= 9 ? kDeflatePassesX9 : + (level >= 7 ? kDeflatePassesX7 : + kDeflatePassesX1)); + + UInt32 algo = + (level >= 5 ? kDeflateAlgoX5 : + kDeflateAlgoX1); + + SetMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo); + SetMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes); + SetMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses); + } + else if (IsBZip2Method(oneMethodInfo.MethodName)) + { + UInt32 numPasses = + (level >= 9 ? kBZip2NumPassesX9 : + (level >= 7 ? kBZip2NumPassesX7 : + kBZip2NumPassesX1)); + + UInt32 dicSize = + (level >= 5 ? kBZip2DicSizeX5 : + (level >= 3 ? kBZip2DicSizeX3 : + kBZip2DicSizeX1)); + + SetMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses); + SetMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize); + #ifndef _7ZIP_ST + SetMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads); + #endif + } + else if (IsPpmdMethod(oneMethodInfo.MethodName)) + { + UInt32 useMemSize = + (level >= 9 ? kPpmdMemSizeX9 : + (level >= 7 ? kPpmdMemSizeX7 : + (level >= 5 ? kPpmdMemSizeX5 : + kPpmdMemSizeX1))); + + UInt32 order = + (level >= 9 ? kPpmdOrderX9 : + (level >= 7 ? kPpmdOrderX7 : + (level >= 5 ? kPpmdOrderX5 : + kPpmdOrderX1))); + + SetMethodProp(oneMethodInfo, NCoderPropID::kUsedMemorySize, useMemSize); + SetMethodProp(oneMethodInfo, NCoderPropID::kOrder, order); + } +} + +static void SplitParams(const UString &srcString, UStringVector &subStrings) +{ + subStrings.Clear(); + UString name; + int len = srcString.Length(); + if (len == 0) + return; + for (int i = 0; i < len; i++) + { + wchar_t c = srcString[i]; + if (c == L':') + { + subStrings.Add(name); + name.Empty(); + } + else + name += c; + } + subStrings.Add(name); +} + +static void SplitParam(const UString ¶m, UString &name, UString &value) +{ + int eqPos = param.Find(L'='); + if (eqPos >= 0) + { + name = param.Left(eqPos); + value = param.Mid(eqPos + 1); + return; + } + for(int i = 0; i < param.Length(); i++) + { + wchar_t c = param[i]; + if (c >= L'0' && c <= L'9') + { + name = param.Left(i); + value = param.Mid(i); + return; + } + } + name = param; +} + +HRESULT COutHandler::SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value) +{ + CProp prop; + int index = FindPropIdExact(name); + if (index < 0) + return E_INVALIDARG; + const CNameToPropID &nameToPropID = g_NameToPropID[index]; + prop.Id = nameToPropID.PropID; + + if (prop.Id == NCoderPropID::kBlockSize || + prop.Id == NCoderPropID::kDictionarySize || + prop.Id == NCoderPropID::kUsedMemorySize) + { + UInt32 dicSize; + RINOK(ParsePropDictionaryValue(value, dicSize)); + prop.Value = dicSize; + } + else + { + NCOM::CPropVariant propValue; + + if (nameToPropID.VarType == VT_BSTR) + propValue = value; + else if (nameToPropID.VarType == VT_BOOL) + { + bool res; + if (!StringToBool(value, res)) + return E_INVALIDARG; + propValue = res; + } + else + { + UInt32 number; + if (ParseStringToUInt32(value, number) == value.Length()) + propValue = number; + else + propValue = value; + } + + if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value)) + return E_INVALIDARG; + } + oneMethodInfo.Props.Add(prop); + return S_OK; +} + +HRESULT COutHandler::SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString) +{ + UStringVector params; + SplitParams(srcString, params); + if (params.Size() > 0) + oneMethodInfo.MethodName = params[0]; + for (int i = 1; i < params.Size(); i++) + { + const UString ¶m = params[i]; + UString name, value; + SplitParam(param, name, value); + RINOK(SetParam(oneMethodInfo, name, value)); + } + return S_OK; +} + +HRESULT COutHandler::SetSolidSettings(const UString &s) +{ + UString s2 = s; + s2.MakeUpper(); + for (int i = 0; i < s2.Length();) + { + const wchar_t *start = ((const wchar_t *)s2) + i; + const wchar_t *end; + UInt64 v = ConvertStringToUInt64(start, &end); + if (start == end) + { + if (s2[i++] != 'E') + return E_INVALIDARG; + _solidExtension = true; + continue; + } + i += (int)(end - start); + if (i == s2.Length()) + return E_INVALIDARG; + wchar_t c = s2[i++]; + switch(c) + { + case 'F': + if (v < 1) + v = 1; + _numSolidFiles = v; + break; + case 'B': + _numSolidBytes = v; + _numSolidBytesDefined = true; + break; + case 'K': + _numSolidBytes = (v << 10); + _numSolidBytesDefined = true; + break; + case 'M': + _numSolidBytes = (v << 20); + _numSolidBytesDefined = true; + break; + case 'G': + _numSolidBytes = (v << 30); + _numSolidBytesDefined = true; + break; + default: + return E_INVALIDARG; + } + } + return S_OK; +} + +HRESULT COutHandler::SetSolidSettings(const PROPVARIANT &value) +{ + 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)) + break; + return SetSolidSettings(value.bstrVal); + default: + return E_INVALIDARG; + } + if (isSolid) + InitSolid(); + else + _numSolidFiles = 1; + return S_OK; +} + +void COutHandler::Init() +{ + _removeSfxBlock = false; + _compressHeaders = true; + _encryptHeadersSpecified = false; + _encryptHeaders = false; + + WriteCTime = false; + WriteATime = false; + WriteMTime = true; + + #ifndef _7ZIP_ST + _numThreads = NSystem::GetNumberOfProcessors(); + #endif + + _level = 5; + _autoFilter = true; + _volumeMode = false; + _crcSize = 4; + InitSolid(); +} + +void COutHandler::BeforeSetProperty() +{ + Init(); + #ifndef _7ZIP_ST + numProcessors = NSystem::GetNumberOfProcessors(); + #endif + + mainDicSize = 0xFFFFFFFF; + mainDicMethodIndex = 0xFFFFFFFF; + minNumber = 0; + _crcSize = 4; +} + +HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) +{ + UString name = nameSpec; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + + if (name[0] == 'X') + { + name.Delete(0); + _level = 9; + return ParsePropValue(name, value, _level); + } + + if (name[0] == L'S') + { + name.Delete(0); + if (name.IsEmpty()) + return SetSolidSettings(value); + if (value.vt != VT_EMPTY) + return E_INVALIDARG; + return SetSolidSettings(name); + } + + if (name == L"CRC") + { + _crcSize = 4; + name.Delete(0, 3); + return ParsePropValue(name, value, _crcSize); + } + + UInt32 number; + int index = ParseStringToUInt32(name, number); + UString realName = name.Mid(index); + if (index == 0) + { + if(name.Left(2).CompareNoCase(L"MT") == 0) + { + #ifndef _7ZIP_ST + RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads)); + #endif + return S_OK; + } + if (name.CompareNoCase(L"RSFX") == 0) return SetBoolProperty(_removeSfxBlock, value); + if (name.CompareNoCase(L"F") == 0) return SetBoolProperty(_autoFilter, value); + if (name.CompareNoCase(L"HC") == 0) return SetBoolProperty(_compressHeaders, value); + if (name.CompareNoCase(L"HCF") == 0) + { + bool compressHeadersFull = true; + RINOK(SetBoolProperty(compressHeadersFull, value)); + if (!compressHeadersFull) + return E_INVALIDARG; + return S_OK; + } + if (name.CompareNoCase(L"HE") == 0) + { + RINOK(SetBoolProperty(_encryptHeaders, value)); + _encryptHeadersSpecified = true; + return S_OK; + } + if (name.CompareNoCase(L"TC") == 0) return SetBoolProperty(WriteCTime, value); + if (name.CompareNoCase(L"TA") == 0) return SetBoolProperty(WriteATime, value); + if (name.CompareNoCase(L"TM") == 0) return SetBoolProperty(WriteMTime, value); + if (name.CompareNoCase(L"V") == 0) return SetBoolProperty(_volumeMode, value); + number = 0; + } + if (number > 10000) + return E_FAIL; + if (number < minNumber) + return E_INVALIDARG; + number -= minNumber; + for(int j = _methods.Size(); j <= (int)number; j++) + { + COneMethodInfo oneMethodInfo; + _methods.Add(oneMethodInfo); + } + + COneMethodInfo &oneMethodInfo = _methods[number]; + + if (realName.Length() == 0) + { + if (value.vt != VT_BSTR) + return E_INVALIDARG; + + RINOK(SetParams(oneMethodInfo, value.bstrVal)); + } + else + { + int index = FindPropIdStart(realName); + if (index < 0) + return E_INVALIDARG; + const CNameToPropID &nameToPropID = g_NameToPropID[index]; + CProp prop; + prop.Id = nameToPropID.PropID; + + if (prop.Id == NCoderPropID::kBlockSize || + prop.Id == NCoderPropID::kDictionarySize || + prop.Id == NCoderPropID::kUsedMemorySize) + { + UInt32 dicSize; + RINOK(ParsePropDictionaryValue(realName.Mid(MyStringLen(nameToPropID.Name)), value, dicSize)); + prop.Value = dicSize; + if (number <= mainDicMethodIndex) + mainDicSize = dicSize; + } + else + { + int index = FindPropIdExact(realName); + if (index < 0) + return E_INVALIDARG; + const CNameToPropID &nameToPropID = g_NameToPropID[index]; + prop.Id = nameToPropID.PropID; + if (!ConvertProperty(value, nameToPropID.VarType, prop.Value)) + return E_INVALIDARG; + } + oneMethodInfo.Props.Add(prop); + } + return S_OK; +} + +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h new file mode 100644 index 000000000..72ea40321 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h @@ -0,0 +1,87 @@ +// HandlerOut.h + +#ifndef __HANDLER_OUT_H +#define __HANDLER_OUT_H + +#include "../../../Common/MyString.h" +#include "../../Common/MethodProps.h" + +namespace NArchive { + +struct COneMethodInfo +{ + CObjectVector<CProp> Props; + UString MethodName; + + bool IsLzma() const; +}; + +class COutHandler +{ +public: + HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value); + + HRESULT SetSolidSettings(const UString &s); + HRESULT SetSolidSettings(const PROPVARIANT &value); + + #ifndef _7ZIP_ST + UInt32 _numThreads; + #endif + + UInt32 _crcSize; + + CObjectVector<COneMethodInfo> _methods; + bool _removeSfxBlock; + + UInt64 _numSolidFiles; + UInt64 _numSolidBytes; + bool _numSolidBytesDefined; + bool _solidExtension; + + bool _compressHeaders; + bool _encryptHeadersSpecified; + bool _encryptHeaders; + + bool WriteCTime; + bool WriteATime; + bool WriteMTime; + + bool _autoFilter; + UInt32 _level; + + bool _volumeMode; + + HRESULT SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value); + HRESULT SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString); + + void SetCompressionMethod2(COneMethodInfo &oneMethodInfo + #ifndef _7ZIP_ST + , UInt32 numThreads + #endif + ); + + void InitSolidFiles() { _numSolidFiles = (UInt64)(Int64)(-1); } + void InitSolidSize() { _numSolidBytes = (UInt64)(Int64)(-1); } + void InitSolid() + { + InitSolidFiles(); + InitSolidSize(); + _solidExtension = false; + _numSolidBytesDefined = false; + } + + void Init(); + + COutHandler() { Init(); } + + void BeforeSetProperty(); + + UInt32 minNumber; + UInt32 numProcessors; + UInt32 mainDicSize; + UInt32 mainDicMethodIndex; +}; + +} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp new file mode 100644 index 000000000..569a56f3b --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp @@ -0,0 +1,42 @@ +// InStreamWithCRC.cpp + +#include "StdAfx.h" + +#include "InStreamWithCRC.h" + +STDMETHODIMP CSequentialInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + if (size > 0 && realProcessedSize == 0) + _wasFinished = true; + _crc = CrcUpdate(_crc, data, realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + /* + if (size > 0 && realProcessedSize == 0) + _wasFinished = true; + */ + _size += realProcessedSize; + _crc = CrcUpdate(_crc, data, realProcessedSize); + if(processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +STDMETHODIMP CInStreamWithCRC::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if (seekOrigin != STREAM_SEEK_SET || offset != 0) + return E_FAIL; + _size = 0; + _crc = CRC_INIT_VAL; + return _stream->Seek(offset, seekOrigin, newPosition); +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h new file mode 100644 index 000000000..31b761e45 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h @@ -0,0 +1,67 @@ +// InStreamWithCRC.h + +#ifndef __IN_STREAM_WITH_CRC_H +#define __IN_STREAM_WITH_CRC_H + +#include "../../../../C/7zCrc.h" + +#include "../../../Common/MyCom.h" + +#include "../../IStream.h" + +class CSequentialInStreamWithCRC: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +private: + CMyComPtr<ISequentialInStream> _stream; + UInt64 _size; + UInt32 _crc; + bool _wasFinished; +public: + void SetStream(ISequentialInStream *stream) { _stream = stream; } + void Init() + { + _size = 0; + _wasFinished = false; + _crc = CRC_INIT_VAL; + } + void ReleaseStream() { _stream.Release(); } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } + UInt64 GetSize() const { return _size; } + bool WasFinished() const { return _wasFinished; } +}; + +class CInStreamWithCRC: + public IInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +private: + CMyComPtr<IInStream> _stream; + UInt64 _size; + UInt32 _crc; + // bool _wasFinished; +public: + void SetStream(IInStream *stream) { _stream = stream; } + void Init() + { + _size = 0; + // _wasFinished = false; + _crc = CRC_INIT_VAL; + } + void ReleaseStream() { _stream.Release(); } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } + UInt64 GetSize() const { return _size; } + // bool WasFinished() const { return _wasFinished; } +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp new file mode 100644 index 000000000..a5e0dc0be --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp @@ -0,0 +1,61 @@ +// Archive/Common/ItemNameUtils.cpp + +#include "StdAfx.h" + +#include "../../../../C/Types.h" + +#include "ItemNameUtils.h" + +namespace NArchive { +namespace NItemName { + +static const wchar_t kOSDirDelimiter = WCHAR_PATH_SEPARATOR; +static const wchar_t kDirDelimiter = L'/'; + +UString MakeLegalName(const UString &name) +{ + UString zipName = name; + zipName.Replace(kOSDirDelimiter, kDirDelimiter); + return zipName; +} + +UString GetOSName(const UString &name) +{ + UString newName = name; + newName.Replace(kDirDelimiter, kOSDirDelimiter); + return newName; +} + +UString GetOSName2(const UString &name) +{ + if (name.IsEmpty()) + return UString(); + UString newName = GetOSName(name); + if (newName[newName.Length() - 1] == kOSDirDelimiter) + newName.Delete(newName.Length() - 1); + return newName; +} + +bool HasTailSlash(const AString &name, UINT codePage) +{ + if (name.IsEmpty()) + return false; + LPCSTR prev = + #if defined(_WIN32) && !defined(UNDER_CE) + CharPrevExA((WORD)codePage, name, &name[name.Length()], 0); + #else + (LPCSTR)(name) + (name.Length() - 1); + #endif + return (*prev == '/'); +} + +#ifndef _WIN32 +UString WinNameToOSName(const UString &name) +{ + UString newName = name; + newName.Replace(L'\\', kOSDirDelimiter); + return newName; +} +#endif + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h new file mode 100644 index 000000000..5eafacb12 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h @@ -0,0 +1,24 @@ +// Archive/Common/ItemNameUtils.h + +#ifndef __ARCHIVE_ITEMNAMEUTILS_H +#define __ARCHIVE_ITEMNAMEUTILS_H + +#include "../../../Common/MyString.h" + +namespace NArchive { +namespace NItemName { + + UString MakeLegalName(const UString &name); + UString GetOSName(const UString &name); + UString GetOSName2(const UString &name); + bool HasTailSlash(const AString &name, UINT codePage); + + #ifdef _WIN32 + inline UString WinNameToOSName(const UString &name) { return name; } + #else + UString WinNameToOSName(const UString &name); + #endif + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp new file mode 100644 index 000000000..04d11cafb --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp @@ -0,0 +1,190 @@ +// MultiStream.cpp + +#include "StdAfx.h" + +#include "MultiStream.h" + +STDMETHODIMP CMultiStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + if (_pos >= _totalLength) + return (_pos == _totalLength) ? S_OK : E_FAIL; + + { + int left = 0, mid = _streamIndex, right = Streams.Size(); + for (;;) + { + CSubStreamInfo &m = Streams[mid]; + if (_pos < m.GlobalOffset) + right = mid; + else if (_pos >= m.GlobalOffset + m.Size) + left = mid + 1; + else + { + _streamIndex = mid; + break; + } + mid = (left + right) / 2; + } + _streamIndex = mid; + } + + CSubStreamInfo &s = Streams[_streamIndex]; + UInt64 localPos = _pos - s.GlobalOffset; + if (localPos != s.LocalPos) + { + RINOK(s.Stream->Seek(localPos, STREAM_SEEK_SET, &s.LocalPos)); + } + UInt64 rem = s.Size - localPos; + if (size > rem) + size = (UInt32)rem; + HRESULT result = s.Stream->Read(data, size, &size); + _pos += size; + s.LocalPos += size; + if (processedSize) + *processedSize = size; + return result; +} + +STDMETHODIMP CMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _pos = offset; break; + case STREAM_SEEK_CUR: _pos = _pos + offset; break; + case STREAM_SEEK_END: _pos = _totalLength + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition != 0) + *newPosition = _pos; + return S_OK; +} + + +/* +class COutVolumeStream: + public ISequentialOutStream, + public CMyUnknownImp +{ + int _volIndex; + UInt64 _volSize; + UInt64 _curPos; + CMyComPtr<ISequentialOutStream> _volumeStream; + COutArchive _archive; + CCRC _crc; + +public: + MY_UNKNOWN_IMP + + CFileItem _file; + CUpdateOptions _options; + CMyComPtr<IArchiveUpdateCallback2> VolumeCallback; + void Init(IArchiveUpdateCallback2 *volumeCallback, + const UString &name) + { + _file.Name = name; + _file.IsStartPosDefined = true; + _file.StartPos = 0; + + VolumeCallback = volumeCallback; + _volIndex = 0; + _volSize = 0; + } + + HRESULT Flush(); + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +}; + +HRESULT COutVolumeStream::Flush() +{ + if (_volumeStream) + { + _file.UnPackSize = _curPos; + _file.FileCRC = _crc.GetDigest(); + RINOK(WriteVolumeHeader(_archive, _file, _options)); + _archive.Close(); + _volumeStream.Release(); + _file.StartPos += _file.UnPackSize; + } + return S_OK; +} +*/ + +/* +STDMETHODIMP COutMultiStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if(processedSize != NULL) + *processedSize = 0; + while(size > 0) + { + if (_streamIndex >= Streams.Size()) + { + CSubStreamInfo subStream; + RINOK(VolumeCallback->GetVolumeSize(Streams.Size(), &subStream.Size)); + RINOK(VolumeCallback->GetVolumeStream(Streams.Size(), &subStream.Stream)); + subStream.Pos = 0; + Streams.Add(subStream); + continue; + } + CSubStreamInfo &subStream = Streams[_streamIndex]; + if (_offsetPos >= subStream.Size) + { + _offsetPos -= subStream.Size; + _streamIndex++; + continue; + } + if (_offsetPos != subStream.Pos) + { + CMyComPtr<IOutStream> outStream; + RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); + RINOK(outStream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); + subStream.Pos = _offsetPos; + } + + UInt32 curSize = (UInt32)MyMin((UInt64)size, subStream.Size - subStream.Pos); + UInt32 realProcessed; + RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); + data = (void *)((Byte *)data + realProcessed); + size -= realProcessed; + subStream.Pos += realProcessed; + _offsetPos += realProcessed; + _absPos += realProcessed; + if (_absPos > _length) + _length = _absPos; + if(processedSize != NULL) + *processedSize += realProcessed; + if (subStream.Pos == subStream.Size) + { + _streamIndex++; + _offsetPos = 0; + } + if (realProcessed != curSize && realProcessed == 0) + return E_FAIL; + } + return S_OK; +} + +STDMETHODIMP COutMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + if(seekOrigin >= 3) + return STG_E_INVALIDFUNCTION; + switch(seekOrigin) + { + case STREAM_SEEK_SET: + _absPos = offset; + break; + case STREAM_SEEK_CUR: + _absPos += offset; + break; + case STREAM_SEEK_END: + _absPos = _length + offset; + break; + } + _offsetPos = _absPos; + _streamIndex = 0; + return S_OK; +} +*/ diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h new file mode 100644 index 000000000..3fceb7cce --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h @@ -0,0 +1,84 @@ +// MultiStream.h + +#ifndef __MULTI_STREAM_H +#define __MULTI_STREAM_H + +#include "../../../Common/MyCom.h" +#include "../../../Common/MyVector.h" + +#include "../../IStream.h" + +class CMultiStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _pos; + UInt64 _totalLength; + int _streamIndex; +public: + struct CSubStreamInfo + { + CMyComPtr<IInStream> Stream; + UInt64 Size; + UInt64 GlobalOffset; + UInt64 LocalPos; + }; + CObjectVector<CSubStreamInfo> Streams; + + HRESULT Init() + { + UInt64 total = 0; + for (int i = 0; i < Streams.Size(); i++) + { + CSubStreamInfo &s = Streams[i]; + s.GlobalOffset = total; + total += Streams[i].Size; + RINOK(s.Stream->Seek(0, STREAM_SEEK_CUR, &s.LocalPos)); + } + _totalLength = total; + _pos = 0; + _streamIndex = 0; + return S_OK; + } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +/* +class COutMultiStream: + public IOutStream, + public CMyUnknownImp +{ + int _streamIndex; // required stream + UInt64 _offsetPos; // offset from start of _streamIndex index + UInt64 _absPos; + UInt64 _length; + + struct CSubStreamInfo + { + CMyComPtr<ISequentialOutStream> Stream; + UInt64 Size; + UInt64 Pos; + }; + CObjectVector<CSubStreamInfo> Streams; +public: + CMyComPtr<IArchiveUpdateCallback2> VolumeCallback; + void Init() + { + _streamIndex = 0; + _offsetPos = 0; + _absPos = 0; + _length = 0; + } + + MY_UNKNOWN_IMP1(IOutStream) + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; +*/ + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp new file mode 100644 index 000000000..f955c2254 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp @@ -0,0 +1,18 @@ +// OutStreamWithCRC.cpp + +#include "StdAfx.h" + +#include "OutStreamWithCRC.h" + +STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + HRESULT result = S_OK; + if (_stream) + result = _stream->Write(data, size, &size); + if (_calculate) + _crc = CrcUpdate(_crc, data, size); + _size += size; + if (processedSize != NULL) + *processedSize = size; + return result; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h new file mode 100644 index 000000000..115b442aa --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h @@ -0,0 +1,36 @@ +// OutStreamWithCRC.h + +#ifndef __OUT_STREAM_WITH_CRC_H +#define __OUT_STREAM_WITH_CRC_H + +#include "../../../../C/7zCrc.h" + +#include "../../../Common/MyCom.h" + +#include "../../IStream.h" + +class COutStreamWithCRC: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialOutStream> _stream; + UInt64 _size; + UInt32 _crc; + bool _calculate; +public: + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void ReleaseStream() { _stream.Release(); } + void Init(bool calculate = true) + { + _size = 0; + _calculate = calculate; + _crc = CRC_INIT_VAL; + } + void InitCRC() { _crc = CRC_INIT_VAL; } + UInt64 GetSize() const { return _size; } + UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); } +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp new file mode 100644 index 000000000..0526c1b1d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.cpp @@ -0,0 +1,18 @@ +// OutStreamWithSha1.cpp + +#include "StdAfx.h" + +#include "OutStreamWithSha1.h" + +STDMETHODIMP COutStreamWithSha1::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + HRESULT result = S_OK; + if (_stream) + result = _stream->Write(data, size, &size); + if (_calculate) + _sha.Update((const Byte *)data, size); + _size += size; + if (processedSize != NULL) + *processedSize = size; + return result; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.h new file mode 100644 index 000000000..3bbfbbe19 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithSha1.h @@ -0,0 +1,36 @@ +// OutStreamWithSha1.h + +#ifndef __OUT_STREAM_WITH_SHA1_H +#define __OUT_STREAM_WITH_SHA1_H + +#include "../../Crypto/Sha1.h" + +#include "../../../Common/MyCom.h" + +#include "../../IStream.h" + +class COutStreamWithSha1: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialOutStream> _stream; + UInt64 _size; + NCrypto::NSha1::CContext _sha; + bool _calculate; +public: + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void ReleaseStream() { _stream.Release(); } + void Init(bool calculate = true) + { + _size = 0; + _calculate = calculate; + _sha.Init(); + } + void InitSha1() { _sha.Init(); } + UInt64 GetSize() const { return _size; } + void Final(Byte *digest) { _sha.Final(digest); } +}; + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.cpp new file mode 100644 index 000000000..5cd849e29 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.cpp @@ -0,0 +1,177 @@ +// ParseProperties.cpp + +#include "StdAfx.h" + +#include "ParseProperties.h" + +#include "Common/StringToInt.h" +#include "Common/MyCom.h" + +HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) +{ + if (prop.vt == VT_UI4) + { + if (!name.IsEmpty()) + return E_INVALIDARG; + resValue = prop.ulVal; + } + else if (prop.vt == VT_EMPTY) + { + if(!name.IsEmpty()) + { + const wchar_t *start = name; + const wchar_t *end; + UInt64 v = ConvertStringToUInt64(start, &end); + if (end - start != name.Length()) + return E_INVALIDARG; + resValue = (UInt32)v; + } + } + else + return E_INVALIDARG; + return S_OK; +} + +static const int kLogarithmicSizeLimit = 32; +static const wchar_t kByteSymbol = L'B'; +static const wchar_t kKiloByteSymbol = L'K'; +static const wchar_t kMegaByteSymbol = L'M'; + +HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize) +{ + UString srcString = srcStringSpec; + srcString.MakeUpper(); + + const wchar_t *start = srcString; + const wchar_t *end; + UInt64 number = ConvertStringToUInt64(start, &end); + int numDigits = (int)(end - start); + if (numDigits == 0 || srcString.Length() > numDigits + 1) + return E_INVALIDARG; + if (srcString.Length() == numDigits) + { + if (number >= kLogarithmicSizeLimit) + return E_INVALIDARG; + dicSize = (UInt32)1 << (int)number; + return S_OK; + } + switch (srcString[numDigits]) + { + case kByteSymbol: + if (number >= ((UInt64)1 << kLogarithmicSizeLimit)) + return E_INVALIDARG; + dicSize = (UInt32)number; + break; + case kKiloByteSymbol: + if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 10))) + return E_INVALIDARG; + dicSize = (UInt32)(number << 10); + break; + case kMegaByteSymbol: + if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 20))) + return E_INVALIDARG; + dicSize = (UInt32)(number << 20); + break; + default: + return E_INVALIDARG; + } + return S_OK; +} + +HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue) +{ + if (name.IsEmpty()) + { + if (prop.vt == VT_UI4) + { + UInt32 logDicSize = prop.ulVal; + if (logDicSize >= 32) + return E_INVALIDARG; + resValue = (UInt32)1 << logDicSize; + return S_OK; + } + if (prop.vt == VT_BSTR) + return ParsePropDictionaryValue(prop.bstrVal, resValue); + return E_INVALIDARG; + } + return ParsePropDictionaryValue(name, resValue); +} + +bool StringToBool(const UString &s, bool &res) +{ + if (s.IsEmpty() || s.CompareNoCase(L"ON") == 0 || s.Compare(L"+") == 0) + { + res = true; + return true; + } + if (s.CompareNoCase(L"OFF") == 0 || s.Compare(L"-") == 0) + { + res = false; + return true; + } + return false; +} + +HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value) +{ + switch(value.vt) + { + case VT_EMPTY: + dest = true; + return S_OK; + case VT_BOOL: + dest = (value.boolVal != VARIANT_FALSE); + return S_OK; + /* + case VT_UI4: + dest = (value.ulVal != 0); + break; + */ + case VT_BSTR: + return StringToBool(value.bstrVal, dest) ? S_OK : E_INVALIDARG; + } + return E_INVALIDARG; +} + +int ParseStringToUInt32(const UString &srcString, UInt32 &number) +{ + const wchar_t *start = srcString; + const wchar_t *end; + UInt64 number64 = ConvertStringToUInt64(start, &end); + if (number64 > 0xFFFFFFFF) + { + number = 0; + return 0; + } + number = (UInt32)number64; + return (int)(end - start); +} + +HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads) +{ + if (name.IsEmpty()) + { + switch(prop.vt) + { + case VT_UI4: + numThreads = prop.ulVal; + break; + default: + { + bool val; + RINOK(SetBoolProperty(val, prop)); + numThreads = (val ? defaultNumThreads : 1); + break; + } + } + } + else + { + UInt32 number; + int index = ParseStringToUInt32(name, number); + if (index != name.Length()) + return E_INVALIDARG; + numThreads = number; + } + return S_OK; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h new file mode 100644 index 000000000..6f80f6344 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h @@ -0,0 +1,18 @@ +// ParseProperties.h + +#ifndef __PARSEPROPERTIES_H +#define __PARSEPROPERTIES_H + +#include "Common/MyString.h" +#include "Common/Types.h" + +HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); +HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize); +HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue); + +bool StringToBool(const UString &s, bool &res); +HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value); +int ParseStringToUInt32(const UString &srcString, UInt32 &number); +HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads); + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/CpioHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/CpioHandler.cpp new file mode 100644 index 000000000..0f32ef663 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/CpioHandler.cpp @@ -0,0 +1,624 @@ +// CpioHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/ItemNameUtils.h" + +namespace NArchive { +namespace NCpio { + +namespace NFileHeader +{ + namespace NMagic + { + const char *kMagic1 = "070701"; + const char *kMagic2 = "070702"; + const char *kMagic3 = "070707"; + const char *kEndName = "TRAILER!!!"; + + const Byte kMagicForRecord2[2] = { 0xC7, 0x71 }; + } + + const UInt32 kRecord2Size = 26; + /* + struct CRecord2 + { + unsigned short c_magic; + short c_dev; + unsigned short c_ino; + unsigned short c_mode; + unsigned short c_uid; + unsigned short c_gid; + unsigned short c_nlink; + short c_rdev; + unsigned short c_mtimes[2]; + unsigned short c_namesize; + unsigned short c_filesizes[2]; + }; + */ + + const UInt32 kRecordSize = 110; + /* + struct CRecord + { + char Magic[6]; // "070701" for "new" portable format, "070702" for CRC format + char inode[8]; + char Mode[8]; + char UID[8]; + char GID[8]; + char nlink[8]; + char mtime[8]; + char Size[8]; // must be 0 for FIFOs and directories + char DevMajor[8]; + char DevMinor[8]; + char RDevMajor[8]; //only valid for chr and blk special files + char RDevMinor[8]; //only valid for chr and blk special files + char NameSize[8]; // count includes terminating NUL in pathname + char ChkSum[8]; // 0 for "new" portable format; for CRC format the sum of all the bytes in the file + bool CheckMagic() const + { return memcmp(Magic, NMagic::kMagic1, 6) == 0 || + memcmp(Magic, NMagic::kMagic2, 6) == 0; }; + }; + */ + + const UInt32 kOctRecordSize = 76; + +} + +struct CItem +{ + AString Name; + UInt32 inode; + UInt32 Mode; + UInt32 UID; + UInt32 GID; + UInt32 Size; + UInt32 MTime; + + // char LinkFlag; + // AString LinkName; ????? + char Magic[8]; + UInt32 NumLinks; + UInt32 DevMajor; + UInt32 DevMinor; + UInt32 RDevMajor; + UInt32 RDevMinor; + UInt32 ChkSum; + + UInt32 Align; + + bool IsDir() const { return (Mode & 0170000) == 0040000; } +}; + +class CItemEx: public CItem +{ +public: + UInt64 HeaderPosition; + UInt32 HeaderSize; + UInt64 GetDataPosition() const { return HeaderPosition + HeaderSize; }; +}; + +const UInt32 kMaxBlockSize = NFileHeader::kRecordSize; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + UInt64 m_Position; + + UInt16 _blockSize; + Byte _block[kMaxBlockSize]; + UInt32 _blockPos; + Byte ReadByte(); + UInt16 ReadUInt16(); + UInt32 ReadUInt32(); + + bool ReadNumber(UInt32 &resultValue); + bool ReadOctNumber(int size, UInt32 &resultValue); + + HRESULT ReadBytes(void *data, UInt32 size, UInt32 &processedSize); +public: + HRESULT Open(IInStream *inStream); + HRESULT GetNextItem(bool &filled, CItemEx &itemInfo); + HRESULT Skip(UInt64 numBytes); + HRESULT SkipDataRecords(UInt64 dataSize, UInt32 align); +}; + +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 &processedSize) +{ + size_t realProcessedSize = size; + RINOK(ReadStream(m_Stream, data, &realProcessedSize)); + processedSize = (UInt32)realProcessedSize; + m_Position += processedSize; + return S_OK; +} + +Byte CInArchive::ReadByte() +{ + if (_blockPos >= _blockSize) + throw "Incorrect cpio archive"; + return _block[_blockPos++]; +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = ReadByte(); + value |= (UInt16(b) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + Byte b = ReadByte(); + value |= (UInt32(b) << (8 * i)); + } + return value; +} + +HRESULT CInArchive::Open(IInStream *inStream) +{ + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position)); + m_Stream = inStream; + return S_OK; +} + +bool CInArchive::ReadNumber(UInt32 &resultValue) +{ + resultValue = 0; + for (int i = 0; i < 8; i++) + { + char c = char(ReadByte()); + int d; + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'A' && c <= 'F') + d = 10 + c - 'A'; + else if (c >= 'a' && c <= 'f') + d = 10 + c - 'a'; + else + return false; + resultValue *= 0x10; + resultValue += d; + } + return true; +} + +static bool OctalToNumber(const char *s, UInt64 &res) +{ + const char *end; + res = ConvertOctStringToUInt64(s, &end); + return (*end == ' ' || *end == 0); +} + +static bool OctalToNumber32(const char *s, UInt32 &res) +{ + UInt64 res64; + if (!OctalToNumber(s, res64)) + return false; + res = (UInt32)res64; + return (res64 <= 0xFFFFFFFF); +} + +bool CInArchive::ReadOctNumber(int size, UInt32 &resultValue) +{ + char sz[32 + 4]; + int i; + for (i = 0; i < size && i < 32; i++) + sz[i] = (char)ReadByte(); + sz[i] = 0; + return OctalToNumber32(sz, resultValue); +} + +#define GetFromHex(y) { if (!ReadNumber(y)) return S_FALSE; } +#define GetFromOct6(y) { if (!ReadOctNumber(6, y)) return S_FALSE; } +#define GetFromOct11(y) { if (!ReadOctNumber(11, y)) return S_FALSE; } + +static unsigned short ConvertValue(unsigned short value, bool convert) +{ + if (!convert) + return value; + return (unsigned short)((((unsigned short)(value & 0xFF)) << 8) | (value >> 8)); +} + +static UInt32 GetAlignedSize(UInt32 size, UInt32 align) +{ + while ((size & (align - 1)) != 0) + size++; + return size; +} + + +HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item) +{ + filled = false; + + UInt32 processedSize; + item.HeaderPosition = m_Position; + + _blockSize = kMaxBlockSize; + RINOK(ReadBytes(_block, 2, processedSize)); + if (processedSize != 2) + return S_FALSE; + _blockPos = 0; + + UInt32 nameSize; + + bool oldBE = + _block[0] == NFileHeader::NMagic::kMagicForRecord2[1] && + _block[1] == NFileHeader::NMagic::kMagicForRecord2[0]; + + bool binMode = (_block[0] == NFileHeader::NMagic::kMagicForRecord2[0] && + _block[1] == NFileHeader::NMagic::kMagicForRecord2[1]) || + oldBE; + + if (binMode) + { + RINOK(ReadBytes(_block + 2, NFileHeader::kRecord2Size - 2, processedSize)); + if (processedSize != NFileHeader::kRecord2Size - 2) + return S_FALSE; + item.Align = 2; + _blockPos = 2; + item.DevMajor = 0; + item.DevMinor = ConvertValue(ReadUInt16(), oldBE); + item.inode = ConvertValue(ReadUInt16(), oldBE); + item.Mode = ConvertValue(ReadUInt16(), oldBE); + item.UID = ConvertValue(ReadUInt16(), oldBE); + item.GID = ConvertValue(ReadUInt16(), oldBE); + item.NumLinks = ConvertValue(ReadUInt16(), oldBE); + item.RDevMajor =0; + item.RDevMinor = ConvertValue(ReadUInt16(), oldBE); + UInt16 timeHigh = ConvertValue(ReadUInt16(), oldBE); + UInt16 timeLow = ConvertValue(ReadUInt16(), oldBE); + item.MTime = (UInt32(timeHigh) << 16) + timeLow; + nameSize = ConvertValue(ReadUInt16(), oldBE); + UInt16 sizeHigh = ConvertValue(ReadUInt16(), oldBE); + UInt16 sizeLow = ConvertValue(ReadUInt16(), oldBE); + item.Size = (UInt32(sizeHigh) << 16) + sizeLow; + + item.ChkSum = 0; + item.HeaderSize = GetAlignedSize( + nameSize + NFileHeader::kRecord2Size, item.Align); + nameSize = item.HeaderSize - NFileHeader::kRecord2Size; + } + else + { + RINOK(ReadBytes(_block + 2, 4, processedSize)); + if (processedSize != 4) + return S_FALSE; + + bool magicOK = + memcmp(_block, NFileHeader::NMagic::kMagic1, 6) == 0 || + memcmp(_block, NFileHeader::NMagic::kMagic2, 6) == 0; + _blockPos = 6; + if (magicOK) + { + RINOK(ReadBytes(_block + 6, NFileHeader::kRecordSize - 6, processedSize)); + if (processedSize != NFileHeader::kRecordSize - 6) + return S_FALSE; + item.Align = 4; + + GetFromHex(item.inode); + GetFromHex(item.Mode); + GetFromHex(item.UID); + GetFromHex(item.GID); + GetFromHex(item.NumLinks); + UInt32 mTime; + GetFromHex(mTime); + item.MTime = mTime; + GetFromHex(item.Size); + GetFromHex(item.DevMajor); + GetFromHex(item.DevMinor); + GetFromHex(item.RDevMajor); + GetFromHex(item.RDevMinor); + GetFromHex(nameSize); + GetFromHex(item.ChkSum); + item.HeaderSize = GetAlignedSize( + nameSize + NFileHeader::kRecordSize, item.Align); + nameSize = item.HeaderSize - NFileHeader::kRecordSize; + } + else + { + if (!memcmp(_block, NFileHeader::NMagic::kMagic3, 6) == 0) + return S_FALSE; + RINOK(ReadBytes(_block + 6, NFileHeader::kOctRecordSize - 6, processedSize)); + if (processedSize != NFileHeader::kOctRecordSize - 6) + return S_FALSE; + item.Align = 1; + item.DevMajor = 0; + GetFromOct6(item.DevMinor); + GetFromOct6(item.inode); + GetFromOct6(item.Mode); + GetFromOct6(item.UID); + GetFromOct6(item.GID); + GetFromOct6(item.NumLinks); + item.RDevMajor = 0; + GetFromOct6(item.RDevMinor); + UInt32 mTime; + GetFromOct11(mTime); + item.MTime = mTime; + GetFromOct6(nameSize); + GetFromOct11(item.Size); // ????? + item.HeaderSize = GetAlignedSize( + nameSize + NFileHeader::kOctRecordSize, item.Align); + nameSize = item.HeaderSize - NFileHeader::kOctRecordSize; + } + } + if (nameSize == 0 || nameSize >= (1 << 27)) + return E_FAIL; + RINOK(ReadBytes(item.Name.GetBuffer(nameSize), nameSize, processedSize)); + if (processedSize != nameSize) + return E_FAIL; + item.Name.ReleaseBuffer(); + if (strcmp(item.Name, NFileHeader::NMagic::kEndName) == 0) + return S_OK; + filled = true; + return S_OK; +} + +HRESULT CInArchive::Skip(UInt64 numBytes) +{ + UInt64 newPostion; + RINOK(m_Stream->Seek(numBytes, STREAM_SEEK_CUR, &newPostion)); + m_Position += numBytes; + if (m_Position != newPostion) + return E_FAIL; + return S_OK; +} + +HRESULT CInArchive::SkipDataRecords(UInt64 dataSize, UInt32 align) +{ + while ((dataSize & (align - 1)) != 0) + dataSize++; + return Skip(dataSize); +} + + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CObjectVector<CItemEx> _items; + CMyComPtr<IInStream> _stream; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +/* +enum +{ + kpidinode = kpidUserDefined, + kpidiChkSum +}; +*/ + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidPosixAttrib, VT_UI4}, + // { L"inode", kpidinode, VT_UI4} + // { L"CheckSum", kpidiChkSum, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + // try + { + CInArchive archive; + + UInt64 endPos = 0; + bool needSetTotal = true; + + if (callback != NULL) + { + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + + RINOK(archive.Open(stream)); + + _items.Clear(); + + for (;;) + { + CItemEx item; + bool filled; + HRESULT result = archive.GetNextItem(filled, item); + if (result == S_FALSE) + return S_FALSE; + if (result != S_OK) + return S_FALSE; + if (!filled) + break; + _items.Add(item); + archive.SkipDataRecords(item.Size, item.Align); + if (callback != NULL) + { + if (needSetTotal) + { + RINOK(callback->SetTotal(NULL, &endPos)); + needSetTotal = false; + } + if (_items.Size() % 100 == 0) + { + UInt64 numFiles = _items.Size(); + UInt64 numBytes = item.HeaderPosition; + RINOK(callback->SetCompleted(&numFiles, &numBytes)); + } + } + } + if (_items.Size() == 0) + return S_FALSE; + + _stream = stream; + } + /* + catch(...) + { + return S_FALSE; + } + */ + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItemEx &item = _items[index]; + + switch(propID) + { + case kpidPath: prop = NItemName::GetOSName(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)item.Size; + break; + case kpidMTime: + { + if (item.MTime != 0) + { + FILETIME utc; + NWindows::NTime::UnixTimeToFileTime(item.MTime, utc); + prop = utc; + } + break; + } + case kpidPosixAttrib: prop = item.Mode; break; + /* + case kpidinode: prop = item.inode; break; + case kpidiChkSum: prop = item.ChkSum; break; + */ + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItemEx &item = _items[index]; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + currentTotalSize += item.Size; + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (testMode) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + RINOK(_stream->Seek(item.GetDataPosition(), STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItemEx &item = _items[index]; + return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NCpio::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Cpio", L"cpio", 0, 0xED, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Cpio) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/CramfsHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/CramfsHandler.cpp new file mode 100644 index 000000000..a55e3743d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/CramfsHandler.cpp @@ -0,0 +1,644 @@ +// CramfsHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/7zCrc.h" +#include "../../../C/CpuArch.h" +#include "../../../C/Alloc.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariantUtils.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" + +namespace NArchive { +namespace NCramfs { + +#define SIGNATURE { 'C','o','m','p','r','e','s','s','e','d',' ','R','O','M','F','S' } + +static const UInt32 kSignatureSize = 16; +static const char kSignature[kSignatureSize] = SIGNATURE; + +static const UInt32 kArcSizeMax = (256 + 16) << 20; +static const UInt32 kNumFilesMax = (1 << 19); +static const unsigned kNumDirLevelsMax = (1 << 8); + +static const UInt32 kHeaderSize = 0x40; +static const unsigned kHeaderNameSize = 16; +static const UInt32 kNodeSize = 12; + +static const UInt32 kFlag_FsVer2 = (1 << 0); + +static const CUInt32PCharPair k_Flags[] = +{ + { 0, "Ver2" }, + { 1, "SortedDirs" }, + { 8, "Holes" }, + { 9, "WrongSignature" }, + { 10, "ShiftedRootOffset" } +}; + +static const unsigned kBlockSizeLog = 12; +static const UInt32 kBlockSize = 1 << kBlockSizeLog; + +/* +struct CNode +{ + UInt16 Mode; + UInt16 Uid; + UInt32 Size; + Byte Gid; + UInt32 NameLen; + UInt32 Offset; + + void Parse(const Byte *p) + { + Mode = GetUi16(p); + Uid = GetUi16(p + 2); + Size = Get32(p + 4) & 0xFFFFFF; + Gid = p[7]; + NameLen = p[8] & 0x3F; + Offset = Get32(p + 8) >> 6; + } +}; +*/ + +#define Get32(p) (be ? GetBe32(p) : GetUi32(p)) + +static UInt32 GetMode(const Byte *p, bool be) { return be ? GetBe16(p) : GetUi16(p); } +static bool IsDir(const Byte *p, bool be) { return (GetMode(p, be) & 0xF000) == 0x4000; } + +static UInt32 GetSize(const Byte *p, bool be) +{ + if (be) + return GetBe32(p + 4) >> 8; + else + return GetUi32(p + 4) & 0xFFFFFF; +} + +static UInt32 GetNameLen(const Byte *p, bool be) +{ + if (be) + return (p[8] & 0xFC); + else + return (p[8] & 0x3F) << 2; +} + +static UInt32 GetOffset(const Byte *p, bool be) +{ + if (be) + return (GetBe32(p + 8) & 0x03FFFFFF) << 2; + else + return GetUi32(p + 8) >> 6 << 2; +} + +struct CItem +{ + UInt32 Offset; + int Parent; +}; + +struct CHeader +{ + bool be; + UInt32 Size; + UInt32 Flags; + // UInt32 Future; + UInt32 Crc; + // UInt32 Edition; + UInt32 NumBlocks; + UInt32 NumFiles; + char Name[kHeaderNameSize]; + + bool Parse(const Byte *p) + { + if (memcmp(p + 16, kSignature, kSignatureSize) != 0) + return false; + switch(GetUi32(p)) + { + case 0x28CD3D45: be = false; break; + case 0x453DCD28: be = true; break; + default: return false; + } + Size = Get32(p + 4); + Flags = Get32(p + 8); + // Future = Get32(p + 0xC); + Crc = Get32(p + 0x20); + // Edition = Get32(p + 0x24); + NumBlocks = Get32(p + 0x28); + NumFiles = Get32(p + 0x2C); + memcpy(Name, p + 0x30, kHeaderNameSize); + return true; + } + + bool IsVer2() const { return (Flags & kFlag_FsVer2) != 0; } +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CRecordVector<CItem> _items; + CMyComPtr<IInStream> _stream; + Byte *_data; + UInt32 _size; + UInt32 _headersSize; + AString _errorMessage; + CHeader _h; + + // Current file + + NCompress::NZlib::CDecoder *_zlibDecoderSpec; + CMyComPtr<ICompressCoder> _zlibDecoder; + + CBufInStream *_inStreamSpec; + CMyComPtr<ISequentialInStream> _inStream; + + CBufPtrSeqOutStream *_outStreamSpec; + CMyComPtr<ISequentialOutStream> _outStream; + + UInt32 _curBlocksOffset; + UInt32 _curNumBlocks; + + HRESULT OpenDir(int parent, UInt32 baseOffsetBase, unsigned level); + HRESULT Open2(IInStream *inStream); + AString GetPath(int index) const; + bool GetPackSize(int index, UInt32 &res) const; + void Free(); +public: + CHandler(): _data(0) {} + ~CHandler() { Free(); } + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize); +}; + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI4}, + { NULL, kpidPackSize, VT_UI4}, + { NULL, kpidPosixAttrib, VT_UI4} + // { NULL, kpidOffset, VT_UI4} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidName, VT_BSTR}, + { NULL, kpidBigEndian, VT_BOOL}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidPhySize, VT_UI4}, + { NULL, kpidHeadersSize, VT_UI4}, + { NULL, kpidNumSubFiles, VT_UI4}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level) +{ + const Byte *p = _data + baseOffset; + bool be = _h.be; + if (!IsDir(p, be)) + return S_OK; + UInt32 offset = GetOffset(p, be); + UInt32 size = GetSize(p, be); + if (offset == 0 && size == 0) + return S_OK; + UInt32 end = offset + size; + if (offset < kHeaderSize || end > _size || level > kNumDirLevelsMax) + return S_FALSE; + if (end > _headersSize) + _headersSize = end; + + int startIndex = _items.Size(); + + while (size != 0) + { + if (size < kNodeSize || (UInt32)_items.Size() >= kNumFilesMax) + return S_FALSE; + CItem item; + item.Parent = parent; + item.Offset = offset; + _items.Add(item); + UInt32 nodeLen = kNodeSize + GetNameLen(_data + offset, be); + if (size < nodeLen) + return S_FALSE; + offset += nodeLen; + size -= nodeLen; + } + + int endIndex = _items.Size(); + for (int i = startIndex; i < endIndex; i++) + { + RINOK(OpenDir(i, _items[i].Offset, level + 1)); + } + return S_OK; +} + +HRESULT CHandler::Open2(IInStream *inStream) +{ + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize)); + if (!_h.Parse(buf)) + return S_FALSE; + if (_h.IsVer2()) + { + if (_h.Size < kHeaderSize || _h.Size > kArcSizeMax || _h.NumFiles > kNumFilesMax) + return S_FALSE; + } + else + { + UInt64 size; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &size)); + if (size > kArcSizeMax) + return S_FALSE; + _h.Size = (UInt32)size; + RINOK(inStream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL)); + } + _data = (Byte *)MidAlloc(_h.Size); + if (_data == 0) + return E_OUTOFMEMORY; + memcpy(_data, buf, kHeaderSize); + size_t processed = _h.Size - kHeaderSize; + RINOK(ReadStream(inStream, _data + kHeaderSize, &processed)); + if (processed < kNodeSize) + return S_FALSE; + _size = kHeaderSize + (UInt32)processed; + if (_size != _h.Size) + _errorMessage = "Unexpected end of archive"; + else + { + SetUi32(_data + 0x20, 0); + if (_h.IsVer2()) + if (CrcCalc(_data, _h.Size) != _h.Crc) + _errorMessage = "CRC error"; + } + if (_h.IsVer2()) + _items.Reserve(_h.NumFiles - 1); + return OpenDir(-1, kHeaderSize, 0); +} + +AString CHandler::GetPath(int index) const +{ + unsigned len = 0; + int indexMem = index; + do + { + const CItem &item = _items[index]; + index = item.Parent; + const Byte *p = _data + item.Offset; + unsigned size = GetNameLen(p, _h.be); + p += kNodeSize; + unsigned i; + for (i = 0; i < size && p[i]; i++); + len += i + 1; + } + while (index >= 0); + len--; + + AString path; + char *dest = path.GetBuffer(len) + len; + index = indexMem; + for (;;) + { + const CItem &item = _items[index]; + index = item.Parent; + const Byte *p = _data + item.Offset; + unsigned size = GetNameLen(p, _h.be); + p += kNodeSize; + unsigned i; + for (i = 0; i < size && p[i]; i++); + dest -= i; + memcpy(dest, p, i); + if (index < 0) + break; + *(--dest) = CHAR_PATH_SEPARATOR; + } + path.ReleaseBuffer(len); + return path; +} + +bool CHandler::GetPackSize(int index, UInt32 &res) const +{ + const CItem &item = _items[index]; + const Byte *p = _data + item.Offset; + bool be = _h.be; + UInt32 offset = GetOffset(p, be); + if (offset < kHeaderSize) + return false; + UInt32 numBlocks = (GetSize(p, be) + kBlockSize - 1) >> kBlockSizeLog; + UInt32 start = offset + numBlocks * 4; + if (start > _size) + return false; + UInt32 end = Get32(_data + start - 4); + if (end < start) + return false; + res = end - start; + return true; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */) +{ + COM_TRY_BEGIN + { + Close(); + RINOK(Open2(stream)); + _stream = stream; + } + return S_OK; + COM_TRY_END +} + +void CHandler::Free() +{ + MidFree(_data); + _data = 0; +} + +STDMETHODIMP CHandler::Close() +{ + _headersSize = 0; + _items.Clear(); + _stream.Release(); + _errorMessage.Empty(); + Free(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidName: + { + char dest[kHeaderNameSize + 4]; + memcpy(dest, _h.Name, kHeaderNameSize); + dest[kHeaderNameSize] = 0; + prop = dest; + break; + } + case kpidBigEndian: prop = _h.be; break; + case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break; + case kpidNumBlocks: if (_h.IsVer2()) prop = _h.NumBlocks; break; + case kpidNumSubFiles: if (_h.IsVer2()) prop = _h.NumFiles; break; + case kpidPhySize: if (_h.IsVer2()) prop = _h.Size; break; + case kpidHeadersSize: prop = _headersSize; break; + case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break; + } + 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; + const CItem &item = _items[index]; + const Byte *p = _data + item.Offset; + bool be = _h.be; + bool isDir = IsDir(p, be); + switch(propID) + { + case kpidPath: prop = MultiByteToUnicodeString(GetPath(index), CP_OEMCP); break; + case kpidIsDir: prop = isDir; break; + // case kpidOffset: prop = (UInt32)GetOffset(p, be); break; + case kpidSize: if (!isDir) prop = GetSize(p, be); break; + case kpidPackSize: + if (!isDir) + { + UInt32 size; + if (GetPackSize(index, size)) + prop = size; + } + break; + case kpidPosixAttrib: prop = (UInt32)GetMode(p, be); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CCramfsInStream: public CCachedInStream +{ + HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize); +public: + CHandler *Handler; +}; + +HRESULT CCramfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) +{ + return Handler->ReadBlock(blockIndex, dest, blockSize); +} + +HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) +{ + if (!_zlibDecoder) + { + _zlibDecoderSpec = new NCompress::NZlib::CDecoder(); + _zlibDecoder = _zlibDecoderSpec; + } + if (!_inStream) + { + _inStreamSpec = new CBufInStream(); + _inStream = _inStreamSpec; + } + if (!_outStream) + { + _outStreamSpec = new CBufPtrSeqOutStream(); + _outStream = _outStreamSpec; + } + bool be = _h.be; + const Byte *p = _data + (_curBlocksOffset + (UInt32)blockIndex * 4); + UInt32 start = (blockIndex == 0 ? _curBlocksOffset + _curNumBlocks * 4: Get32(p - 4)); + UInt32 end = Get32(p); + if (end < start || end > _size) + return S_FALSE; + UInt32 inSize = end - start; + _inStreamSpec->Init(_data + start, inSize); + _outStreamSpec->Init(dest, blockSize); + RINOK(_zlibDecoder->Code(_inStream, _outStream, NULL, NULL, NULL)); + return (_zlibDecoderSpec->GetInputProcessedSize() == inSize && + _outStreamSpec->GetPos() == blockSize) ? S_OK : S_FALSE; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + bool be = _h.be; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const Byte *p = _data + _items[allFilesMode ? i : indices[i]].Offset; + if (!IsDir(p, be)) + totalSize += GetSize(p, be); + } + extractCallback->SetTotal(totalSize); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + const Byte *p = _data + item.Offset; + + if (IsDir(p, be)) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + UInt32 curSize = GetSize(p, be); + totalSize += curSize; + UInt32 packSize; + if (GetPackSize(index, packSize)) + totalPackSize += packSize; + + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + UInt32 offset = GetOffset(p, be); + if (offset < kHeaderSize) + curSize = 0; + + int res = NExtract::NOperationResult::kDataError; + { + CMyComPtr<ISequentialInStream> inSeqStream; + CMyComPtr<IInStream> inStream; + HRESULT hres = GetStream(index, &inSeqStream); + if (inSeqStream) + inSeqStream.QueryInterface(IID_IInStream, &inStream); + if (hres == E_OUTOFMEMORY) + return E_OUTOFMEMORY; + if (hres == S_FALSE || !inStream) + res = NExtract::NOperationResult::kUnSupportedMethod; + else + { + RINOK(hres); + if (inStream) + { + HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (hres != S_OK && hres != S_FALSE) + { + RINOK(hres); + } + if (copyCoderSpec->TotalSize == curSize && hres == S_OK) + res = NExtract::NOperationResult::kOK; + } + } + } + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + + const CItem &item = _items[index]; + const Byte *p = _data + item.Offset; + + bool be = _h.be; + if (IsDir(p, be)) + return E_FAIL; + + UInt32 size = GetSize(p, be); + UInt32 numBlocks = (size + kBlockSize - 1) >> kBlockSizeLog; + UInt32 offset = GetOffset(p, be); + if (offset < kHeaderSize) + { + if (offset != 0) + return S_FALSE; + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + streamSpec->Init(NULL, 0); + *stream = streamTemp.Detach(); + return S_OK; + } + + if (offset + numBlocks * 4 > _size) + return S_FALSE; + UInt32 prev = offset; + for (UInt32 i = 0; i < numBlocks; i++) + { + UInt32 next = Get32(_data + offset + i * 4); + if (next < prev || next > _size) + return S_FALSE; + prev = next; + } + + CCramfsInStream *streamSpec = new CCramfsInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + _curNumBlocks = numBlocks; + _curBlocksOffset = offset; + streamSpec->Handler = this; + if (!streamSpec->Alloc(kBlockSizeLog, 21 - kBlockSizeLog)) + return E_OUTOFMEMORY; + streamSpec->Init(size); + *stream = streamTemp.Detach(); + + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NCramfs::CHandler; } + +static CArcInfo g_ArcInfo = + { L"CramFS", L"cramfs", 0, 0xD3, SIGNATURE, kSignatureSize, false, CreateArc, 0 }; + +REGISTER_ARC(Cramfs) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/DebHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/DebHandler.cpp new file mode 100644 index 000000000..82d2cde88 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/DebHandler.cpp @@ -0,0 +1,413 @@ +// DebHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/ItemNameUtils.h" + +using namespace NWindows; +using namespace NTime; + +namespace NArchive { +namespace NDeb { + +namespace NHeader +{ + const int kSignatureLen = 8; + + const char *kSignature = "!<arch>\n"; + + const int kNameSize = 16; + const int kTimeSize = 12; + const int kModeSize = 8; + const int kSizeSize = 10; + + /* + struct CHeader + { + char Name[kNameSize]; + char MTime[kTimeSize]; + char Number0[6]; + char Number1[6]; + char Mode[kModeSize]; + char Size[kSizeSize]; + char Quote; + char NewLine; + }; + */ + const int kHeaderSize = kNameSize + kTimeSize + 6 + 6 + kModeSize + kSizeSize + 1 + 1; +} + +struct CItem +{ + AString Name; + UInt64 Size; + UInt32 MTime; + UInt32 Mode; + + UInt64 HeaderPos; + UInt64 GetDataPos() const { return HeaderPos + NHeader::kHeaderSize; }; + // UInt64 GetFullSize() const { return NFileHeader::kRecordSize + Size; }; +}; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + + HRESULT GetNextItemReal(bool &filled, CItem &itemInfo); +public: + UInt64 m_Position; + HRESULT Open(IInStream *inStream); + HRESULT GetNextItem(bool &filled, CItem &itemInfo); + HRESULT SkipData(UInt64 dataSize); +}; + +HRESULT CInArchive::Open(IInStream *inStream) +{ + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position)); + char signature[NHeader::kSignatureLen]; + RINOK(ReadStream_FALSE(inStream, signature, NHeader::kSignatureLen)); + m_Position += NHeader::kSignatureLen; + if (memcmp(signature, NHeader::kSignature, NHeader::kSignatureLen) != 0) + return S_FALSE; + m_Stream = inStream; + return S_OK; +} + +static void MyStrNCpy(char *dest, const char *src, int size) +{ + for (int i = 0; i < size; i++) + { + char c = src[i]; + dest[i] = c; + if (c == 0) + break; + } +} + +static bool OctalToNumber(const char *s, int size, UInt64 &res) +{ + char sz[32]; + MyStrNCpy(sz, s, size); + sz[size] = 0; + const char *end; + int i; + for (i = 0; sz[i] == ' '; i++); + res = ConvertOctStringToUInt64(sz + i, &end); + return (*end == ' ' || *end == 0); +} + +static bool OctalToNumber32(const char *s, int size, UInt32 &res) +{ + UInt64 res64; + if (!OctalToNumber(s, size, res64)) + return false; + res = (UInt32)res64; + return (res64 <= 0xFFFFFFFF); +} + +static bool DecimalToNumber(const char *s, int size, UInt64 &res) +{ + char sz[32]; + MyStrNCpy(sz, s, size); + sz[size] = 0; + const char *end; + int i; + for (i = 0; sz[i] == ' '; i++); + res = ConvertStringToUInt64(sz + i, &end); + return (*end == ' ' || *end == 0); +} + +static bool DecimalToNumber32(const char *s, int size, UInt32 &res) +{ + UInt64 res64; + if (!DecimalToNumber(s, size, res64)) + return false; + res = (UInt32)res64; + return (res64 <= 0xFFFFFFFF); +} + +#define RIF(x) { if (!(x)) return S_FALSE; } + + +HRESULT CInArchive::GetNextItemReal(bool &filled, CItem &item) +{ + filled = false; + + char header[NHeader::kHeaderSize]; + const char *cur = header; + + size_t processedSize = sizeof(header); + item.HeaderPos = m_Position; + RINOK(ReadStream(m_Stream, header, &processedSize)); + if (processedSize != sizeof(header)) + return S_OK; + m_Position += processedSize; + + char tempString[NHeader::kNameSize + 1]; + MyStrNCpy(tempString, cur, NHeader::kNameSize); + cur += NHeader::kNameSize; + tempString[NHeader::kNameSize] = '\0'; + item.Name = tempString; + item.Name.Trim(); + + for (int i = 0; i < item.Name.Length(); i++) + if (((Byte)item.Name[i]) < 0x20) + return S_FALSE; + + RIF(DecimalToNumber32(cur, NHeader::kTimeSize, item.MTime)); + cur += NHeader::kTimeSize; + + cur += 6 + 6; + + RIF(OctalToNumber32(cur, NHeader::kModeSize, item.Mode)); + cur += NHeader::kModeSize; + + RIF(DecimalToNumber(cur, NHeader::kSizeSize, item.Size)); + cur += NHeader::kSizeSize; + + filled = true; + return S_OK; +} + +HRESULT CInArchive::GetNextItem(bool &filled, CItem &item) +{ + for (;;) + { + RINOK(GetNextItemReal(filled, item)); + if (!filled) + return S_OK; + if (item.Name.Compare("debian-binary") != 0) + return S_OK; + if (item.Size != 4) + return S_OK; + SkipData(item.Size); + } +} + +HRESULT CInArchive::SkipData(UInt64 dataSize) +{ + return m_Stream->Seek((dataSize + 1) & (~((UInt64)0x1)), STREAM_SEEK_CUR, &m_Position); +} + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CObjectVector<CItem> _items; + CMyComPtr<IInStream> _stream; + Int32 _mainSubfile; + UInt64 _phySize; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidPhySize, VT_UI8} +}; + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + { + _mainSubfile = -1; + CInArchive archive; + if (archive.Open(stream) != S_OK) + return S_FALSE; + _items.Clear(); + + if (openArchiveCallback != NULL) + { + RINOK(openArchiveCallback->SetTotal(NULL, NULL)); + UInt64 numFiles = _items.Size(); + RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); + } + + for (;;) + { + CItem item; + bool filled; + HRESULT result = archive.GetNextItem(filled, item); + if (result == S_FALSE) + return S_FALSE; + if (result != S_OK) + return S_FALSE; + if (!filled) + break; + if (item.Name.Left(5) == "data.") + _mainSubfile = _items.Size(); + _items.Add(item); + archive.SkipData(item.Size); + if (openArchiveCallback != NULL) + { + UInt64 numFiles = _items.Size(); + RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); + } + } + _stream = stream; + _phySize = archive.m_Position; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _items.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: prop = _phySize; break; + case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItem &item = _items[index]; + + switch(propID) + { + case kpidPath: prop = (const wchar_t *)NItemName::GetOSName2(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break; + case kpidSize: + case kpidPackSize: + prop = item.Size; + break; + case kpidMTime: + { + if (item.MTime != 0) + { + FILETIME fileTime; + NTime::UnixTimeToFileTime(item.MTime, fileTime); + prop = fileTime; + } + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + currentTotalSize += item.Size; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (testMode) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + RINOK(_stream->Seek(item.GetDataPos(), STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItem &item = _items[index]; + return CreateLimitedInStream(_stream, item.GetDataPos(), item.Size, stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NDeb::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Deb", L"deb", 0, 0xEC, { '!', '<', 'a', 'r', 'c', 'h', '>', '\n' }, 8, false, CreateArc, 0 }; + +REGISTER_ARC(Deb) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.cpp new file mode 100644 index 000000000..8498e0565 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.cpp @@ -0,0 +1,118 @@ +// DeflateProps.cpp + +#include "StdAfx.h" + +#include "Windows/PropVariant.h" + +#include "Common/ParseProperties.h" + +#include "DeflateProps.h" + +namespace NArchive { + +static const UInt32 kAlgo1 = 0; +static const UInt32 kAlgo5 = 1; + +static const UInt32 kPasses1 = 1; +static const UInt32 kPasses7 = 3; +static const UInt32 kPasses9 = 10; + +static const UInt32 kFb1 = 32; +static const UInt32 kFb7 = 64; +static const UInt32 kFb9 = 128; + +void CDeflateProps::Normalize() +{ + UInt32 level = Level; + if (level == 0xFFFFFFFF) + level = 5; + + if (Algo == 0xFFFFFFFF) + Algo = (level >= 5 ? + kAlgo5 : + kAlgo1); + + if (NumPasses == 0xFFFFFFFF) + NumPasses = + (level >= 9 ? kPasses9 : + (level >= 7 ? kPasses7 : + kPasses1)); + if (Fb == 0xFFFFFFFF) + Fb = + (level >= 9 ? kFb9 : + (level >= 7 ? kFb7 : + kFb1)); +} + +HRESULT CDeflateProps::SetCoderProperties(ICompressSetCoderProperties *setCoderProperties) +{ + Normalize(); + + NWindows::NCOM::CPropVariant props[] = + { + Algo, + NumPasses, + Fb, + Mc + }; + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kNumPasses, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinderCycles + }; + int numProps = sizeof(propIDs) / sizeof(propIDs[0]); + if (!McDefined) + numProps--; + return setCoderProperties->SetCoderProperties(propIDs, props, numProps); +} + +HRESULT CDeflateProps::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + Init(); + for (int i = 0; i < numProps; i++) + { + UString name = names[i]; + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + const PROPVARIANT &prop = values[i]; + if (name[0] == L'X') + { + UInt32 a = 9; + RINOK(ParsePropValue(name.Mid(1), prop, a)); + Level = a; + } + else if (name.Left(1) == L"A") + { + UInt32 a = kAlgo5; + RINOK(ParsePropValue(name.Mid(1), prop, a)); + Algo = a; + } + else if (name.Left(4) == L"PASS") + { + UInt32 a = kPasses9; + RINOK(ParsePropValue(name.Mid(4), prop, a)); + NumPasses = a; + } + else if (name.Left(2) == L"FB") + { + UInt32 a = kFb9; + RINOK(ParsePropValue(name.Mid(2), prop, a)); + Fb = a; + } + else if (name.Left(2) == L"MC") + { + UInt32 a = 0xFFFFFFFF; + RINOK(ParsePropValue(name.Mid(2), prop, a)); + Mc = a; + McDefined = true; + } + else + return E_INVALIDARG; + } + return S_OK; +} + +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.h b/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.h new file mode 100644 index 000000000..e05a9d4aa --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/DeflateProps.h @@ -0,0 +1,35 @@ +// DeflateProps.h + +#ifndef __DEFLATE_PROPS_H +#define __DEFLATE_PROPS_H + +#include "../ICoder.h" + +namespace NArchive { + +class CDeflateProps +{ + UInt32 Level; + UInt32 NumPasses; + UInt32 Fb; + UInt32 Algo; + UInt32 Mc; + bool McDefined; + + void Init() + { + Level = NumPasses = Fb = Algo = Mc = 0xFFFFFFFF; + McDefined = false; + } + void Normalize(); +public: + CDeflateProps() { Init(); } + bool IsMaximum() const { return Algo > 0; } + + HRESULT SetCoderProperties(ICompressSetCoderProperties *setCoderProperties); + HRESULT SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); +}; + +} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/DllExports2.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/DllExports2.cpp new file mode 100644 index 000000000..1febea714 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/DllExports2.cpp @@ -0,0 +1,76 @@ +// DLLExports.cpp + +#include "StdAfx.h" + +#include "../../Common/MyInitGuid.h" + +#if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES) +#include "../../../C/Alloc.h" +#endif + +#include "../../Common/ComTry.h" + +#include "../../Windows/NtCheck.h" +#include "../../Windows/PropVariant.h" + +#include "../ICoder.h" +#include "../IPassword.h" + +#include "IArchive.h" + +HINSTANCE g_hInstance; + +#define NT_CHECK_FAIL_ACTION return FALSE; + +#ifdef _WIN32 +extern "C" +BOOL WINAPI DllMain( + #ifdef UNDER_CE + HANDLE + #else + HINSTANCE + #endif + hInstance, DWORD dwReason, LPVOID /*lpReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + g_hInstance = (HINSTANCE)hInstance; + NT_CHECK; + } + return TRUE; +} +#endif + +DEFINE_GUID(CLSID_CArchiveHandler, +0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00); + +static const UInt16 kDecodeId = 0x2790; + +DEFINE_GUID(CLSID_CCodec, +0x23170F69, 0x40C1, kDecodeId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +STDAPI CreateCoder(const GUID *clsid, const GUID *iid, void **outObject); +STDAPI CreateArchiver(const GUID *classID, const GUID *iid, void **outObject); + +STDAPI CreateObject(const GUID *clsid, const GUID *iid, void **outObject) +{ + // COM_TRY_BEGIN + *outObject = 0; + if (*iid == IID_ICompressCoder || *iid == IID_ICompressCoder2 || *iid == IID_ICompressFilter) + { + return CreateCoder(clsid, iid, outObject); + } + else + { + return CreateArchiver(clsid, iid, outObject); + } + // COM_TRY_END +} + +STDAPI SetLargePageMode() +{ + #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES) + SetLargePageSize(); + #endif + return S_OK; +} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/DmgHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/DmgHandler.cpp new file mode 100644 index 000000000..5040d5182 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/DmgHandler.cpp @@ -0,0 +1,918 @@ +// DmgHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyXml.h" +#include "Common/UTFConvert.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/BZip2Decoder.h" +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" + +// #define DMG_SHOW_RAW + +// #include <stdio.h> +#define PRF(x) // x + +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +static int Base64ToByte(char c) +{ + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + if (c == '=') return 0; + return -1; +} + +static int Base64ToBin(Byte *dest, const char *src, int srcLen) +{ + int srcPos = 0; + int destPos = 0; + while (srcPos < srcLen) + { + Byte buf[4]; + int filled = 0; + while (srcPos < srcLen) + { + int n = Base64ToByte(src[srcPos++]); + if (n >= 0) + { + buf[filled++] = (Byte)n; + if (filled == 4) + break; + } + } + if (filled >= 2) { if (dest) dest[destPos] = (buf[0] << 2) | (buf[1] >> 4); destPos++; } + if (filled >= 3) { if (dest) dest[destPos] = (buf[1] << 4) | (buf[2] >> 2); destPos++; } + if (filled >= 4) { if (dest) dest[destPos] = (buf[2] << 6) | (buf[3] ); destPos++; } + } + return destPos; +} + +static UString GetSizeString(UInt64 value) +{ + wchar_t s[32]; + wchar_t c; + if (value < (UInt64)20000) c = 0; + else if (value < ((UInt64)20000 << 10)) { value >>= 10; c = L'K'; } + else if (value < ((UInt64)20000 << 20)) { value >>= 20; c = L'M'; } + else { value >>= 30; c = L'G'; } + ConvertUInt64ToString(value, s); + int p = MyStringLen(s); + s[p++] = c; + s[p++] = L'\0'; + return s; +} + +namespace NArchive { +namespace NDmg { + +struct CBlock +{ + UInt32 Type; + UInt64 UnpPos; + UInt64 UnpSize; + UInt64 PackPos; + UInt64 PackSize; + + UInt64 GetNextPackOffset() const { return PackPos + PackSize; } +}; + +struct CFile +{ + CByteBuffer Raw; + UInt64 StartPos; + CRecordVector<CBlock> Blocks; + UInt64 GetUnpackSize() const + { + UInt64 size = 0; + for (int i = 0; i < Blocks.Size(); i++) + size += Blocks[i].UnpSize; + return size; + }; + UInt64 GetPackSize() const + { + UInt64 size = 0; + for (int i = 0; i < Blocks.Size(); i++) + size += Blocks[i].PackSize; + return size; + }; + + AString Name; +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + + AString _xml; + CObjectVector<CFile> _files; + CRecordVector<int> _fileIndices; + + HRESULT Open2(IInStream *stream); + HRESULT Extract(IInStream *stream); +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +const UInt32 kXmlSizeMax = ((UInt32)1 << 31) - (1 << 14); + +enum +{ + METHOD_ZERO_0 = 0, + METHOD_COPY = 1, + METHOD_ZERO_2 = 2, + METHOD_ADC = 0x80000004, + METHOD_ZLIB = 0x80000005, + METHOD_BZIP2 = 0x80000006, + METHOD_DUMMY = 0x7FFFFFFE, + METHOD_END = 0xFFFFFFFF +}; + +struct CMethodStat +{ + UInt32 NumBlocks; + UInt64 PackSize; + UInt64 UnpSize; + CMethodStat(): NumBlocks(0), PackSize(0), UnpSize(0) {} +}; + +struct CMethods +{ + CRecordVector<CMethodStat> Stats; + CRecordVector<UInt32> Types; + void Update(const CFile &file); + UString GetString() const; +}; + +void CMethods::Update(const CFile &file) +{ + for (int i = 0; i < file.Blocks.Size(); i++) + { + const CBlock &b = file.Blocks[i]; + int index = Types.FindInSorted(b.Type); + if (index < 0) + { + index = Types.AddToUniqueSorted(b.Type); + Stats.Insert(index, CMethodStat()); + } + CMethodStat &m = Stats[index]; + m.PackSize += b.PackSize; + m.UnpSize += b.UnpSize; + m.NumBlocks++; + } +} + +UString CMethods::GetString() const +{ + UString res; + for (int i = 0; i < Types.Size(); i++) + { + if (i != 0) + res += L' '; + wchar_t buf[32]; + const wchar_t *s; + const CMethodStat &m = Stats[i]; + bool showPack = true; + UInt32 type = Types[i]; + switch(type) + { + case METHOD_ZERO_0: s = L"zero0"; showPack = (m.PackSize != 0); break; + case METHOD_ZERO_2: s = L"zero2"; showPack = (m.PackSize != 0); break; + case METHOD_COPY: s = L"copy"; showPack = (m.UnpSize != m.PackSize); break; + case METHOD_ADC: s = L"adc"; break; + case METHOD_ZLIB: s = L"zlib"; break; + case METHOD_BZIP2: s = L"bzip2"; break; + default: ConvertUInt64ToString(type, buf); s = buf; + } + res += s; + if (m.NumBlocks != 1) + { + res += L'['; + ConvertUInt64ToString(m.NumBlocks, buf); + res += buf; + res += L']'; + } + res += L'-'; + res += GetSizeString(m.UnpSize); + if (showPack) + { + res += L'-'; + res += GetSizeString(m.PackSize); + } + } + return res; +} + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidMethod, VT_BSTR} +}; + +IMP_IInArchive_Props + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + CMethods m; + for (int i = 0; i < _files.Size(); i++) + m.Update(_files[i]); + prop = m.GetString(); + break; + } + case kpidNumBlocks: + { + UInt64 numBlocks = 0; + for (int i = 0; i < _files.Size(); i++) + numBlocks += _files[i].Blocks.Size(); + prop = numBlocks; + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +IMP_IInArchive_ArcProps + +static int FindKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag) +{ + for (int i = 0; i + 1 < item.SubItems.Size(); i++) + { + const CXmlItem &si = item.SubItems[i]; + if (si.IsTagged("key") && si.GetSubString() == key && item.SubItems[i + 1].IsTagged(nextTag)) + return i + 1; + } + return -1; +} + +static AString GetStringFromKeyPair(const CXmlItem &item, const AString &key, const AString &nextTag) +{ + int index = FindKeyPair(item, key, nextTag); + if (index >= 0) + return item.SubItems[index].GetSubString(); + return AString(); +} + +HRESULT CHandler::Open2(IInStream *stream) +{ + const int HEADER_SIZE = 0x1E0; + + UInt64 headerPos; + RINOK(stream->Seek(-HEADER_SIZE, STREAM_SEEK_END, &headerPos)); + Byte buf[HEADER_SIZE]; + RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE)); + UInt64 address1 = Get64(buf + 0); + UInt64 address2 = Get64(buf + 0xB8); + UInt64 size64 = Get64(buf + 0xC0); + if (address1 != address2 || size64 >= kXmlSizeMax || size64 == 0 || + address1 >= headerPos || address1 + size64 > headerPos) + return S_FALSE; + RINOK(stream->Seek(address1, STREAM_SEEK_SET, NULL)); + size_t size = (size_t)size64; + + char *ss = _xml.GetBuffer((int)size + 1); + RINOK(ReadStream_FALSE(stream, ss, size)); + ss[size] = 0; + _xml.ReleaseBuffer(); + + CXml xml; + if (!xml.Parse(_xml)) + return S_FALSE; + if (xml.Root.Name != "plist") + return S_FALSE; + + int dictIndex = xml.Root.FindSubTag("dict"); + if (dictIndex < 0) + return S_FALSE; + + const CXmlItem &dictItem = xml.Root.SubItems[dictIndex]; + int rfDictIndex = FindKeyPair(dictItem, "resource-fork", "dict"); + if (rfDictIndex < 0) + return S_FALSE; + + const CXmlItem &rfDictItem = dictItem.SubItems[rfDictIndex]; + int arrIndex = FindKeyPair(rfDictItem, "blkx", "array"); + if (arrIndex < 0) + return S_FALSE; + + const CXmlItem &arrItem = rfDictItem.SubItems[arrIndex]; + + int i; + for (i = 0; i < arrItem.SubItems.Size(); i++) + { + const CXmlItem &item = arrItem.SubItems[i]; + if (!item.IsTagged("dict")) + continue; + + CFile file; + file.StartPos = 0; + + int destLen; + { + AString dataString; + AString name = GetStringFromKeyPair(item, "Name", "string"); + if (name.IsEmpty()) + name = GetStringFromKeyPair(item, "CFName", "string"); + file.Name = name; + dataString = GetStringFromKeyPair(item, "Data", "data"); + + destLen = Base64ToBin(NULL, dataString, dataString.Length()); + file.Raw.SetCapacity(destLen); + Base64ToBin(file.Raw, dataString, dataString.Length()); + } + + if (destLen > 0xCC && Get32(file.Raw) == 0x6D697368) + { + PRF(printf("\n\n index = %d", _files.Size())); + const int kRecordSize = 40; + for (int offset = 0xCC; offset + kRecordSize <= destLen; offset += kRecordSize) + { + const Byte *p = (const Byte *)file.Raw + offset; + CBlock b; + b.Type = Get32(p); + if (b.Type == METHOD_END) + break; + if (b.Type == METHOD_DUMMY) + continue; + + b.UnpPos = Get64(p + 0x08) << 9; + b.UnpSize = Get64(p + 0x10) << 9; + b.PackPos = Get64(p + 0x18); + b.PackSize = Get64(p + 0x20); + + file.Blocks.Add(b); + + PRF(printf("\nType=%8x m[1]=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x", + b.Type, Get32(p + 4), (UInt32)b.UnpPos, (UInt32)b.UnpSize, (UInt32)b.PackPos, (UInt32)b.PackSize)); + } + } + int itemIndex = _files.Add(file); + if (file.Blocks.Size() > 0) + { + // if (file.Name.Find("HFS") >= 0) + _fileIndices.Add(itemIndex); + } + } + + // PackPos for each new file is 0 in some DMG files. So we use additional StartPos + + bool allStartAreZeros = true; + for (i = 0; i < _files.Size(); i++) + { + const CFile &file = _files[i]; + if (!file.Blocks.IsEmpty() && file.Blocks[0].PackPos != 0) + allStartAreZeros = false; + } + UInt64 startPos = 0; + if (allStartAreZeros) + { + for (i = 0; i < _files.Size(); i++) + { + CFile &file = _files[i]; + file.StartPos = startPos; + if (!file.Blocks.IsEmpty()) + startPos += file.Blocks.Back().GetNextPackOffset(); + } + } + + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + { + Close(); + if (Open2(stream) != S_OK) + return S_FALSE; + _inStream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _fileIndices.Clear(); + _files.Clear(); + _xml.Empty(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _fileIndices.Size() + #ifdef DMG_SHOW_RAW + + _files.Size() + 1; + #endif + ; + return S_OK; +} + +#define RAW_PREFIX L"raw" WSTRING_PATH_SEPARATOR + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + #ifdef DMG_SHOW_RAW + if ((int)index == _fileIndices.Size()) + { + switch(propID) + { + case kpidPath: + prop = RAW_PREFIX L"a.xml"; + break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)_xml.Length(); + break; + } + } + else if ((int)index > _fileIndices.Size()) + { + int rawIndex = (int)index - (_fileIndices.Size() + 1); + switch(propID) + { + case kpidPath: + { + wchar_t s[32] = RAW_PREFIX; + ConvertUInt64ToString(rawIndex, s + MyStringLen(s)); + prop = s; + break; + } + case kpidSize: + case kpidPackSize: + prop = (UInt64)_files[rawIndex].Raw.GetCapacity(); + break; + } + } + else + #endif + { + int itemIndex = _fileIndices[index]; + const CFile &item = _files[itemIndex]; + switch(propID) + { + case kpidMethod: + { + CMethods m; + m.Update(item); + UString resString = m.GetString(); + if (!resString.IsEmpty()) + prop = resString; + break; + } + + // case kpidExtension: prop = L"hfs"; break; + + case kpidPath: + { + // break; + UString name; + wchar_t s[32]; + ConvertUInt64ToString(index, s); + name = s; + int num = 10; + int numDigits; + for (numDigits = 1; num < _fileIndices.Size(); numDigits++) + num *= 10; + while (name.Length() < numDigits) + name = L'0' + name; + + AString subName; + int pos1 = item.Name.Find('('); + if (pos1 >= 0) + { + pos1++; + int pos2 = item.Name.Find(')', pos1); + if (pos2 >= 0) + { + subName = item.Name.Mid(pos1, pos2 - pos1); + pos1 = subName.Find(':'); + if (pos1 >= 0) + subName = subName.Left(pos1); + } + } + subName.Trim(); + if (!subName.IsEmpty()) + { + if (subName == "Apple_HFS") + subName = "hfs"; + else if (subName == "Apple_HFSX") + subName = "hfsx"; + else if (subName == "Apple_Free") + subName = "free"; + else if (subName == "DDM") + subName = "ddm"; + UString name2; + ConvertUTF8ToUnicode(subName, name2); + name += L'.'; + name += name2; + } + else + { + UString name2; + ConvertUTF8ToUnicode(item.Name, name2); + if (!name2.IsEmpty()) + name += L" - "; + name += name2; + } + prop = name; + break; + } + case kpidComment: + { + UString name; + ConvertUTF8ToUnicode(item.Name, name); + prop = name; + break; + } + + case kpidSize: prop = item.GetUnpackSize(); break; + case kpidPackSize: prop = item.GetPackSize(); break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CAdcDecoder: + public ICompressCoder, + public CMyUnknownImp +{ + CLzOutWindow m_OutWindowStream; + CInBuffer m_InStream; + + void ReleaseStreams() + { + m_OutWindowStream.ReleaseStream(); + m_InStream.ReleaseStream(); + } + + class CCoderReleaser + { + CAdcDecoder *m_Coder; + public: + bool NeedFlush; + CCoderReleaser(CAdcDecoder *coder): m_Coder(coder), NeedFlush(true) {} + ~CCoderReleaser() + { + if (NeedFlush) + m_Coder->m_OutWindowStream.Flush(); + m_Coder->ReleaseStreams(); + } + }; + friend class CCoderReleaser; + +public: + MY_UNKNOWN_IMP + + STDMETHOD(CodeReal)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); + + STDMETHOD(Code)(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress); +}; + +STDMETHODIMP CAdcDecoder::CodeReal(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + if (!m_OutWindowStream.Create(1 << 18)) + return E_OUTOFMEMORY; + if (!m_InStream.Create(1 << 18)) + return E_OUTOFMEMORY; + + m_OutWindowStream.SetStream(outStream); + m_OutWindowStream.Init(false); + m_InStream.SetStream(inStream); + m_InStream.Init(); + + CCoderReleaser coderReleaser(this); + + const UInt32 kStep = (1 << 20); + UInt64 nextLimit = kStep; + + UInt64 pos = 0; + while (pos < *outSize) + { + if (pos > nextLimit && progress) + { + UInt64 packSize = m_InStream.GetProcessedSize(); + RINOK(progress->SetRatioInfo(&packSize, &pos)); + nextLimit += kStep; + } + Byte b; + if (!m_InStream.ReadByte(b)) + return S_FALSE; + UInt64 rem = *outSize - pos; + if (b & 0x80) + { + unsigned num = (b & 0x7F) + 1; + if (num > rem) + return S_FALSE; + for (unsigned i = 0; i < num; i++) + { + if (!m_InStream.ReadByte(b)) + return S_FALSE; + m_OutWindowStream.PutByte(b); + } + pos += num; + continue; + } + Byte b1; + if (!m_InStream.ReadByte(b1)) + return S_FALSE; + + UInt32 len, distance; + + if (b & 0x40) + { + len = ((UInt32)b & 0x3F) + 4; + Byte b2; + if (!m_InStream.ReadByte(b2)) + return S_FALSE; + distance = ((UInt32)b1 << 8) + b2; + } + else + { + b &= 0x3F; + len = ((UInt32)b >> 2) + 3; + distance = (((UInt32)b & 3) << 8) + b1; + } + + if (distance >= pos || len > rem) + return S_FALSE; + m_OutWindowStream.CopyBlock(distance, len); + pos += len; + } + if (*inSize != m_InStream.GetProcessedSize()) + return S_FALSE; + coderReleaser.NeedFlush = false; + return m_OutWindowStream.Flush(); +} + +STDMETHODIMP CAdcDecoder::Code(ISequentialInStream *inStream, + ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize, + ICompressProgressInfo *progress) +{ + try { return CodeReal(inStream, outStream, inSize, outSize, progress);} + catch(const CInBufferException &e) { return e.ErrorCode; } + catch(const CLzOutWindowException &e) { return e.ErrorCode; } + catch(...) { return S_FALSE; } +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _files.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + int index = (int)(allFilesMode ? i : indices[i]); + #ifdef DMG_SHOW_RAW + if (index == _fileIndices.Size()) + totalSize += _xml.Length(); + else if (index > _fileIndices.Size()) + totalSize += _files[index - (_fileIndices.Size() + 1)].Raw.GetCapacity(); + else + #endif + totalSize += _files[_fileIndices[index]].GetUnpackSize(); + } + extractCallback->SetTotal(totalSize); + + UInt64 currentPackTotal = 0; + UInt64 currentUnpTotal = 0; + UInt64 currentPackSize = 0; + UInt64 currentUnpSize = 0; + + const UInt32 kZeroBufSize = (1 << 14); + CByteBuffer zeroBuf; + zeroBuf.SetCapacity(kZeroBufSize); + memset(zeroBuf, 0, kZeroBufSize); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder(); + CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec; + + NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); + CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; + + CAdcDecoder *adcCoderSpec = new CAdcDecoder(); + CMyComPtr<ICompressCoder> adcCoder = adcCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_inStream); + + for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize) + { + lps->InSize = currentPackTotal; + lps->OutSize = currentUnpTotal; + currentPackSize = 0; + currentUnpSize = 0; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + // const CItemEx &item = _files[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + + realOutStream.Release(); + + Int32 opRes = NExtract::NOperationResult::kOK; + #ifdef DMG_SHOW_RAW + if (index > _fileIndices.Size()) + { + const CByteBuffer &buf = _files[index - (_fileIndices.Size() + 1)].Raw; + outStreamSpec->Init(buf.GetCapacity()); + RINOK(WriteStream(outStream, buf, buf.GetCapacity())); + currentPackSize = currentUnpSize = buf.GetCapacity(); + } + else if (index == _fileIndices.Size()) + { + outStreamSpec->Init(_xml.Length()); + RINOK(WriteStream(outStream, (const char *)_xml, _xml.Length())); + currentPackSize = currentUnpSize = _xml.Length(); + } + else + #endif + { + const CFile &item = _files[_fileIndices[index]]; + currentPackSize = item.GetPackSize(); + currentUnpSize = item.GetUnpackSize(); + + UInt64 unpPos = 0; + UInt64 packPos = 0; + { + for (int j = 0; j < item.Blocks.Size(); j++) + { + lps->InSize = currentPackTotal + packPos; + lps->OutSize = currentUnpTotal + unpPos; + RINOK(lps->SetCur()); + + const CBlock &block = item.Blocks[j]; + + packPos += block.PackSize; + if (block.UnpPos != unpPos) + { + opRes = NExtract::NOperationResult::kDataError; + break; + } + + RINOK(_inStream->Seek(item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL)); + streamSpec->Init(block.PackSize); + // UInt64 startSize = outStreamSpec->GetSize(); + bool realMethod = true; + outStreamSpec->Init(block.UnpSize); + HRESULT res = S_OK; + + switch(block.Type) + { + case METHOD_ZERO_0: + case METHOD_ZERO_2: + realMethod = false; + if (block.PackSize != 0) + opRes = NExtract::NOperationResult::kUnSupportedMethod; + break; + + case METHOD_COPY: + if (block.UnpSize != block.PackSize) + { + opRes = NExtract::NOperationResult::kUnSupportedMethod; + break; + } + res = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + break; + + case METHOD_ADC: + { + res = adcCoder->Code(inStream, outStream, &block.PackSize, &block.UnpSize, progress); + break; + } + + case METHOD_ZLIB: + { + res = zlibCoder->Code(inStream, outStream, NULL, NULL, progress); + break; + } + + case METHOD_BZIP2: + { + res = bzip2Coder->Code(inStream, outStream, NULL, NULL, progress); + if (res == S_OK) + if (streamSpec->GetSize() != block.PackSize) + opRes = NExtract::NOperationResult::kDataError; + break; + } + + default: + opRes = NExtract::NOperationResult::kUnSupportedMethod; + break; + } + if (res != S_OK) + { + if (res != S_FALSE) + return res; + if (opRes == NExtract::NOperationResult::kOK) + opRes = NExtract::NOperationResult::kDataError; + } + unpPos += block.UnpSize; + if (!outStreamSpec->IsFinishedOK()) + { + if (realMethod && opRes == NExtract::NOperationResult::kOK) + opRes = NExtract::NOperationResult::kDataError; + + while (outStreamSpec->GetRem() != 0) + { + UInt64 rem = outStreamSpec->GetRem(); + UInt32 size = (UInt32)MyMin(rem, (UInt64)kZeroBufSize); + RINOK(WriteStream(outStream, zeroBuf, size)); + } + } + } + } + } + outStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Dmg", L"dmg", 0, 0xE4, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Dmg) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/ElfHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/ElfHandler.cpp new file mode 100644 index 000000000..c4ad78e9e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/ElfHandler.cpp @@ -0,0 +1,534 @@ +// ElfHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariantUtils.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +static UInt16 Get16(const Byte *p, int be) { if (be) return GetBe16(p); return GetUi16(p); } +static UInt32 Get32(const Byte *p, int be) { if (be) return GetBe32(p); return GetUi32(p); } +static UInt64 Get64(const Byte *p, int be) { if (be) return GetBe64(p); return GetUi64(p); } + +using namespace NWindows; + +namespace NArchive { +namespace NElf { + +#define ELF_CLASS_32 1 +#define ELF_CLASS_64 2 + +#define ELF_DATA_2LSB 1 +#define ELF_DATA_2MSB 2 + +#define NUM_SCAN_SECTIONS_MAX (1 << 6) + +struct CHeader +{ + bool Mode64; + bool Be; + Byte Os; + Byte AbiVer; + + UInt16 Type; + UInt16 Machine; + // UInt32 Version; + + // UInt64 EntryVa; + UInt64 ProgOffset; + UInt64 SectOffset; + UInt32 Flags; + UInt16 ElfHeaderSize; + UInt16 SegmentEntrySize; + UInt16 NumSegments; + UInt16 SectEntrySize; + UInt16 NumSections; + // UInt16 SectNameStringTableIndex; + + bool Parse(const Byte *buf); + + bool CheckSegmentEntrySize() const + { + return (Mode64 && SegmentEntrySize == 0x38) || (!Mode64 && SegmentEntrySize == 0x20); + }; + + UInt64 GetHeadersSize() const + { return ElfHeaderSize + + (UInt64)SegmentEntrySize * NumSegments + + (UInt64)SectEntrySize * NumSections; } + +}; + +bool CHeader::Parse(const Byte *p) +{ + switch(p[4]) + { + case ELF_CLASS_32: Mode64 = false; break; + case ELF_CLASS_64: Mode64 = true; break; + default: return false; + } + bool be; + switch(p[5]) + { + case ELF_DATA_2LSB: be = false; break; + case ELF_DATA_2MSB: be = true; break; + default: return false; + } + Be = be; + if (p[6] != 1) // Version + return false; + Os = p[7]; + AbiVer = p[8]; + for (int i = 9; i < 16; i++) + if (p[i] != 0) + return false; + + Type = Get16(p + 0x10, be); + Machine = Get16(p + 0x12, be); + if (Get32(p + 0x14, be) != 1) // Version + return false; + + if (Mode64) + { + // EntryVa = Get64(p + 0x18, be); + ProgOffset = Get64(p + 0x20, be); + SectOffset = Get64(p + 0x28, be); + p += 0x30; + } + else + { + // EntryVa = Get32(p + 0x18, be); + ProgOffset = Get32(p + 0x1C, be); + SectOffset = Get32(p + 0x20, be); + p += 0x24; + } + + Flags = Get32(p + 0, be); + ElfHeaderSize = Get16(p + 4, be); + SegmentEntrySize = Get16(p + 6, be); + NumSegments = Get16(p + 8, be); + SectEntrySize = Get16(p + 10, be); + NumSections = Get16(p + 12, be); + // SectNameStringTableIndex = Get16(p + 14, be); + return CheckSegmentEntrySize(); +} + +struct CSegment +{ + UInt32 Type; + UInt32 Flags; + UInt64 Offset; + UInt64 Va; + // UInt64 Pa; + UInt64 PSize; + UInt64 VSize; + // UInt64 Align; + + void UpdateTotalSize(UInt64 &totalSize) + { + UInt64 t = Offset + PSize; + if (t > totalSize) + totalSize = t; + } + void Parse(const Byte *p, bool mode64, bool be); +}; + +void CSegment::Parse(const Byte *p, bool mode64, bool be) +{ + Type = Get32(p, be); + if (mode64) + { + Flags = Get32(p + 4, be); + Offset = Get64(p + 8, be); + Va = Get64(p + 0x10, be); + // Pa = Get64(p + 0x18, be); + PSize = Get64(p + 0x20, be); + VSize = Get64(p + 0x28, be); + // Align = Get64(p + 0x30, be); + } + else + { + Offset = Get32(p + 4, be); + Va = Get32(p + 8, be); + // Pa = Get32(p + 12, be); + PSize = Get32(p + 16, be); + VSize = Get32(p + 20, be); + Flags = Get32(p + 24, be); + // Align = Get32(p + 28, be); + } +} + +static const CUInt32PCharPair g_MachinePairs[] = +{ + { 0, "None" }, + { 1, "AT&T WE 32100" }, + { 2, "SPARC" }, + { 3, "Intel 386" }, + { 4, "Motorola 68000" }, + { 5, "Motorola 88000" }, + { 6, "Intel 486" }, + { 7, "Intel i860" }, + { 8, "MIPS" }, + { 9, "IBM S/370" }, + { 10, "MIPS RS3000 LE" }, + { 11, "RS6000" }, + + { 15, "PA-RISC" }, + { 16, "nCUBE" }, + { 17, "Fujitsu VPP500" }, + { 18, "SPARC 32+" }, + { 19, "Intel i960" }, + { 20, "PowerPC" }, + { 21, "PowerPC 64-bit" }, + { 22, "IBM S/390" }, + + { 36, "NEX v800" }, + { 37, "Fujitsu FR20" }, + { 38, "TRW RH-32" }, + { 39, "Motorola RCE" }, + { 40, "ARM" }, + { 41, "Alpha" }, + { 42, "Hitachi SH" }, + { 43, "SPARC-V9" }, + { 44, "Siemens Tricore" }, + { 45, "ARC" }, + { 46, "H8/300" }, + { 47, "H8/300H" }, + { 48, "H8S" }, + { 49, "H8/500" }, + { 50, "IA-64" }, + { 51, "Stanford MIPS-X" }, + { 52, "Motorola ColdFire" }, + { 53, "M68HC12" }, + { 54, "Fujitsu MMA" }, + { 55, "Siemens PCP" }, + { 56, "Sony nCPU" }, + { 57, "Denso NDR1" }, + { 58, "Motorola StarCore" }, + { 59, "Toyota ME16" }, + { 60, "ST100" }, + { 61, "Advanced Logic TinyJ" }, + { 62, "AMD64" }, + { 63, "Sony DSP" }, + + { 66, "Siemens FX66" }, + { 67, "ST9+" }, + { 68, "ST7" }, + { 69, "MC68HC16" }, + { 70, "MC68HC11" }, + { 71, "MC68HC08" }, + { 72, "MC68HC05" }, + { 73, "Silicon Graphics SVx" }, + { 74, "ST19" }, + { 75, "Digital VAX" }, + { 76, "Axis CRIS" }, + { 77, "Infineon JAVELIN" }, + { 78, "Element 14 FirePath" }, + { 79, "LSI ZSP" }, + { 80, "MMIX" }, + { 81, "HUANY" }, + { 82, "SiTera Prism" }, + { 83, "Atmel AVR" }, + { 84, "Fujitsu FR30" }, + { 85, "Mitsubishi D10V" }, + { 86, "Mitsubishi D30V" }, + { 87, "NEC v850" }, + { 88, "Mitsubishi M32R" }, + { 89, "Matsushita MN10300" }, + { 90, "Matsushita MN10200" }, + { 91, "picoJava" }, + { 92, "OpenRISC" }, + { 93, "ARC Tangent-A5" }, + { 94, "Tensilica Xtensa" }, + { 0x9026, "Alpha" } +}; + +static const CUInt32PCharPair g_AbiOS[] = +{ + { 0, "None" }, + { 1, "HP-UX" }, + { 2, "NetBSD" }, + { 3, "Linux" }, + + { 6, "Solaris" }, + { 7, "AIX" }, + { 8, "IRIX" }, + { 9, "FreeBSD" }, + { 10, "TRU64" }, + { 11, "Novell Modesto" }, + { 12, "OpenBSD" }, + { 13, "OpenVMS" }, + { 14, "HP NSK" }, + { 15, "AROS" }, + { 97, "ARM" }, + { 255, "Standalone" } +}; + +static const CUInt32PCharPair g_SegmentFlags[] = +{ + { 0, "Execute" }, + { 1, "Write" }, + { 2, "Read" } +}; + +static const char *g_Types[] = +{ + "None", + "Relocatable file", + "Executable file", + "Shared object file", + "Core file" +}; + +static const char *g_SegnmentTypes[] = +{ + "Unused", + "Loadable segment", + "Dynamic linking tables", + "Program interpreter path name", + "Note section", + "SHLIB", + "Program header table", + "TLS" +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + CObjectVector<CSegment> _sections; + UInt32 _peOffset; + CHeader _header; + UInt64 _totalSize; + HRESULT Open2(IInStream *stream); + bool Parse(const Byte *buf, UInt32 size); +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +#define ELF_PT_PHDR 6 + +bool CHandler::Parse(const Byte *buf, UInt32 size) +{ + if (size < 64) + return false; + if (!_header.Parse(buf)) + return false; + if (_header.ProgOffset > size || + _header.ProgOffset + (UInt64)_header.SegmentEntrySize * _header.NumSegments > size || + _header.NumSegments > NUM_SCAN_SECTIONS_MAX) + return false; + const Byte *p = buf + _header.ProgOffset; + _totalSize = _header.ProgOffset; + + for (int i = 0; i < _header.NumSegments; i++, p += _header.SegmentEntrySize) + { + CSegment sect; + sect.Parse(p, _header.Mode64, _header.Be); + sect.UpdateTotalSize(_totalSize); + if (sect.Type != ELF_PT_PHDR) + _sections.Add(sect); + } + UInt64 total2 = _header.SectOffset + (UInt64)_header.SectEntrySize * _header.NumSections; + if (total2 > _totalSize) + _totalSize = total2; + return true; +} + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidCpu, VT_BSTR}, + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidBigEndian, VT_BOOL}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8} + }; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidType, VT_BSTR}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidVa, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: prop = _totalSize; break; + case kpidHeadersSize: prop = _header.GetHeadersSize(); break; + case kpidBit64: if (_header.Mode64) prop = _header.Mode64; break; + case kpidBigEndian: if (_header.Be) prop = _header.Be; break; + case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break; + case kpidHostOS: PAIR_TO_PROP(g_AbiOS, _header.Os, prop); break; + case kpidCharacts: TYPE_TO_PROP(g_Types, _header.Type, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CSegment &item = _sections[index]; + switch(propID) + { + case kpidPath: + { + wchar_t sz[32]; + ConvertUInt64ToString(index, sz); + prop = sz; + break; + } + case kpidSize: prop = (UInt64)item.VSize; break; + case kpidPackSize: prop = (UInt64)item.PSize; break; + case kpidOffset: prop = item.Offset; break; + case kpidVa: prop = item.Va; break; + case kpidType: TYPE_TO_PROP(g_SegnmentTypes, item.Type, prop); break; + case kpidCharacts: FLAGS_TO_PROP(g_SegmentFlags, item.Flags, prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Open2(IInStream *stream) +{ + const UInt32 kBufSize = 1 << 18; + const UInt32 kSigSize = 4; + + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + size_t processed = kSigSize; + RINOK(ReadStream_FALSE(stream, buf, processed)); + if (buf[0] != 0x7F || buf[1] != 'E' || buf[2] != 'L' || buf[3] != 'F') + return S_FALSE; + processed = kBufSize - kSigSize; + RINOK(ReadStream(stream, buf + kSigSize, &processed)); + processed += kSigSize; + if (!Parse(buf, (UInt32)processed)) + return S_FALSE; + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + return (fileSize == _totalSize) ? S_OK : S_FALSE; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(inStream)); + _inStream = inStream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _sections.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _sections.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _sections.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _sections[allFilesMode ? i : indices[i]].PSize; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_inStream); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CSegment &item = _sections[index]; + currentItemSize = item.PSize; + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(_inStream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"ELF", L"", 0, 0xDE, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Elf) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/FatHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/FatHandler.cpp new file mode 100644 index 000000000..1c374a444 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/FatHandler.cpp @@ -0,0 +1,996 @@ +// FatHandler.cpp + +#include "StdAfx.h" + +// #include <stdio.h> + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyCom.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/DummyOutStream.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) + +#define PRF(x) /* x */ + +namespace NArchive { +namespace NFat { + +static const UInt32 kFatItemUsedByDirMask = (UInt32)1 << 31; + +struct CHeader +{ + UInt32 NumSectors; + UInt16 NumReservedSectors; + Byte NumFats; + UInt32 NumFatSectors; + UInt32 RootDirSector; + UInt32 NumRootDirSectors; + UInt32 DataSector; + + UInt32 FatSize; + UInt32 BadCluster; + + Byte NumFatBits; + Byte SectorSizeLog; + Byte SectorsPerClusterLog; + Byte ClusterSizeLog; + + UInt16 SectorsPerTrack; + UInt16 NumHeads; + UInt32 NumHiddenSectors; + + bool VolFieldsDefined; + + UInt32 VolId; + // Byte VolName[11]; + // Byte FileSys[8]; + + // Byte OemName[5]; + Byte MediaType; + + // 32-bit FAT + UInt16 Flags; + UInt16 FsInfoSector; + UInt32 RootCluster; + + bool IsFat32() const { return NumFatBits == 32; } + UInt64 GetPhySize() const { return (UInt64)NumSectors << SectorSizeLog; } + UInt32 SectorSize() const { return (UInt32)1 << SectorSizeLog; } + UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; } + UInt32 ClusterToSector(UInt32 c) const { return DataSector + ((c - 2) << SectorsPerClusterLog); } + UInt32 IsEoc(UInt32 c) const { return c > BadCluster; } + UInt32 IsEocAndUnused(UInt32 c) const { return c > BadCluster && (c & kFatItemUsedByDirMask) == 0; } + UInt32 IsValidCluster(UInt32 c) const { return c >= 2 && c < FatSize; } + UInt32 SizeToSectors(UInt32 size) const { return (size + SectorSize() - 1) >> SectorSizeLog; } + UInt32 CalcFatSizeInSectors() const { return SizeToSectors((FatSize * (NumFatBits / 4) + 1) / 2); } + + UInt32 GetFatSector() const + { + UInt32 index = (IsFat32() && (Flags & 0x80) != 0) ? (Flags & 0xF) : 0; + if (index > NumFats) + index = 0; + return NumReservedSectors + index * NumFatSectors; + } + + UInt64 GetFilePackSize(UInt32 unpackSize) const + { + UInt64 mask = ClusterSize() - 1; + return (unpackSize + mask) & ~mask; + } + + UInt32 GetNumClusters(UInt32 size) const + { return (UInt32)(((UInt64)size + ClusterSize() - 1) >> ClusterSizeLog); } + + bool Parse(const Byte *p); +}; + +static int GetLog(UInt32 num) +{ + for (int i = 0; i < 31; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + +bool CHeader::Parse(const Byte *p) +{ + if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA) + return false; + + int codeOffset = 0; + switch (p[0]) + { + case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break; + case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break; + default: return false; + } + { + int s = GetLog(Get16(p + 11)); + if (s < 9 || s > 12) + return false; + SectorSizeLog = (Byte)s; + s = GetLog(p[13]); + if (s < 0) + return false; + SectorsPerClusterLog = (Byte)s; + ClusterSizeLog = SectorSizeLog + SectorsPerClusterLog; + } + + NumReservedSectors = Get16(p + 14); + if (NumReservedSectors == 0) + return false; + + NumFats = p[16]; + if (NumFats < 1 || NumFats > 4) + return false; + + UInt16 numRootDirEntries = Get16(p + 17); + if (numRootDirEntries == 0) + { + if (codeOffset < 90) + return false; + NumFatBits = 32; + NumRootDirSectors = 0; + } + else + { + if (codeOffset < 62) + return false; + NumFatBits = 0; + UInt32 mask = (1 << (SectorSizeLog - 5)) - 1; + if ((numRootDirEntries & mask) != 0) + return false; + NumRootDirSectors = (numRootDirEntries + mask) >> (SectorSizeLog - 5); + } + + NumSectors = Get16(p + 19); + if (NumSectors == 0) + NumSectors = Get32(p + 32); + else if (IsFat32()) + return false; + + MediaType = p[21]; + NumFatSectors = Get16(p + 22); + SectorsPerTrack = Get16(p + 24); + NumHeads = Get16(p + 26); + NumHiddenSectors = Get32(p + 28); + + // memcpy(OemName, p + 3, 5); + + p += 36; + if (IsFat32()) + { + if (NumFatSectors != 0) + return false; + NumFatSectors = Get32(p); + if (NumFatSectors >= (1 << 24)) + return false; + + Flags = Get16(p + 4); + if (Get16(p + 6) != 0) + return false; + RootCluster = Get32(p + 8); + FsInfoSector = Get16(p + 12); + for (int i = 16; i < 28; i++) + if (p[i] != 0) + return false; + p += 28; + } + + // DriveNumber = p[0]; + VolFieldsDefined = (p[2] == 0x29); // ExtendedBootSig + VolId = Get32(p + 3); + // memcpy(VolName, p + 7, 11); + // memcpy(FileSys, p + 18, 8); + + if (NumFatSectors == 0) + return false; + RootDirSector = NumReservedSectors + NumFatSectors * NumFats; + DataSector = RootDirSector + NumRootDirSectors; + if (NumSectors < DataSector) + return false; + UInt32 numDataSectors = NumSectors - DataSector; + UInt32 numClusters = numDataSectors >> SectorsPerClusterLog; + + BadCluster = 0x0FFFFFF7; + if (numClusters < 0xFFF5) + { + if (NumFatBits == 32) + return false; + NumFatBits = (numClusters < 0xFF5) ? 12: 16; + BadCluster &= ((1 << NumFatBits) - 1); + } + else if (NumFatBits != 32) + return false; + + FatSize = numClusters + 2; + if (FatSize > BadCluster || CalcFatSizeInSectors() > NumFatSectors) + return false; + return true; +} + +struct CItem +{ + UString UName; + char DosName[11]; + Byte CTime2; + UInt32 CTime; + UInt32 MTime; + UInt16 ADate; + Byte Attrib; + Byte Flags; + UInt32 Size; + UInt32 Cluster; + Int32 Parent; + + // NT uses Flags to store Low Case status + bool NameIsLow() const { return (Flags & 0x8) != 0; } + bool ExtIsLow() const { return (Flags & 0x10) != 0; } + bool IsDir() const { return (Attrib & 0x10) != 0; } + UString GetShortName() const; + UString GetName() const; + UString GetVolName() const; +}; + +static int CopyAndTrim(char *dest, const char *src, int size, bool toLower) +{ + int i; + memcpy(dest, src, size); + if (toLower) + for (i = 0; i < size; i++) + { + char c = dest[i]; + if (c >= 'A' && c <= 'Z') + dest[i] = c + 0x20; + } + for (i = size - 1; i >= 0 && dest[i] == ' '; i--); + return i + 1; +} + +static UString FatStringToUnicode(const char *s) +{ + return MultiByteToUnicodeString(s, CP_OEMCP); +} + +UString CItem::GetShortName() const +{ + char s[16]; + int i = CopyAndTrim(s, DosName, 8, NameIsLow()); + s[i++] = '.'; + int j = CopyAndTrim(s + i, DosName + 8, 3, ExtIsLow()); + if (j == 0) + j--; + s[i + j] = 0; + return FatStringToUnicode(s); +} + +UString CItem::GetName() const +{ + if (!UName.IsEmpty()) + return UName; + return GetShortName(); +} + +UString CItem::GetVolName() const +{ + if (!UName.IsEmpty()) + return UName; + char s[12]; + int i = CopyAndTrim(s, DosName, 11, false); + s[i] = 0; + return FatStringToUnicode(s); +} + +struct CDatabase +{ + CHeader Header; + CObjectVector<CItem> Items; + UInt32 *Fat; + CMyComPtr<IInStream> InStream; + IArchiveOpenCallback *OpenCallback; + + UInt32 NumFreeClusters; + bool VolItemDefined; + CItem VolItem; + UInt32 NumDirClusters; + CByteBuffer ByteBuf; + UInt64 NumCurUsedBytes; + + CDatabase(): Fat(0) {} + ~CDatabase() { ClearAndClose(); } + + void Clear(); + void ClearAndClose(); + HRESULT OpenProgressFat(bool changeTotal = true); + HRESULT OpenProgress(); + + UString GetItemPath(Int32 index) const; + HRESULT Open(); + HRESULT ReadDir(Int32 parent, UInt32 cluster, int level); + + UInt64 GetHeadersSize() const + { + return (UInt64)(Header.DataSector + (NumDirClusters << Header.SectorsPerClusterLog)) << Header.SectorSizeLog; + } + HRESULT SeekToSector(UInt32 sector); + HRESULT SeekToCluster(UInt32 cluster) { return SeekToSector(Header.ClusterToSector(cluster)); } +}; + +HRESULT CDatabase::SeekToSector(UInt32 sector) +{ + return InStream->Seek((UInt64)sector << Header.SectorSizeLog, STREAM_SEEK_SET, NULL); +} + +void CDatabase::Clear() +{ + VolItemDefined = false; + NumDirClusters = 0; + NumCurUsedBytes = 0; + + Items.Clear(); + delete []Fat; + Fat = 0; +} + +void CDatabase::ClearAndClose() +{ + Clear(); + InStream.Release(); +} + +HRESULT CDatabase::OpenProgressFat(bool changeTotal) +{ + if (!OpenCallback) + return S_OK; + if (changeTotal) + { + UInt64 numTotalBytes = (Header.CalcFatSizeInSectors() << Header.SectorSizeLog) + + ((UInt64)(Header.FatSize - NumFreeClusters) << Header.ClusterSizeLog); + RINOK(OpenCallback->SetTotal(NULL, &numTotalBytes)); + } + return OpenCallback->SetCompleted(NULL, &NumCurUsedBytes); +} + +HRESULT CDatabase::OpenProgress() +{ + if (!OpenCallback) + return S_OK; + UInt64 numItems = Items.Size(); + return OpenCallback->SetCompleted(&numItems, &NumCurUsedBytes); +} + +UString CDatabase::GetItemPath(Int32 index) const +{ + const CItem *item = &Items[index]; + UString name = item->GetName(); + for (;;) + { + index = item->Parent; + if (index < 0) + return name; + item = &Items[index]; + name = item->GetName() + WCHAR_PATH_SEPARATOR + name; + } +} + +static wchar_t *AddSubStringToName(wchar_t *dest, const Byte *p, int numChars) +{ + for (int i = 0; i < numChars; i++) + { + wchar_t c = Get16(p + i * 2); + if (c != 0 && c != 0xFFFF) + *dest++ = c; + } + *dest = 0; + return dest; +} + +HRESULT CDatabase::ReadDir(Int32 parent, UInt32 cluster, int level) +{ + int startIndex = Items.Size(); + if (startIndex >= (1 << 30) || level > 256) + return S_FALSE; + + UInt32 sectorIndex = 0; + UInt32 blockSize = Header.ClusterSize(); + bool clusterMode = (Header.IsFat32() || parent >= 0); + if (!clusterMode) + { + blockSize = Header.SectorSize(); + RINOK(SeekToSector(Header.RootDirSector)); + } + + ByteBuf.SetCapacity(blockSize); + UString curName; + int checkSum = -1; + int numLongRecords = -1; + for (UInt32 pos = blockSize;; pos += 32) + { + if (pos == blockSize) + { + pos = 0; + + if ((NumDirClusters & 0xFF) == 0) + { + RINOK(OpenProgress()); + } + + if (clusterMode) + { + if (Header.IsEoc(cluster)) + break; + if (!Header.IsValidCluster(cluster)) + return S_FALSE; + PRF(printf("\nCluster = %4X", cluster)); + RINOK(SeekToCluster(cluster)); + UInt32 newCluster = Fat[cluster]; + if ((newCluster & kFatItemUsedByDirMask) != 0) + return S_FALSE; + Fat[cluster] |= kFatItemUsedByDirMask; + cluster = newCluster; + NumDirClusters++; + NumCurUsedBytes += Header.ClusterSize(); + } + else if (sectorIndex++ >= Header.NumRootDirSectors) + break; + + RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize)); + } + const Byte *p = ByteBuf + pos; + if (p[0] == 0) + { + /* + // FreeDOS formats FAT partition with cluster chain longer than required. + if (clusterMode && !Header.IsEoc(cluster)) + return S_FALSE; + */ + break; + } + if (p[0] == 0xE5) + { + if (numLongRecords > 0) + return S_FALSE; + continue; + } + + Byte attrib = p[11]; + if ((attrib & 0x3F) == 0xF) + { + if (p[0] > 0x7F || Get16(p + 26) != 0) + return S_FALSE; + int longIndex = p[0] & 0x3F; + if (longIndex == 0) + return S_FALSE; + bool isLast = (p[0] & 0x40) != 0; + if (numLongRecords < 0) + { + if (!isLast) + return S_FALSE; + numLongRecords = longIndex; + } + else if (isLast || numLongRecords != longIndex) + return S_FALSE; + + numLongRecords--; + + if (p[12] == 0) + { + wchar_t nameBuf[14]; + wchar_t *dest; + + dest = AddSubStringToName(nameBuf, p + 1, 5); + dest = AddSubStringToName(dest, p + 14, 6); + AddSubStringToName(dest, p + 28, 2); + curName = nameBuf + curName; + if (isLast) + checkSum = p[13]; + if (checkSum != p[13]) + return S_FALSE; + } + } + else + { + if (numLongRecords > 0) + return S_FALSE; + CItem item; + memcpy(item.DosName, p, 11); + + if (checkSum >= 0) + { + Byte sum = 0; + for (int i = 0; i < 11; i++) + sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + (Byte)item.DosName[i]; + if (sum == checkSum) + item.UName = curName; + } + + if (item.DosName[0] == 5) + item.DosName[0] = (char)(Byte)0xE5; + item.Attrib = attrib; + item.Flags = p[12]; + item.Size = Get32(p + 28); + item.Cluster = Get16(p + 26); + if (Header.NumFatBits > 16) + item.Cluster |= ((UInt32)Get16(p + 20) << 16); + else + { + // OS/2 and WinNT probably can store EA (extended atributes) in that field. + } + + item.CTime = Get32(p + 14); + item.CTime2 = p[13]; + item.ADate = Get16(p + 18); + item.MTime = Get32(p + 22); + item.Parent = parent; + + if (attrib == 8) + { + VolItem = item; + VolItemDefined = true; + } + else + if (memcmp(item.DosName, ". ", 11) != 0 && + memcmp(item.DosName, ".. ", 11) != 0) + { + if (!item.IsDir()) + NumCurUsedBytes += Header.GetFilePackSize(item.Size); + Items.Add(item); + PRF(printf("\n%7d: %S", Items.Size(), GetItemPath(Items.Size() - 1))); + } + numLongRecords = -1; + curName.Empty(); + checkSum = -1; + } + } + + int finishIndex = Items.Size(); + for (int i = startIndex; i < finishIndex; i++) + { + const CItem &item = Items[i]; + if (item.IsDir()) + { + PRF(printf("\n%S", GetItemPath(i))); + RINOK(CDatabase::ReadDir(i, item.Cluster, level + 1)); + } + } + return S_OK; +} + +HRESULT CDatabase::Open() +{ + Clear(); + bool numFreeClustersDefined = false; + { + static const UInt32 kHeaderSize = 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize)); + if (!Header.Parse(buf)) + return S_FALSE; + UInt64 fileSize; + RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize)); + + /* we comment that check to support truncated images */ + /* + if (fileSize < Header.GetPhySize()) + return S_FALSE; + */ + + if (Header.IsFat32()) + { + SeekToSector(Header.FsInfoSector); + RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize)); + if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA) + return S_FALSE; + if (Get32(buf) == 0x41615252 && Get32(buf + 484) == 0x61417272) + { + NumFreeClusters = Get32(buf + 488); + numFreeClustersDefined = (NumFreeClusters <= Header.FatSize); + } + } + } + + // numFreeClustersDefined = false; // to recalculate NumFreeClusters + if (!numFreeClustersDefined) + NumFreeClusters = 0; + + CByteBuffer byteBuf; + Fat = new UInt32[Header.FatSize]; + + RINOK(OpenProgressFat()); + RINOK(SeekToSector(Header.GetFatSector())); + if (Header.NumFatBits == 32) + { + const UInt32 kBufSize = (1 << 15); + byteBuf.SetCapacity(kBufSize); + for (UInt32 i = 0; i < Header.FatSize;) + { + UInt32 size = Header.FatSize - i; + const UInt32 kBufSize32 = kBufSize / 4; + if (size > kBufSize32) + size = kBufSize32; + UInt32 readSize = Header.SizeToSectors(size * 4) << Header.SectorSizeLog; + RINOK(ReadStream_FALSE(InStream, byteBuf, readSize)); + NumCurUsedBytes += readSize; + + const UInt32 *src = (const UInt32 *)(const Byte *)byteBuf; + UInt32 *dest = Fat + i; + if (numFreeClustersDefined) + for (UInt32 j = 0; j < size; j++) + dest[j] = Get32(src + j) & 0x0FFFFFFF; + else + { + UInt32 numFreeClusters = 0; + for (UInt32 j = 0; j < size; j++) + { + UInt32 v = Get32(src + j) & 0x0FFFFFFF; + numFreeClusters += (UInt32)(v - 1) >> 31; + dest[j] = v; + } + NumFreeClusters += numFreeClusters; + } + i += size; + if ((i & 0xFFFFF) == 0) + { + RINOK(OpenProgressFat(!numFreeClustersDefined)); + } + } + } + else + { + const UInt32 kBufSize = (UInt32)Header.CalcFatSizeInSectors() << Header.SectorSizeLog; + NumCurUsedBytes += kBufSize; + byteBuf.SetCapacity(kBufSize); + Byte *p = byteBuf; + RINOK(ReadStream_FALSE(InStream, p, kBufSize)); + UInt32 fatSize = Header.FatSize; + UInt32 *fat = &Fat[0]; + if (Header.NumFatBits == 16) + for (UInt32 j = 0; j < fatSize; j++) + fat[j] = Get16(p + j * 2); + else + for (UInt32 j = 0; j < fatSize; j++) + fat[j] = (Get16(p + j * 3 / 2) >> ((j & 1) << 2)) & 0xFFF; + + if (!numFreeClustersDefined) + { + UInt32 numFreeClusters = 0; + for (UInt32 i = 0; i < fatSize; i++) + numFreeClusters += (UInt32)(fat[i] - 1) >> 31; + NumFreeClusters = numFreeClusters; + } + } + + RINOK(OpenProgressFat()); + + if ((Fat[0] & 0xFF) != Header.MediaType) + return S_FALSE; + + return ReadDir(-1, Header.RootCluster, 0); +} + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp, + CDatabase +{ +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + const CItem &item = Items[index]; + CClusterInStream *streamSpec = new CClusterInStream; + CMyComPtr<ISequentialInStream> streamTemp = streamSpec; + streamSpec->Stream = InStream; + streamSpec->StartOffset = Header.DataSector << Header.SectorSizeLog; + streamSpec->BlockSizeLog = Header.ClusterSizeLog; + streamSpec->Size = item.Size; + + UInt32 numClusters = Header.GetNumClusters(item.Size); + streamSpec->Vector.Reserve(numClusters); + UInt32 cluster = item.Cluster; + UInt32 size = item.Size; + + if (size == 0) + { + if (cluster != 0) + return S_FALSE; + } + else + { + UInt32 clusterSize = Header.ClusterSize(); + for (;; size -= clusterSize) + { + if (!Header.IsValidCluster(cluster)) + return S_FALSE; + streamSpec->Vector.Add(cluster - 2); + cluster = Fat[cluster]; + if (size <= clusterSize) + break; + } + if (!Header.IsEocAndUnused(cluster)) + return S_FALSE; + } + RINOK(streamSpec->InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI8}, + { NULL, kpidShortName, VT_BSTR} +}; + +enum +{ + kpidNumFats = kpidUserDefined + // kpidOemName, + // kpidVolName, + // kpidFileSysType +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidFileSystem, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidFreeSpace, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidVolumeName, VT_BSTR}, + + { L"FATs", kpidNumFats, VT_UI4}, + { NULL, kpidSectorSize, VT_UI4}, + { NULL, kpidId, VT_UI4}, + // { L"OEM Name", kpidOemName, VT_BSTR}, + // { L"Volume Name", kpidVolName, VT_BSTR}, + // { L"File System Type", kpidFileSysType, VT_BSTR} + // { NULL, kpidSectorsPerTrack, VT_UI4}, + // { NULL, kpidNumHeads, VT_UI4}, + // { NULL, kpidHiddenSectors, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_WITH_NAME + +static void FatTimeToProp(UInt32 dosTime, UInt32 ms10, NWindows::NCOM::CPropVariant &prop) +{ + FILETIME localFileTime, utc; + if (NWindows::NTime::DosTimeToFileTime(dosTime, localFileTime)) + if (LocalFileTimeToFileTime(&localFileTime, &utc)) + { + UInt64 t64 = (((UInt64)utc.dwHighDateTime) << 32) + utc.dwLowDateTime; + t64 += ms10 * 100000; + utc.dwLowDateTime = (DWORD)t64; + utc.dwHighDateTime = (DWORD)(t64 >> 32); + prop = utc; + } +} + +/* +static void StringToProp(const Byte *src, int size, NWindows::NCOM::CPropVariant &prop) +{ + char dest[32]; + memcpy(dest, src, size); + dest[size] = 0; + prop = FatStringToUnicode(dest); +} + +#define STRING_TO_PROP(s, p) StringToProp(s, sizeof(s), prop) +*/ + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidFileSystem: + { + wchar_t s[32] = { L'F', L'A', L'T' }; + ConvertUInt32ToString(Header.NumFatBits, s + 3); + prop = s; + break; + } + case kpidClusterSize: prop = Header.ClusterSize(); break; + case kpidPhySize: prop = Header.GetPhySize(); break; + case kpidFreeSpace: prop = (UInt64)NumFreeClusters << Header.ClusterSizeLog; break; + case kpidHeadersSize: prop = GetHeadersSize(); break; + case kpidMTime: if (VolItemDefined) FatTimeToProp(VolItem.MTime, 0, prop); break; + case kpidVolumeName: if (VolItemDefined) prop = VolItem.GetVolName(); break; + case kpidNumFats: if (Header.NumFats != 2) prop = Header.NumFats; break; + case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break; + // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break; + // case kpidNumHeads: prop = Header.NumHeads; break; + // case kpidOemName: STRING_TO_PROP(Header.OemName, prop); break; + case kpidId: if (Header.VolFieldsDefined) prop = Header.VolId; break; + // case kpidVolName: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.VolName, prop); break; + // case kpidFileSysType: if (Header.VolFieldsDefined) STRING_TO_PROP(Header.FileSys, prop); break; + // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break; + } + 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; + const CItem &item = Items[index]; + switch(propID) + { + case kpidPath: prop = GetItemPath(index); break; + case kpidShortName: prop = item.GetShortName(); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidMTime: FatTimeToProp(item.MTime, 0, prop); break; + case kpidCTime: FatTimeToProp(item.CTime, item.CTime2, prop); break; + case kpidATime: FatTimeToProp(((UInt32)item.ADate << 16), 0, prop); break; + case kpidAttrib: prop = (UInt32)item.Attrib; break; + case kpidSize: if (!item.IsDir()) prop = item.Size; break; + case kpidPackSize: if (!item.IsDir()) prop = Header.GetFilePackSize(item.Size); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + OpenCallback = callback; + InStream = stream; + HRESULT res; + try + { + res = CDatabase::Open(); + if (res == S_OK) + return S_OK; + } + catch(...) + { + Close(); + throw; + } + Close(); + return res; + } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + ClearAndClose(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = Items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + const CItem &item = Items[allFilesMode ? i : indices[i]]; + if (!item.IsDir()) + totalSize += item.Size; + } + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = Items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + totalPackSize += Header.GetFilePackSize(item.Size); + totalSize += item.Size; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(); + + int res = NExtract::NOperationResult::kDataError; + CMyComPtr<ISequentialInStream> inStream; + HRESULT hres = GetStream(index, &inStream); + if (hres != S_FALSE) + { + RINOK(hres); + if (inStream) + { + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize == item.Size) + res = NExtract::NOperationResult::kOK; + } + } + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = Items.Size(); + return S_OK; +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"FAT", L"fat img", 0, 0xDA, { 0x55, 0xAA }, 2, false, CreateArc, 0 }; + +REGISTER_ARC(Fat) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp new file mode 100644 index 000000000..a22c29e30 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/FlvHandler.cpp @@ -0,0 +1,544 @@ +// FlvHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +// #include "Common/Defs.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#define GetBe24(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 16) | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((const Byte *)(p))[2] ) + +#define Get16(p) GetBe16(p) +#define Get24(p) GetBe24(p) +#define Get32(p) GetBe32(p) + +namespace NArchive { +namespace NFlv { + +static const UInt32 kFileSizeMax = (UInt32)1 << 30; +static const int kNumChunksMax = (UInt32)1 << 23; + +const UInt32 kTagHeaderSize = 11; + +static const Byte kFlag_Video = 1; +static const Byte kFlag_Audio = 4; + +static const Byte kType_Audio = 8; +static const Byte kType_Video = 9; +static const Byte kType_Meta = 18; +static const int kNumTypes = 19; + +struct CItem +{ + UInt32 Offset; + UInt32 Size; + // UInt32 Time; + Byte Type; +}; + +struct CItem2 +{ + Byte Type; + Byte SubType; + Byte Props; + bool SameSubTypes; + int NumChunks; + size_t Size; + + CReferenceBuf *BufSpec; + CMyComPtr<IUnknown> RefBuf; + + bool IsAudio() const { return Type == kType_Audio; } +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + int _isRaw; + CMyComPtr<IInStream> _stream; + CObjectVector<CItem2> _items2; + // CByteBuffer _metadata; + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); + AString GetComment(); +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidNumBlocks, VT_UI4}, + { NULL, kpidComment, VT_BSTR} +}; + +/* +STATPROPSTG kArcProps[] = +{ + { NULL, kpidComment, VT_BSTR} +}; +*/ + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +static const char *g_AudioTypes[16] = +{ + "pcm", + "adpcm", + "mp3", + "pcm_le", + "nellymoser16", + "nellymoser8", + "nellymoser", + "g711a", + "g711m", + "audio9", + "aac", + "speex", + "audio12", + "audio13", + "mp3", + "audio15" +}; + +static const char *g_VideoTypes[16] = +{ + "video0", + "jpeg", + "h263", + "screen", + "vp6", + "vp6alpha", + "screen2", + "avc", + "video8", + "video9", + "video10", + "video11", + "video12", + "video13", + "video14", + "video15" +}; + +static const char *g_Rates[4] = +{ + "5.5 kHz", + "11 kHz", + "22 kHz", + "44 kHz" +}; + +static void MyStrCat(char *d, const char *s) +{ + MyStringCopy(d + MyStringLen(d), s); +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + const CItem2 &item = _items2[index]; + switch(propID) + { + case kpidExtension: + prop = _isRaw ? + (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) : + (item.IsAudio() ? "audio.flv" : "video.flv"); + break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)item.Size; + break; + case kpidNumBlocks: prop = (UInt32)item.NumChunks; break; + case kpidComment: + { + char sz[64]; + MyStringCopy(sz, (item.IsAudio() ? g_AudioTypes[item.SubType] : g_VideoTypes[item.SubType]) ); + if (item.IsAudio()) + { + MyStrCat(sz, " "); + MyStrCat(sz, g_Rates[(item.Props >> 2) & 3]); + MyStrCat(sz, (item.Props & 2) ? " 16-bit" : " 8-bit"); + MyStrCat(sz, (item.Props & 1) ? " stereo" : " mono"); + } + prop = sz; + break; + } + } + prop.Detach(value); + return S_OK; +} + +/* +AString CHandler::GetComment() +{ + const Byte *p = _metadata; + size_t size = _metadata.GetCapacity(); + AString res; + if (size > 0) + { + p++; + size--; + for (;;) + { + if (size < 2) + break; + int len = Get16(p); + p += 2; + size -= 2; + if (len == 0 || (size_t)len > size) + break; + { + AString temp; + char *sz = temp.GetBuffer(len); + memcpy(sz, p, len); + sz[len] = 0; + temp.ReleaseBuffer(); + if (!res.IsEmpty()) + res += '\n'; + res += temp; + } + p += len; + size -= len; + if (size < 1) + break; + Byte type = *p++; + size--; + bool ok = false; + switch(type) + { + case 0: + { + if (size < 8) + break; + ok = true; + Byte reverse[8]; + for (int i = 0; i < 8; i++) + { + bool little_endian = 1; + if (little_endian) + reverse[i] = p[7 - i]; + else + reverse[i] = p[i]; + } + double d = *(double *)reverse; + char temp[32]; + sprintf(temp, " = %.3f", d); + res += temp; + p += 8; + size -= 8; + break; + } + case 8: + { + if (size < 4) + break; + ok = true; + // UInt32 numItems = Get32(p); + p += 4; + size -= 4; + break; + } + } + if (!ok) + break; + } + } + return res; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidComment: prop = GetComment(); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} +*/ + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) +{ + CRecordVector<CItem> items; + + const UInt32 kHeaderSize = 13; + Byte header[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, header, kHeaderSize)); + if (header[0] != 'F' || + header[1] != 'L' || + header[2] != 'V' || + header[3] != 1 || + (header[4] & 0xFA) != 0) + return S_FALSE; + UInt32 offset = Get32(header + 5); + if (offset != 9 || Get32(header + 9) != 0) + return S_FALSE; + offset += 4; + + CByteBuffer inBuf; + size_t fileSize; + { + UInt64 fileSize64; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize64)); + if (fileSize64 > kFileSizeMax) + return S_FALSE; + + if (callback) + RINOK(callback->SetTotal(NULL, &fileSize64)) + + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + fileSize = (size_t)fileSize64; + inBuf.SetCapacity(fileSize); + for (size_t pos = 0; pos < fileSize;) + { + UInt64 offset64 = pos; + if (callback) + RINOK(callback->SetCompleted(NULL, &offset64)) + size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20)); + RINOK(ReadStream_FALSE(stream, inBuf + pos, rem)); + pos += rem; + } + } + + int lasts[kNumTypes]; + int i; + for (i = 0; i < kNumTypes; i++) + lasts[i] = -1; + + while (offset < fileSize) + { + CItem item; + item.Offset = offset; + const Byte *buf = inBuf + offset; + offset += kTagHeaderSize; + if (offset > fileSize) + return S_FALSE; + + item.Type = buf[0]; + UInt32 size = Get24(buf + 1); + if (size < 1) + return S_FALSE; + // item.Time = Get24(buf + 4); + // item.Time |= (UInt32)buf[7] << 24; + if (Get24(buf + 8) != 0) // streamID + return S_FALSE; + + UInt32 curSize = kTagHeaderSize + size + 4; + item.Size = curSize; + + offset += curSize - kTagHeaderSize; + if (offset > fileSize) + return S_FALSE; + + if (Get32(buf + kTagHeaderSize + size) != kTagHeaderSize + size) + return S_FALSE; + + // printf("\noffset = %6X type = %2d time = %6d size = %6d", (UInt32)offset, item.Type, item.Time, item.Size); + + if (item.Type == kType_Meta) + { + // _metadata = item.Buf; + } + else + { + if (item.Type != kType_Audio && item.Type != kType_Video) + return S_FALSE; + if (items.Size() >= kNumChunksMax) + return S_FALSE; + Byte firstByte = buf[kTagHeaderSize]; + Byte subType, props; + if (item.Type == kType_Audio) + { + subType = firstByte >> 4; + props = firstByte & 0xF; + } + else + { + subType = firstByte & 0xF; + props = firstByte >> 4; + } + int last = lasts[item.Type]; + if (last < 0) + { + CItem2 item2; + item2.RefBuf = item2.BufSpec = new CReferenceBuf; + item2.Size = curSize; + item2.Type = item.Type; + item2.SubType = subType; + item2.Props = props; + item2.NumChunks = 1; + item2.SameSubTypes = true; + lasts[item.Type] = _items2.Add(item2); + } + else + { + CItem2 &item2 = _items2[last]; + if (subType != item2.SubType) + item2.SameSubTypes = false; + item2.Size += curSize; + item2.NumChunks++; + } + items.Add(item); + } + } + + _isRaw = (_items2.Size() == 1); + for (i = 0; i < _items2.Size(); i++) + { + CItem2 &item2 = _items2[i]; + CByteBuffer &itemBuf = item2.BufSpec->Buf; + if (_isRaw) + { + if (!item2.SameSubTypes) + return S_FALSE; + itemBuf.SetCapacity((size_t)item2.Size - (kTagHeaderSize + 4 + 1) * item2.NumChunks); + item2.Size = 0; + } + else + { + itemBuf.SetCapacity(kHeaderSize + (size_t)item2.Size); + memcpy(itemBuf, header, kHeaderSize); + itemBuf[4] = item2.IsAudio() ? kFlag_Audio : kFlag_Video; + item2.Size = kHeaderSize; + } + } + + for (i = 0; i < items.Size(); i++) + { + const CItem &item = items[i]; + CItem2 &item2 = _items2[lasts[item.Type]]; + size_t size = item.Size; + const Byte *src = inBuf + item.Offset; + if (_isRaw) + { + src += kTagHeaderSize + 1; + size -= (kTagHeaderSize + 4 + 1); + } + memcpy(item2.BufSpec->Buf + item2.Size, src, size); + item2.Size += size; + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + HRESULT res; + try + { + res = Open2(inStream, callback); + if (res == S_OK) + _stream = inStream; + } + catch(...) { res = S_FALSE; } + if (res != S_OK) + { + Close(); + return S_FALSE; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _items2.Clear(); + // _metadata.SetCapacity(0); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items2.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items2.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items2[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + totalSize = 0; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CItem2 &item = _items2[index]; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + totalSize += item.Size; + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (outStream) + { + RINOK(WriteStream(outStream, item.BufSpec->Buf, item.BufSpec->Buf.GetCapacity())); + } + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> streamTemp = streamSpec; + streamSpec->Init(_items2[index].BufSpec); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"FLV", L"flv", 0, 0xD6, { 'F', 'L', 'V' }, 3, false, CreateArc, 0 }; + +REGISTER_ARC(Flv) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/GzHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/GzHandler.cpp new file mode 100644 index 000000000..7b73bddc3 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/GzHandler.cpp @@ -0,0 +1,698 @@ +// GzHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/DeflateDecoder.h" +#include "../Compress/DeflateEncoder.h" + +#include "Common/InStreamWithCRC.h" +#include "Common/OutStreamWithCRC.h" + +#include "DeflateProps.h" + +#define Get32(p) GetUi32(p) + +using namespace NWindows; + +namespace NArchive { +namespace NGz { + +static const UInt16 kSignature = 0x8B1F; + +namespace NHeader +{ + namespace NFlags + { + const Byte kIsText = 1 << 0; + const Byte kCrc = 1 << 1; + const Byte kExtra = 1 << 2; + const Byte kName = 1 << 3; + const Byte kComment = 1 << 4; + } + + namespace NExtraFlags + { + const Byte kMaximum = 2; + const Byte kFastest = 4; + } + + namespace NCompressionMethod + { + const Byte kDeflate = 8; + } + + namespace NHostOS + { + enum EEnum + { + kFAT = 0, + kAMIGA, + kVMS, + kUnix, + kVM_CMS, + kAtari, + kHPFS, + kMac, + kZ_System, + kCPM, + kTOPS20, + kNTFS, + kQDOS, + kAcorn, + kVFAT, + kMVS, + kBeOS, + kTandem, + + kUnknown = 255 + }; + } +} + +static const char *kHostOSes[] = +{ + "FAT", + "AMIGA", + "VMS", + "Unix", + "VM/CMS", + "Atari", + "HPFS", + "Macintosh", + "Z-System", + "CP/M", + "TOPS-20", + "NTFS", + "SMS/QDOS", + "Acorn", + "VFAT", + "MVS", + "BeOS", + "Tandem", + "OS/400", + "OS/X" +}; + +static const char *kUnknownOS = "Unknown"; + +class CItem +{ + bool TestFlag(Byte flag) const { return (Flags & flag) != 0; } +public: + Byte Method; + Byte Flags; + Byte ExtraFlags; + Byte HostOS; + UInt32 Time; + UInt32 Crc; + UInt32 Size32; + + AString Name; + AString Comment; + // CByteBuffer Extra; + + // bool IsText() const { return TestFlag(NHeader::NFlags::kIsText); } + bool HeaderCrcIsPresent() const { return TestFlag(NHeader::NFlags::kCrc); } + bool ExtraFieldIsPresent() const { return TestFlag(NHeader::NFlags::kExtra); } + bool NameIsPresent() const { return TestFlag(NHeader::NFlags::kName); } + bool CommentIsPresent() const { return TestFlag(NHeader::NFlags::kComment); } + + void Clear() + { + Name.Empty(); + Comment.Empty(); + // Extra.SetCapacity(0); + } + + HRESULT ReadHeader(NCompress::NDeflate::NDecoder::CCOMCoder *stream); + HRESULT ReadFooter1(NCompress::NDeflate::NDecoder::CCOMCoder *stream); + HRESULT ReadFooter2(ISequentialInStream *stream); + + HRESULT WriteHeader(ISequentialOutStream *stream); + HRESULT WriteFooter(ISequentialOutStream *stream); +}; + +static HRESULT ReadBytes(NCompress::NDeflate::NDecoder::CCOMCoder *stream, Byte *data, UInt32 size) +{ + for (UInt32 i = 0; i < size; i++) + data[i] = stream->ReadByte(); + return stream->InputEofError() ? S_FALSE : S_OK; +} + +static HRESULT SkipBytes(NCompress::NDeflate::NDecoder::CCOMCoder *stream, UInt32 size) +{ + for (UInt32 i = 0; i < size; i++) + stream->ReadByte(); + return stream->InputEofError() ? S_FALSE : S_OK; +} + +static HRESULT ReadUInt16(NCompress::NDeflate::NDecoder::CCOMCoder *stream, UInt16 &value /* , UInt32 &crc */) +{ + value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = stream->ReadByte(); + if (stream->InputEofError()) + return S_FALSE; + // crc = CRC_UPDATE_BYTE(crc, b); + value |= (UInt16(b) << (8 * i)); + } + return S_OK; +} + +static HRESULT ReadString(NCompress::NDeflate::NDecoder::CCOMCoder *stream, AString &s, UInt32 limit /* , UInt32 &crc */) +{ + s.Empty(); + for (UInt32 i = 0; i < limit; i++) + { + Byte b = stream->ReadByte(); + if (stream->InputEofError()) + return S_FALSE; + // crc = CRC_UPDATE_BYTE(crc, b); + if (b == 0) + return S_OK; + s += (char)b; + } + return S_FALSE; +} + +HRESULT CItem::ReadHeader(NCompress::NDeflate::NDecoder::CCOMCoder *stream) +{ + Clear(); + + // Header-CRC field had another meaning in old version of gzip! + // UInt32 crc = CRC_INIT_VAL; + Byte buf[10]; + + RINOK(ReadBytes(stream, buf, 10)); + + if (GetUi16(buf) != kSignature) + return S_FALSE; + + Method = buf[2]; + + if (Method != NHeader::NCompressionMethod::kDeflate) + return S_FALSE; + + Flags = buf[3]; + Time = Get32(buf + 4); + ExtraFlags = buf[8]; + HostOS = buf[9]; + + // crc = CrcUpdate(crc, buf, 10); + + if (ExtraFieldIsPresent()) + { + UInt16 extraSize; + RINOK(ReadUInt16(stream, extraSize /* , crc */)); + RINOK(SkipBytes(stream, extraSize)); + // Extra.SetCapacity(extraSize); + // RINOK(ReadStream_FALSE(stream, Extra, extraSize)); + // crc = CrcUpdate(crc, Extra, extraSize); + } + if (NameIsPresent()) + RINOK(ReadString(stream, Name, (1 << 10) /* , crc */)); + if (CommentIsPresent()) + RINOK(ReadString(stream, Comment, (1 << 16) /* , crc */)); + + if (HeaderCrcIsPresent()) + { + UInt16 headerCRC; + // UInt32 dummy = 0; + RINOK(ReadUInt16(stream, headerCRC /* , dummy */)); + /* + if ((UInt16)CRC_GET_DIGEST(crc) != headerCRC) + return S_FALSE; + */ + } + return stream->InputEofError() ? S_FALSE : S_OK; +} + +HRESULT CItem::ReadFooter1(NCompress::NDeflate::NDecoder::CCOMCoder *stream) +{ + Byte buf[8]; + RINOK(ReadBytes(stream, buf, 8)); + Crc = Get32(buf); + Size32 = Get32(buf + 4); + return stream->InputEofError() ? S_FALSE : S_OK; +} + +HRESULT CItem::ReadFooter2(ISequentialInStream *stream) +{ + Byte buf[8]; + RINOK(ReadStream_FALSE(stream, buf, 8)); + Crc = Get32(buf); + Size32 = Get32(buf + 4); + return S_OK; +} + +HRESULT CItem::WriteHeader(ISequentialOutStream *stream) +{ + Byte buf[10]; + SetUi16(buf, kSignature); + buf[2] = Method; + buf[3] = Flags & NHeader::NFlags::kName; + // buf[3] |= NHeader::NFlags::kCrc; + SetUi32(buf + 4, Time); + buf[8] = ExtraFlags; + buf[9] = HostOS; + RINOK(WriteStream(stream, buf, 10)); + // crc = CrcUpdate(CRC_INIT_VAL, buf, 10); + if (NameIsPresent()) + { + // crc = CrcUpdate(crc, (const char *)Name, Name.Length() + 1); + RINOK(WriteStream(stream, (const char *)Name, Name.Length() + 1)); + } + // SetUi16(buf, (UInt16)CRC_GET_DIGEST(crc)); + // RINOK(WriteStream(stream, buf, 2)); + return S_OK; +} + +HRESULT CItem::WriteFooter(ISequentialOutStream *stream) +{ + Byte buf[8]; + SetUi32(buf, Crc); + SetUi32(buf + 4, Size32); + return WriteStream(stream, buf, 8); +} + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public IOutArchive, + public ISetProperties, + public CMyUnknownImp +{ + CItem _item; + UInt64 _startPosition; + UInt64 _headerSize; + UInt64 _packSize; + bool _packSizeDefined; + CMyComPtr<IInStream> _stream; + CMyComPtr<ICompressCoder> _decoder; + NCompress::NDeflate::NDecoder::CCOMCoder *_decoderSpec; + + CDeflateProps _method; + +public: + MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties) + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); + + CHandler() + { + _decoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder; + _decoder = _decoderSpec; + } +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidCRC, VT_UI4} + // { NULL, kpidComment, VT_BSTR} +} +; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPath: + if (_item.NameIsPresent()) + prop = MultiByteToUnicodeString(_item.Name, CP_ACP); + break; + // case kpidComment: if (_item.CommentIsPresent()) prop = MultiByteToUnicodeString(_item.Comment, CP_ACP); break; + case kpidMTime: + { + if (_item.Time != 0) + { + FILETIME utc; + NTime::UnixTimeToFileTime(_item.Time, utc); + prop = utc; + } + break; + } + case kpidSize: if (_stream) prop = (UInt64)_item.Size32; break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + case kpidHostOS: prop = (_item.HostOS < sizeof(kHostOSes) / sizeof(kHostOSes[0])) ? + kHostOSes[_item.HostOS] : kUnknownOS; break; + case kpidCRC: if (_stream) prop = _item.Crc; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) +{ + COM_TRY_BEGIN + HRESULT res; + try + { + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPosition)); + res = OpenSeq(stream); + if (res == S_OK) + { + UInt64 endPos; + res = stream->Seek(-8, STREAM_SEEK_END, &endPos); + _packSize = endPos + 8 - _startPosition; + _packSizeDefined = true; + if (res == S_OK) + { + res = _item.ReadFooter2(stream); + _stream = stream; + } + } + } + catch(...) { res = S_FALSE; } + if (res != S_OK) + Close(); + return res; + COM_TRY_END +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + COM_TRY_BEGIN + HRESULT res; + try + { + Close(); + _decoderSpec->SetInStream(stream); + _decoderSpec->InitInStream(true); + res = _item.ReadHeader(_decoderSpec); + _headerSize = _decoderSpec->GetInputProcessedSize(); + } + catch(...) { res = S_FALSE; } + if (res != S_OK) + Close(); + return res; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _stream.Release(); + _decoderSpec->ReleaseInStream(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + if (_stream) + extractCallback->SetTotal(_packSize); + UInt64 currentTotalPacked = 0; + RINOK(extractCallback->SetCompleted(¤tTotalPacked)); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + if (_stream) + { + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + _decoderSpec->InitInStream(true); + } + bool firstItem = true; + Int32 opRes; + for (;;) + { + lps->InSize = _packSize = _decoderSpec->GetInputProcessedSize(); + _packSizeDefined = true; + lps->OutSize = outStreamSpec->GetSize(); + RINOK(lps->SetCur()); + + CItem item; + if (!firstItem || _stream) + { + HRESULT result = item.ReadHeader(_decoderSpec); + if (result != S_OK) + { + if (result != S_FALSE) + return result; + opRes = firstItem ? + NExtract::NOperationResult::kDataError : + NExtract::NOperationResult::kOK; + break; + } + } + firstItem = false; + + UInt64 startOffset = outStreamSpec->GetSize(); + outStreamSpec->InitCRC(); + + HRESULT result = _decoderSpec->CodeResume(outStream, NULL, progress); + if (result != S_OK) + { + if (result != S_FALSE) + return result; + opRes = NExtract::NOperationResult::kDataError; + break; + } + + _decoderSpec->AlignToByte(); + if (item.ReadFooter1(_decoderSpec) != S_OK) + { + opRes = NExtract::NOperationResult::kDataError; + break; + } + if (item.Crc != outStreamSpec->GetCRC() || + item.Size32 != (UInt32)(outStreamSpec->GetSize() - startOffset)) + { + opRes = NExtract::NOperationResult::kCRCError; + break; + } + } + outStream.Release(); + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +static const Byte kHostOS = + #ifdef _WIN32 + NHeader::NHostOS::kFAT; + #else + NHeader::NHostOS::kUnix; + #endif + +static HRESULT UpdateArchive( + ISequentialOutStream *outStream, + UInt64 unpackSize, + const CItem &newItem, + CDeflateProps &deflateProps, + IArchiveUpdateCallback *updateCallback) +{ + UInt64 complexity = 0; + RINOK(updateCallback->SetTotal(unpackSize)); + RINOK(updateCallback->SetCompleted(&complexity)); + + CMyComPtr<ISequentialInStream> fileInStream; + + RINOK(updateCallback->GetStream(0, &fileInStream)); + + CSequentialInStreamWithCRC *inStreamSpec = new CSequentialInStreamWithCRC; + CMyComPtr<ISequentialInStream> crcStream(inStreamSpec); + inStreamSpec->SetStream(fileInStream); + inStreamSpec->Init(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CItem item = newItem; + item.Method = NHeader::NCompressionMethod::kDeflate; + item.ExtraFlags = deflateProps.IsMaximum() ? + NHeader::NExtraFlags::kMaximum : + NHeader::NExtraFlags::kFastest; + + item.HostOS = kHostOS; + + RINOK(item.WriteHeader(outStream)); + + NCompress::NDeflate::NEncoder::CCOMCoder *deflateEncoderSpec = new NCompress::NDeflate::NEncoder::CCOMCoder; + CMyComPtr<ICompressCoder> deflateEncoder = deflateEncoderSpec; + RINOK(deflateProps.SetCoderProperties(deflateEncoderSpec)); + RINOK(deflateEncoder->Code(crcStream, outStream, NULL, NULL, progress)); + + item.Crc = inStreamSpec->GetCRC(); + item.Size32 = (UInt32)inStreamSpec->GetSize(); + RINOK(item.WriteFooter(outStream)); + return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); +} + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kUnix; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + if (numItems != 1) + return E_INVALIDARG; + + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); + + CItem newItem = _item; + newItem.ExtraFlags = 0; + newItem.Flags = 0; + if (IntToBool(newProps)) + { + { + FILETIME utcTime; + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidMTime, &prop)); + if (prop.vt != VT_FILETIME) + return E_INVALIDARG; + utcTime = prop.filetime; + if (!NTime::FileTimeToUnixTime(utcTime, newItem.Time)) + return E_INVALIDARG; + } + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidPath, &prop)); + if (prop.vt == VT_BSTR) + { + UString name = prop.bstrVal; + int dirDelimiterPos = name.ReverseFind(CHAR_PATH_SEPARATOR); + if (dirDelimiterPos >= 0) + name = name.Mid(dirDelimiterPos + 1); + newItem.Name = UnicodeStringToMultiByte(name, CP_ACP); + if (!newItem.Name.IsEmpty()) + newItem.Flags |= NHeader::NFlags::kName; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + } + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); + if (prop.vt == VT_BOOL) + { + if (prop.boolVal != VARIANT_FALSE) + return E_INVALIDARG; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + } + } + + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + + return UpdateArchive(outStream, size, newItem, _method, updateCallback); + } + + if (indexInArchive != 0) + return E_INVALIDARG; + + if (!_stream) + return E_NOTIMPL; + + UInt64 offset = _startPosition; + if (IntToBool(newProps)) + { + newItem.WriteHeader(outStream); + offset += _headerSize; + } + RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL)); + return NCompress::CopyStream(_stream, outStream, NULL); +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + return _method.SetProperties(names, values, numProps); +} + +static IInArchive *CreateArc() { return new CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"gzip", L"gz gzip tgz tpz", L"* * .tar .tar", 0xEF, { 0x1F, 0x8B, 8 }, 3, true, CreateArc, CreateArcOut }; + +REGISTER_ARC(GZip) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.cpp new file mode 100644 index 000000000..f226458d4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.cpp @@ -0,0 +1,243 @@ +// HfsHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Windows/PropVariant.h" +#include "../../Common/StreamUtils.h" +#include "HfsHandler.h" + +namespace NArchive { +namespace NHfs { + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME} +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidFreeSpace, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidMTime, VT_FILETIME} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static void HfsTimeToProp(UInt32 hfsTime, NWindows::NCOM::CPropVariant &prop) +{ + FILETIME ft; + HfsTimeToFileTime(hfsTime, ft); + prop = ft; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: prop = _db.Header.IsHfsX() ? L"HFSX" : L"HFS+"; break; + case kpidClusterSize: prop = (UInt32)1 << _db.Header.BlockSizeLog; break; + case kpidFreeSpace: prop = (UInt64)_db.Header.NumFreeBlocks << _db.Header.BlockSizeLog; break; + case kpidMTime: HfsTimeToProp(_db.Header.MTime, prop); break; + case kpidCTime: + { + FILETIME localFt, ft; + HfsTimeToFileTime(_db.Header.CTime, localFt); + if (LocalFileTimeToFileTime(&localFt, &ft)) + prop = ft; + break; + } + } + 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; + const CItem &item = _db.Items[index]; + switch(propID) + { + case kpidPath: prop = _db.GetItemPath(index); break; + case kpidIsDir: prop = item.IsDir(); break; + + case kpidCTime: HfsTimeToProp(item.CTime, prop); break; + case kpidMTime: HfsTimeToProp(item.MTime, prop); break; + case kpidATime: HfsTimeToProp(item.ATime, prop); break; + + case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumBlocks << _db.Header.BlockSizeLog; break; + case kpidSize: if (!item.IsDir()) prop = item.Size; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> _callback; +public: + HRESULT SetTotal(UInt64 numFiles); + HRESULT SetCompleted(UInt64 numFiles); + CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {} +}; + +HRESULT CProgressImp::SetTotal(UInt64 numFiles) +{ + if (_callback) + return _callback->SetTotal(&numFiles, NULL); + return S_OK; +} + +HRESULT CProgressImp::SetCompleted(UInt64 numFiles) +{ + if (_callback) + return _callback->SetCompleted(&numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + try + { + CProgressImp progressImp(callback); + HRESULT res = _db.Open(inStream, &progressImp); + if (res == E_ABORT) + return res; + if (res != S_OK) + return S_FALSE; + _stream = inStream; + } + catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _db.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _db.Items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + const CItem &item = _db.Items[allFilesMode ? i : indices[i]]; + if (!item.IsDir()) + totalSize += item.Size; + } + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 currentTotalSize = 0, currentItemSize = 0; + + CByteBuffer buf; + const UInt32 kBufSize = (1 << 16); + buf.SetCapacity(kBufSize); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + RINOK(extractCallback->SetCompleted(¤tTotalSize)); + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _db.Items[index]; + currentItemSize = 0; + if (!item.IsDir()) + currentItemSize = item.Size; + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + UInt64 pos = 0; + int res = NExtract::NOperationResult::kOK; + int i; + for (i = 0; i < item.Extents.Size(); i++) + { + if (item.Size == pos) + break; + if (res != NExtract::NOperationResult::kOK) + break; + const CExtent &e = item.Extents[i]; + RINOK(_stream->Seek((UInt64)e.Pos << _db.Header.BlockSizeLog, STREAM_SEEK_SET, NULL)); + UInt64 extentSize = (UInt64)e.NumBlocks << _db.Header.BlockSizeLog; + for (;;) + { + if (extentSize == 0) + break; + UInt64 rem = item.Size - pos; + if (rem == 0) + { + if (extentSize >= (UInt64)((UInt32)1 << _db.Header.BlockSizeLog)) + res = NExtract::NOperationResult::kDataError; + break; + } + UInt32 curSize = kBufSize; + if (curSize > rem) + curSize = (UInt32)rem; + if (curSize > extentSize) + curSize = (UInt32)extentSize; + RINOK(ReadStream_FALSE(_stream, buf, curSize)); + if (realOutStream) + { + RINOK(WriteStream(realOutStream, buf, curSize)); + } + pos += curSize; + extentSize -= curSize; + UInt64 processed = currentTotalSize + pos; + RINOK(extractCallback->SetCompleted(&processed)); + } + } + if (i != item.Extents.Size() || item.Size != pos) + res = NExtract::NOperationResult::kDataError; + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _db.Items.Size(); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.h new file mode 100644 index 000000000..269af218e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsHandler.h @@ -0,0 +1,26 @@ +// HfsHandler.h + +#ifndef __ARCHIVE_HFS_HANDLER_H +#define __ARCHIVE_HFS_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" +#include "HfsIn.h" + +namespace NArchive { +namespace NHfs { + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CDatabase _db; +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.cpp new file mode 100644 index 000000000..8391dd936 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.cpp @@ -0,0 +1,480 @@ +// HfsIn.cpp + +#include "StdAfx.h" + +#include "../../Common/StreamUtils.h" +#include "Common/IntToString.h" + +#include "HfsIn.h" + +#include "../../../../C/CpuArch.h" + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +namespace NArchive { +namespace NHfs { + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareIdToIndex(const CIdIndexPair *p1, const CIdIndexPair *p2, void * /* param */) +{ + RINOZ(MyCompare(p1->ID, p2->ID)); + return MyCompare(p1->Index, p2->Index); +} + +bool operator< (const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID < a2.ID); } +bool operator> (const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID > a2.ID); } +bool operator==(const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID == a2.ID); } +bool operator!=(const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID != a2.ID); } + +static UString GetSpecName(const UString &name, UInt32 /* id */) +{ + UString name2 = name; + name2.Trim(); + if (name2.IsEmpty()) + { + /* + wchar_t s[32]; + ConvertUInt64ToString(id, s); + return L"[" + (UString)s + L"]"; + */ + return L"[]"; + } + return name; +} + +UString CDatabase::GetItemPath(int index) const +{ + const CItem *item = &Items[index]; + UString name = GetSpecName(item->Name, item->ID); + + for (int i = 0; i < 1000; i++) + { + if (item->ParentID < 16 && item->ParentID != 2) + { + if (item->ParentID != 1) + break; + return name; + } + CIdIndexPair pair; + pair.ID = item->ParentID; + pair.Index = 0; + int indexInMap = IdToIndexMap.FindInSorted(pair); + if (indexInMap < 0) + break; + item = &Items[IdToIndexMap[indexInMap].Index]; + name = GetSpecName(item->Name, item->ID) + WCHAR_PATH_SEPARATOR + name; + } + return (UString)L"Unknown" + WCHAR_PATH_SEPARATOR + name; +} + +void CFork::Parse(const Byte *p) +{ + Size = Get64(p); + // ClumpSize = Get32(p + 8); + NumBlocks = Get32(p + 0xC); + for (int i = 0; i < 8; i++) + { + CExtent &e = Extents[i]; + e.Pos = Get32(p + 0x10 + i * 8); + e.NumBlocks = Get32(p + 0x10 + i * 8 + 4); + } +} + +static HRESULT ReadExtent(int blockSizeLog, IInStream *inStream, Byte *buf, const CExtent &e) +{ + RINOK(inStream->Seek((UInt64)e.Pos << blockSizeLog, STREAM_SEEK_SET, NULL)); + return ReadStream_FALSE(inStream, buf, (size_t)e.NumBlocks << blockSizeLog); +} + +HRESULT CDatabase::ReadFile(const CFork &fork, CByteBuffer &buf, IInStream *inStream) +{ + if (fork.NumBlocks >= Header.NumBlocks) + return S_FALSE; + size_t totalSize = (size_t)fork.NumBlocks << Header.BlockSizeLog; + if ((totalSize >> Header.BlockSizeLog) != fork.NumBlocks) + return S_FALSE; + buf.SetCapacity(totalSize); + UInt32 curBlock = 0; + for (int i = 0; i < 8; i++) + { + if (curBlock >= fork.NumBlocks) + break; + const CExtent &e = fork.Extents[i]; + if (fork.NumBlocks - curBlock < e.NumBlocks || e.Pos >= Header.NumBlocks) + return S_FALSE; + RINOK(ReadExtent(Header.BlockSizeLog, inStream, + (Byte *)buf + ((size_t)curBlock << Header.BlockSizeLog), e)); + curBlock += e.NumBlocks; + } + return S_OK; +} + +struct CNodeDescriptor +{ + UInt32 fLink; + UInt32 bLink; + Byte Kind; + Byte Height; + UInt16 NumRecords; + // UInt16 Reserved; + void Parse(const Byte *p); +}; + +void CNodeDescriptor::Parse(const Byte *p) +{ + fLink = Get32(p); + bLink = Get32(p + 4); + Kind = p[8]; + Height = p[9]; + NumRecords = Get16(p + 10); +} + +struct CHeaderRec +{ + // UInt16 TreeDepth; + // UInt32 RootNode; + // UInt32 LeafRecords; + UInt32 FirstLeafNode; + // UInt32 LastLeafNode; + int NodeSizeLog; + // UInt16 MaxKeyLength; + UInt32 TotalNodes; + // UInt32 FreeNodes; + // UInt16 Reserved1; + // UInt32 ClumpSize; + // Byte BtreeType; + // Byte KeyCompareType; + // UInt32 Attributes; + // UInt32 Reserved3[16]; + + HRESULT Parse(const Byte *p); +}; + +HRESULT CHeaderRec::Parse(const Byte *p) +{ + // TreeDepth = Get16(p); + // RootNode = Get32(p + 2); + // LeafRecords = Get32(p + 6); + FirstLeafNode = Get32(p + 0xA); + // LastLeafNode = Get32(p + 0xE); + UInt32 nodeSize = Get16(p + 0x12); + + int i; + for (i = 9; ((UInt32)1 << i) != nodeSize; i++) + if (i == 16) + return S_FALSE; + NodeSizeLog = i; + + // MaxKeyLength = Get16(p + 0x14); + TotalNodes = Get32(p + 0x16); + // FreeNodes = Get32(p + 0x1A); + // Reserved1 = Get16(p + 0x1E); + // ClumpSize = Get32(p + 0x20); + // BtreeType = p[0x24]; + // KeyCompareType = p[0x25]; + // Attributes = Get32(p + 0x26); + /* + for (int i = 0; i < 16; i++) + Reserved3[i] = Get32(p + 0x2A + i * 4); + */ + return S_OK; +} + + +enum ENodeType +{ + NODE_TYPE_LEAF = 0xFF, + NODE_TYPE_INDEX = 0, + NODE_TYPE_HEADER = 1, + NODE_TYPE_MODE = 2 +}; + +HRESULT CDatabase::LoadExtentFile(IInStream *inStream) +{ + // FileExtents.Clear(); + // ResExtents.Clear(); + + CByteBuffer extents; + RINOK(ReadFile(Header.ExtentsFile, extents, inStream)); + + const Byte *p = (const Byte *)extents; + + // CNodeDescriptor nodeDesc; + // nodeDesc.Parse(p); + CHeaderRec hr; + RINOK(hr.Parse(p + 14)); + + UInt32 node = hr.FirstLeafNode; + if (node != 0) + return S_FALSE; + /* + while (node != 0) + { + size_t nodeOffset = node * hr.NodeSize; + if ((node + 1)* hr.NodeSize > CatalogBuf.GetCapacity()) + return S_FALSE; + CNodeDescriptor desc; + desc.Parse(p + nodeOffset); + if (desc.Kind != NODE_TYPE_LEAF) + return S_FALSE; + UInt32 ptr = hr.NodeSize; + for (int i = 0; i < desc.NumRecords; i++) + { + UInt32 offs = Get16(p + nodeOffset + hr.NodeSize - (i + 1) * 2); + UInt32 offsNext = Get16(p + nodeOffset + hr.NodeSize - (i + 2) * 2); + + const Byte *r = p + nodeOffset + offs; + int keyLength = Get16(r); + Byte forkType = r[2]; + UInt32 id = Get16(r + 4); + UInt32 startBlock = Get16(r + 4); + CObjectVector<CIdExtents> *extents = (forkType == 0) ? &FileExtents : &ResExtents; + if (extents->Size() == 0) + extents->Add(CIdExtents()); + else + { + CIdExtents &e = extents->Back(); + if (e.ID != id) + { + if (e.ID > id) + return S_FALSE; + extents->Add(CIdExtents()); + } + } + CIdExtents &e = extents->Back(); + for (UInt32 k = offs + 10 + 2; k + 8 <= offsNext; k += 8) + { + CExtent ee; + ee.Pos = Get32(p + nodeOffset + k); + ee.NumBlocks = Get32(p + nodeOffset + k * 4); + e.Extents.Add(ee); + } + } + node = desc.fLink; + } + */ + return S_OK; +} + + +HRESULT CDatabase::LoadCatalog(IInStream *inStream, CProgressVirt *progress) +{ + Items.Clear(); + IdToIndexMap.ClearAndFree(); + + CByteBuffer catalogBuf; + RINOK(ReadFile(Header.CatalogFile, catalogBuf, inStream)); + const Byte *p = (const Byte *)catalogBuf; + + // CNodeDescriptor nodeDesc; + // nodeDesc.Parse(p); + CHeaderRec hr; + hr.Parse(p + 14); + + // CaseSensetive = (Header.IsHfsX() && hr.KeyCompareType == 0xBC); + + if ((catalogBuf.GetCapacity() >> hr.NodeSizeLog) < hr.TotalNodes) + return S_FALSE; + + CByteBuffer usedBuf; + usedBuf.SetCapacity(hr.TotalNodes); + for (UInt32 i = 0; i < hr.TotalNodes; i++) + usedBuf[i] = 0; + + UInt32 node = hr.FirstLeafNode; + while (node != 0) + { + if (node >= hr.TotalNodes) + return S_FALSE; + if (usedBuf[node]) + return S_FALSE; + usedBuf[node] = 1; + size_t nodeOffset = (size_t)node << hr.NodeSizeLog; + CNodeDescriptor desc; + desc.Parse(p + nodeOffset); + if (desc.Kind != NODE_TYPE_LEAF) + return S_FALSE; + for (int i = 0; i < desc.NumRecords; i++) + { + UInt32 nodeSize = (1 << hr.NodeSizeLog); + UInt32 offs = Get16(p + nodeOffset + nodeSize - (i + 1) * 2); + UInt32 offsNext = Get16(p + nodeOffset + nodeSize - (i + 2) * 2); + UInt32 recSize = offsNext - offs; + if (offsNext >= nodeSize || offsNext < offs || recSize < 6) + return S_FALSE; + + CItem item; + + const Byte *r = p + nodeOffset + offs; + UInt32 keyLength = Get16(r); + item.ParentID = Get32(r + 2); + UString name; + if (keyLength < 6 || (keyLength & 1) != 0 || keyLength + 2 > recSize) + return S_FALSE; + r += 6; + recSize -= 6; + keyLength -= 6; + + int nameLength = Get16(r); + if (nameLength * 2 != (int)keyLength) + return S_FALSE; + r += 2; + recSize -= 2; + + wchar_t *pp = name.GetBuffer(nameLength + 1); + + int j; + for (j = 0; j < nameLength; j++) + pp[j] = ((wchar_t)r[j * 2] << 8) | r[j * 2 + 1]; + pp[j] = 0; + name.ReleaseBuffer(); + r += j * 2; + recSize -= j * 2; + + if (recSize < 2) + return S_FALSE; + item.Type = Get16(r); + + if (item.Type != RECORD_TYPE_FOLDER && item.Type != RECORD_TYPE_FILE) + continue; + if (recSize < 0x58) + return S_FALSE; + + // item.Flags = Get16(r + 2); + // item.Valence = Get32(r + 4); + item.ID = Get32(r + 8); + item.CTime = Get32(r + 0xC); + item.MTime = Get32(r + 0x10); + // item.AttrMTime = Get32(r + 0x14); + item.ATime = Get32(r + 0x18); + // item.BackupDate = Get32(r + 0x1C); + + /* + item.OwnerID = Get32(r + 0x20); + item.GroupID = Get32(r + 0x24); + item.AdminFlags = r[0x28]; + item.OwnerFlags = r[0x29]; + item.FileMode = Get16(r + 0x2A); + item.special.iNodeNum = Get16(r + 0x2C); + */ + + item.Name = name; + + if (item.IsDir()) + { + CIdIndexPair pair; + pair.ID = item.ID; + pair.Index = Items.Size(); + IdToIndexMap.Add(pair); + } + else + { + CFork fd; + recSize -= 0x58; + r += 0x58; + if (recSize < 0x50 * 2) + return S_FALSE; + fd.Parse(r); + item.Size = fd.Size; + item.NumBlocks = fd.NumBlocks; + UInt32 curBlock = 0; + for (int j = 0; j < 8; j++) + { + if (curBlock >= fd.NumBlocks) + break; + const CExtent &e = fd.Extents[j]; + item.Extents.Add(e); + curBlock += e.NumBlocks; + } + } + Items.Add(item); + if (progress && Items.Size() % 100 == 0) + { + RINOK(progress->SetCompleted(Items.Size())); + } + } + node = desc.fLink; + } + IdToIndexMap.Sort(CompareIdToIndex, NULL); + return S_OK; +} + +HRESULT CDatabase::Open(IInStream *inStream, CProgressVirt *progress) +{ + static const UInt32 kHeaderSize = 1024 + 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize)); + int i; + for (i = 0; i < 1024; i++) + if (buf[i] != 0) + return S_FALSE; + const Byte *p = buf + 1024; + CVolHeader &h = Header; + + h.Header[0] = p[0]; + h.Header[1] = p[1]; + if (p[0] != 'H' || (p[1] != '+' && p[1] != 'X')) + return S_FALSE; + h.Version = Get16(p + 2); + if (h.Version < 4 || h.Version > 5) + return S_FALSE; + + // h.Attr = Get32(p + 4); + // h.LastMountedVersion = Get32(p + 8); + // h.JournalInfoBlock = Get32(p + 0xC); + + h.CTime = Get32(p + 0x10); + h.MTime = Get32(p + 0x14); + // h.BackupTime = Get32(p + 0x18); + // h.CheckedTime = Get32(p + 0x1C); + + // h.NumFiles = Get32(p + 0x20); + // h.NumFolders = Get32(p + 0x24); + + UInt32 numFiles = Get32(p + 0x20); + UInt32 numFolders = Get32(p + 0x24);; + if (progress) + { + RINOK(progress->SetTotal(numFolders + numFiles)); + } + + UInt32 blockSize = Get32(p + 0x28); + + for (i = 9; ((UInt32)1 << i) != blockSize; i++) + if (i == 31) + return S_FALSE; + h.BlockSizeLog = i; + + h.NumBlocks = Get32(p + 0x2C); + h.NumFreeBlocks = Get32(p + 0x30); + + /* + h.WriteCount = Get32(p + 0x44); + for (i = 0; i < 6; i++) + h.FinderInfo[i] = Get32(p + 0x50 + i * 4); + h.VolID = Get64(p + 0x68); + */ + + UInt64 endPos; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos)); + if ((endPos >> h.BlockSizeLog) < h.NumBlocks) + return S_FALSE; + + // h.AllocationFile.Parse(p + 0x70 + 0x50 * 0); + h.ExtentsFile.Parse( p + 0x70 + 0x50 * 1); + h.CatalogFile.Parse( p + 0x70 + 0x50 * 2); + // h.AttributesFile.Parse(p + 0x70 + 0x50 * 3); + // h.StartupFile.Parse( p + 0x70 + 0x50 * 4); + + RINOK(LoadExtentFile(inStream)); + RINOK(LoadCatalog(inStream, progress)); + + // if (Header.NumFiles + Header.NumFolders != (UInt32)Items.Size()) return S_OK; + + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.h new file mode 100644 index 000000000..c19539057 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsIn.h @@ -0,0 +1,154 @@ +// HfsIn.h + +#ifndef __ARCHIVE_HFS_IN_H +#define __ARCHIVE_HFS_IN_H + +#include "Common/MyString.h" +#include "Common/Buffer.h" + +namespace NArchive { +namespace NHfs { + +struct CExtent +{ + UInt32 Pos; + UInt32 NumBlocks; +}; + +struct CFork +{ + UInt64 Size; + // UInt32 ClumpSize; + UInt32 NumBlocks; + CExtent Extents[8]; + void Parse(const Byte *p); +}; + +struct CVolHeader +{ + Byte Header[2]; + UInt16 Version; + // UInt32 Attr; + // UInt32 LastMountedVersion; + // UInt32 JournalInfoBlock; + + UInt32 CTime; + UInt32 MTime; + // UInt32 BackupTime; + // UInt32 CheckedTime; + + // UInt32 NumFiles; + // UInt32 NumFolders; + int BlockSizeLog; + UInt32 NumBlocks; + UInt32 NumFreeBlocks; + + // UInt32 WriteCount; + // UInt32 FinderInfo[8]; + // UInt64 VolID; + + // CFork AllocationFile; + CFork ExtentsFile; + CFork CatalogFile; + // CFork AttributesFile; + // CFork StartupFile; + + bool IsHfsX() const { return Version > 4; } +}; + +inline void HfsTimeToFileTime(UInt32 hfsTime, FILETIME &ft) +{ + UInt64 v = ((UInt64)3600 * 24 * (365 * 303 + 24 * 3) + hfsTime) * 10000000; + ft.dwLowDateTime = (DWORD)v; + ft.dwHighDateTime = (DWORD)(v >> 32); +} + +enum ERecordType +{ + RECORD_TYPE_FOLDER = 1, + RECORD_TYPE_FILE = 2, + RECORD_TYPE_FOLDER_THREAD = 3, + RECORD_TYPE_FILE_THREAD = 4 +}; + +struct CItem +{ + UString Name; + + UInt32 ParentID; + + UInt16 Type; + // UInt16 Flags; + // UInt32 Valence; + UInt32 ID; + UInt32 CTime; + UInt32 MTime; + // UInt32 AttrMTime; + UInt32 ATime; + // UInt32 BackupDate; + + /* + UInt32 OwnerID; + UInt32 GroupID; + Byte AdminFlags; + Byte OwnerFlags; + UInt16 FileMode; + union + { + UInt32 iNodeNum; + UInt32 LinkCount; + UInt32 RawDevice; + } special; + */ + + UInt64 Size; + UInt32 NumBlocks; + CRecordVector<CExtent> Extents; + + bool IsDir() const { return Type == RECORD_TYPE_FOLDER; } + CItem(): Size(0), NumBlocks(0) {} +}; + +struct CIdIndexPair +{ + UInt32 ID; + int Index; +}; + +struct CProgressVirt +{ + virtual HRESULT SetTotal(UInt64 numFiles) PURE; + virtual HRESULT SetCompleted(UInt64 numFiles) PURE; +}; + +class CDatabase +{ + // CObjectVector<CIdExtents> FileExtents; + // CObjectVector<CIdExtents> ResExtents; + CRecordVector<CIdIndexPair> IdToIndexMap; + + HRESULT LoadExtentFile(IInStream *inStream); + HRESULT LoadCatalog(IInStream *inStream, CProgressVirt *progress); + + HRESULT ReadFile(const CFork &fork, CByteBuffer &buf, IInStream *inStream); +public: + CVolHeader Header; + CObjectVector<CItem> Items; + // bool CaseSensetive; + + void Clear() + { + // CaseSensetive = false; + Items.Clear(); + // FileExtents.Clear(); + // ResExtents.Clear(); + IdToIndexMap.Clear(); + } + + UString GetItemPath(int index) const; + HRESULT Open(IInStream *inStream, CProgressVirt *progress); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsRegister.cpp new file mode 100644 index 000000000..51c3c2b15 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Hfs/HfsRegister.cpp @@ -0,0 +1,13 @@ +// HfsRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "HfsHandler.h" +static IInArchive *CreateArc() { return new NArchive::NHfs::CHandler; } + +static CArcInfo g_ArcInfo = + { L"HFS", L"hfs", 0, 0xE3, { 'H', '+', 0, 4 }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Hfs) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h b/src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h new file mode 100644 index 000000000..853202767 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h @@ -0,0 +1,234 @@ +// IArchive.h + +#ifndef __IARCHIVE_H +#define __IARCHIVE_H + +#include "../IProgress.h" +#include "../IStream.h" +#include "../PropID.h" + +#define ARCHIVE_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 6, x) +#define ARCHIVE_INTERFACE(i, x) ARCHIVE_INTERFACE_SUB(i, IUnknown, x) + +namespace NFileTimeType +{ + enum EEnum + { + kWindows, + kUnix, + kDOS + }; +} + +namespace NArchive +{ + enum + { + kName = 0, + kClassID, + kExtension, + kAddExtension, + kUpdate, + kKeepName, + kStartSignature, + kFinishSignature, + kAssociate + }; + + namespace NExtract + { + namespace NAskMode + { + enum + { + kExtract = 0, + kTest, + kSkip + }; + } + namespace NOperationResult + { + enum + { + kOK = 0, + kUnSupportedMethod, + kDataError, + kCRCError + }; + } + } + namespace NUpdate + { + namespace NOperationResult + { + enum + { + kOK = 0, + kError + }; + } + } +} + +#define INTERFACE_IArchiveOpenCallback(x) \ + STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes) x; \ + STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes) x; \ + +ARCHIVE_INTERFACE(IArchiveOpenCallback, 0x10) +{ + INTERFACE_IArchiveOpenCallback(PURE); +}; + + +#define INTERFACE_IArchiveExtractCallback(x) \ + INTERFACE_IProgress(x) \ + STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) x; \ + STDMETHOD(PrepareOperation)(Int32 askExtractMode) x; \ + STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) x; \ + +ARCHIVE_INTERFACE_SUB(IArchiveExtractCallback, IProgress, 0x20) +{ + INTERFACE_IArchiveExtractCallback(PURE) +}; + + +#define INTERFACE_IArchiveOpenVolumeCallback(x) \ + STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) x; \ + +ARCHIVE_INTERFACE(IArchiveOpenVolumeCallback, 0x30) +{ + INTERFACE_IArchiveOpenVolumeCallback(PURE); +}; + + +ARCHIVE_INTERFACE(IInArchiveGetStream, 0x40) +{ + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) PURE; +}; + + +ARCHIVE_INTERFACE(IArchiveOpenSetSubArchiveName, 0x50) +{ + STDMETHOD(SetSubArchiveName)(const wchar_t *name) PURE; +}; + + +/* +IInArchive::Extract: + indices must be sorted + numItems = 0xFFFFFFFF means "all files" + testMode != 0 means "test files without writing to outStream" +*/ + +#define INTERFACE_IInArchive(x) \ + STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) x; \ + STDMETHOD(Close)() x; \ + STDMETHOD(GetNumberOfItems)(UInt32 *numItems) x; \ + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) x; \ + STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties) x; \ + STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x; \ + STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties) x; \ + STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x; + +ARCHIVE_INTERFACE(IInArchive, 0x60) +{ + INTERFACE_IInArchive(PURE) +}; + +ARCHIVE_INTERFACE(IArchiveOpenSeq, 0x61) +{ + STDMETHOD(OpenSeq)(ISequentialInStream *stream) PURE; +}; + +#define INTERFACE_IArchiveUpdateCallback(x) \ + INTERFACE_IProgress(x); \ + STDMETHOD(GetUpdateItemInfo)(UInt32 index, \ + Int32 *newData, /*1 - new data, 0 - old data */ \ + Int32 *newProperties, /* 1 - new properties, 0 - old properties */ \ + UInt32 *indexInArchive /* -1 if there is no in archive, or if doesn't matter */ \ + ) x; \ + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \ + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream) x; \ + STDMETHOD(SetOperationResult)(Int32 operationResult) x; \ + +ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback, IProgress, 0x80) +{ + INTERFACE_IArchiveUpdateCallback(PURE); +}; + +#define INTERFACE_IArchiveUpdateCallback2(x) \ + INTERFACE_IArchiveUpdateCallback(x) \ + STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size) x; \ + STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream) x; \ + +ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82) +{ + INTERFACE_IArchiveUpdateCallback2(PURE); +}; + + +#define INTERFACE_IOutArchive(x) \ + STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) x; \ + STDMETHOD(GetFileTimeType)(UInt32 *type) x; + +ARCHIVE_INTERFACE(IOutArchive, 0xA0) +{ + INTERFACE_IOutArchive(PURE) +}; + + +ARCHIVE_INTERFACE(ISetProperties, 0x03) +{ + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) PURE; +}; + + +#define IMP_IInArchive_GetProp(k) \ + (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ + { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \ + const STATPROPSTG &srcItem = k[index]; \ + *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; } \ + +#define IMP_IInArchive_GetProp_WITH_NAME(k) \ + (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \ + { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \ + const STATPROPSTG &srcItem = k[index]; \ + *propID = srcItem.propid; *varType = srcItem.vt; \ + if (srcItem.lpwstrName == 0) *name = 0; else *name = ::SysAllocString(srcItem.lpwstrName); return S_OK; } \ + +#define IMP_IInArchive_Props \ + STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp(kProps) + +#define IMP_IInArchive_Props_WITH_NAME \ + STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kProps) + + +#define IMP_IInArchive_ArcProps \ + STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kArcProps) / sizeof(kArcProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp(kArcProps) + +#define IMP_IInArchive_ArcProps_WITH_NAME \ + STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \ + { *numProperties = sizeof(kArcProps) / sizeof(kArcProps[0]); return S_OK; } \ + STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kArcProps) + +#define IMP_IInArchive_ArcProps_NO_Table \ + STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \ + { *numProperties = 0; return S_OK; } \ + STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *) \ + { return E_NOTIMPL; } \ + +#define IMP_IInArchive_ArcProps_NO \ + IMP_IInArchive_ArcProps_NO_Table \ + STDMETHODIMP CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value) \ + { value->vt = VT_EMPTY; return S_OK; } + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.cpp new file mode 100644 index 000000000..7a9f1a458 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.cpp @@ -0,0 +1,326 @@ +// IsoHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "../Common/ItemNameUtils.h" + +#include "IsoHandler.h" + +using namespace NWindows; +using namespace NTime; + +namespace NArchive { +namespace NIso { + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidMTime, VT_FILETIME} + // { NULL, kpidPhySize, VT_UI8}, + // { NULL, kpidHeadersSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + // try + { + if (_archive.Open(stream) != S_OK) + return S_FALSE; + _stream = stream; + } + // catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _archive.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _archive.Refs.Size() + _archive.BootEntries.Size(); + return S_OK; +} + +static void AddString(AString &s, const char *name, const Byte *p, int size) +{ + int i; + for (i = 0; i < size && p[i]; i++); + for (; i > 0 && p[i - 1] == ' '; i--); + if (i != 0) + { + AString d; + memcpy(d.GetBuffer(i), p, i); + d.ReleaseBuffer(i); + s += '\n'; + s += name; + s += ": "; + s += d; + } +} + +#define ADD_STRING(n, V) AddString(s, n, vol. V, sizeof(vol. V)) + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CVolumeDescriptor &vol = _archive.VolDescs[_archive.MainVolDescIndex]; + switch(propID) + { + case kpidComment: + { + AString s; + ADD_STRING("System", SystemId); + ADD_STRING("Volume", VolumeId); + ADD_STRING("VolumeSet", VolumeSetId); + ADD_STRING("Publisher", PublisherId); + ADD_STRING("Preparer", DataPreparerId); + ADD_STRING("Application", ApplicationId); + ADD_STRING("Copyright", CopyrightFileId); + ADD_STRING("Abstract", AbstractFileId); + ADD_STRING("Bib", BibFileId); + prop = s; + break; + } + case kpidCTime: { FILETIME utc; if (vol.CTime.GetFileTime(utc)) prop = utc; break; } + case kpidMTime: { FILETIME utc; if (vol.MTime.GetFileTime(utc)) prop = utc; break; } + // case kpidPhySize: break; + // case kpidHeadersSize: break; + case kpidError: if (_archive.IncorrectBigEndian) prop = "Incorrect big-endian headers"; break; + } + 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; + if (index >= (UInt32)_archive.Refs.Size()) + { + index -= _archive.Refs.Size(); + const CBootInitialEntry &be = _archive.BootEntries[index]; + switch(propID) + { + case kpidPath: + { + // wchar_t name[32]; + // ConvertUInt64ToString(index + 1, name); + UString s = L"[BOOT]" WSTRING_PATH_SEPARATOR; + // s += name; + // s += L"-"; + s += be.GetName(); + prop = (const wchar_t *)s; + break; + } + case kpidIsDir: prop = false; break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)_archive.GetBootItemSize(index); + break; + } + } + else + { + const CRef &ref = _archive.Refs[index]; + const CDir &item = ref.Dir->_subItems[ref.Index]; + switch(propID) + { + case kpidPath: + // if (item.FileId.GetCapacity() >= 0) + { + UString s; + if (_archive.IsJoliet()) + s = item.GetPathU(); + else + s = MultiByteToUnicodeString(item.GetPath(_archive.IsSusp, _archive.SuspSkipSize), CP_OEMCP); + + int pos = s.ReverseFind(L';'); + if (pos >= 0 && pos == s.Length() - 2) + if (s[s.Length() - 1] == L'1') + s = s.Left(pos); + if (!s.IsEmpty()) + if (s[s.Length() - 1] == L'.') + s = s.Left(s.Length() - 1); + prop = (const wchar_t *)NItemName::GetOSName2(s); + } + break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: + case kpidPackSize: + if (!item.IsDir()) + prop = (UInt64)item.DataLength; + break; + case kpidMTime: + { + FILETIME utc; + if (item.DateTime.GetFileTime(utc)) + prop = utc; + break; + } + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _archive.Refs.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for(i = 0; i < numItems; i++) + { + UInt32 index = (allFilesMode ? i : indices[i]); + if (index < (UInt32)_archive.Refs.Size()) + { + const CRef &ref = _archive.Refs[index]; + const CDir &item = ref.Dir->_subItems[ref.Index]; + totalSize += item.DataLength; + } + else + { + totalSize += _archive.GetBootItemSize(index - _archive.Refs.Size()); + } + } + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + currentItemSize = 0; + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + UInt64 blockIndex; + if (index < (UInt32)_archive.Refs.Size()) + { + const CRef &ref = _archive.Refs[index]; + const CDir &item = ref.Dir->_subItems[ref.Index]; + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + currentItemSize = item.DataLength; + blockIndex = item.ExtentLocation; + } + else + { + int bootIndex = index - _archive.Refs.Size(); + const CBootInitialEntry &be = _archive.BootEntries[bootIndex]; + currentItemSize = _archive.GetBootItemSize(bootIndex); + blockIndex = be.LoadRBA; + } + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(currentItemSize); + RINOK(_stream->Seek(blockIndex * _archive.BlockSize, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(outStreamSpec->IsFinishedOK() ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + UInt64 blockIndex; + UInt64 currentItemSize; + if (index < (UInt32)_archive.Refs.Size()) + { + const CRef &ref = _archive.Refs[index]; + const CDir &item = ref.Dir->_subItems[ref.Index]; + if (item.IsDir()) + return S_FALSE; + currentItemSize = item.DataLength; + blockIndex = item.ExtentLocation; + } + else + { + int bootIndex = index - _archive.Refs.Size(); + const CBootInitialEntry &be = _archive.BootEntries[bootIndex]; + currentItemSize = _archive.GetBootItemSize(bootIndex); + blockIndex = be.LoadRBA; + } + return CreateLimitedInStream(_stream, blockIndex * _archive.BlockSize, currentItemSize, stream); + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.h new file mode 100644 index 000000000..1dcade8f9 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHandler.h @@ -0,0 +1,30 @@ +// IsoHandler.h + +#ifndef __ISO_HANDLER_H +#define __ISO_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" + +#include "IsoIn.h" +#include "IsoItem.h" + +namespace NArchive { +namespace NIso { + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CInArchive _archive; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.cpp new file mode 100644 index 000000000..b3e418bbc --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.cpp @@ -0,0 +1,21 @@ +// Archive/Iso/Header.h + +#include "StdAfx.h" + +#include "IsoHeader.h" + +namespace NArchive { +namespace NIso { + +const char *kElToritoSpec = "EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0"; + +const wchar_t *kMediaTypes[5] = +{ + L"NoEmulation", + L"1.2M", + L"1.44M", + L"2.88M", + L"HardDisk" +}; + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.h new file mode 100644 index 000000000..9702d70ae --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoHeader.h @@ -0,0 +1,61 @@ +// Archive/IsoHeader.h + +#ifndef __ARCHIVE_ISO_HEADER_H +#define __ARCHIVE_ISO_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NIso { + +namespace NVolDescType +{ + const Byte kBootRecord = 0; + const Byte kPrimaryVol = 1; + const Byte kSupplementaryVol = 2; + const Byte kVolParttition = 3; + const Byte kTerminator = 255; +} + +const Byte kVersion = 1; + +namespace NFileFlags +{ + const Byte kDirectory = 1 << 1; +} + +extern const char *kElToritoSpec; + +const UInt32 kStartPos = 0x8000; + +namespace NBootEntryId +{ + const Byte kValidationEntry = 1; + const Byte kInitialEntryNotBootable = 0; + const Byte kInitialEntryBootable = 0x88; +} + +namespace NBootPlatformId +{ + const Byte kX86 = 0; + const Byte kPowerPC = 1; + const Byte kMac = 2; +} + +const BYTE kBootMediaTypeMask = 0xF; + +namespace NBootMediaType +{ + const Byte kNoEmulation = 0; + const Byte k1d2Floppy = 1; + const Byte k1d44Floppy = 2; + const Byte k2d88Floppy = 3; + const Byte kHardDisk = 4; +} + +const int kNumBootMediaTypes = 5; +extern const wchar_t *kMediaTypes[]; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.cpp new file mode 100644 index 000000000..7ed618d29 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.cpp @@ -0,0 +1,453 @@ +// Archive/IsoIn.cpp + +#include "StdAfx.h" + +#include "IsoIn.h" + +#include "../../Common/StreamUtils.h" + +namespace NArchive { +namespace NIso { + +Byte CInArchive::ReadByte() +{ + if (m_BufferPos >= BlockSize) + m_BufferPos = 0; + if (m_BufferPos == 0) + { + size_t processedSize = BlockSize; + if (ReadStream(_stream, m_Buffer, &processedSize) != S_OK) + throw 1; + if (processedSize != BlockSize) + throw 1; + } + Byte b = m_Buffer[m_BufferPos++]; + _position++; + return b; +} + +void CInArchive::ReadBytes(Byte *data, UInt32 size) +{ + for (UInt32 i = 0; i < size; i++) + data[i] = ReadByte(); +} + +void CInArchive::Skip(size_t size) +{ + while (size-- != 0) + ReadByte(); +} + +void CInArchive::SkipZeros(size_t size) +{ + while (size-- != 0) + { + Byte b = ReadByte(); + if (b != 0) + throw 1; + } +} + +UInt16 CInArchive::ReadUInt16Spec() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + value |= ((UInt16)(ReadByte()) << (8 * i)); + return value; +} + + +UInt16 CInArchive::ReadUInt16() +{ + Byte b[4]; + ReadBytes(b, 4); + UInt32 value = 0; + for (int i = 0; i < 2; i++) + { + if (b[i] != b[3 - i]) + IncorrectBigEndian = true; + value |= ((UInt16)(b[i]) << (8 * i)); + } + return (UInt16)value; +} + +UInt32 CInArchive::ReadUInt32Le() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + value |= ((UInt32)(ReadByte()) << (8 * i)); + return value; +} + +UInt32 CInArchive::ReadUInt32Be() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + value <<= 8; + value |= ReadByte(); + } + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + Byte b[8]; + ReadBytes(b, 8); + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + if (b[i] != b[7 - i]) + throw 1; + value |= ((UInt32)(b[i]) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::ReadDigits(int numDigits) +{ + UInt32 res = 0; + for (int i = 0; i < numDigits; i++) + { + Byte b = ReadByte(); + if (b < '0' || b > '9') + { + if (b == 0) // it's bug in some CD's + b = '0'; + else + throw 1; + } + UInt32 d = (UInt32)(b - '0'); + res *= 10; + res += d; + } + return res; +} + +void CInArchive::ReadDateTime(CDateTime &d) +{ + d.Year = (UInt16)ReadDigits(4); + d.Month = (Byte)ReadDigits(2); + d.Day = (Byte)ReadDigits(2); + d.Hour = (Byte)ReadDigits(2); + d.Minute = (Byte)ReadDigits(2); + d.Second = (Byte)ReadDigits(2); + d.Hundredths = (Byte)ReadDigits(2); + d.GmtOffset = (signed char)ReadByte(); +} + +void CInArchive::ReadBootRecordDescriptor(CBootRecordDescriptor &d) +{ + ReadBytes(d.BootSystemId, sizeof(d.BootSystemId)); + ReadBytes(d.BootId, sizeof(d.BootId)); + ReadBytes(d.BootSystemUse, sizeof(d.BootSystemUse)); +} + +void CInArchive::ReadRecordingDateTime(CRecordingDateTime &t) +{ + t.Year = ReadByte(); + t.Month = ReadByte(); + t.Day = ReadByte(); + t.Hour = ReadByte(); + t.Minute = ReadByte(); + t.Second = ReadByte(); + t.GmtOffset = (signed char)ReadByte(); +} + +void CInArchive::ReadDirRecord2(CDirRecord &r, Byte len) +{ + r.ExtendedAttributeRecordLen = ReadByte(); + if (r.ExtendedAttributeRecordLen != 0) + throw 1; + r.ExtentLocation = ReadUInt32(); + r.DataLength = ReadUInt32(); + ReadRecordingDateTime(r.DateTime); + r.FileFlags = ReadByte(); + r.FileUnitSize = ReadByte(); + r.InterleaveGapSize = ReadByte(); + r.VolSequenceNumber = ReadUInt16(); + Byte idLen = ReadByte(); + r.FileId.SetCapacity(idLen); + ReadBytes((Byte *)r.FileId, idLen); + int padSize = 1 - (idLen & 1); + + // SkipZeros(1 - (idLen & 1)); + Skip(1 - (idLen & 1)); // it's bug in some cd's. Must be zeros + + int curPos = 33 + idLen + padSize; + if (curPos > len) + throw 1; + int rem = len - curPos; + r.SystemUse.SetCapacity(rem); + ReadBytes((Byte *)r.SystemUse, rem); +} + +void CInArchive::ReadDirRecord(CDirRecord &r) +{ + Byte len = ReadByte(); + // Some CDs can have incorrect value len = 48 ('0') in VolumeDescriptor. + // But maybe we must use real "len" for other records. + len = 34; + ReadDirRecord2(r, len); +} + +void CInArchive::ReadVolumeDescriptor(CVolumeDescriptor &d) +{ + d.VolFlags = ReadByte(); + ReadBytes(d.SystemId, sizeof(d.SystemId)); + ReadBytes(d.VolumeId, sizeof(d.VolumeId)); + SkipZeros(8); + d.VolumeSpaceSize = ReadUInt32(); + ReadBytes(d.EscapeSequence, sizeof(d.EscapeSequence)); + d.VolumeSetSize = ReadUInt16(); + d.VolumeSequenceNumber = ReadUInt16(); + d.LogicalBlockSize = ReadUInt16(); + d.PathTableSize = ReadUInt32(); + d.LPathTableLocation = ReadUInt32Le(); + d.LOptionalPathTableLocation = ReadUInt32Le(); + d.MPathTableLocation = ReadUInt32Be(); + d.MOptionalPathTableLocation = ReadUInt32Be(); + ReadDirRecord(d.RootDirRecord); + ReadBytes(d.VolumeSetId, sizeof(d.VolumeSetId)); + ReadBytes(d.PublisherId, sizeof(d.PublisherId)); + ReadBytes(d.DataPreparerId, sizeof(d.DataPreparerId)); + ReadBytes(d.ApplicationId, sizeof(d.ApplicationId)); + ReadBytes(d.CopyrightFileId, sizeof(d.CopyrightFileId)); + ReadBytes(d.AbstractFileId, sizeof(d.AbstractFileId)); + ReadBytes(d.BibFileId, sizeof(d.BibFileId)); + ReadDateTime(d.CTime); + ReadDateTime(d.MTime); + ReadDateTime(d.ExpirationTime); + ReadDateTime(d.EffectiveTime); + d.FileStructureVersion = ReadByte(); // = 1 + SkipZeros(1); + ReadBytes(d.ApplicationUse, sizeof(d.ApplicationUse)); + SkipZeros(653); +} + +static const Byte kSig_CD001[5] = { 'C', 'D', '0', '0', '1' }; + +static const Byte kSig_NSR02[5] = { 'N', 'S', 'R', '0', '2' }; +static const Byte kSig_NSR03[5] = { 'N', 'S', 'R', '0', '3' }; +static const Byte kSig_BEA01[5] = { 'B', 'E', 'A', '0', '1' }; +static const Byte kSig_TEA01[5] = { 'T', 'E', 'A', '0', '1' }; + +static inline bool CheckSignature(const Byte *sig, const Byte *data) +{ + for (int i = 0; i < 5; i++) + if (sig[i] != data[i]) + return false; + return true; +} + +void CInArchive::SeekToBlock(UInt32 blockIndex) +{ + if (_stream->Seek((UInt64)blockIndex * VolDescs[MainVolDescIndex].LogicalBlockSize, STREAM_SEEK_SET, &_position) != S_OK) + throw 1; + m_BufferPos = 0; +} + +void CInArchive::ReadDir(CDir &d, int level) +{ + if (!d.IsDir()) + return; + SeekToBlock(d.ExtentLocation); + UInt64 startPos = _position; + + bool firstItem = true; + for (;;) + { + UInt64 offset = _position - startPos; + if (offset >= d.DataLength) + break; + Byte len = ReadByte(); + if (len == 0) + continue; + CDir subItem; + ReadDirRecord2(subItem, len); + if (firstItem && level == 0) + IsSusp = subItem.CheckSusp(SuspSkipSize); + + if (!subItem.IsSystemItem()) + d._subItems.Add(subItem); + + firstItem = false; + } + for (int i = 0; i < d._subItems.Size(); i++) + ReadDir(d._subItems[i], level + 1); +} + +void CInArchive::CreateRefs(CDir &d) +{ + if (!d.IsDir()) + return; + for (int i = 0; i < d._subItems.Size(); i++) + { + CRef ref; + CDir &subItem = d._subItems[i]; + subItem.Parent = &d; + ref.Dir = &d; + ref.Index = i; + Refs.Add(ref); + CreateRefs(subItem); + } +} + +void CInArchive::ReadBootInfo() +{ + if (!_bootIsDefined) + return; + if (memcmp(_bootDesc.BootSystemId, kElToritoSpec, sizeof(_bootDesc.BootSystemId)) != 0) + return; + + const Byte *p = (const Byte *)_bootDesc.BootSystemUse; + UInt32 blockIndex = p[0] | ((UInt32)p[1] << 8) | ((UInt32)p[2] << 16) | ((UInt32)p[3] << 24); + SeekToBlock(blockIndex); + Byte b = ReadByte(); + if (b != NBootEntryId::kValidationEntry) + return; + { + CBootValidationEntry e; + e.PlatformId = ReadByte(); + if (ReadUInt16Spec() != 0) + throw 1; + ReadBytes(e.Id, sizeof(e.Id)); + /* UInt16 checkSum = */ ReadUInt16Spec(); + if (ReadByte() != 0x55) + throw 1; + if (ReadByte() != 0xAA) + throw 1; + } + b = ReadByte(); + if (b == NBootEntryId::kInitialEntryBootable || b == NBootEntryId::kInitialEntryNotBootable) + { + CBootInitialEntry e; + e.Bootable = (b == NBootEntryId::kInitialEntryBootable); + e.BootMediaType = ReadByte(); + e.LoadSegment = ReadUInt16Spec(); + e.SystemType = ReadByte(); + if (ReadByte() != 0) + throw 1; + e.SectorCount = ReadUInt16Spec(); + e.LoadRBA = ReadUInt32Le(); + if (ReadByte() != 0) + throw 1; + BootEntries.Add(e); + } + else + return; +} + +HRESULT CInArchive::Open2() +{ + Clear(); + RINOK(_stream->Seek(kStartPos, STREAM_SEEK_CUR, &_position)); + + m_BufferPos = 0; + BlockSize = kBlockSize; + for (;;) + { + Byte sig[7]; + ReadBytes(sig, 7); + Byte ver = sig[6]; + if (!CheckSignature(kSig_CD001, sig + 1)) + { + return S_FALSE; + /* + if (sig[0] != 0 || ver != 1) + break; + if (CheckSignature(kSig_BEA01, sig + 1)) + { + } + else if (CheckSignature(kSig_TEA01, sig + 1)) + { + break; + } + else if (CheckSignature(kSig_NSR02, sig + 1)) + { + } + else + break; + SkipZeros(0x800 - 7); + continue; + */ + } + // version = 2 for ISO 9660:1999? + if (ver > 2) + throw S_FALSE; + + if (sig[0] == NVolDescType::kTerminator) + { + break; + // Skip(0x800 - 7); + // continue; + } + switch(sig[0]) + { + case NVolDescType::kBootRecord: + { + _bootIsDefined = true; + ReadBootRecordDescriptor(_bootDesc); + break; + } + case NVolDescType::kPrimaryVol: + case NVolDescType::kSupplementaryVol: + { + // some ISOs have two PrimaryVols. + CVolumeDescriptor vd; + ReadVolumeDescriptor(vd); + if (sig[0] == NVolDescType::kPrimaryVol) + { + // some burners write "Joliet" Escape Sequence to primary volume + memset(vd.EscapeSequence, 0, sizeof(vd.EscapeSequence)); + } + VolDescs.Add(vd); + break; + } + default: + break; + } + } + if (VolDescs.IsEmpty()) + return S_FALSE; + for (MainVolDescIndex = VolDescs.Size() - 1; MainVolDescIndex > 0; MainVolDescIndex--) + if (VolDescs[MainVolDescIndex].IsJoliet()) + break; + // MainVolDescIndex = 0; // to read primary volume + const CVolumeDescriptor &vd = VolDescs[MainVolDescIndex]; + if (vd.LogicalBlockSize != kBlockSize) + return S_FALSE; + (CDirRecord &)_rootDir = vd.RootDirRecord; + ReadDir(_rootDir, 0); + CreateRefs(_rootDir); + ReadBootInfo(); + return S_OK; +} + +HRESULT CInArchive::Open(IInStream *inStream) +{ + _stream = inStream; + UInt64 pos; + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &pos)); + RINOK(_stream->Seek(0, STREAM_SEEK_END, &_archiveSize)); + RINOK(_stream->Seek(pos, STREAM_SEEK_SET, &_position)); + HRESULT res = S_FALSE; + try { res = Open2(); } + catch(...) { Clear(); res = S_FALSE; } + _stream.Release(); + return res; +} + +void CInArchive::Clear() +{ + IncorrectBigEndian = false; + Refs.Clear(); + _rootDir.Clear(); + VolDescs.Clear(); + _bootIsDefined = false; + BootEntries.Clear(); + SuspSkipSize = 0; + IsSusp = false; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.h new file mode 100644 index 000000000..f9c6f6403 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoIn.h @@ -0,0 +1,315 @@ +// Archive/IsoIn.h + +#ifndef __ARCHIVE_ISO_IN_H +#define __ARCHIVE_ISO_IN_H + +#include "Common/IntToString.h" +#include "Common/MyCom.h" + +#include "../../IStream.h" + +#include "IsoHeader.h" +#include "IsoItem.h" + +namespace NArchive { +namespace NIso { + +struct CDir: public CDirRecord +{ + CDir *Parent; + CObjectVector<CDir> _subItems; + + void Clear() + { + Parent = 0; + _subItems.Clear(); + } + + int GetLength(bool checkSusp, int skipSize) const + { + int len = GetLengthCur(checkSusp, skipSize); + if (Parent != 0) + if (Parent->Parent != 0) + len += 1 + Parent->GetLength(checkSusp, skipSize); + return len; + } + + int GetLengthU() const + { + int len = (int)(FileId.GetCapacity() / 2); + if (Parent != 0) + if (Parent->Parent != 0) + len += 1 + Parent->GetLengthU(); + return len; + } + + AString GetPath(bool checkSusp, int skipSize) const + { + AString s; + int len = GetLength(checkSusp, skipSize); + char *p = s.GetBuffer(len + 1); + p += len; + *p = 0; + const CDir *cur = this; + for (;;) + { + int curLen = cur->GetLengthCur(checkSusp, skipSize); + p -= curLen; + memmove(p, (const char *)(const Byte *)cur->GetNameCur(checkSusp, skipSize), curLen); + cur = cur->Parent; + if (cur == 0) + break; + if (cur->Parent == 0) + break; + p--; + *p = CHAR_PATH_SEPARATOR; + } + s.ReleaseBuffer(); + return s; + } + + UString GetPathU() const + { + UString s; + int len = GetLengthU(); + wchar_t *p = s.GetBuffer(len + 1); + p += len; + *p = 0; + const CDir *cur = this; + for (;;) + { + int curLen = (int)(cur->FileId.GetCapacity() / 2); + p -= curLen; + for (int i = 0; i < curLen; i++) + { + Byte b0 = ((const Byte *)cur->FileId)[i * 2]; + Byte b1 = ((const Byte *)cur->FileId)[i * 2 + 1]; + p[i] = (wchar_t)(((wchar_t)b0 << 8) | b1); + } + cur = cur->Parent; + if (cur == 0) + break; + if (cur->Parent == 0) + break; + p--; + *p = WCHAR_PATH_SEPARATOR; + } + s.ReleaseBuffer(); + return s; + } +}; + +struct CDateTime +{ + UInt16 Year; + Byte Month; + Byte Day; + Byte Hour; + Byte Minute; + Byte Second; + Byte Hundredths; + signed char GmtOffset; // min intervals from -48 (West) to +52 (East) recorded. + bool NotSpecified() const { return Year == 0 && Month == 0 && Day == 0 && + Hour == 0 && Minute == 0 && Second == 0 && GmtOffset == 0; } + + bool GetFileTime(FILETIME &ft) const + { + UInt64 value; + bool res = NWindows::NTime::GetSecondsSince1601(Year, Month, Day, Hour, Minute, Second, value); + if (res) + { + value -= (UInt64)((Int64)GmtOffset * 15 * 60); + value *= 10000000; + } + ft.dwLowDateTime = (DWORD)value; + ft.dwHighDateTime = (DWORD)(value >> 32); + return res; + } +}; + +struct CBootRecordDescriptor +{ + Byte BootSystemId[32]; // a-characters + Byte BootId[32]; // a-characters + Byte BootSystemUse[1977]; +}; + +struct CBootValidationEntry +{ + Byte PlatformId; + Byte Id[24]; // to identify the manufacturer/developer of the CD-ROM. +}; + +struct CBootInitialEntry +{ + bool Bootable; + Byte BootMediaType; + UInt16 LoadSegment; + /* This is the load segment for the initial boot image. If this + value is 0 the system will use the traditional segment of 7C0. If this value + is non-zero the system will use the specified segment. This applies to x86 + architectures only. For "flat" model architectures (such as Motorola) this + is the address divided by 10. */ + Byte SystemType; // This must be a copy of byte 5 (System Type) from the + // Partition Table found in the boot image. + UInt16 SectorCount; // This is the number of virtual/emulated sectors the system + // will store at Load Segment during the initial boot procedure. + UInt32 LoadRBA; // This is the start address of the virtual disk. CD’s use + // Relative/Logical block addressing. + + UInt64 GetSize() const + { + // if (BootMediaType == NBootMediaType::k1d44Floppy) (1440 << 10); + return SectorCount * 512; + } + + UString GetName() const + { + UString s; + if (Bootable) + s += L"Bootable"; + else + s += L"NotBootable"; + s += L"_"; + if (BootMediaType >= kNumBootMediaTypes) + { + wchar_t name[16]; + ConvertUInt32ToString(BootMediaType, name); + s += name; + } + else + s += kMediaTypes[BootMediaType]; + s += L".img"; + return s; + } +}; + +struct CVolumeDescriptor +{ + Byte VolFlags; + Byte SystemId[32]; // a-characters. An identification of a system + // which can recognize and act upon the content of the Logical + // Sectors with logical Sector Numbers 0 to 15 of the volume. + Byte VolumeId[32]; // d-characters. An identification of the volume. + UInt32 VolumeSpaceSize; // the number of Logical Blocks in which the Volume Space of the volume is recorded + Byte EscapeSequence[32]; + UInt16 VolumeSetSize; + UInt16 VolumeSequenceNumber; // the ordinal number of the volume in the Volume Set of which the volume is a member. + UInt16 LogicalBlockSize; + UInt32 PathTableSize; + UInt32 LPathTableLocation; + UInt32 LOptionalPathTableLocation; + UInt32 MPathTableLocation; + UInt32 MOptionalPathTableLocation; + CDirRecord RootDirRecord; + Byte VolumeSetId[128]; + Byte PublisherId[128]; + Byte DataPreparerId[128]; + Byte ApplicationId[128]; + Byte CopyrightFileId[37]; + Byte AbstractFileId[37]; + Byte BibFileId[37]; + CDateTime CTime; + CDateTime MTime; + CDateTime ExpirationTime; + CDateTime EffectiveTime; + Byte FileStructureVersion; // = 1; + Byte ApplicationUse[512]; + + bool IsJoliet() const + { + if ((VolFlags & 1) != 0) + return false; + Byte b = EscapeSequence[2]; + return (EscapeSequence[0] == 0x25 && EscapeSequence[1] == 0x2F && + (b == 0x40 || b == 0x43 || b == 0x45)); + } +}; + +struct CRef +{ + CDir *Dir; + UInt32 Index; +}; + +const UInt32 kBlockSize = 1 << 11; + +class CInArchive +{ + CMyComPtr<IInStream> _stream; + UInt64 _position; + + Byte m_Buffer[kBlockSize]; + UInt32 m_BufferPos; + + CDir _rootDir; + bool _bootIsDefined; + CBootRecordDescriptor _bootDesc; + + void Skip(size_t size); + void SkipZeros(size_t size); + Byte ReadByte(); + void ReadBytes(Byte *data, UInt32 size); + UInt16 ReadUInt16Spec(); + UInt16 ReadUInt16(); + UInt32 ReadUInt32Le(); + UInt32 ReadUInt32Be(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + UInt32 ReadDigits(int numDigits); + void ReadDateTime(CDateTime &d); + void ReadRecordingDateTime(CRecordingDateTime &t); + void ReadDirRecord2(CDirRecord &r, Byte len); + void ReadDirRecord(CDirRecord &r); + + void ReadBootRecordDescriptor(CBootRecordDescriptor &d); + void ReadVolumeDescriptor(CVolumeDescriptor &d); + + void SeekToBlock(UInt32 blockIndex); + void ReadDir(CDir &d, int level); + void CreateRefs(CDir &d); + + void ReadBootInfo(); + HRESULT Open2(); +public: + HRESULT Open(IInStream *inStream); + void Clear(); + + UInt64 _archiveSize; + + CRecordVector<CRef> Refs; + CObjectVector<CVolumeDescriptor> VolDescs; + int MainVolDescIndex; + UInt32 BlockSize; + CObjectVector<CBootInitialEntry> BootEntries; + bool IncorrectBigEndian; + + + bool IsJoliet() const { return VolDescs[MainVolDescIndex].IsJoliet(); } + + UInt64 GetBootItemSize(int index) const + { + const CBootInitialEntry &be = BootEntries[index]; + UInt64 size = be.GetSize(); + if (be.BootMediaType == NBootMediaType::k1d2Floppy) + size = (1200 << 10); + else if (be.BootMediaType == NBootMediaType::k1d44Floppy) + size = (1440 << 10); + else if (be.BootMediaType == NBootMediaType::k2d88Floppy) + size = (2880 << 10); + UInt64 startPos = be.LoadRBA * BlockSize; + if (startPos < _archiveSize) + { + if (_archiveSize - startPos < size) + size = _archiveSize - startPos; + } + return size; + } + + bool IsSusp; + int SuspSkipSize; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoItem.h new file mode 100644 index 000000000..f39c2f5d2 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoItem.h @@ -0,0 +1,141 @@ +// Archive/IsoItem.h + +#ifndef __ARCHIVE_ISO_ITEM_H +#define __ARCHIVE_ISO_ITEM_H + +#include "Common/Types.h" +#include "Common/MyString.h" +#include "Common/Buffer.h" + +#include "Windows/Time.h" + +#include "IsoHeader.h" + +namespace NArchive { +namespace NIso { + +struct CRecordingDateTime +{ + Byte Year; + Byte Month; + Byte Day; + Byte Hour; + Byte Minute; + Byte Second; + signed char GmtOffset; // min intervals from -48 (West) to +52 (East) recorded. + + bool GetFileTime(FILETIME &ft) const + { + UInt64 value; + bool res = NWindows::NTime::GetSecondsSince1601(Year + 1900, Month, Day, Hour, Minute, Second, value); + if (res) + { + value -= (UInt64)((Int64)GmtOffset * 15 * 60); + value *= 10000000; + } + ft.dwLowDateTime = (DWORD)value; + ft.dwHighDateTime = (DWORD)(value >> 32); + return res; + } +}; + +struct CDirRecord +{ + Byte ExtendedAttributeRecordLen; + UInt32 ExtentLocation; + UInt32 DataLength; + CRecordingDateTime DateTime; + Byte FileFlags; + Byte FileUnitSize; + Byte InterleaveGapSize; + UInt16 VolSequenceNumber; + CByteBuffer FileId; + CByteBuffer SystemUse; + + bool IsDir() const { return (FileFlags & NFileFlags::kDirectory) != 0; } + bool IsSystemItem() const + { + if (FileId.GetCapacity() != 1) + return false; + Byte b = *(const Byte *)FileId; + return (b == 0 || b == 1); + } + + const Byte* FindSuspName(int skipSize, int &lenRes) const + { + lenRes = 0; + const Byte *p = (const Byte *)SystemUse + skipSize; + int length = (int)(SystemUse.GetCapacity() - skipSize); + while (length >= 5) + { + int len = p[2]; + if (p[0] == 'N' && p[1] == 'M' && p[3] == 1) + { + lenRes = len - 5; + return p + 5; + } + p += len; + length -= len; + } + return 0; + } + + int GetLengthCur(bool checkSusp, int skipSize) const + { + if (checkSusp) + { + int len; + const Byte *res = FindSuspName(skipSize, len); + if (res != 0) + return len; + } + return (int)FileId.GetCapacity(); + } + + const Byte* GetNameCur(bool checkSusp, int skipSize) const + { + if (checkSusp) + { + int len; + const Byte *res = FindSuspName(skipSize, len); + if (res != 0) + return res; + } + return (const Byte *)FileId; + } + + + bool CheckSusp(const Byte *p, int &startPos) const + { + if (p[0] == 'S' && + p[1] == 'P' && + p[2] == 0x7 && + p[3] == 0x1 && + p[4] == 0xBE && + p[5] == 0xEF) + { + startPos = p[6]; + return true; + } + return false; + } + + bool CheckSusp(int &startPos) const + { + const Byte *p = (const Byte *)SystemUse; + int length = (int)SystemUse.GetCapacity(); + const int kMinLen = 7; + if (length < kMinLen) + return false; + if (CheckSusp(p, startPos)) + return true; + const int kOffset2 = 14; + if (length < kOffset2 + kMinLen) + return false; + return CheckSusp(p + kOffset2, startPos); + } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoRegister.cpp new file mode 100644 index 000000000..67a09c769 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Iso/IsoRegister.cpp @@ -0,0 +1,13 @@ +// IsoRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "IsoHandler.h" +static IInArchive *CreateArc() { return new NArchive::NIso::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Iso", L"iso img", 0, 0xE7, { 'C', 'D', '0', '0', '1', 0x1 }, 7, false, CreateArc, 0 }; + +REGISTER_ARC(Iso) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/LzhHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/LzhHandler.cpp new file mode 100644 index 000000000..2c4eb9089 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/LzhHandler.cpp @@ -0,0 +1,775 @@ +// LzhHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../ICoder.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/LzhDecoder.h" + +#include "IArchive.h" + +#include "Common/ItemNameUtils.h" + +using namespace NWindows; +using namespace NTime; + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) + +namespace NArchive { +namespace NLzh{ + +const int kMethodIdSize = 5; + +const Byte kExtIdFileName = 0x01; +const Byte kExtIdDirName = 0x02; +const Byte kExtIdUnixTime = 0x54; + +struct CExtension +{ + Byte Type; + CByteBuffer Data; + AString GetString() const + { + AString s; + for (size_t i = 0; i < Data.GetCapacity(); i++) + { + char c = (char)Data[i]; + if (c == 0) + break; + s += c; + } + return s; + } +}; + +struct CItem +{ + AString Name; + Byte Method[kMethodIdSize]; + Byte Attributes; + Byte Level; + Byte OsId; + UInt32 PackSize; + UInt32 Size; + UInt32 ModifiedTime; + UInt16 CRC; + CObjectVector<CExtension> Extensions; + + bool IsValidMethod() const { return (Method[0] == '-' && Method[1] == 'l' && Method[4] == '-'); } + bool IsLhMethod() const {return (IsValidMethod() && Method[2] == 'h'); } + bool IsDir() const {return (IsLhMethod() && Method[3] == 'd'); } + + bool IsCopyMethod() const + { + return (IsLhMethod() && Method[3] == '0') || + (IsValidMethod() && Method[2] == 'z' && Method[3] == '4'); + } + + bool IsLh1GroupMethod() const + { + if (!IsLhMethod()) + return false; + switch(Method[3]) + { + case '1': + return true; + } + return false; + } + + bool IsLh4GroupMethod() const + { + if (!IsLhMethod()) + return false; + switch(Method[3]) + { + case '4': + case '5': + case '6': + case '7': + return true; + } + return false; + } + + int GetNumDictBits() const + { + if (!IsLhMethod()) + return 0; + switch(Method[3]) + { + case '1': return 12; + case '2': return 13; + case '3': return 13; + case '4': return 12; + case '5': return 13; + case '6': return 15; + case '7': return 16; + } + return 0; + } + + int FindExt(Byte type) const + { + for (int i = 0; i < Extensions.Size(); i++) + if (Extensions[i].Type == type) + return i; + return -1; + } + bool GetUnixTime(UInt32 &value) const + { + int index = FindExt(kExtIdUnixTime); + if (index < 0) + { + if (Level == 2) + { + value = ModifiedTime; + return true; + } + return false; + } + const Byte *data = (const Byte *)(Extensions[index].Data); + value = GetUi32(data); + return true; + } + + AString GetDirName() const + { + int index = FindExt(kExtIdDirName); + if (index < 0) + return AString(); + return Extensions[index].GetString(); + } + + AString GetFileName() const + { + int index = FindExt(kExtIdFileName); + if (index < 0) + return Name; + return Extensions[index].GetString(); + } + + AString GetName() const + { + AString dirName = GetDirName(); + const char kDirSeparator = CHAR_PATH_SEPARATOR; // '\\'; + // check kDirSeparator in Linux + dirName.Replace((char)(unsigned char)0xFF, kDirSeparator); + if (!dirName.IsEmpty() && dirName.Back() != kDirSeparator) + dirName += kDirSeparator; + return dirName + GetFileName(); + } +}; + +struct CItemEx: public CItem +{ + UInt64 DataPosition; +}; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + UInt64 m_Position; + + HRESULT ReadBytes(void *data, UInt32 size, UInt32 &processedSize); + HRESULT CheckReadBytes(void *data, UInt32 size); +public: + HRESULT Open(IInStream *inStream); + HRESULT GetNextItem(bool &filled, CItemEx &itemInfo); + HRESULT Skip(UInt64 numBytes); +}; + +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 &processedSize) +{ + size_t realProcessedSize = size; + RINOK(ReadStream(m_Stream, data, &realProcessedSize)); + processedSize = (UInt32)realProcessedSize; + m_Position += processedSize; + return S_OK; +} + +HRESULT CInArchive::CheckReadBytes(void *data, UInt32 size) +{ + UInt32 processedSize; + RINOK(ReadBytes(data, size, processedSize)); + return (processedSize == size) ? S_OK: S_FALSE; +} + +HRESULT CInArchive::Open(IInStream *inStream) +{ + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position)); + m_Stream = inStream; + return S_OK; +} + +static const Byte *ReadUInt16(const Byte *p, UInt16 &v) +{ + v = Get16(p); + return p + 2; +} + +static const Byte *ReadString(const Byte *p, size_t size, AString &s) +{ + s.Empty(); + for (size_t i = 0; i < size; i++) + { + char c = p[i]; + if (c == 0) + break; + s += c; + } + return p + size; +} + +static Byte CalcSum(const Byte *data, size_t size) +{ + Byte sum = 0; + for (size_t i = 0; i < size; i++) + sum = (Byte)(sum + data[i]); + return sum; +} + +HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item) +{ + filled = false; + + UInt32 processedSize; + Byte startHeader[2]; + RINOK(ReadBytes(startHeader, 2, processedSize)) + if (processedSize == 0) + return S_OK; + if (processedSize == 1) + return (startHeader[0] == 0) ? S_OK: S_FALSE; + if (startHeader[0] == 0 && startHeader[1] == 0) + return S_OK; + + Byte header[256]; + const UInt32 kBasicPartSize = 22; + RINOK(ReadBytes(header, kBasicPartSize, processedSize)); + if (processedSize != kBasicPartSize) + return (startHeader[0] == 0) ? S_OK: S_FALSE; + + const Byte *p = header; + memcpy(item.Method, p, kMethodIdSize); + if (!item.IsValidMethod()) + return S_OK; + p += kMethodIdSize; + item.PackSize = Get32(p); + item.Size = Get32(p + 4); + item.ModifiedTime = Get32(p + 8); + item.Attributes = p[12]; + item.Level = p[13]; + p += 14; + if (item.Level > 2) + return S_FALSE; + UInt32 headerSize; + if (item.Level < 2) + { + headerSize = startHeader[0]; + if (headerSize < kBasicPartSize) + return S_FALSE; + UInt32 remain = headerSize - kBasicPartSize; + RINOK(CheckReadBytes(header + kBasicPartSize, remain)); + if (startHeader[1] != CalcSum(header, headerSize)) + return S_FALSE; + size_t nameLength = *p++; + if ((p - header) + nameLength + 2 > headerSize) + return S_FALSE; + p = ReadString(p, nameLength, item.Name); + } + else + headerSize = startHeader[0] | ((UInt32)startHeader[1] << 8); + p = ReadUInt16(p, item.CRC); + if (item.Level != 0) + { + if (item.Level == 2) + { + RINOK(CheckReadBytes(header + kBasicPartSize, 2)); + } + if ((size_t)(p - header) + 3 > headerSize) + return S_FALSE; + item.OsId = *p++; + UInt16 nextSize; + p = ReadUInt16(p, nextSize); + while (nextSize != 0) + { + if (nextSize < 3) + return S_FALSE; + if (item.Level == 1) + { + if (item.PackSize < nextSize) + return S_FALSE; + item.PackSize -= nextSize; + } + CExtension ext; + RINOK(CheckReadBytes(&ext.Type, 1)) + nextSize -= 3; + ext.Data.SetCapacity(nextSize); + RINOK(CheckReadBytes((Byte *)ext.Data, nextSize)) + item.Extensions.Add(ext); + Byte hdr2[2]; + RINOK(CheckReadBytes(hdr2, 2)); + ReadUInt16(hdr2, nextSize); + } + } + item.DataPosition = m_Position; + filled = true; + return S_OK; +} + +HRESULT CInArchive::Skip(UInt64 numBytes) +{ + UInt64 newPostion; + RINOK(m_Stream->Seek(numBytes, STREAM_SEEK_CUR, &newPostion)); + m_Position += numBytes; + if (m_Position != newPostion) + return E_FAIL; + return S_OK; +} + +struct COsPair +{ + Byte Id; + const char *Name; +}; + +static COsPair g_OsPairs[] = +{ + { 0, "MS-DOS" }, + { 'M', "MS-DOS" }, + { '2', "OS/2" }, + { '9', "OS9" }, + { 'K', "OS/68K" }, + { '3', "OS/386" }, + { 'H', "HUMAN" }, + { 'U', "UNIX" }, + { 'C', "CP/M" }, + { 'F', "FLEX" }, + { 'm', "Mac" }, + { 'R', "Runser" }, + { 'T', "TownsOS" }, + { 'X', "XOSK" }, + { 'w', "Windows 95" }, + { 'W', "Windows NT" }, + { 'J', "Java VM" } +}; + +static const char *kUnknownOS = "Unknown"; + +static const char *GetOS(Byte osId) +{ + for (int i = 0; i < sizeof(g_OsPairs) / sizeof(g_OsPairs[0]); i++) + if (g_OsPairs[i].Id == osId) + return g_OsPairs[i].Name; + return kUnknownOS; +} + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + // { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidCRC, VT_UI4}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidHostOS, VT_BSTR} +}; + +class CCRC +{ + UInt16 _value; +public: + static UInt16 Table[256]; + static void InitTable(); + + CCRC(): _value(0) {} + void Init() { _value = 0; } + void Update(const void *data, size_t size); + UInt16 GetDigest() const { return _value; } +}; + +static const UInt16 kCRCPoly = 0xA001; + +UInt16 CCRC::Table[256]; + +void CCRC::InitTable() +{ + for (UInt32 i = 0; i < 256; i++) + { + UInt32 r = i; + for (int j = 0; j < 8; j++) + if (r & 1) + r = (r >> 1) ^ kCRCPoly; + else + r >>= 1; + CCRC::Table[i] = (UInt16)r; + } +} + +class CCRCTableInit +{ +public: + CCRCTableInit() { CCRC::InitTable(); } +} g_CRCTableInit; + +void CCRC::Update(const void *data, size_t size) +{ + UInt16 v = _value; + const Byte *p = (const Byte *)data; + for (; size > 0; size--, p++) + v = (UInt16)(Table[((Byte)(v)) ^ *p] ^ (v >> 8)); + _value = v; +} + + +class COutStreamWithCRC: + public ISequentialOutStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); +private: + CCRC _crc; + CMyComPtr<ISequentialOutStream> _stream; +public: + void Init(ISequentialOutStream *stream) + { + _stream = stream; + _crc.Init(); + } + void ReleaseStream() { _stream.Release(); } + UInt32 GetCRC() const { return _crc.GetDigest(); } + void InitCRC() { _crc.Init(); } +}; + +STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result; + if (!_stream) + { + realProcessedSize = size; + result = S_OK; + } + else + result = _stream->Write(data, size, &realProcessedSize); + _crc.Update(data, realProcessedSize); + if (processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CObjectVector<CItemEx> _items; + CMyComPtr<IInStream> _stream; +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) + CHandler(); +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +CHandler::CHandler() {} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItemEx &item = _items[index]; + switch(propID) + { + case kpidPath: + { + UString s = NItemName::WinNameToOSName(MultiByteToUnicodeString(item.GetName(), CP_OEMCP)); + if (!s.IsEmpty()) + { + if (s[s.Length() - 1] == WCHAR_PATH_SEPARATOR) + s.Delete(s.Length() - 1); + prop = s; + } + break; + } + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.Size; break; + case kpidPackSize: prop = item.PackSize; break; + case kpidCRC: prop = (UInt32)item.CRC; break; + case kpidHostOS: prop = GetOS(item.OsId); break; + case kpidMTime: + { + FILETIME utc; + UInt32 unixTime; + if (item.GetUnixTime(unixTime)) + NTime::UnixTimeToFileTime(unixTime, utc); + else + { + FILETIME localFileTime; + if (DosTimeToFileTime(item.ModifiedTime, localFileTime)) + { + if (!LocalFileTimeToFileTime(&localFileTime, &utc)) + utc.dwHighDateTime = utc.dwLowDateTime = 0; + } + else + utc.dwHighDateTime = utc.dwLowDateTime = 0; + } + prop = utc; + break; + } + // case kpidAttrib: prop = (UInt32)item.Attributes; break; + case kpidMethod: + { + char method2[kMethodIdSize + 1]; + method2[kMethodIdSize] = 0; + memcpy(method2, item.Method, kMethodIdSize); + prop = method2; + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + try + { + _items.Clear(); + CInArchive archive; + + UInt64 endPos = 0; + bool needSetTotal = true; + + if (callback != NULL) + { + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + + RINOK(archive.Open(stream)); + for (;;) + { + CItemEx item; + bool filled; + HRESULT result = archive.GetNextItem(filled, item); + if (result == S_FALSE) + return S_FALSE; + if (result != S_OK) + return S_FALSE; + if (!filled) + break; + _items.Add(item); + archive.Skip(item.PackSize); + if (callback != NULL) + { + if (needSetTotal) + { + RINOK(callback->SetTotal(NULL, &endPos)); + needSetTotal = false; + } + if (_items.Size() % 100 == 0) + { + UInt64 numFiles = _items.Size(); + UInt64 numBytes = item.DataPosition; + RINOK(callback->SetCompleted(&numFiles, &numBytes)); + } + } + } + if (_items.IsEmpty()) + return S_FALSE; + + _stream = stream; + } + catch(...) + { + return S_FALSE; + } + COM_TRY_END + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testModeSpec, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool testMode = (testModeSpec != 0); + UInt64 totalUnPacked = 0, totalPacked = 0; + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CItemEx &item = _items[allFilesMode ? i : indices[i]]; + totalUnPacked += item.Size; + totalPacked += item.PackSize; + } + RINOK(extractCallback->SetTotal(totalUnPacked)); + + UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0; + UInt64 currentItemUnPacked, currentItemPacked; + + NCompress::NLzh::NDecoder::CCoder *lzhDecoderSpec = 0; + CMyComPtr<ICompressCoder> lzhDecoder; + CMyComPtr<ICompressCoder> lzh1Decoder; + CMyComPtr<ICompressCoder> arj2Decoder; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked, + currentTotalPacked += currentItemPacked) + { + currentItemUnPacked = 0; + currentItemPacked = 0; + + lps->InSize = currentTotalPacked; + lps->OutSize = currentTotalUnPacked; + RINOK(lps->SetCur()); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode; + askMode = testMode ? NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItemEx &item = _items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (item.IsDir()) + { + // if (!testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + continue; + } + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + currentItemUnPacked = item.Size; + currentItemPacked = item.PackSize; + + { + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->Init(realOutStream); + realOutStream.Release(); + + UInt64 pos; + _stream->Seek(item.DataPosition, STREAM_SEEK_SET, &pos); + + streamSpec->Init(item.PackSize); + + HRESULT result = S_OK; + Int32 opRes = NExtract::NOperationResult::kOK; + + if (item.IsCopyMethod()) + { + result = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (result == S_OK && copyCoderSpec->TotalSize != item.PackSize) + result = S_FALSE; + } + else if (item.IsLh4GroupMethod()) + { + if (!lzhDecoder) + { + lzhDecoderSpec = new NCompress::NLzh::NDecoder::CCoder; + lzhDecoder = lzhDecoderSpec; + } + lzhDecoderSpec->SetDictionary(item.GetNumDictBits()); + result = lzhDecoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); + } + /* + else if (item.IsLh1GroupMethod()) + { + if (!lzh1Decoder) + { + lzh1DecoderSpec = new NCompress::NLzh1::NDecoder::CCoder; + lzh1Decoder = lzh1DecoderSpec; + } + lzh1DecoderSpec->SetDictionary(item.GetNumDictBits()); + result = lzh1Decoder->Code(inStream, outStream, NULL, ¤tItemUnPacked, progress); + } + */ + else + opRes = NExtract::NOperationResult::kUnSupportedMethod; + + if (opRes == NExtract::NOperationResult::kOK) + { + if (result == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else + { + RINOK(result); + if (outStreamSpec->GetCRC() != item.CRC) + opRes = NExtract::NOperationResult::kCRCError; + } + } + outStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Lzh", L"lzh lha", 0, 6, { '-', 'l' }, 2, false, CreateArc, 0 }; + +REGISTER_ARC(Lzh) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp new file mode 100644 index 000000000..a83e6a1ad --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp @@ -0,0 +1,430 @@ +// LzmaHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/CreateCoder.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/LzmaDecoder.h" + +#include "Common/DummyOutStream.h" + +using namespace NWindows; + +namespace NArchive { +namespace NLzma { + +static bool CheckDicSize(const Byte *p) +{ + UInt32 dicSize = GetUi32(p); + for (int i = 1; i <= 30; i++) + if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i)) + return true; + return (dicSize == 0xFFFFFFFF); +} + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR} +}; + +struct CHeader +{ + UInt64 Size; + Byte FilterID; + Byte LzmaProps[5]; + + UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); } + bool HasSize() const { return (Size != (UInt64)(Int64)-1); } + bool Parse(const Byte *buf, bool isThereFilter); +}; + +bool CHeader::Parse(const Byte *buf, bool isThereFilter) +{ + FilterID = 0; + if (isThereFilter) + FilterID = buf[0]; + const Byte *sig = buf + (isThereFilter ? 1 : 0); + for (int i = 0; i < 5; i++) + LzmaProps[i] = sig[i]; + Size = GetUi64(sig + 5); + return + LzmaProps[0] < 5 * 5 * 9 && + FilterID < 2 && + (!HasSize() || Size < ((UInt64)1 << 56)) && + CheckDicSize(LzmaProps + 1); +} + +class CDecoder +{ + NCompress::NLzma::CDecoder *_lzmaDecoderSpec; + CMyComPtr<ICompressCoder> _lzmaDecoder; + CMyComPtr<ISequentialOutStream> _bcjStream; +public: + ~CDecoder(); + HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS + bool filtered, ISequentialInStream *inStream); + + HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress); + + UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); } + + void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); } + + HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize) + { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); } +}; + +static const UInt64 k_BCJ = 0x03030103; + +HRESULT CDecoder::Create( + DECL_EXTERNAL_CODECS_LOC_VARS + bool filteredMode, ISequentialInStream *inStream) +{ + if (!_lzmaDecoder) + { + _lzmaDecoderSpec = new NCompress::NLzma::CDecoder; + _lzmaDecoder = _lzmaDecoderSpec; + } + + if (filteredMode) + { + if (!_bcjStream) + { + CMyComPtr<ICompressCoder> coder; + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_BCJ, coder, false)); + if (!coder) + return E_NOTIMPL; + coder.QueryInterface(IID_ISequentialOutStream, &_bcjStream); + if (!_bcjStream) + return E_NOTIMPL; + } + } + + return _lzmaDecoderSpec->SetInStream(inStream); +} + +CDecoder::~CDecoder() +{ + ReleaseInStream(); +} + +HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream, + ICompressProgressInfo *progress) +{ + if (header.FilterID > 1) + return E_NOTIMPL; + + { + CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties; + _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); + if (!setDecoderProperties) + return E_NOTIMPL; + RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5)); + } + + CMyComPtr<ICompressSetOutStream> setOutStream; + + bool filteredMode = (header.FilterID == 1); + + if (filteredMode) + { + _bcjStream.QueryInterface(IID_ICompressSetOutStream, &setOutStream); + if (!setOutStream) + return E_NOTIMPL; + RINOK(setOutStream->SetOutStream(outStream)); + outStream = _bcjStream; + } + + const UInt64 *Size = header.HasSize() ? &header.Size : NULL; + HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress); + + if (filteredMode) + { + CMyComPtr<IOutStreamFlush> flush; + _bcjStream.QueryInterface(IID_IOutStreamFlush, &flush); + if (flush) + { + HRESULT res2 = flush->Flush(); + if (res == S_OK) + res = res2; + } + HRESULT res2 = setOutStream->ReleaseOutStream(); + if (res == S_OK) + res = res2; + } + RINOK(res); + + return S_OK; +} + + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ + CHeader _header; + bool _lzma86; + UInt64 _startPosition; + UInt64 _packSize; + bool _packSizeDefined; + CMyComPtr<IInStream> _stream; + CMyComPtr<ISequentialInStream> _seqStream; + + DECL_EXTERNAL_CODECS_VARS + DECL_ISetCompressCodecsInfo + +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + + CHandler(bool lzma86) { _lzma86 = lzma86; } + + unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); } + +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +static void DictSizeToString(UInt32 value, char *s) +{ + for (int i = 0; i <= 31; i++) + if ((UInt32(1) << i) == value) + { + ::ConvertUInt32ToString(i, s); + return; + } + char c = 'b'; + if ((value & ((1 << 20) - 1)) == 0) + { + value >>= 20; + c = 'm'; + } + else if ((value & ((1 << 10) - 1)) == 0) + { + value >>= 10; + c = 'k'; + } + ::ConvertUInt32ToString(value, s); + int p = MyStringLen(s); + s[p++] = c; + s[p++] = '\0'; +} + +static void MyStrCat(char *d, const char *s) +{ + MyStringCopy(d + MyStringLen(d), s); +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + case kpidMethod: + if (_stream) + { + char s[64]; + s[0] = '\0'; + if (_header.FilterID != 0) + MyStrCat(s, "BCJ "); + MyStrCat(s, "LZMA:"); + DictSizeToString(_header.GetDicSize(), s + MyStringLen(s)); + prop = s; + } + break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *) +{ + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition)); + + const UInt32 kBufSize = 1 + 5 + 8 + 1; + Byte buf[kBufSize]; + + RINOK(ReadStream_FALSE(inStream, buf, kBufSize)); + + if (!_header.Parse(buf, _lzma86)) + return S_FALSE; + const Byte *start = buf + GetHeaderSize(); + if (start[0] != 0) + return S_FALSE; + + UInt64 endPos; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos)); + _packSize = endPos - _startPosition; + _packSizeDefined = true; + + _stream = inStream; + _seqStream = inStream; + return S_OK; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + _seqStream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _stream.Release(); + _seqStream.Release(); + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + if (_stream) + extractCallback->SetTotal(_packSize); + + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + if (_stream) + { + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + } + + CDecoder decoder; + HRESULT result = decoder.Create( + EXTERNAL_CODECS_VARS + _lzma86, _seqStream); + RINOK(result); + + Int32 opRes = NExtract::NOperationResult::kOK; + bool firstItem = true; + + for (;;) + { + lps->OutSize = outStreamSpec->GetSize(); + lps->InSize = _packSize = decoder.GetInputProcessedSize(); + _packSizeDefined = true; + RINOK(lps->SetCur()); + + CHeader st; + + const UInt32 kBufSize = 1 + 5 + 8; + Byte buf[kBufSize]; + const UInt32 headerSize = GetHeaderSize(); + UInt32 processed; + RINOK(decoder.ReadInput(buf, headerSize, &processed)); + if (processed != headerSize) + break; + + if (!st.Parse(buf, _lzma86)) + break; + firstItem = false; + + result = decoder.Code(st, outStream, progress); + if (result == E_NOTIMPL) + { + opRes = NExtract::NOperationResult::kUnSupportedMethod; + break; + } + if (result == S_FALSE) + { + opRes = NExtract::NOperationResult::kDataError; + break; + } + RINOK(result); + } + if (firstItem) + return E_FAIL; + outStream.Release(); + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +IMPL_ISetCompressCodecsInfo + +static IInArchive *CreateArc() { return new CHandler(false); } +static IInArchive *CreateArc86() { return new CHandler(true); } + +namespace NLzmaAr { + +static CArcInfo g_ArcInfo = + { L"lzma", L"lzma", 0, 0xA, { 0 }, 0, true, CreateArc, NULL }; +REGISTER_ARC(Lzma) + +} + +namespace NLzma86Ar { + +static CArcInfo g_ArcInfo = + { L"lzma86", L"lzma86", 0, 0xB, { 0 }, 0, true, CreateArc86, NULL }; +REGISTER_ARC(Lzma86) + +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/MachoHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/MachoHandler.cpp new file mode 100644 index 000000000..a6261f34d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/MachoHandler.cpp @@ -0,0 +1,500 @@ +// MachoHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" + +#include "Windows/PropVariantUtils.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +static UInt32 Get32(const Byte *p, int be) { if (be) return GetBe32(p); return GetUi32(p); } +static UInt64 Get64(const Byte *p, int be) { if (be) return GetBe64(p); return GetUi64(p); } + +using namespace NWindows; + +namespace NArchive { +namespace NMacho { + +#define MACH_ARCH_ABI64 (1 << 24) +#define MACH_MACHINE_386 7 +#define MACH_MACHINE_ARM 12 +#define MACH_MACHINE_SPARC 14 +#define MACH_MACHINE_PPC 18 + +#define MACH_MACHINE_PPC64 (MACH_ARCH_ABI64 | MACH_MACHINE_PPC) +#define MACH_MACHINE_AMD64 (MACH_ARCH_ABI64 | MACH_MACHINE_386) + +#define MACH_CMD_SEGMENT_32 1 +#define MACH_CMD_SEGMENT_64 0x19 + +#define MACH_SECT_TYPE_MASK 0x000000FF +#define MACH_SECT_ATTR_MASK 0xFFFFFF00 + +#define MACH_SECT_ATTR_ZEROFILL 1 + +static const char *g_SectTypes[] = +{ + "REGULAR", + "ZEROFILL", + "CSTRINGS", + "4BYTE_LITERALS", + "8BYTE_LITERALS", + "LITERAL_POINTERS", + "NON_LAZY_SYMBOL_POINTERS", + "LAZY_SYMBOL_POINTERS", + "SYMBOL_STUBS", + "MOD_INIT_FUNC_POINTERS", + "MOD_TERM_FUNC_POINTERS", + "COALESCED", + "GB_ZEROFILL", + "INTERPOSING", + "16BYTE_LITERALS" +}; + +static const char *g_FileTypes[] = +{ + "0", + "OBJECT", + "EXECUTE", + "FVMLIB", + "CORE", + "PRELOAD", + "DYLIB", + "DYLINKER", + "BUNDLE", + "DYLIB_STUB", + "DSYM" +}; + +static const CUInt32PCharPair g_Flags[] = +{ + { 31, "PURE_INSTRUCTIONS" }, + { 30, "NO_TOC" }, + { 29, "STRIP_STATIC_SYMS" }, + { 28, "NO_DEAD_STRIP" }, + { 27, "LIVE_SUPPORT" }, + { 26, "SELF_MODIFYING_CODE" }, + { 25, "DEBUG" }, + { 10, "SOME_INSTRUCTIONS" }, + { 9, "EXT_RELOC" }, + { 8, "LOC_RELOC" } +}; + +static const CUInt32PCharPair g_MachinePairs[] = +{ + { MACH_MACHINE_386, "x86" }, + { MACH_MACHINE_ARM, "ARM" }, + { MACH_MACHINE_SPARC, "SPARC" }, + { MACH_MACHINE_PPC, "PowerPC" }, + { MACH_MACHINE_PPC64, "PowerPC 64-bit" }, + { MACH_MACHINE_AMD64, "x64" } +}; + +static const int kNameSize = 16; + +struct CSegment +{ + char Name[kNameSize]; +}; + +struct CSection +{ + char Name[kNameSize]; + char SegName[kNameSize]; + UInt64 Va; + UInt64 Pa; + UInt64 VSize; + UInt64 PSize; + + UInt32 Flags; + int SegmentIndex; + + bool IsDummy; + + CSection(): IsDummy(false) {} + // UInt64 GetPackSize() const { return Flags == MACH_SECT_ATTR_ZEROFILL ? 0 : Size; } + UInt64 GetPackSize() const { return PSize; } +}; + + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + CObjectVector<CSegment> _segments; + CObjectVector<CSection> _sections; + bool _mode64; + bool _be; + UInt32 _machine; + UInt32 _type; + UInt32 _headersSize; + UInt64 _totalSize; + HRESULT Open2(ISequentialInStream *stream); + bool Parse(const Byte *buf, UInt32 size); +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +bool CHandler::Parse(const Byte *buf, UInt32 size) +{ + bool mode64 = _mode64; + bool be = _be; + + const Byte *bufStart = buf; + bool reduceCommands = false; + if (size < 512) + return false; + + _machine = Get32(buf + 4, be); + _type = Get32(buf + 0xC, be); + + UInt32 numCommands = Get32(buf + 0x10, be); + UInt32 commandsSize = Get32(buf + 0x14, be); + if (commandsSize > size) + return false; + + if (commandsSize > (1 << 24) || numCommands > (1 << 18)) + return false; + + if (numCommands > 16) + { + reduceCommands = true; + numCommands = 16; + } + + _headersSize = 0; + + buf += 0x1C; + size -= 0x1C; + + if (mode64) + { + buf += 4; + size -= 4; + } + + _totalSize = (UInt32)(buf - bufStart); + if (commandsSize < size) + size = commandsSize; + + for (UInt32 cmdIndex = 0; cmdIndex < numCommands; cmdIndex++) + { + if (size < 8) + return false; + UInt32 cmd = Get32(buf, be); + UInt32 cmdSize = Get32(buf + 4, be); + if (size < cmdSize) + return false; + if (cmd == MACH_CMD_SEGMENT_32 || cmd == MACH_CMD_SEGMENT_64) + { + UInt32 offs = (cmd == MACH_CMD_SEGMENT_64) ? 0x48 : 0x38; + if (cmdSize < offs) + break; + + UInt64 vmAddr, vmSize, phAddr, phSize; + + { + if (cmd == MACH_CMD_SEGMENT_64) + { + vmAddr = Get64(buf + 0x18, be); + vmSize = Get64(buf + 0x20, be); + phAddr = Get64(buf + 0x28, be); + phSize = Get64(buf + 0x30, be); + } + else + { + vmAddr = Get32(buf + 0x18, be); + vmSize = Get32(buf + 0x1C, be); + phAddr = Get32(buf + 0x20, be); + phSize = Get32(buf + 0x24, be); + } + { + UInt64 totalSize = phAddr + phSize; + if (totalSize > _totalSize) + _totalSize = totalSize; + } + } + + CSegment seg; + memcpy(seg.Name, buf + 8, kNameSize); + _segments.Add(seg); + + UInt32 numSections = Get32(buf + offs - 8, be); + if (numSections > (1 << 8)) + return false; + + if (numSections == 0) + { + CSection section; + section.IsDummy = true; + section.SegmentIndex = _segments.Size() - 1; + section.Va = vmAddr; + section.PSize = phSize; + section.VSize = vmSize; + section.Pa = phAddr; + section.Flags = 0; + _sections.Add(section); + } + else do + { + CSection section; + UInt32 headerSize = (cmd == MACH_CMD_SEGMENT_64) ? 0x50 : 0x44; + const Byte *p = buf + offs; + if (cmdSize - offs < headerSize) + break; + if (cmd == MACH_CMD_SEGMENT_64) + { + section.Va = Get64(p + 0x20, be); + section.VSize = Get64(p + 0x28, be); + section.Pa = Get32(p + 0x30, be); + section.Flags = Get32(p + 0x40, be); + } + else + { + section.Va = Get32(p + 0x20, be); + section.VSize = Get32(p + 0x24, be); + section.Pa = Get32(p + 0x28, be); + section.Flags = Get32(p + 0x38, be); + } + if (section.Flags == MACH_SECT_ATTR_ZEROFILL) + section.PSize = 0; + else + section.PSize = section.VSize; + memcpy(section.Name, p, kNameSize); + memcpy(section.SegName, p + kNameSize, kNameSize); + section.SegmentIndex = _segments.Size() - 1; + _sections.Add(section); + offs += headerSize; + } + while (--numSections); + + if (offs != cmdSize) + return false; + } + buf += cmdSize; + size -= cmdSize; + } + _headersSize = (UInt32)(buf - bufStart); + return reduceCommands || (size == 0); +} + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidCpu, VT_BSTR}, + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidBigEndian, VT_BOOL}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI4} +}; + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidVa, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _machine, prop); break; + case kpidCharacts: TYPE_TO_PROP(g_FileTypes, _type, prop); break; + case kpidPhySize: prop = _totalSize; break; + case kpidHeadersSize: prop = _headersSize; break; + case kpidBit64: if (_mode64) prop = _mode64; break; + case kpidBigEndian: if (_be) prop = _be; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +static AString GetName(const char *name) +{ + char res[kNameSize + 1]; + memcpy(res, name, kNameSize); + res[kNameSize] = 0; + return res; +} + +static AString SectFlagsToString(UInt32 flags) +{ + AString res = TypeToString(g_SectTypes, sizeof(g_SectTypes) / sizeof(g_SectTypes[0]), + flags & MACH_SECT_TYPE_MASK); + AString s = FlagsToString(g_Flags, sizeof(g_Flags) / sizeof(g_Flags[0]), + flags & MACH_SECT_ATTR_MASK); + if (!s.IsEmpty()) + { + res += ' '; + res += s; + } + return res; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CSection &item = _sections[index]; + switch(propID) + { + case kpidPath: + { + AString s = GetName(_segments[item.SegmentIndex].Name); + if (!item.IsDummy) + s += GetName(item.Name); + StringToProp(s, prop); + break; + } + case kpidSize: /* prop = (UInt64)item.VSize; break; */ + case kpidPackSize: prop = (UInt64)item.GetPackSize(); break; + case kpidCharacts: if (!item.IsDummy) StringToProp(SectFlagsToString(item.Flags), prop); break; + case kpidOffset: prop = item.Pa; break; + case kpidVa: prop = item.Va; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Open2(ISequentialInStream *stream) +{ + const UInt32 kBufSize = 1 << 18; + const UInt32 kSigSize = 4; + + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + size_t processed = kSigSize; + RINOK(ReadStream_FALSE(stream, buf, processed)); + UInt32 sig = GetUi32(buf); + bool be, mode64; + switch(sig) + { + case 0xCEFAEDFE: be = true; mode64 = false; break; + case 0xCFFAEDFE: be = true; mode64 = true; break; + case 0xFEEDFACE: be = false; mode64 = false; break; + case 0xFEEDFACF: be = false; mode64 = true; break; + default: return S_FALSE; + } + processed = kBufSize - kSigSize; + RINOK(ReadStream(stream, buf + kSigSize, &processed)); + _mode64 = mode64; + _be = be; + return Parse(buf, (UInt32)processed + kSigSize) ? S_OK : S_FALSE; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(inStream)); + _inStream = inStream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _sections.Clear(); + _segments.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _sections.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _sections.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _sections[allFilesMode ? i : indices[i]].GetPackSize(); + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_inStream); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CSection &item = _sections[index]; + currentItemSize = item.GetPackSize(); + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(_inStream->Seek(item.Pa, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == currentItemSize ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"MachO", L"", 0, 0xDF, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Macho) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/MbrHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/MbrHandler.cpp new file mode 100644 index 000000000..b6d791829 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/MbrHandler.cpp @@ -0,0 +1,507 @@ +// MbrHandler.cpp + +#include "StdAfx.h" + +// #define SHOW_DEBUG_INFO + +#ifdef SHOW_DEBUG_INFO +#include <stdio.h> +#endif + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +using namespace NWindows; + +namespace NArchive { +namespace NMbr { + +struct CChs +{ + Byte Head; + Byte SectCyl; + Byte Cyl8; + + UInt32 GetSector() const { return SectCyl & 0x3F; } + UInt32 GetCyl() const { return ((UInt32)SectCyl >> 6 << 8) | Cyl8; } + void ToString(NCOM::CPropVariant &prop) const; + + void Parse(const Byte *p) + { + Head = p[0]; + SectCyl = p[1]; + Cyl8 = p[2]; + } + bool Check() const { return GetSector() > 0; } +}; + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareChs(const CChs &c1, const CChs &c2) +{ + RINOZ(MyCompare(c1.GetCyl(), c2.GetCyl())); + RINOZ(MyCompare(c1.Head, c2.Head)); + return MyCompare(c1.GetSector(), c2.GetSector()); +} + +static void AddUIntToString(UInt32 val, AString &res) +{ + char s[16]; + ConvertUInt32ToString(val, s); + res += s; +} + +void CChs::ToString(NCOM::CPropVariant &prop) const +{ + AString s; + AddUIntToString(GetCyl(), s); + s += '-'; + AddUIntToString(Head, s); + s += '-'; + AddUIntToString(GetSector(), s); + prop = s; +} + +struct CPartition +{ + Byte Status; + CChs BeginChs; + Byte Type; + CChs EndChs; + UInt32 Lba; + UInt32 NumBlocks; + + CPartition() { memset (this, 0, sizeof(*this)); } + + bool IsEmpty() const { return Type == 0; } + bool IsExtended() const { return Type == 5 || Type == 0xF; } + UInt32 GetLimit() const { return Lba + NumBlocks; } + // bool IsActive() const { return Status == 0x80; } + UInt64 GetPos() const { return (UInt64)Lba * 512; } + UInt64 GetSize() const { return (UInt64)NumBlocks * 512; } + + bool CheckLbaLimits() const { return (UInt32)0xFFFFFFFF - Lba >= NumBlocks; } + bool Parse(const Byte *p) + { + Status = p[0]; + BeginChs.Parse(p + 1); + Type = p[4]; + EndChs.Parse(p + 5); + Lba = GetUi32(p + 8); + NumBlocks = GetUi32(p + 12); + if (Type == 0) + return true; + if (Status != 0 && Status != 0x80) + return false; + return + BeginChs.Check() && + EndChs.Check() && + CompareChs(BeginChs, EndChs) <= 0 && + NumBlocks > 0 && + CheckLbaLimits(); + } + + #ifdef SHOW_DEBUG_INFO + void Print() const + { + NCOM::CPropVariant prop, prop2; + BeginChs.ToString(prop); + EndChs.ToString(prop2); + printf(" %2x %2x %8X %8X %12S %12S", (int)Status, (int)Type, Lba, NumBlocks, prop.bstrVal, prop2.bstrVal); + } + #endif +}; + +struct CPartType +{ + UInt32 Id; + const char *Ext; + const char *Name; +}; + +static const char *kFat = "fat"; + +static const CPartType kPartTypes[] = +{ + { 0x01, kFat, "FAT12" }, + { 0x04, kFat, "FAT16 DOS 3.0+" }, + { 0x05, 0, "Extended" }, + { 0x06, kFat, "FAT16 DOS 3.31+" }, + { 0x07, "ntfs", "NTFS" }, + { 0x0B, kFat, "FAT32" }, + { 0x0C, kFat, "FAT32-LBA" }, + { 0x0E, kFat, "FAT16-LBA" }, + { 0x0F, 0, "Extended-LBA" }, + { 0x11, kFat, "FAT12-Hidden" }, + { 0x14, kFat, "FAT16-Hidden < 32 MB" }, + { 0x16, kFat, "FAT16-Hidden >= 32 MB" }, + { 0x1B, kFat, "FAT32-Hidden" }, + { 0x1C, kFat, "FAT32-LBA-Hidden" }, + { 0x1E, kFat, "FAT16-LBA-WIN95-Hidden" }, + { 0x82, 0, "Solaris x86 / Linux swap" }, + { 0x83, 0, "Linux" }, + { 0xBE, 0, "Solaris 8 boot" }, + { 0xBF, 0, "New Solaris x86" }, + { 0xC2, 0, "Linux-Hidden" }, + { 0xC3, 0, "Linux swap-Hidden" }, + { 0xEE, 0, "EFI-MBR" }, + { 0xEE, 0, "EFI" } +}; + +static int FindPartType(UInt32 type) +{ + for (int i = 0; i < sizeof(kPartTypes) / sizeof(kPartTypes[0]); i++) + if (kPartTypes[i].Id == type) + return i; + return -1; +} + +struct CItem +{ + bool IsReal; + bool IsPrim; + UInt64 Size; + CPartition Part; +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CObjectVector<CItem> _items; + UInt64 _totalSize; + CByteBuffer _buffer; + + HRESULT ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, int level); +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +HRESULT CHandler::ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, int level) +{ + if (level >= 128 || _items.Size() >= 128) + return S_FALSE; + + const int kNumHeaderParts = 4; + CPartition parts[kNumHeaderParts]; + + { + const UInt32 kSectorSize = 512; + _buffer.SetCapacity(kSectorSize); + Byte *buf = _buffer; + UInt64 newPos = (UInt64)lba << 9; + if (newPos + 512 > _totalSize) + return S_FALSE; + RINOK(stream->Seek(newPos, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf, kSectorSize)); + + if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA) + return S_FALSE; + + for (int i = 0; i < kNumHeaderParts; i++) + if (!parts[i].Parse(buf + 0x1BE + 16 * i)) + return S_FALSE; + } + + PRF(printf("\n# %8X", lba)); + + UInt32 limLba = lba + 1; + if (limLba == 0) + return S_FALSE; + + for (int i = 0; i < kNumHeaderParts; i++) + { + CPartition &part = parts[i]; + + if (part.IsEmpty()) + continue; + PRF(printf("\n %2d ", (int)level)); + #ifdef SHOW_DEBUG_INFO + part.Print(); + #endif + + int numItems = _items.Size(); + UInt32 newLba = lba + part.Lba; + + if (part.IsExtended()) + { + // if (part.Type == 5) // Check it! + newLba = baseLba + part.Lba; + if (newLba < limLba) + return S_FALSE; + HRESULT res = ReadTables(stream, level < 1 ? newLba : baseLba, newLba, level + 1); + if (res != S_FALSE && res != S_OK) + return res; + } + if (newLba < limLba) + return S_FALSE; + part.Lba = newLba; + if (!part.CheckLbaLimits()) + return S_FALSE; + + CItem n; + n.Part = part; + bool addItem = false; + if (numItems == _items.Size()) + { + n.IsPrim = (level == 0); + n.IsReal = true; + addItem = true; + } + else + { + const CItem &back = _items.Back(); + UInt32 backLimit = back.Part.GetLimit(); + UInt32 partLimit = part.GetLimit(); + if (backLimit < partLimit) + { + n.IsReal = false; + n.Part.Lba = backLimit; + n.Part.NumBlocks = partLimit - backLimit; + addItem = true; + } + } + if (addItem) + { + if (n.Part.GetLimit() < limLba) + return S_FALSE; + limLba = n.Part.GetLimit(); + n.Size = n.Part.GetSize(); + _items.Add(n); + } + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_END, &_totalSize)); + RINOK(ReadTables(stream, 0, 0, 0)); + if (_items.IsEmpty()) + return S_FALSE; + UInt32 lbaLimit = _items.Back().Part.GetLimit(); + UInt64 lim = (UInt64)lbaLimit << 9; + if (lim < _totalSize) + { + CItem n; + n.Part.Lba = lbaLimit; + n.Size = _totalSize - lim; + n.IsReal = false; + _items.Add(n); + } + _stream = stream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +enum +{ + kpidPrimary = kpidUserDefined, + kpidBegChs, + kpidEndChs +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidFileSystem, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { L"Primary", kpidPrimary, VT_BOOL}, + { L"Begin CHS", kpidBegChs, VT_BSTR}, + { L"End CHS", kpidEndChs, VT_BSTR} +}; + +IMP_IInArchive_Props_WITH_NAME +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidMainSubfile: + { + int mainIndex = -1; + for (int i = 0; i < _items.Size(); i++) + if (_items[i].IsReal) + { + if (mainIndex >= 0) + { + mainIndex = -1; + break; + } + mainIndex = i; + } + if (mainIndex >= 0) + prop = (UInt32)mainIndex; + break; + } + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + const CItem &item = _items[index]; + const CPartition &part = item.Part; + switch(propID) + { + case kpidPath: + { + AString s; + AddUIntToString(index, s); + if (item.IsReal) + { + int typeIndex = FindPartType(part.Type); + s += '.'; + const char *ext = "img"; + if (typeIndex >= 0 && kPartTypes[typeIndex].Ext != 0) + ext = kPartTypes[typeIndex].Ext; + s += ext; + } + prop = s; + break; + } + case kpidFileSystem: + if (item.IsReal) + { + char s[32]; + ConvertUInt32ToString(part.Type, s); + const char *res = s; + int typeIndex = FindPartType(part.Type); + if (typeIndex >= 0 && kPartTypes[typeIndex].Name) + res = kPartTypes[typeIndex].Name; + prop = res; + } + break; + case kpidSize: prop = item.Size; break;; + case kpidPackSize: prop = item.Size; break; + case kpidOffset: prop = part.GetPos(); break; + case kpidPrimary: if (item.IsReal) prop = item.IsPrim; break; + case kpidBegChs: if (item.IsReal) part.BeginChs.ToString(prop); break; + case kpidEndChs: if (item.IsReal) part.EndChs.ToString(prop); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + totalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + const CPartition &part = item.Part; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + totalSize += item.Size; + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + RINOK(_stream->Seek(part.GetPos(), STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == item.Size ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItem &item = _items[index]; + return CreateLimitedInStream(_stream, item.Part.GetPos(), item.Size, stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"MBR", L"mbr", 0, 0xDB, { 1, 1, 0 }, 3, false, CreateArc, 0 }; + +REGISTER_ARC(Mbr) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/MslzHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/MslzHandler.cpp new file mode 100644 index 000000000..67495e765 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/MslzHandler.cpp @@ -0,0 +1,257 @@ +// MslzHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/InBuffer.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "Common/DummyOutStream.h" + +namespace NArchive { +namespace NMslz { + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + UInt32 _size; + UInt64 _packSize; + UString _name; +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPath: if (!_name.IsEmpty()) prop = _name; break; + case kpidSize: prop = _size; break; + case kpidPackSize: prop = _packSize; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +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 wchar_t *g_Exts[] = +{ + L"dll", + L"exe", + L"kmd", + L"sys" +}; + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + Close(); + Byte buffer[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buffer, kHeaderSize)); + if (memcmp(buffer, signature, kSignatureSize) != 0) + return S_FALSE; + _size = GetUi32(buffer + 10); + if (_size > 0xFFFFFFE0) + return S_FALSE; + RINOK(stream->Seek(0, STREAM_SEEK_END, &_packSize)); + + 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; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _name.Empty(); + return S_OK; +} + +// MslzDec is modified LZSS algorithm of Haruhiko Okumura: +// maxLen = 18; Okumura +// maxLen = 16; MS + +#define PROGRESS_AND_WRITE \ + if ((dest & kMask) == 0) { RINOK(WriteStream(outStream, buf, kBufSize)); \ + if ((dest & ((1 << 20) - 1)) == 0) \ + { UInt64 inSize = inStream.GetProcessedSize(); UInt64 outSize = dest; \ + RINOK(progress->SetRatioInfo(&inSize, &outSize)); }} + +static HRESULT MslzDec(CInBuffer &inStream, ISequentialOutStream *outStream, UInt32 unpackSize, ICompressProgressInfo *progress) +{ + const unsigned kBufSize = (1 << 12); + const unsigned kMask = kBufSize - 1; + Byte buf[kBufSize]; + UInt32 dest = 0; + memset(buf, ' ', kBufSize); + while (dest < unpackSize) + { + Byte b; + if (!inStream.ReadByte(b)) + return S_FALSE; + for (unsigned mask = (unsigned)b | 0x100; mask > 1 && dest < unpackSize; mask >>= 1) + { + if (!inStream.ReadByte(b)) + return S_FALSE; + if (mask & 1) + { + buf[dest++ & kMask] = b; + PROGRESS_AND_WRITE + } + else + { + Byte b1; + if (!inStream.ReadByte(b1)) + 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; + if (len > kMaxLen || dest + len > unpackSize) + return S_FALSE; + do + { + buf[dest++ & kMask] = buf[src++ & kMask]; + PROGRESS_AND_WRITE + } + while (--len != 0); + } + } + } + return WriteStream(outStream, buf, dest & kMask); +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + extractCallback->SetTotal(_size); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + 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]; + Int32 opRes = NExtract::NOperationResult::kDataError; + if (s.ReadBytes(buffer, kHeaderSize) == kHeaderSize) + { + HRESULT result = MslzDec(s, outStream, _size, progress); + if (result == S_OK) + opRes = NExtract::NOperationResult::kOK; + else if (result != S_FALSE) + return result; + } + outStream.Release(); + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"MsLZ", L"", 0, 0xD5, MSLZ_SIGNATURE, kSignatureSize, false, CreateArc, 0 }; + +REGISTER_ARC(Mslz) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/MubHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/MubHandler.cpp new file mode 100644 index 000000000..da4df24c9 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/MubHandler.cpp @@ -0,0 +1,266 @@ +// MubHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#define Get32(p) GetBe32(p) + +namespace NArchive { +namespace NMub { + +struct CItem +{ + UInt32 Type; + UInt32 SubType; + UInt64 Offset; + UInt64 Size; + UInt32 Align; + bool IsTail; +}; + +const UInt32 kNumFilesMax = 10; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + UInt64 _startPos; + CMyComPtr<IInStream> _stream; + UInt32 _numItems; + CItem _items[kNumFilesMax + 1]; + HRESULT Open2(IInStream *stream); +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +#define MACH_ARCH_ABI64 0x1000000 +#define MACH_MACHINE_386 7 +#define MACH_MACHINE_ARM 12 +#define MACH_MACHINE_SPARC 14 +#define MACH_MACHINE_PPC 18 + +#define MACH_MACHINE_PPC64 (MACH_MACHINE_PPC | MACH_ARCH_ABI64) +#define MACH_MACHINE_AMD64 (MACH_MACHINE_386 | MACH_ARCH_ABI64) + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + const CItem &item = _items[index]; + switch(propID) + { + case kpidExtension: + { + const wchar_t *ext; + if (item.IsTail) + ext = L"tail"; + else + { + switch(item.Type) + { + case MACH_MACHINE_386: ext = L"86"; break; + case MACH_MACHINE_ARM: ext = L"arm"; break; + case MACH_MACHINE_SPARC: ext = L"sparc"; break; + case MACH_MACHINE_PPC: ext = L"ppc"; break; + case MACH_MACHINE_PPC64: ext = L"ppc64"; break; + case MACH_MACHINE_AMD64: ext = L"x64"; break; + default: ext = L"unknown"; break; + } + } + prop = ext; + break; + } + case kpidSize: + case kpidPackSize: + prop = (UInt64)item.Size; + break; + } + prop.Detach(value); + return S_OK; +} + +#define MACH_TYPE_ABI64 (1 << 24) +#define MACH_SUBTYPE_ABI64 (1 << 31) + +HRESULT CHandler::Open2(IInStream *stream) +{ + RINOK(stream->Seek(0, STREAM_SEEK_SET, &_startPos)); + + const UInt32 kHeaderSize = 8; + const UInt32 kRecordSize = 5 * 4; + const UInt32 kBufSize = kHeaderSize + kNumFilesMax * kRecordSize; + Byte buf[kBufSize]; + size_t processed = kBufSize; + RINOK(ReadStream(stream, buf, &processed)); + if (processed < kHeaderSize) + return S_FALSE; + UInt32 num = Get32(buf + 4); + if (Get32(buf) != 0xCAFEBABE || num > kNumFilesMax || processed < kHeaderSize + num * kRecordSize) + return S_FALSE; + UInt64 endPosMax = kHeaderSize; + for (UInt32 i = 0; i < num; i++) + { + const Byte *p = buf + kHeaderSize + i * kRecordSize; + CItem &sb = _items[i]; + sb.IsTail = false; + sb.Type = Get32(p); + sb.SubType = Get32(p + 4); + sb.Offset = Get32(p + 8); + sb.Size = Get32(p + 12); + sb.Align = Get32(p + 16); + + if ((sb.Type & ~MACH_TYPE_ABI64) >= 0x100 || + (sb.SubType & ~MACH_SUBTYPE_ABI64) >= 0x100 || + sb.Align > 31) + return S_FALSE; + + UInt64 endPos = (UInt64)sb.Offset + sb.Size; + if (endPos > endPosMax) + endPosMax = endPos; + } + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + fileSize -= _startPos; + _numItems = num; + if (fileSize > endPosMax) + { + CItem &sb = _items[_numItems++]; + sb.IsTail = true; + sb.Type = 0; + sb.SubType = 0; + sb.Offset = endPosMax; + sb.Size = fileSize - endPosMax; + sb.Align = 0; + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + try + { + if (Open2(inStream) != S_OK) + return S_FALSE; + _stream = inStream; + } + catch(...) { return S_FALSE; } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _numItems = 0; + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _numItems; + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _numItems; + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + currentTotalSize += item.Size; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (testMode) + { + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + RINOK(_stream->Seek(_startPos + item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(item.Size); + RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItem &item = _items[index]; + return CreateLimitedInStream(_stream, _startPos + item.Offset, item.Size, stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Mub", L"", 0, 0xE2, { 0xCA, 0xFE, 0xBA, 0xBE, 0, 0, 0 }, 7, false, CreateArc, 0 }; + +REGISTER_ARC(Mub) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.cpp new file mode 100644 index 000000000..0845f965d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.cpp @@ -0,0 +1,130 @@ +// NsisDecode.cpp + +#include "StdAfx.h" + +#include "NsisDecode.h" + +#include "../../Common/StreamUtils.h" + +#include "../../Common/MethodId.h" + +#include "../../Compress/BZip2Decoder.h" +#include "../../Compress/DeflateDecoder.h" +#include "../../Compress/LzmaDecoder.h" + +namespace NArchive { +namespace NNsis { + +static const CMethodId k_BCJ_X86 = 0x03030103; + +HRESULT CDecoder::Init( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, NMethodType::EEnum method, bool thereIsFilterFlag, bool &useFilter) +{ + useFilter = false; + CObjectVector< CMyComPtr<ISequentialInStream> > inStreams; + + if (_decoderInStream) + if (method != _method) + Release(); + _method = method; + if (!_codecInStream) + { + switch (method) + { + // case NMethodType::kCopy: return E_NOTIMPL; + case NMethodType::kDeflate: _codecInStream = new NCompress::NDeflate::NDecoder::CNsisCOMCoder(); break; + case NMethodType::kBZip2: _codecInStream = new NCompress::NBZip2::CNsisDecoder(); break; + case NMethodType::kLZMA: _codecInStream = new NCompress::NLzma::CDecoder(); break; + default: return E_NOTIMPL; + } + } + + if (thereIsFilterFlag) + { + UInt32 processedSize; + BYTE flag; + RINOK(inStream->Read(&flag, 1, &processedSize)); + if (processedSize != 1) + return E_FAIL; + if (flag > 1) + return E_NOTIMPL; + useFilter = (flag != 0); + } + + if (useFilter) + { + if (!_filterInStream) + { + CMyComPtr<ICompressCoder> coder; + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + k_BCJ_X86, coder, false)); + if (!coder) + return E_NOTIMPL; + coder.QueryInterface(IID_ISequentialInStream, &_filterInStream); + if (!_filterInStream) + return E_NOTIMPL; + } + CMyComPtr<ICompressSetInStream> setInStream; + _filterInStream.QueryInterface(IID_ICompressSetInStream, &setInStream); + if (!setInStream) + return E_NOTIMPL; + RINOK(setInStream->SetInStream(_codecInStream)); + _decoderInStream = _filterInStream; + } + else + _decoderInStream = _codecInStream; + + if (method == NMethodType::kLZMA) + { + CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties; + _codecInStream.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties); + if (setDecoderProperties) + { + static const UInt32 kPropertiesSize = 5; + BYTE properties[kPropertiesSize]; + UInt32 processedSize; + RINOK(inStream->Read(properties, kPropertiesSize, &processedSize)); + if (processedSize != kPropertiesSize) + return E_FAIL; + RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)properties, kPropertiesSize)); + } + } + + { + CMyComPtr<ICompressSetInStream> setInStream; + _codecInStream.QueryInterface(IID_ICompressSetInStream, &setInStream); + if (!setInStream) + return E_NOTIMPL; + RINOK(setInStream->SetInStream(inStream)); + } + + { + CMyComPtr<ICompressSetOutStreamSize> setOutStreamSize; + _codecInStream.QueryInterface(IID_ICompressSetOutStreamSize, &setOutStreamSize); + if (!setOutStreamSize) + return E_NOTIMPL; + RINOK(setOutStreamSize->SetOutStreamSize(NULL)); + } + + if (useFilter) + { + /* + CMyComPtr<ICompressSetOutStreamSize> setOutStreamSize; + _filterInStream.QueryInterface(IID_ICompressSetOutStreamSize, &setOutStreamSize); + if (!setOutStreamSize) + return E_NOTIMPL; + RINOK(setOutStreamSize->SetOutStreamSize(NULL)); + */ + } + + return S_OK; +} + +HRESULT CDecoder::Read(void *data, size_t *processedSize) +{ + return ReadStream(_decoderInStream, data, processedSize);; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.h b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.h new file mode 100644 index 000000000..36aeb2b14 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisDecode.h @@ -0,0 +1,47 @@ +// NsisDecode.h + +#ifndef __NSIS_DECODE_H +#define __NSIS_DECODE_H + +#include "../../IStream.h" + +#include "../../Common/CreateCoder.h" + +namespace NArchive { +namespace NNsis { + +namespace NMethodType +{ + enum EEnum + { + kCopy, + kDeflate, + kBZip2, + kLZMA + }; +} + +class CDecoder +{ + NMethodType::EEnum _method; + + CMyComPtr<ISequentialInStream> _filterInStream; + CMyComPtr<ISequentialInStream> _codecInStream; + CMyComPtr<ISequentialInStream> _decoderInStream; + +public: + void Release() + { + _filterInStream.Release(); + _codecInStream.Release(); + _decoderInStream.Release(); + } + HRESULT Init( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, NMethodType::EEnum method, bool thereIsFilterFlag, bool &useFilter); + HRESULT Read(void *data, size_t *processedSize); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.cpp new file mode 100644 index 000000000..4058bd2af --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.cpp @@ -0,0 +1,510 @@ +// NSisHandler.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" + +#include "../../Common/StreamUtils.h" + +#include "../Common/ItemNameUtils.h" + +#include "NsisHandler.h" + +#define Get32(p) GetUi32(p) + +using namespace NWindows; + +namespace NArchive { +namespace NNsis { + +static const char *kBcjMethod = "BCJ"; +static const char *kUnknownMethod = "Unknown"; + +static const char *kMethods[] = +{ + "Copy", + "Deflate", + "BZip2", + "LZMA" +}; + +static const int kNumMethods = sizeof(kMethods) / sizeof(kMethods[0]); + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidSolid, VT_BOOL} +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidSolid, VT_BOOL} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + UInt32 dict = 1; + bool filter = false; + for (int i = 0; i < _archive.Items.Size(); i++) + { + const CItem &item = _archive.Items[i]; + filter |= item.UseFilter; + if (item.DictionarySize > dict) + dict = item.DictionarySize; + } + prop = GetMethod(filter, dict); + break; + } + case kpidSolid: prop = _archive.IsSolid; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * maxCheckStartPosition, IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + { + if (_archive.Open( + EXTERNAL_CODECS_VARS + stream, maxCheckStartPosition) != S_OK) + return S_FALSE; + _inStream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _archive.Clear(); + _archive.Release(); + _inStream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _archive.Items.Size() + #ifdef NSIS_SCRIPT + + 1 + #endif + ; + return S_OK; +} + +static AString UInt32ToString(UInt32 value) +{ + char buffer[16]; + ConvertUInt32ToString(value, buffer); + return buffer; +} + +static AString GetStringForSizeValue(UInt32 value) +{ + for (int i = 31; i >= 0; i--) + if (((UInt32)1 << i) == value) + return UInt32ToString(i); + char c = 'b'; + if (value % (1 << 20) == 0) + { + value >>= 20; + c = 'm'; + } + else if (value % (1 << 10) == 0) + { + value >>= 10; + c = 'k'; + } + return UInt32ToString(value) + c; +} + +AString CHandler::GetMethod(bool useItemFilter, UInt32 dictionary) const +{ + NMethodType::EEnum methodIndex = _archive.Method; + AString method; + if (_archive.IsSolid && _archive.UseFilter || !_archive.IsSolid && useItemFilter) + { + method += kBcjMethod; + method += ' '; + } + method += (methodIndex < kNumMethods) ? kMethods[methodIndex] : kUnknownMethod; + if (methodIndex == NMethodType::kLZMA) + { + method += ':'; + method += GetStringForSizeValue(_archive.IsSolid ? _archive.DictionarySize: dictionary); + } + return method; +} + +bool CHandler::GetUncompressedSize(int index, UInt32 &size) +{ + size = 0; + const CItem &item = _archive.Items[index]; + if (item.SizeIsDefined) + size = item.Size; + else if (_archive.IsSolid && item.EstimatedSizeIsDefined) + size = item.EstimatedSize; + else + return false; + return true; +} + +bool CHandler::GetCompressedSize(int index, UInt32 &size) +{ + size = 0; + const CItem &item = _archive.Items[index]; + if (item.CompressedSizeIsDefined) + size = item.CompressedSize; + else + { + if (_archive.IsSolid) + { + if (index == 0) + size = _archive.FirstHeader.GetDataSize(); + else + return false; + } + else + { + if (!item.IsCompressed) + size = item.Size; + else + return false; + } + } + return true; +} + + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + #ifdef NSIS_SCRIPT + if (index >= (UInt32)_archive.Items.Size()) + { + switch(propID) + { + case kpidPath: prop = L"[NSIS].nsi"; break; + case kpidSize: + case kpidPackSize: prop = (UInt64)_archive.Script.Length(); break; + case kpidSolid: prop = false; break; + } + } + else + #endif + { + const CItem &item = _archive.Items[index]; + switch(propID) + { + case kpidPath: + { + UString s = NItemName::WinNameToOSName(item.GetReducedName(_archive.IsUnicode)); + if (!s.IsEmpty()) + prop = (const wchar_t *)s; + break; + } + case kpidSize: + { + UInt32 size; + if (GetUncompressedSize(index, size)) + prop = (UInt64)size; + break; + } + case kpidPackSize: + { + UInt32 size; + if (GetCompressedSize(index, size)) + prop = (UInt64)size; + break; + } + case kpidMTime: + { + if (item.MTime.dwHighDateTime > 0x01000000 && + item.MTime.dwHighDateTime < 0xFF000000) + prop = item.MTime; + break; + } + case kpidMethod: prop = GetMethod(item.UseFilter, item.DictionarySize); break; + case kpidSolid: prop = _archive.IsSolid; break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + GetNumberOfItems(&numItems); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + + UInt32 i; + for (i = 0; i < numItems; i++) + { + UInt32 index = (allFilesMode ? i : indices[i]); + #ifdef NSIS_SCRIPT + if (index >= (UInt32)_archive.Items.Size()) + totalSize += _archive.Script.Length(); + else + #endif + { + UInt32 size; + if (_archive.IsSolid) + { + GetUncompressedSize(index, size); + UInt64 pos = _archive.GetPosOfSolidItem(index); + if (pos > totalSize) + totalSize = pos + size; + } + else + { + GetCompressedSize(index, size); + totalSize += size; + } + } + } + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt32 currentItemSize = 0; + + UInt64 streamPos = 0; + if (_archive.IsSolid) + { + RINOK(_inStream->Seek(_archive.StreamOffset, STREAM_SEEK_SET, NULL)); + bool useFilter; + RINOK(_archive.Decoder.Init( + EXTERNAL_CODECS_VARS + _inStream, _archive.Method, _archive.FilterFlag, useFilter)); + } + + CByteBuffer byteBuf; + const UInt32 kBufferLength = 1 << 16; + byteBuf.SetCapacity(kBufferLength); + Byte *buffer = byteBuf; + + CByteBuffer tempBuf; + + bool dataError = false; + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + currentItemSize = 0; + RINOK(extractCallback->SetCompleted(¤tTotalSize)); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + #ifdef NSIS_SCRIPT + if (index >= (UInt32)_archive.Items.Size()) + { + currentItemSize = _archive.Script.Length(); + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + if (!testMode) + RINOK(WriteStream(realOutStream, (const char *)_archive.Script, (UInt32)_archive.Script.Length())); + } + else + #endif + { + const CItem &item = _archive.Items[index]; + + if (_archive.IsSolid) + GetUncompressedSize(index, currentItemSize); + else + GetCompressedSize(index, currentItemSize); + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + + if (!dataError) + { + bool needDecompress = false; + bool sizeIsKnown = false; + UInt32 fullSize = 0; + + bool writeToTemp = false; + bool readFromTemp = false; + + if (_archive.IsSolid) + { + UInt64 pos = _archive.GetPosOfSolidItem(index); + while (streamPos < pos) + { + size_t processedSize = (UInt32)MyMin(pos - streamPos, (UInt64)kBufferLength); + HRESULT res = _archive.Decoder.Read(buffer, &processedSize); + if (res != S_OK) + { + if (res != S_FALSE) + return res; + dataError = true; + break; + } + if (processedSize == 0) + { + dataError = true; + break; + } + streamPos += processedSize; + } + if (streamPos == pos) + { + Byte buffer2[4]; + size_t processedSize = 4; + RINOK(_archive.Decoder.Read(buffer2, &processedSize)); + if (processedSize != 4) + return E_FAIL; + streamPos += processedSize; + fullSize = Get32(buffer2); + sizeIsKnown = true; + needDecompress = true; + + if (!testMode && i + 1 < numItems) + { + UInt64 nextPos = _archive.GetPosOfSolidItem(allFilesMode ? i : indices[i + 1]); + if (nextPos < streamPos + fullSize) + { + tempBuf.Free(); + tempBuf.SetCapacity(fullSize); + writeToTemp = true; + } + } + } + else + readFromTemp = true; + } + else + { + RINOK(_inStream->Seek(_archive.GetPosOfNonSolidItem(index) + 4, STREAM_SEEK_SET, NULL)); + if (item.IsCompressed) + { + needDecompress = true; + bool useFilter; + RINOK(_archive.Decoder.Init( + EXTERNAL_CODECS_VARS + _inStream, _archive.Method, _archive.FilterFlag, useFilter)); + // fullSize = Get32(buffer); // It's bug !!! + // Test it: what is exact fullSize? + fullSize = 0xFFFFFFFF; + } + else + fullSize = item.Size; + } + if (!dataError) + { + if (needDecompress) + { + UInt64 offset = 0; + while (!sizeIsKnown || fullSize > 0) + { + UInt32 curSize = kBufferLength; + if (sizeIsKnown && curSize > fullSize) + curSize = fullSize; + size_t processedSize = curSize; + HRESULT res = _archive.Decoder.Read(buffer, &processedSize); + if (res != S_OK) + { + if (res != S_FALSE) + return res; + dataError = true; + break; + } + if (processedSize == 0) + { + if (sizeIsKnown) + dataError = true; + break; + } + + if (writeToTemp) + memcpy((Byte *)tempBuf + (size_t)offset, buffer, processedSize); + + fullSize -= (UInt32)processedSize; + streamPos += processedSize; + offset += processedSize; + + UInt64 completed; + if (_archive.IsSolid) + completed = currentTotalSize + offset; + else + completed = streamPos; + RINOK(extractCallback->SetCompleted(&completed)); + if (!testMode) + RINOK(WriteStream(realOutStream, buffer, processedSize)); + } + } + else + { + if (readFromTemp) + { + if (!testMode) + RINOK(WriteStream(realOutStream, tempBuf, tempBuf.GetCapacity())); + } + else + while (fullSize > 0) + { + UInt32 curSize = MyMin(fullSize, kBufferLength); + UInt32 processedSize; + RINOK(_inStream->Read(buffer, curSize, &processedSize)); + if (processedSize == 0) + { + dataError = true; + break; + } + fullSize -= processedSize; + streamPos += processedSize; + if (!testMode) + RINOK(WriteStream(realOutStream, buffer, processedSize)); + } + } + } + } + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(dataError ? + NExtract::NOperationResult::kDataError : + NExtract::NOperationResult::kOK)); + } + return S_OK; + COM_TRY_END +} + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.h new file mode 100644 index 000000000..6de493df8 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisHandler.h @@ -0,0 +1,43 @@ +// NSisHandler.h + +#ifndef __NSIS_HANDLER_H +#define __NSIS_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" + +#include "NsisIn.h" + +#include "../../Common/CreateCoder.h" + +namespace NArchive { +namespace NNsis { + +class CHandler: + public IInArchive, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + CInArchive _archive; + + DECL_EXTERNAL_CODECS_VARS + + bool GetUncompressedSize(int index, UInt32 &size); + bool GetCompressedSize(int index, UInt32 &size); + + AString GetMethod(bool useItemFilter, UInt32 dictionary) const; +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + + DECL_ISetCompressCodecsInfo +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.cpp new file mode 100644 index 000000000..407560085 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.cpp @@ -0,0 +1,1461 @@ +// NsisIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/IntToString.h" + +#include "../../Common/StreamUtils.h" + +#include "NsisIn.h" + +#define Get32(p) GetUi32(p) + +namespace NArchive { +namespace NNsis { + +Byte kSignature[kSignatureSize] = NSIS_SIGNATURE; + +#ifdef NSIS_SCRIPT +static const char *kCrLf = "\x0D\x0A"; +#endif + +#define NS_UN_SKIP_CODE 0xE000 +#define NS_UN_VAR_CODE 0xE001 +#define NS_UN_SHELL_CODE 0xE002 +#define NS_UN_LANG_CODE 0xE003 +#define NS_UN_CODES_START NS_UN_SKIP_CODE +#define NS_UN_CODES_END NS_UN_LANG_CODE + +Byte CInArchive::ReadByte() +{ + if (_posInData >= _size) + throw 1; + return _data[_posInData++]; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + value |= ((UInt32)(ReadByte()) << (8 * i)); + return value; +} + +void CInArchive::ReadBlockHeader(CBlockHeader &bh) +{ + bh.Offset = ReadUInt32(); + bh.Num = ReadUInt32(); +} + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareItems(void *const *p1, void *const *p2, void * /* param */) +{ + const CItem &i1 = **(CItem **)p1; + const CItem &i2 = **(CItem **)p2; + RINOZ(MyCompare(i1.Pos, i2.Pos)); + if (i1.IsUnicode) + { + RINOZ(i1.PrefixU.Compare(i2.PrefixU)); + RINOZ(i1.NameU.Compare(i2.NameU)); + } + else + { + RINOZ(i1.PrefixA.Compare(i2.PrefixA)); + RINOZ(i1.NameA.Compare(i2.NameA)); + } + return 0; +} + +static AString UIntToString(UInt32 v) +{ + char sz[32]; + ConvertUInt64ToString(v, sz); + return sz; +} + +static AString IntToString(Int32 v) +{ + char sz[32]; + ConvertInt64ToString(v, sz); + return sz; +} + +AString CInArchive::ReadStringA(UInt32 pos) const +{ + AString s; + if (pos >= _size) + return IntToString((Int32)pos); + UInt32 offset = GetOffset() + _stringsPos + pos; + for (;;) + { + if (offset >= _size) + break; // throw 1; + char c = _data[offset++]; + if (c == 0) + break; + s += c; + } + return s; +} + +UString CInArchive::ReadStringU(UInt32 pos) const +{ + UString s; + UInt32 offset = GetOffset() + _stringsPos + (pos * 2); + for (;;) + { + if (offset >= _size || offset + 1 >= _size) + return s; // throw 1; + char c0 = _data[offset++]; + char c1 = _data[offset++]; + wchar_t c = (c0 | ((wchar_t)c1 << 8)); + if (c == 0) + break; + s += c; + } + return s; +} + +/* +static AString ParsePrefix(const AString &prefix) +{ + AString res = prefix; + if (prefix.Length() >= 3) + { + if ((Byte)prefix[0] == 0xFD && (Byte)prefix[1] == 0x95 && (Byte)prefix[2] == 0x80) + res = "$INSTDIR" + prefix.Mid(3); + else if ((Byte)prefix[0] == 0xFD && (Byte)prefix[1] == 0x96 && (Byte)prefix[2] == 0x80) + res = "$OUTDIR" + prefix.Mid(3); + } + return res; +} +*/ + +#define SYSREGKEY "Software\\Microsoft\\Windows\\CurrentVersion" + +/* +# define CSIDL_PROGRAMS 0x2 +# define CSIDL_PRINTERS 0x4 +# define CSIDL_PERSONAL 0x5 +# define CSIDL_FAVORITES 0x6 +# define CSIDL_STARTUP 0x7 +# define CSIDL_RECENT 0x8 +# define CSIDL_SENDTO 0x9 +# define CSIDL_STARTMENU 0xB +# define CSIDL_MYMUSIC 0xD +# define CSIDL_MYVIDEO 0xE + +# define CSIDL_DESKTOPDIRECTORY 0x10 +# define CSIDL_NETHOOD 0x13 +# define CSIDL_FONTS 0x14 +# define CSIDL_TEMPLATES 0x15 +# define CSIDL_COMMON_STARTMENU 0x16 +# define CSIDL_COMMON_PROGRAMS 0x17 +# define CSIDL_COMMON_STARTUP 0x18 +# define CSIDL_COMMON_DESKTOPDIRECTORY 0x19 +# define CSIDL_APPDATA 0x1A +# define CSIDL_PRINTHOOD 0x1B +# define CSIDL_LOCAL_APPDATA 0x1C +# define CSIDL_ALTSTARTUP 0x1D +# define CSIDL_COMMON_ALTSTARTUP 0x1E +# define CSIDL_COMMON_FAVORITES 0x1F + +# define CSIDL_INTERNET_CACHE 0x20 +# define CSIDL_COOKIES 0x21 +# define CSIDL_HISTORY 0x22 +# define CSIDL_COMMON_APPDATA 0x23 +# define CSIDL_WINDOWS 0x24 +# define CSIDL_SYSTEM 0x25 +# define CSIDL_PROGRAM_FILES 0x26 +# define CSIDL_MYPICTURES 0x27 +# define CSIDL_PROFILE 0x28 +# define CSIDL_PROGRAM_FILES_COMMON 0x2B +# define CSIDL_COMMON_TEMPLATES 0x2D +# define CSIDL_COMMON_DOCUMENTS 0x2E +# define CSIDL_COMMON_ADMINTOOLS 0x2F + +# define CSIDL_ADMINTOOLS 0x30 +# define CSIDL_COMMON_MUSIC 0x35 +# define CSIDL_COMMON_PICTURES 0x36 +# define CSIDL_COMMON_VIDEO 0x37 +# define CSIDL_RESOURCES 0x38 +# define CSIDL_RESOURCES_LOCALIZED 0x39 +# define CSIDL_CDBURN_AREA 0x3B +*/ + +struct CCommandPair +{ + int NumParams; + const char *Name; +}; + +enum +{ + // 0 + EW_INVALID_OPCODE, // zero is invalid. useful for catching errors. (otherwise an all zeroes instruction + // does nothing, which is easily ignored but means something is wrong. + EW_RET, // return from function call + EW_NOP, // Nop/Jump, do nothing: 1, [?new address+1:advance one] + EW_ABORT, // Abort: 1 [status] + EW_QUIT, // Quit: 0 + EW_CALL, // Call: 1 [new address+1] + EW_UPDATETEXT, // Update status text: 2 [update str, ui_st_updateflag=?ui_st_updateflag:this] + EW_SLEEP, // Sleep: 1 [sleep time in milliseconds] + EW_BRINGTOFRONT, // BringToFront: 0 + EW_CHDETAILSVIEW, // SetDetailsView: 2 [listaction,buttonaction] + + // 10 + EW_SETFILEATTRIBUTES, // SetFileAttributes: 2 [filename, attributes] + EW_CREATEDIR, // Create directory: 2, [path, ?update$INSTDIR] + EW_IFFILEEXISTS, // IfFileExists: 3, [file name, jump amount if exists, jump amount if not exists] + EW_SETFLAG, // Sets a flag: 2 [id, data] + EW_IFFLAG, // If a flag: 4 [on, off, id, new value mask] + EW_GETFLAG, // Gets a flag: 2 [output, id] + EW_RENAME, // Rename: 3 [old, new, rebootok] + EW_GETFULLPATHNAME, // GetFullPathName: 2 [output, input, ?lfn:sfn] + EW_SEARCHPATH, // SearchPath: 2 [output, filename] + EW_GETTEMPFILENAME, // GetTempFileName: 2 [output, base_dir] + + // 20 + EW_EXTRACTFILE, // File to extract: 6 [overwriteflag, output filename, compressed filedata, filedatetimelow, filedatetimehigh, allow ignore] + // overwriteflag: 0x1 = no. 0x0=force, 0x2=try, 0x3=if date is newer + EW_DELETEFILE, // Delete File: 2, [filename, rebootok] + EW_MESSAGEBOX, // MessageBox: 5,[MB_flags,text,retv1:retv2,moveonretv1:moveonretv2] + EW_RMDIR, // RMDir: 2 [path, recursiveflag] + EW_STRLEN, // StrLen: 2 [output, input] + EW_ASSIGNVAR, // Assign: 4 [variable (0-9) to assign, string to assign, maxlen, startpos] + EW_STRCMP, // StrCmp: 5 [str1, str2, jump_if_equal, jump_if_not_equal, case-sensitive?] + EW_READENVSTR, // ReadEnvStr/ExpandEnvStrings: 3 [output, string_with_env_variables, IsRead] + EW_INTCMP, // IntCmp: 6 [val1, val2, equal, val1<val2, val1>val2, unsigned?] + EW_INTOP, // IntOp: 4 [output, input1, input2, op] where op: 0=add, 1=sub, 2=mul, 3=div, 4=bor, 5=band, 6=bxor, 7=bnot input1, 8=lnot input1, 9=lor, 10=land], 11=1%2 + + // 30 + EW_INTFMT, // IntFmt: [output, format, input] + EW_PUSHPOP, // Push/Pop/Exchange: 3 [variable/string, ?pop:push, ?exch] + EW_FINDWINDOW, // FindWindow: 5, [outputvar, window class,window name, window_parent, window_after] + EW_SENDMESSAGE, // SendMessage: 6 [output, hwnd, msg, wparam, lparam, [wparamstring?1:0 | lparamstring?2:0 | timeout<<2] + EW_ISWINDOW, // IsWindow: 3 [hwnd, jump_if_window, jump_if_notwindow] + EW_GETDLGITEM, // GetDlgItem: 3: [outputvar, dialog, item_id] + EW_SETCTLCOLORS, // SerCtlColors: 3: [hwnd, pointer to struct colors] + EW_SETBRANDINGIMAGE, // SetBrandingImage: 1: [Bitmap file] + EW_CREATEFONT, // CreateFont: 5: [handle output, face name, height, weight, flags] + EW_SHOWWINDOW, // ShowWindow: 2: [hwnd, show state] + + // 40 + EW_SHELLEXEC, // ShellExecute program: 4, [shell action, complete commandline, parameters, showwindow] + EW_EXECUTE, // Execute program: 3,[complete command line,waitflag,>=0?output errorcode] + EW_GETFILETIME, // GetFileTime; 3 [file highout lowout] + EW_GETDLLVERSION, // GetDLLVersion: 3 [file highout lowout] + EW_REGISTERDLL, // Register DLL: 3,[DLL file name, string ptr of function to call, text to put in display (<0 if none/pass parms), 1 - no unload, 0 - unload] + EW_CREATESHORTCUT, // Make Shortcut: 5, [link file, target file, parameters, icon file, iconindex|show mode<<8|hotkey<<16] + EW_COPYFILES, // CopyFiles: 3 [source mask, destination location, flags] + EW_REBOOT, // Reboot: 0 + EW_WRITEINI, // Write INI String: 4, [Section, Name, Value, INI File] + EW_READINISTR, // ReadINIStr: 4 [output, section, name, ini_file] + + // 50 + EW_DELREG, // DeleteRegValue/DeleteRegKey: 4, [root key(int), KeyName, ValueName, delkeyonlyifempty]. ValueName is -1 if delete key + EW_WRITEREG, // Write Registry value: 5, [RootKey(int),KeyName,ItemName,ItemData,typelen] + // typelen=1 for str, 2 for dword, 3 for binary, 0 for expanded str + EW_READREGSTR, // ReadRegStr: 5 [output, rootkey(int), keyname, itemname, ==1?int::str] + EW_REGENUM, // RegEnum: 5 [output, rootkey, keyname, index, ?key:value] + EW_FCLOSE, // FileClose: 1 [handle] + EW_FOPEN, // FileOpen: 4 [name, openmode, createmode, outputhandle] + EW_FPUTS, // FileWrite: 3 [handle, string, ?int:string] + EW_FGETS, // FileRead: 4 [handle, output, maxlen, ?getchar:gets] + EW_FSEEK, // FileSeek: 4 [handle, offset, mode, >=0?positionoutput] + EW_FINDCLOSE, // FindClose: 1 [handle] + + // 60 + EW_FINDNEXT, // FindNext: 2 [output, handle] + EW_FINDFIRST, // FindFirst: 2 [filespec, output, handleoutput] + EW_WRITEUNINSTALLER, // WriteUninstaller: 3 [name, offset, icon_size] + EW_LOG, // LogText: 2 [0, text] / LogSet: [1, logstate] + EW_SECTIONSET, // SectionSetText: 3: [idx, 0, text] + // SectionGetText: 3: [idx, 1, output] + // SectionSetFlags: 3: [idx, 2, flags] + // SectionGetFlags: 3: [idx, 3, output] + EW_INSTTYPESET, // InstTypeSetFlags: 3: [idx, 0, flags] + // InstTypeGetFlags: 3: [idx, 1, output] + // instructions not actually implemented in exehead, but used in compiler. + EW_GETLABELADDR, // both of these get converted to EW_ASSIGNVAR + EW_GETFUNCTIONADDR, + + EW_LOCKWINDOW +}; + +#ifdef NSIS_SCRIPT +static CCommandPair kCommandPairs[] = +{ + { 0, "Invalid" }, + { 0, "Return" }, + { 1, "Goto" }, + { 0, "Abort" }, + { 0, "Quit" }, + { 1, "Call" }, + { 2, "UpdateSatusText" }, + { 1, "Sleep" }, + { 0, "BringToFront" }, + { 2, "SetDetailsView" }, + + { 2, "SetFileAttributes" }, + { 2, "SetOutPath" }, + { 3, "IfFileExists" }, + { 2, "SetFlag" }, + { 4, "IfFlag" }, + { 2, "GetFlag" }, + { 3, "Rename" }, + { 2, "GetFullPathName" }, + { 2, "SearchPath" }, + { 2, "GetTempFileName" }, + + { 6, "File" }, + { 2, "Delete" }, + { 5, "MessageBox" }, + { 2, "RMDir" }, + { 2, "StrLen" }, + { 4, "StrCpy" }, + { 5, "StrCmp" }, + { 3, "ReadEnvStr" }, + { 6, "IntCmp" }, + { 4, "IntOp" }, + + { 3, "IntFmt" }, + { 3, "PushPop" }, + { 5, "FindWindow" }, + { 6, "SendMessage" }, + { 3, "IsWindow" }, + { 3, "GetDlgItem" }, + { 3, "SerCtlColors" }, + { 1, "SetBrandingImage" }, + { 5, "CreateFont" }, + { 2, "ShowWindow" }, + + { 4, "ShellExecute" }, + { 3, "Execute" }, + { 3, "GetFileTime" }, + { 3, "GetDLLVersion" }, + { 3, "RegisterDLL" }, + { 5, "CreateShortCut" }, + { 3, "CopyFiles" }, + { 0, "Reboot" }, + { 4, "WriteINIStr" }, + { 4, "ReadINIStr" }, + + { 4, "DelReg" }, + { 5, "WriteReg" }, + { 5, "ReadRegStr" }, + { 5, "RegEnum" }, + { 1, "FileClose" }, + { 4, "FileOpen" }, + { 3, "FileWrite" }, + { 4, "FileRead" }, + { 4, "FileSeek" }, + { 1, "FindClose" }, + + { 2, "FindNext" }, + { 2, "FindFirst" }, + { 3, "WriteUninstaller" }, + { 2, "LogText" }, + { 3, "Section?etText" }, + { 3, "InstType?etFlags" }, + { 6, "GetLabelAddr" }, + { 2, "GetFunctionAddress" }, + { 6, "LockWindow" } +}; + +#endif + +static const char *kShellStrings[] = +{ + "", + "", + + "SMPROGRAMS", + "", + "PRINTERS", + "DOCUMENTS", + "FAVORITES", + "SMSTARTUP", + "RECENT", + "SENDTO", + "", + "STARTMENU", + "", + "MUSIC", + "VIDEO", + "", + + "DESKTOP", + "", + "", + "NETHOOD", + "FONTS", + "TEMPLATES", + "COMMONSTARTMENU", + "COMMONFILES", + "COMMON_STARTUP", + "COMMON_DESKTOPDIRECTORY", + "QUICKLAUNCH", + "PRINTHOOD", + "LOCALAPPDATA", + "ALTSTARTUP", + "ALTSTARTUP", + "FAVORITES", + + "INTERNET_CACHE", + "COOKIES", + "HISTORY", + "APPDATA", + "WINDIR", + "SYSDIR", + "PROGRAMFILES", + "PICTURES", + "PROFILE", + "", + "", + "COMMONFILES", + "", + "TEMPLATES", + "DOCUMENTS", + "ADMINTOOLS", + + "ADMINTOOLS", + "", + "", + "", + "", + "MUSIC", + "PICTURES", + "VIDEO", + "RESOURCES", + "RESOURCES_LOCALIZED", + "", + "CDBURN_AREA" +}; + +static const int kNumShellStrings = sizeof(kShellStrings) / sizeof(kShellStrings[0]); + +/* +# define CMDLINE 20 // everything before here doesn't have trailing slash removal +# define INSTDIR 21 +# define OUTDIR 22 +# define EXEDIR 23 +# define LANGUAGE 24 +# define TEMP 25 +# define PLUGINSDIR 26 +# define HWNDPARENT 27 +# define _CLICK 28 +# define _OUTDIR 29 +*/ + +static const char *kVarStrings[] = +{ + "CMDLINE", + "INSTDIR", + "OUTDIR", + "EXEDIR", + "LANGUAGE", + "TEMP", + "PLUGINSDIR", + "EXEPATH", // test it + "EXEFILE", // test it + "HWNDPARENT", + "_CLICK", + "_OUTDIR" +}; + +static const int kNumVarStrings = sizeof(kVarStrings) / sizeof(kVarStrings[0]); + + +static AString GetVar(UInt32 index) +{ + AString res = "$"; + if (index < 10) + res += UIntToString(index); + else if (index < 20) + { + res += "R"; + res += UIntToString(index - 10); + } + else if (index < 20 + kNumVarStrings) + res += kVarStrings[index - 20]; + else + { + res += "["; + res += UIntToString(index); + res += "]"; + } + return res; +} + +#define NS_SKIP_CODE 252 +#define NS_VAR_CODE 253 +#define NS_SHELL_CODE 254 +#define NS_LANG_CODE 255 +#define NS_CODES_START NS_SKIP_CODE + +static AString GetShellString(int index) +{ + AString res = "$"; + if (index < kNumShellStrings) + { + const char *sz = kShellStrings[index]; + if (sz[0] != 0) + return res + sz; + } + res += "SHELL["; + res += UIntToString(index); + res += "]"; + return res; +} + +// Based on Dave Laundon's simplified process_string +AString GetNsisString(const AString &s) +{ + AString res; + for (int i = 0; i < s.Length();) + { + unsigned char nVarIdx = s[i++]; + if (nVarIdx > NS_CODES_START && i + 2 <= s.Length()) + { + int nData = s[i++] & 0x7F; + unsigned char c1 = s[i++]; + nData |= (((int)(c1 & 0x7F)) << 7); + + if (nVarIdx == NS_SHELL_CODE) + res += GetShellString(c1); + else if (nVarIdx == NS_VAR_CODE) + res += GetVar(nData); + else if (nVarIdx == NS_LANG_CODE) + res += "NS_LANG_CODE"; + } + else if (nVarIdx == NS_SKIP_CODE) + { + if (i < s.Length()) + res += s[i++]; + } + else // Normal char + res += (char)nVarIdx; + } + return res; +} + +UString GetNsisString(const UString &s) +{ + UString res; + for (int i = 0; i < s.Length();) + { + wchar_t nVarIdx = s[i++]; + if (nVarIdx > NS_UN_CODES_START && nVarIdx <= NS_UN_CODES_END) + { + if (i == s.Length()) + break; + int nData = s[i++] & 0x7FFF; + + if (nVarIdx == NS_UN_SHELL_CODE) + res += GetUnicodeString(GetShellString(nData >> 8)); + else if (nVarIdx == NS_UN_VAR_CODE) + res += GetUnicodeString(GetVar(nData)); + else if (nVarIdx == NS_UN_LANG_CODE) + res += L"NS_LANG_CODE"; + } + else if (nVarIdx == NS_UN_SKIP_CODE) + { + if (i == s.Length()) + break; + res += s[i++]; + } + else // Normal char + res += (char)nVarIdx; + } + return res; +} + +AString CInArchive::ReadString2A(UInt32 pos) const +{ + return GetNsisString(ReadStringA(pos)); +} + +UString CInArchive::ReadString2U(UInt32 pos) const +{ + return GetNsisString(ReadStringU(pos)); +} + +AString CInArchive::ReadString2(UInt32 pos) const +{ + if (IsUnicode) + return UnicodeStringToMultiByte(ReadString2U(pos)); + else + return ReadString2A(pos); +} + +AString CInArchive::ReadString2Qw(UInt32 pos) const +{ + return "\"" + ReadString2(pos) + "\""; +} + +#define DEL_DIR 1 +#define DEL_RECURSE 2 +#define DEL_REBOOT 4 +// #define DEL_SIMPLE 8 + +static const int kNumEntryParams = 6; + +struct CEntry +{ + UInt32 Which; + UInt32 Params[kNumEntryParams]; + AString GetParamsString(int numParams); + CEntry() + { + Which = 0; + for (UInt32 j = 0; j < kNumEntryParams; j++) + Params[j] = 0; + } +}; + +AString CEntry::GetParamsString(int numParams) +{ + AString s; + for (int i = 0; i < numParams; i++) + { + s += " "; + UInt32 v = Params[i]; + if (v > 0xFFF00000) + s += IntToString((Int32)Params[i]); + else + s += UIntToString(Params[i]); + } + return s; +} + +#ifdef NSIS_SCRIPT + +static AString GetRegRootID(UInt32 val) +{ + const char *s; + switch(val) + { + case 0: s = "SHCTX"; break; + case 0x80000000: s = "HKCR"; break; + case 0x80000001: s = "HKCU"; break; + case 0x80000002: s = "HKLM"; break; + case 0x80000003: s = "HKU"; break; + case 0x80000004: s = "HKPD"; break; + case 0x80000005: s = "HKCC"; break; + case 0x80000006: s = "HKDD"; break; + case 0x80000050: s = "HKPT"; break; + case 0x80000060: s = "HKPN"; break; + default: + return UIntToString(val); break; + } + return s; +} + +#endif + +HRESULT CInArchive::ReadEntries(const CBlockHeader &bh) +{ + _posInData = bh.Offset + GetOffset(); + AString prefixA; + UString prefixU; + for (UInt32 i = 0; i < bh.Num; i++) + { + CEntry e; + e.Which = ReadUInt32(); + for (UInt32 j = 0; j < kNumEntryParams; j++) + e.Params[j] = ReadUInt32(); + #ifdef NSIS_SCRIPT + if (e.Which != EW_PUSHPOP && e.Which < sizeof(kCommandPairs) / sizeof(kCommandPairs[0])) + { + const CCommandPair &pair = kCommandPairs[e.Which]; + Script += pair.Name; + } + #endif + + switch (e.Which) + { + case EW_CREATEDIR: + { + if (IsUnicode) + { + prefixU.Empty(); + prefixU = ReadString2U(e.Params[0]); + } + else + { + prefixA.Empty(); + prefixA = ReadString2A(e.Params[0]); + } + #ifdef NSIS_SCRIPT + Script += " "; + if (IsUnicode) + Script += UnicodeStringToMultiByte(prefixU); + else + Script += prefixA; + #endif + break; + } + + case EW_EXTRACTFILE: + { + CItem item; + item.IsUnicode = IsUnicode; + if (IsUnicode) + { + item.PrefixU = prefixU; + item.NameU = ReadString2U(e.Params[1]); + } + else + { + item.PrefixA = prefixA; + item.NameA = ReadString2A(e.Params[1]); + } + /* UInt32 overwriteFlag = e.Params[0]; */ + item.Pos = e.Params[2]; + item.MTime.dwLowDateTime = e.Params[3]; + item.MTime.dwHighDateTime = e.Params[4]; + /* UInt32 allowIgnore = e.Params[5]; */ + if (Items.Size() > 0) + { + /* + if (item.Pos == Items.Back().Pos) + continue; + */ + } + Items.Add(item); + #ifdef NSIS_SCRIPT + Script += " "; + + if (IsUnicode) + Script += UnicodeStringToMultiByte(item.NameU); + else + Script += item.NameA; + #endif + break; + } + + + #ifdef NSIS_SCRIPT + case EW_UPDATETEXT: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += UIntToString(e.Params[1]); + break; + } + case EW_SETFILEATTRIBUTES: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += UIntToString(e.Params[1]); + break; + } + case EW_IFFILEEXISTS: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += UIntToString(e.Params[1]); + Script += " "; + Script += UIntToString(e.Params[2]); + break; + } + case EW_RENAME: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + Script += " "; + Script += UIntToString(e.Params[2]); + break; + } + case EW_GETFULLPATHNAME: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + Script += " "; + Script += UIntToString(e.Params[2]); + break; + } + case EW_SEARCHPATH: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + break; + } + case EW_GETTEMPFILENAME: + { + AString s; + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + break; + } + + case EW_DELETEFILE: + { + UInt64 flag = e.Params[1]; + if (flag != 0) + { + Script += " "; + if (flag == DEL_REBOOT) + Script += "/REBOOTOK"; + else + Script += UIntToString(e.Params[1]); + } + Script += " "; + Script += ReadString2(e.Params[0]); + break; + } + case EW_RMDIR: + { + UInt64 flag = e.Params[1]; + if (flag != 0) + { + if ((flag & DEL_REBOOT) != 0) + Script += " /REBOOTOK"; + if ((flag & DEL_RECURSE) != 0) + Script += " /r"; + } + Script += " "; + Script += ReadString2(e.Params[0]); + break; + } + case EW_STRLEN: + { + Script += " "; + Script += GetVar(e.Params[0]);; + Script += " "; + Script += ReadString2Qw(e.Params[1]); + break; + } + case EW_ASSIGNVAR: + { + Script += " "; + Script += GetVar(e.Params[0]);; + Script += " "; + Script += ReadString2Qw(e.Params[1]); + AString maxLen, startOffset; + if (e.Params[2] != 0) + maxLen = ReadString2(e.Params[2]); + if (e.Params[3] != 0) + startOffset = ReadString2(e.Params[3]); + if (!maxLen.IsEmpty() || !startOffset.IsEmpty()) + { + Script += " "; + if (maxLen.IsEmpty()) + Script += "\"\""; + else + Script += maxLen; + if (!startOffset.IsEmpty()) + { + Script += " "; + Script += startOffset; + } + } + break; + } + case EW_STRCMP: + { + Script += " "; + + Script += " "; + Script += ReadString2Qw(e.Params[0]); + + Script += " "; + Script += ReadString2Qw(e.Params[1]); + + for (int j = 2; j < 5; j++) + { + Script += " "; + Script += UIntToString(e.Params[j]); + } + break; + } + case EW_INTCMP: + { + if (e.Params[5] != 0) + Script += "U"; + + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + + for (int i = 2; i < 5; i++) + { + Script += " "; + Script += UIntToString(e.Params[i]); + } + break; + } + case EW_INTOP: + { + Script += " "; + Script += GetVar(e.Params[0]); + Script += " "; + int numOps = 2; + AString op; + switch (e.Params[3]) + { + case 0: op = '+'; break; + case 1: op = '-'; break; + case 2: op = '*'; break; + case 3: op = '/'; break; + case 4: op = '|'; break; + case 5: op = '&'; break; + case 6: op = '^'; break; + case 7: op = '~'; numOps = 1; break; + case 8: op = '!'; numOps = 1; break; + case 9: op = "||"; break; + case 10: op = "&&"; break; + case 11: op = '%'; break; + default: op = UIntToString(e.Params[3]); + } + AString p1 = ReadString2(e.Params[1]); + if (numOps == 1) + { + Script += op; + Script += p1; + } + else + { + Script += p1; + Script += " "; + Script += op; + Script += " "; + Script += ReadString2(e.Params[2]); + } + break; + } + + case EW_PUSHPOP: + { + int isPop = (e.Params[1] != 0); + if (isPop) + { + Script += "Pop"; + Script += " "; + Script += GetVar(e.Params[0]);; + } + else + { + int isExch = (e.Params[2] != 0); + if (isExch) + { + Script += "Exch"; + } + else + { + Script += "Push"; + Script += " "; + Script += ReadString2(e.Params[0]); + } + } + break; + } + + case EW_SENDMESSAGE: + { + // SendMessage: 6 [output, hwnd, msg, wparam, lparam, [wparamstring?1:0 | lparamstring?2:0 | timeout<<2] + Script += " "; + // Script += ReadString2(e.Params[0]); + // Script += " "; + Script += ReadString2(e.Params[1]); + Script += " "; + Script += ReadString2(e.Params[2]); + + Script += " "; + UInt32 spec = e.Params[5]; + // if (spec & 1) + Script += IntToString(e.Params[3]); + // else + // Script += ReadString2(e.Params[3]); + + Script += " "; + // if (spec & 2) + Script += IntToString(e.Params[4]); + // else + // Script += ReadString2(e.Params[4]); + + if ((Int32)e.Params[0] >= 0) + { + Script += " "; + Script += GetVar(e.Params[1]); + } + + spec >>= 2; + if (spec != 0) + { + Script += " /TIMEOUT="; + Script += IntToString(spec); + } + break; + } + + case EW_GETDLGITEM: + { + Script += " "; + Script += GetVar(e.Params[0]);; + Script += " "; + Script += ReadString2(e.Params[1]); + Script += " "; + Script += ReadString2(e.Params[2]); + break; + } + + + case EW_REGISTERDLL: + { + Script += " "; + Script += ReadString2(e.Params[0]); + Script += " "; + Script += ReadString2(e.Params[1]); + Script += " "; + Script += UIntToString(e.Params[2]); + break; + } + + case EW_CREATESHORTCUT: + { + AString s; + + Script += " "; + Script += ReadString2Qw(e.Params[0]); + + Script += " "; + Script += ReadString2Qw(e.Params[1]); + + for (int j = 2; j < 5; j++) + { + Script += " "; + Script += UIntToString(e.Params[j]); + } + break; + } + + /* + case EW_DELREG: + { + AString keyName, valueName; + keyName = ReadString2(e.Params[1]); + bool isValue = (e.Params[2] != -1); + if (isValue) + { + valueName = ReadString2(e.Params[2]); + Script += "Key"; + } + else + Script += "Value"; + Script += " "; + Script += UIntToString(e.Params[0]); + Script += " "; + Script += keyName; + if (isValue) + { + Script += " "; + Script += valueName; + } + Script += " "; + Script += UIntToString(e.Params[3]); + break; + } + */ + + case EW_WRITEREG: + { + AString s; + switch(e.Params[4]) + { + case 1: s = "Str"; break; + case 2: s = "ExpandStr"; break; + case 3: s = "Bin"; break; + case 4: s = "DWORD"; break; + default: s = "?" + UIntToString(e.Params[4]); break; + } + Script += s; + Script += " "; + Script += GetRegRootID(e.Params[0]); + Script += " "; + + AString keyName, valueName; + keyName = ReadString2Qw(e.Params[1]); + Script += keyName; + Script += " "; + + valueName = ReadString2Qw(e.Params[2]); + Script += valueName; + Script += " "; + + valueName = ReadString2Qw(e.Params[3]); + Script += valueName; + Script += " "; + + break; + } + + case EW_WRITEUNINSTALLER: + { + Script += " "; + Script += ReadString2(e.Params[0]); + for (int j = 1; j < 3; j++) + { + Script += " "; + Script += UIntToString(e.Params[j]); + } + break; + } + + default: + { + int numParams = kNumEntryParams; + if (e.Which < sizeof(kCommandPairs) / sizeof(kCommandPairs[0])) + { + const CCommandPair &pair = kCommandPairs[e.Which]; + // Script += pair.Name; + numParams = pair.NumParams; + } + else + { + Script += "Unknown"; + Script += UIntToString(e.Which); + } + Script += e.GetParamsString(numParams); + } + #endif + } + #ifdef NSIS_SCRIPT + Script += kCrLf; + #endif + } + + { + Items.Sort(CompareItems, 0); + int i; + // if (IsSolid) + for (i = 0; i + 1 < Items.Size();) + { + bool sameName = IsUnicode ? + (Items[i].NameU == Items[i + 1].NameU) : + (Items[i].NameA == Items[i + 1].NameA); + if (Items[i].Pos == Items[i + 1].Pos && sameName) + Items.Delete(i + 1); + else + i++; + } + for (i = 0; i < Items.Size(); i++) + { + CItem &item = Items[i]; + UInt32 curPos = item.Pos + 4; + for (int nextIndex = i + 1; nextIndex < Items.Size(); nextIndex++) + { + UInt32 nextPos = Items[nextIndex].Pos; + if (curPos <= nextPos) + { + item.EstimatedSizeIsDefined = true; + item.EstimatedSize = nextPos - curPos; + break; + } + } + } + if (!IsSolid) + { + for (i = 0; i < Items.Size(); i++) + { + CItem &item = Items[i]; + RINOK(_stream->Seek(GetPosOfNonSolidItem(i), STREAM_SEEK_SET, NULL)); + const UInt32 kSigSize = 4 + 1 + 5; + BYTE sig[kSigSize]; + size_t processedSize = kSigSize; + RINOK(ReadStream(_stream, sig, &processedSize)); + if (processedSize < 4) + return S_FALSE; + UInt32 size = Get32(sig); + if ((size & 0x80000000) != 0) + { + item.IsCompressed = true; + // is compressed; + size &= ~0x80000000; + if (Method == NMethodType::kLZMA) + { + if (processedSize < 9) + return S_FALSE; + if (FilterFlag) + item.UseFilter = (sig[4] != 0); + item.DictionarySize = Get32(sig + 5 + (FilterFlag ? 1 : 0)); + } + } + else + { + item.IsCompressed = false; + item.Size = size; + item.SizeIsDefined = true; + } + item.CompressedSize = size; + item.CompressedSizeIsDefined = true; + } + } + } + return S_OK; +} + +HRESULT CInArchive::Parse() +{ + // UInt32 offset = ReadUInt32(); + // ???? offset == FirstHeader.HeaderLength + /* UInt32 ehFlags = */ ReadUInt32(); + CBlockHeader bhPages, bhSections, bhEntries, bhStrings, bhLangTables, bhCtlColors, bhData; + // CBlockHeader bgFont; + ReadBlockHeader(bhPages); + ReadBlockHeader(bhSections); + ReadBlockHeader(bhEntries); + ReadBlockHeader(bhStrings); + ReadBlockHeader(bhLangTables); + ReadBlockHeader(bhCtlColors); + // ReadBlockHeader(bgFont); + ReadBlockHeader(bhData); + + _stringsPos = bhStrings.Offset; + UInt32 pos = GetOffset() + _stringsPos; + int numZeros0 = 0; + int numZeros1 = 0; + int i; + const int kBlockSize = 256; + for (i = 0; i < kBlockSize; i++) + { + if (pos >= _size || pos + 1 >= _size) + break; + char c0 = _data[pos++]; + char c1 = _data[pos++]; + wchar_t c = (c0 | ((wchar_t)c1 << 8)); + + if (c >= NS_UN_CODES_START && c < NS_UN_CODES_END) + { + if (pos >= _size || pos + 1 >= _size) + break; + pos += 2; + numZeros1++; + } + else + { + if (c0 == 0 && c1 != 0) + numZeros0++; + if (c1 == 0) + numZeros1++; + } + // printf("\nnumZeros0 = %2x %2x", _data[pos + 0], _data[pos + 1]); + } + IsUnicode = (numZeros1 > numZeros0 * 3 + kBlockSize / 16); + // printf("\nnumZeros0 = %3d numZeros1 = %3d", numZeros0, numZeros1); + return ReadEntries(bhEntries); +} + +static bool IsLZMA(const Byte *p, UInt32 &dictionary) +{ + dictionary = Get32(p + 1); + return (p[0] == 0x5D && p[1] == 0x00 && p[2] == 0x00 && p[5] == 0x00); +} + +static bool IsLZMA(const Byte *p, UInt32 &dictionary, bool &thereIsFlag) +{ + if (IsLZMA(p, dictionary)) + { + thereIsFlag = false; + return true; + } + if (IsLZMA(p + 1, dictionary)) + { + thereIsFlag = true; + return true; + } + return false; +} + +static bool IsBZip2(const Byte *p) +{ + return (p[0] == 0x31 && p[1] < 14); +} + +HRESULT CInArchive::Open2( + DECL_EXTERNAL_CODECS_LOC_VARS2 + ) +{ + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &StreamOffset)); + + const UInt32 kSigSize = 4 + 1 + 5 + 1; // size, flag, lzma props, lzma first byte + BYTE sig[kSigSize]; + RINOK(ReadStream_FALSE(_stream, sig, kSigSize)); + UInt64 position; + RINOK(_stream->Seek(StreamOffset, STREAM_SEEK_SET, &position)); + + _headerIsCompressed = true; + IsSolid = true; + FilterFlag = false; + DictionarySize = 1; + + UInt32 compressedHeaderSize = Get32(sig); + + if (compressedHeaderSize == FirstHeader.HeaderLength) + { + _headerIsCompressed = false; + IsSolid = false; + Method = NMethodType::kCopy; + } + else if (IsLZMA(sig, DictionarySize, FilterFlag)) + { + Method = NMethodType::kLZMA; + } + else if (IsLZMA(sig + 4, DictionarySize, FilterFlag)) + { + IsSolid = false; + Method = NMethodType::kLZMA; + } + else if (sig[3] == 0x80) + { + IsSolid = false; + if (IsBZip2(sig + 4)) + Method = NMethodType::kBZip2; + else + Method = NMethodType::kDeflate; + } + else if (IsBZip2(sig)) + { + Method = NMethodType::kBZip2; + } + else + { + Method = NMethodType::kDeflate; + } + + _posInData = 0; + if (!IsSolid) + { + _headerIsCompressed = ((compressedHeaderSize & 0x80000000) != 0); + if (_headerIsCompressed) + compressedHeaderSize &= ~0x80000000; + _nonSolidStartOffset = compressedHeaderSize; + RINOK(_stream->Seek(StreamOffset + 4, STREAM_SEEK_SET, NULL)); + } + UInt32 unpackSize = FirstHeader.HeaderLength; + if (_headerIsCompressed) + { + // unpackSize = (1 << 23); + _data.SetCapacity(unpackSize); + RINOK(Decoder.Init( + EXTERNAL_CODECS_LOC_VARS + _stream, Method, FilterFlag, UseFilter)); + size_t processedSize = unpackSize; + RINOK(Decoder.Read(_data, &processedSize)); + if (processedSize != unpackSize) + return S_FALSE; + _size = processedSize; + if (IsSolid) + { + UInt32 size2 = ReadUInt32(); + if (size2 < _size) + _size = size2; + } + } + else + { + _data.SetCapacity(unpackSize); + _size = (size_t)unpackSize; + RINOK(ReadStream_FALSE(_stream, (Byte *)_data, unpackSize)); + } + return Parse(); +} + +/* +NsisExe = +{ + ExeStub + Archive // must start from 512 * N + #ifndef NSIS_CONFIG_CRC_ANAL + { + Some additional data + } +} + +Archive +{ + FirstHeader + Data + #ifdef NSIS_CONFIG_CRC_SUPPORT && FirstHeader.ThereIsCrc() + { + CRC + } +} + +FirstHeader +{ + UInt32 Flags; + Byte Signature[16]; + // points to the header+sections+entries+stringtable in the datablock + UInt32 HeaderLength; + UInt32 ArchiveSize; +} +*/ + +HRESULT CInArchive::Open( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, const UInt64 *maxCheckStartPosition) +{ + Clear(); + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + UInt64 maxSize = ((maxCheckStartPosition != 0) ? *maxCheckStartPosition : 0); + const UInt32 kStep = 512; + Byte buffer[kStep]; + + UInt64 position = 0; + for (; position <= maxSize; position += kStep) + { + RINOK(ReadStream_FALSE(inStream, buffer, kStep)); + if (memcmp(buffer + 4, kSignature, kSignatureSize) == 0) + break; + } + if (position > maxSize) + return S_FALSE; + const UInt32 kStartHeaderSize = 4 * 7; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &_archiveSize)); + RINOK(inStream->Seek(position + kStartHeaderSize, STREAM_SEEK_SET, 0)); + FirstHeader.Flags = Get32(buffer); + FirstHeader.HeaderLength = Get32(buffer + kSignatureSize + 4); + FirstHeader.ArchiveSize = Get32(buffer + kSignatureSize + 8); + if (_archiveSize - position < FirstHeader.ArchiveSize) + return S_FALSE; + + try + { + _stream = inStream; + HRESULT res = Open2(EXTERNAL_CODECS_LOC_VARS2); + if (res != S_OK) + Clear(); + _stream.Release(); + return res; + } + catch(...) { Clear(); return S_FALSE; } +} + +void CInArchive::Clear() +{ + #ifdef NSIS_SCRIPT + Script.Empty(); + #endif + Items.Clear(); + _stream.Release(); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.h new file mode 100644 index 000000000..87ae3f1ca --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisIn.h @@ -0,0 +1,181 @@ +// NsisIn.h + +#ifndef __ARCHIVE_NSIS_IN_H +#define __ARCHIVE_NSIS_IN_H + +#include "Common/Buffer.h" +#include "Common/MyCom.h" +#include "Common/StringConvert.h" + +#include "NsisDecode.h" + +// #define NSIS_SCRIPT + +namespace NArchive { +namespace NNsis { + +const int kSignatureSize = 16; +#define NSIS_SIGNATURE { 0xEF, 0xBE, 0xAD, 0xDE, 0x4E, 0x75, 0x6C, 0x6C, 0x73, 0x6F, 0x66, 0x74, 0x49, 0x6E, 0x73, 0x74} + +extern Byte kSignature[kSignatureSize]; + +const UInt32 kFlagsMask = 0xF; +namespace NFlags +{ + const UInt32 kUninstall = 1; + const UInt32 kSilent = 2; + const UInt32 kNoCrc = 4; + const UInt32 kForceCrc = 8; +} + +struct CFirstHeader +{ + UInt32 Flags; + UInt32 HeaderLength; + + UInt32 ArchiveSize; + + bool ThereIsCrc() const + { + if ((Flags & NFlags::kForceCrc ) != 0) + return true; + return ((Flags & NFlags::kNoCrc) == 0); + } + + UInt32 GetDataSize() const { return ArchiveSize - (ThereIsCrc() ? 4 : 0); } +}; + + +struct CBlockHeader +{ + UInt32 Offset; + UInt32 Num; +}; + +struct CItem +{ + AString PrefixA; + UString PrefixU; + AString NameA; + UString NameU; + FILETIME MTime; + bool IsUnicode; + bool UseFilter; + bool IsCompressed; + bool SizeIsDefined; + bool CompressedSizeIsDefined; + bool EstimatedSizeIsDefined; + UInt32 Pos; + UInt32 Size; + UInt32 CompressedSize; + UInt32 EstimatedSize; + UInt32 DictionarySize; + + CItem(): IsUnicode(false), UseFilter(false), IsCompressed(true), SizeIsDefined(false), + CompressedSizeIsDefined(false), EstimatedSizeIsDefined(false), Size(0), DictionarySize(1) {} + + bool IsINSTDIR() const + { + return (PrefixA.Length() >= 3 || PrefixU.Length() >= 3); + } + + UString GetReducedName(bool unicode) const + { + UString s; + if (unicode) + s = PrefixU; + else + s = MultiByteToUnicodeString(PrefixA); + if (s.Length() > 0) + if (s[s.Length() - 1] != L'\\') + s += L'\\'; + if (unicode) + s += NameU; + else + s += MultiByteToUnicodeString(NameA); + const int len = 9; + if (s.Left(len).CompareNoCase(L"$INSTDIR\\") == 0) + s = s.Mid(len); + return s; + } +}; + +class CInArchive +{ + UInt64 _archiveSize; + CMyComPtr<IInStream> _stream; + + Byte ReadByte(); + UInt32 ReadUInt32(); + HRESULT Open2( + DECL_EXTERNAL_CODECS_LOC_VARS2 + ); + void ReadBlockHeader(CBlockHeader &bh); + AString ReadStringA(UInt32 pos) const; + UString ReadStringU(UInt32 pos) const; + AString ReadString2A(UInt32 pos) const; + UString ReadString2U(UInt32 pos) const; + AString ReadString2(UInt32 pos) const; + AString ReadString2Qw(UInt32 pos) const; + HRESULT ReadEntries(const CBlockHeader &bh); + HRESULT Parse(); + + CByteBuffer _data; + UInt64 _size; + + size_t _posInData; + + UInt32 _stringsPos; + + + bool _headerIsCompressed; + UInt32 _nonSolidStartOffset; +public: + HRESULT Open( + DECL_EXTERNAL_CODECS_LOC_VARS + IInStream *inStream, const UInt64 *maxCheckStartPosition); + void Clear(); + + UInt64 StreamOffset; + CDecoder Decoder; + CObjectVector<CItem> Items; + CFirstHeader FirstHeader; + NMethodType::EEnum Method; + UInt32 DictionarySize; + bool IsSolid; + bool UseFilter; + bool FilterFlag; + bool IsUnicode; + + #ifdef NSIS_SCRIPT + AString Script; + #endif + UInt32 GetOffset() const { return IsSolid ? 4 : 0; } + UInt64 GetDataPos(int index) + { + const CItem &item = Items[index]; + return GetOffset() + FirstHeader.HeaderLength + item.Pos; + } + + UInt64 GetPosOfSolidItem(int index) const + { + const CItem &item = Items[index]; + return 4 + FirstHeader.HeaderLength + item.Pos; + } + + UInt64 GetPosOfNonSolidItem(int index) const + { + const CItem &item = Items[index]; + return StreamOffset + _nonSolidStartOffset + 4 + item.Pos; + } + + void Release() + { + Decoder.Release(); + } + +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisRegister.cpp new file mode 100644 index 000000000..41dedb0d3 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Nsis/NsisRegister.cpp @@ -0,0 +1,13 @@ +// NsisRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "NsisHandler.h" +static IInArchive *CreateArc() { return new NArchive::NNsis::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Nsis", L"", 0, 0x9, NSIS_SIGNATURE, NArchive::NNsis::kSignatureSize, false, CreateArc, 0 }; + +REGISTER_ARC(Nsis) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/NtfsHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/NtfsHandler.cpp new file mode 100644 index 000000000..505486fc5 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/NtfsHandler.cpp @@ -0,0 +1,1764 @@ +// NtfsHandler.cpp + +#include "StdAfx.h" + +// #define SHOW_DEBUG_INFO +// #define SHOW_DEBUG_INFO2 + +#if defined(SHOW_DEBUG_INFO) || defined(SHOW_DEBUG_INFO2) +#include <stdio.h> +#endif + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyCom.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/DummyOutStream.h" + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +#ifdef SHOW_DEBUG_INFO2 +#define PRF2(x) x +#else +#define PRF2(x) +#endif + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +#define G16(p, dest) dest = Get16(p); +#define G32(p, dest) dest = Get32(p); +#define G64(p, dest) dest = Get64(p); + +namespace NArchive { +namespace Ntfs { + +static const UInt32 kNumSysRecs = 16; +static const UInt32 kRecIndex_Volume = 3; +static const UInt32 kRecIndex_BadClus = 8; + +struct CHeader +{ + Byte SectorSizeLog; + Byte ClusterSizeLog; + // Byte MediaType; + UInt32 NumHiddenSectors; + UInt64 NumClusters; + UInt64 MftCluster; + UInt64 SerialNumber; + UInt16 SectorsPerTrack; + UInt16 NumHeads; + + UInt64 GetPhySize() const { return NumClusters << ClusterSizeLog; } + UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; } + bool Parse(const Byte *p); +}; + +static int GetLog(UInt32 num) +{ + for (int i = 0; i < 31; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + +bool CHeader::Parse(const Byte *p) +{ + if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA) + return false; + + int codeOffset = 0; + switch (p[0]) + { + case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break; + case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break; + default: return false; + } + Byte sectorsPerClusterLog; + + if (memcmp(p + 3, "NTFS ", 8) != 0) + return false; + { + int s = GetLog(Get16(p + 11)); + if (s < 9 || s > 12) + return false; + SectorSizeLog = (Byte)s; + s = GetLog(p[13]); + if (s < 0) + return false; + sectorsPerClusterLog = (Byte)s; + ClusterSizeLog = SectorSizeLog + sectorsPerClusterLog; + } + + for (int i = 14; i < 21; i++) + if (p[i] != 0) + return false; + + // MediaType = p[21]; + if (Get16(p + 22) != 0) // NumFatSectors + return false; + G16(p + 24, SectorsPerTrack); + G16(p + 26, NumHeads); + G32(p + 28, NumHiddenSectors); + if (Get32(p + 32) != 0) // NumSectors32 + return false; + + // DriveNumber = p[0x24]; + if (p[0x25] != 0) // CurrentHead + return false; + /* + NTFS-HDD: p[0x26] = 0x80 + NTFS-FLASH: p[0x26] = 0 + */ + if (p[0x26] != 0x80 && p[0x26] != 0) // ExtendedBootSig + return false; + if (p[0x27] != 0) // reserved + return false; + UInt64 numSectors = Get64(p + 0x28); + NumClusters = numSectors >> sectorsPerClusterLog; + + G64(p + 0x30, MftCluster); + // G64(p + 0x38, Mft2Cluster); + G64(p + 0x48, SerialNumber); + UInt32 numClustersInMftRec; + UInt32 numClustersInIndexBlock; + G32(p + 0x40, numClustersInMftRec); + G32(p + 0x44, numClustersInIndexBlock); + return (numClustersInMftRec < 256 && numClustersInIndexBlock < 256); +} + +struct CMftRef +{ + UInt64 Val; + UInt64 GetIndex() const { return Val & (((UInt64)1 << 48) - 1); } + UInt16 GetNumber() const { return (UInt16)(Val >> 48); } + bool IsBaseItself() const { return Val == 0; } +}; + +#define ATNAME(n) ATTR_TYPE_ ## n +#define DEF_ATTR_TYPE(v, n) ATNAME(n) = v + +enum +{ + DEF_ATTR_TYPE(0x00, UNUSED), + DEF_ATTR_TYPE(0x10, STANDARD_INFO), + DEF_ATTR_TYPE(0x20, ATTRIBUTE_LIST), + DEF_ATTR_TYPE(0x30, FILE_NAME), + DEF_ATTR_TYPE(0x40, OBJECT_ID), + DEF_ATTR_TYPE(0x50, SECURITY_DESCRIPTOR), + DEF_ATTR_TYPE(0x60, VOLUME_NAME), + DEF_ATTR_TYPE(0x70, VOLUME_INFO), + DEF_ATTR_TYPE(0x80, DATA), + DEF_ATTR_TYPE(0x90, INDEX_ROOT), + DEF_ATTR_TYPE(0xA0, INDEX_ALLOCATION), + DEF_ATTR_TYPE(0xB0, BITMAP), + DEF_ATTR_TYPE(0xC0, REPARSE_POINT), + DEF_ATTR_TYPE(0xD0, EA_INFO), + DEF_ATTR_TYPE(0xE0, EA), + DEF_ATTR_TYPE(0xF0, PROPERTY_SET), + DEF_ATTR_TYPE(0x100, LOGGED_UTILITY_STREAM), + DEF_ATTR_TYPE(0x1000, FIRST_USER_DEFINED_ATTRIBUTE) +}; + +static const Byte kFileNameType_Posix = 0; +static const Byte kFileNameType_Win32 = 1; +static const Byte kFileNameType_Dos = 2; +static const Byte kFileNameType_Win32Dos = 3; + +struct CFileNameAttr +{ + CMftRef ParentDirRef; + // UInt64 CTime; + // UInt64 MTime; + // UInt64 ThisRecMTime; + // UInt64 ATime; + // UInt64 AllocatedSize; + // UInt64 DataSize; + // UInt16 PackedEaSize; + UString Name; + UInt32 Attrib; + Byte NameType; + + bool IsDos() const { return NameType == kFileNameType_Dos; } + bool Parse(const Byte *p, unsigned size); +}; + +static void GetString(const Byte *p, unsigned length, UString &res) +{ + wchar_t *s = res.GetBuffer(length); + for (unsigned i = 0; i < length; i++) + s[i] = Get16(p + i * 2); + s[length] = 0; + res.ReleaseBuffer(); +} + +bool CFileNameAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 0x42) + return false; + G64(p + 0x00, ParentDirRef.Val); + // G64(p + 0x08, CTime); + // G64(p + 0x10, MTime); + // G64(p + 0x18, ThisRecMTime); + // G64(p + 0x20, ATime); + // G64(p + 0x28, AllocatedSize); + // G64(p + 0x30, DataSize); + G32(p + 0x38, Attrib); + // G16(p + 0x3C, PackedEaSize); + NameType = p[0x41]; + unsigned length = p[0x40]; + if (0x42 + length > size) + return false; + GetString(p + 0x42, length, Name); + return true; +} + +struct CSiAttr +{ + UInt64 CTime; + UInt64 MTime; + // UInt64 ThisRecMTime; + UInt64 ATime; + UInt32 Attrib; + + /* + UInt32 MaxVersions; + UInt32 Version; + UInt32 ClassId; + UInt32 OwnerId; + UInt32 SecurityId; + UInt64 QuotaCharged; + */ + + bool Parse(const Byte *p, unsigned size); +}; + +bool CSiAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 0x24) + return false; + G64(p + 0x00, CTime); + G64(p + 0x08, MTime); + // G64(p + 0x10, ThisRecMTime); + G64(p + 0x18, ATime); + G32(p + 0x20, Attrib); + return true; +} + +static const UInt64 kEmptyExtent = (UInt64)(Int64)-1; + +struct CExtent +{ + UInt64 Virt; + UInt64 Phy; + + bool IsEmpty() const { return Phy == kEmptyExtent; } +}; + +struct CVolInfo +{ + Byte MajorVer; + Byte MinorVer; + // UInt16 Flags; + + bool Parse(const Byte *p, unsigned size); +}; + +bool CVolInfo::Parse(const Byte *p, unsigned size) +{ + if (size < 12) + return false; + MajorVer = p[8]; + MinorVer = p[9]; + // Flags = Get16(p + 10); + return true; +} + +struct CAttr +{ + UInt32 Type; + // UInt32 Length; + UString Name; + // UInt16 Flags; + // UInt16 Instance; + CByteBuffer Data; + Byte NonResident; + + // Non-Resident + Byte CompressionUnit; + UInt64 LowVcn; + UInt64 HighVcn; + UInt64 AllocatedSize; + UInt64 Size; + UInt64 PackSize; + UInt64 InitializedSize; + + // Resident + // UInt16 ResidentFlags; + + bool IsCompressionUnitSupported() const { return CompressionUnit == 0 || CompressionUnit == 4; } + + UInt32 Parse(const Byte *p, unsigned size); + bool ParseFileName(CFileNameAttr &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseSi(CSiAttr &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseVolInfo(CVolInfo &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseExtents(CRecordVector<CExtent> &extents, UInt64 numClustersMax, int compressionUnit) const; + UInt64 GetSize() const { return NonResident ? Size : Data.GetCapacity(); } + UInt64 GetPackSize() const + { + if (!NonResident) + return Data.GetCapacity(); + if (CompressionUnit != 0) + return PackSize; + return AllocatedSize; + } +}; + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareAttr(void *const *elem1, void *const *elem2, void *) +{ + const CAttr &a1 = *(*((const CAttr **)elem1)); + const CAttr &a2 = *(*((const CAttr **)elem2)); + RINOZ(MyCompare(a1.Type, a2.Type)); + RINOZ(MyCompare(a1.Name, a2.Name)); + return MyCompare(a1.LowVcn, a2.LowVcn); +} + +UInt32 CAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 4) + return 0; + G32(p, Type); + if (Type == 0xFFFFFFFF) + return 4; + if (size < 0x18) + return 0; + PRF(printf(" T=%2X", Type)); + + UInt32 length = Get32(p + 0x04); + PRF(printf(" L=%3d", length)); + if (length > size) + return 0; + NonResident = p[0x08]; + { + int nameLength = p[9]; + UInt32 nameOffset = Get16(p + 0x0A); + if (nameLength != 0) + { + if (nameOffset + nameLength * 2 > length) + return 0; + GetString(p + nameOffset, nameLength, Name); + PRF(printf(" N=%S", Name)); + } + } + + // G16(p + 0x0C, Flags); + // G16(p + 0x0E, Instance); + // PRF(printf(" F=%4X", Flags)); + // PRF(printf(" Inst=%d", Instance)); + + UInt32 dataSize; + UInt32 offs; + if (NonResident) + { + if (length < 0x40) + return 0; + PRF(printf(" NR")); + G64(p + 0x10, LowVcn); + G64(p + 0x18, HighVcn); + G64(p + 0x28, AllocatedSize); + G64(p + 0x30, Size); + G64(p + 0x38, InitializedSize); + G16(p + 0x20, offs); + CompressionUnit = p[0x22]; + + PackSize = Size; + if (CompressionUnit != 0) + { + if (length < 0x48) + return 0; + G64(p + 0x40, PackSize); + PRF(printf(" PS=%I64x", PackSize)); + } + + // PRF(printf("\n")); + PRF(printf(" ASize=%4I64d", AllocatedSize)); + PRF(printf(" Size=%I64d", Size)); + PRF(printf(" IS=%I64d", InitializedSize)); + PRF(printf(" Low=%I64d", LowVcn)); + PRF(printf(" High=%I64d", HighVcn)); + PRF(printf(" CU=%d", (int)CompressionUnit)); + dataSize = length - offs; + } + else + { + if (length < 0x18) + return 0; + PRF(printf(" RES")); + dataSize = Get32(p + 0x10); + PRF(printf(" dataSize=%3d", dataSize)); + offs = Get16(p + 0x14); + // G16(p + 0x16, ResidentFlags); + // PRF(printf(" ResFlags=%4X", ResidentFlags)); + } + if (offs > length || dataSize > length || length - dataSize < offs) + return 0; + Data.SetCapacity(dataSize); + memcpy(Data, p + offs, dataSize); + #ifdef SHOW_DEBUG_INFO + PRF(printf(" : ")); + for (unsigned i = 0; i < Data.GetCapacity(); i++) + { + PRF(printf(" %02X", (int)Data[i])); + } + #endif + return length; +} + +bool CAttr::ParseExtents(CRecordVector<CExtent> &extents, UInt64 numClustersMax, int compressionUnit) const +{ + const Byte *p = Data; + unsigned size = (unsigned)Data.GetCapacity(); + UInt64 vcn = LowVcn; + UInt64 lcn = 0; + UInt64 highVcn1 = HighVcn + 1; + if (LowVcn != extents.Back().Virt || highVcn1 > (UInt64)1 << 63) + return false; + + extents.DeleteBack(); + + PRF2(printf("\n# ParseExtents # LowVcn = %4I64X # HighVcn = %4I64X", LowVcn, HighVcn)); + + while (size > 0) + { + Byte b = *p++; + size--; + if (b == 0) + break; + UInt32 num = b & 0xF; + if (num == 0 || num > 8 || num > size) + return false; + + int i; + UInt64 vSize = p[num - 1]; + for (i = (int)num - 2; i >= 0; i--) + vSize = (vSize << 8) | p[i]; + if (vSize == 0) + return false; + p += num; + size -= num; + if ((highVcn1 - vcn) < vSize) + return false; + + num = (b >> 4) & 0xF; + if (num > 8 || num > size) + return false; + CExtent e; + e.Virt = vcn; + if (num == 0) + { + if (compressionUnit == 0) + return false; + e.Phy = kEmptyExtent; + } + else + { + Int64 v = (signed char)p[num - 1]; + for (i = (int)num - 2; i >= 0; i--) + v = (v << 8) | p[i]; + p += num; + size -= num; + lcn += v; + if (lcn > numClustersMax) + return false; + e.Phy = lcn; + } + extents.Add(e); + vcn += vSize; + } + CExtent e; + e.Phy = kEmptyExtent; + e.Virt = vcn; + extents.Add(e); + return (highVcn1 == vcn); +} + +static const UInt64 kEmptyTag = (UInt64)(Int64)-1; + +static const int kNumCacheChunksLog = 1; +static const UInt32 kNumCacheChunks = (1 << kNumCacheChunksLog); + +class CInStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _virtPos; + UInt64 _physPos; + UInt64 _curRem; + bool _sparseMode; + size_t _compressedPos; + + UInt64 _tags[kNumCacheChunks]; + int _chunkSizeLog; + CByteBuffer _inBuf; + CByteBuffer _outBuf; +public: + CMyComPtr<IInStream> Stream; + UInt64 Size; + UInt64 InitializedSize; + int BlockSizeLog; + int CompressionUnit; + bool InUse; + CRecordVector<CExtent> Extents; + + HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } + + UInt32 GetCuSize() const { return (UInt32)1 << (BlockSizeLog + CompressionUnit); } + HRESULT InitAndSeek(int compressionUnit) + { + CompressionUnit = compressionUnit; + if (compressionUnit != 0) + { + UInt32 cuSize = GetCuSize(); + _inBuf.SetCapacity(cuSize); + _chunkSizeLog = BlockSizeLog + CompressionUnit; + _outBuf.SetCapacity(kNumCacheChunks << _chunkSizeLog); + } + for (int i = 0; i < kNumCacheChunks; i++) + _tags[i] = kEmptyTag; + + _sparseMode = false; + _curRem = 0; + _virtPos = 0; + _physPos = 0; + const CExtent &e = Extents[0]; + if (!e.IsEmpty()) + _physPos = e.Phy << BlockSizeLog; + return SeekToPhys(); + } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +static size_t Lznt1Dec(Byte *dest, size_t outBufLim, size_t destLen, const Byte *src, size_t srcLen) +{ + size_t destSize = 0; + while (destSize < destLen) + { + if (srcLen < 2 || (destSize & 0xFFF) != 0) + break; + UInt32 v = Get16(src); + if (v == 0) + break; + src += 2; + srcLen -= 2; + UInt32 comprSize = (v & 0xFFF) + 1; + if (comprSize > srcLen) + break; + srcLen -= comprSize; + if ((v & 0x8000) == 0) + { + if (comprSize != (1 << 12)) + break; + memcpy(dest + destSize, src, comprSize); + src += comprSize; + destSize += comprSize; + } + else + { + if (destSize + (1 << 12) > outBufLim || (src[0] & 1) != 0) + return 0; + int numDistBits = 4; + UInt32 sbOffset = 0; + UInt32 pos = 0; + + do + { + comprSize--; + for (UInt32 mask = src[pos++] | 0x100; mask > 1 && comprSize > 0; mask >>= 1) + { + if ((mask & 1) == 0) + { + if (sbOffset >= (1 << 12)) + return 0; + dest[destSize++] = src[pos++]; + sbOffset++; + comprSize--; + } + else + { + if (comprSize < 2) + return 0; + UInt32 v = Get16(src + pos); + pos += 2; + comprSize -= 2; + + while (((sbOffset - 1) >> numDistBits) != 0) + numDistBits++; + + UInt32 len = (v & (0xFFFF >> numDistBits)) + 3; + if (sbOffset + len > (1 << 12)) + return 0; + UInt32 dist = (v >> (16 - numDistBits)); + if (dist >= sbOffset) + return 0; + Int32 offs = -1 - dist; + Byte *p = dest + destSize; + for (UInt32 t = 0; t < len; t++) + p[t] = p[t + offs]; + destSize += len; + sbOffset += len; + } + } + } + while (comprSize > 0); + src += pos; + } + } + return destSize; +} + +STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + if (_virtPos >= Size) + return (Size == _virtPos) ? S_OK: E_FAIL; + if (size == 0) + return S_OK; + UInt64 rem = Size - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (_virtPos >= InitializedSize) + { + memset((Byte *)data, 0, size); + _virtPos += size; + *processedSize = size; + return S_OK; + } + rem = InitializedSize - _virtPos; + if (size > rem) + size = (UInt32)rem; + + while (_curRem == 0) + { + UInt64 cacheTag = _virtPos >> _chunkSizeLog; + UInt32 cacheIndex = (UInt32)cacheTag & (kNumCacheChunks - 1); + if (_tags[cacheIndex] == cacheTag) + { + UInt32 chunkSize = (UInt32)1 << _chunkSizeLog; + UInt32 offset = (UInt32)_virtPos & (chunkSize - 1); + UInt32 cur = MyMin(chunkSize - offset, size); + memcpy(data, _outBuf + (cacheIndex << _chunkSizeLog) + offset, cur); + *processedSize = cur; + _virtPos += cur; + return S_OK; + } + + PRF2(printf("\nVirtPos = %6d", _virtPos)); + + UInt32 comprUnitSize = (UInt32)1 << CompressionUnit; + UInt64 virtBlock = _virtPos >> BlockSizeLog; + UInt64 virtBlock2 = virtBlock & ~((UInt64)comprUnitSize - 1); + + int left = 0, right = Extents.Size(); + for (;;) + { + int mid = (left + right) / 2; + if (mid == left) + break; + if (virtBlock2 < Extents[mid].Virt) + right = mid; + else + left = mid; + } + + bool isCompressed = false; + UInt64 virtBlock2End = virtBlock2 + comprUnitSize; + if (CompressionUnit != 0) + for (int i = left; i < Extents.Size(); i++) + { + const CExtent &e = Extents[i]; + if (e.Virt >= virtBlock2End) + break; + if (e.IsEmpty()) + { + isCompressed = true; + break; + } + } + + int i; + for (i = left; Extents[i + 1].Virt <= virtBlock; i++); + + _sparseMode = false; + if (!isCompressed) + { + const CExtent &e = Extents[i]; + UInt64 newPos = (e.Phy << BlockSizeLog) + _virtPos - (e.Virt << BlockSizeLog); + if (newPos != _physPos) + { + _physPos = newPos; + RINOK(SeekToPhys()); + } + UInt64 next = Extents[i + 1].Virt; + if (next > virtBlock2End) + next &= ~((UInt64)comprUnitSize - 1); + next <<= BlockSizeLog; + if (next > Size) + next = Size; + _curRem = next - _virtPos; + break; + } + bool thereArePhy = false; + for (int i2 = left; i2 < Extents.Size(); i2++) + { + const CExtent &e = Extents[i2]; + if (e.Virt >= virtBlock2End) + break; + if (!e.IsEmpty()) + { + thereArePhy = true; + break; + } + } + if (!thereArePhy) + { + _curRem = (Extents[i + 1].Virt << BlockSizeLog) - _virtPos; + _sparseMode = true; + break; + } + + size_t offs = 0; + UInt64 curVirt = virtBlock2; + for (i = left; i < Extents.Size(); i++) + { + const CExtent &e = Extents[i]; + if (e.IsEmpty()) + break; + if (e.Virt >= virtBlock2End) + return S_FALSE; + UInt64 newPos = (e.Phy + (curVirt - e.Virt)) << BlockSizeLog; + if (newPos != _physPos) + { + _physPos = newPos; + RINOK(SeekToPhys()); + } + UInt64 numChunks = Extents[i + 1].Virt - curVirt; + if (curVirt + numChunks > virtBlock2End) + numChunks = virtBlock2End - curVirt; + size_t compressed = (size_t)numChunks << BlockSizeLog; + RINOK(ReadStream_FALSE(Stream, _inBuf + offs, compressed)); + curVirt += numChunks; + _physPos += compressed; + offs += compressed; + } + size_t destLenMax = GetCuSize(); + size_t destLen = destLenMax; + UInt64 rem = Size - (virtBlock2 << BlockSizeLog); + if (destLen > rem) + destLen = (size_t)rem; + + Byte *dest = _outBuf + (cacheIndex << _chunkSizeLog); + size_t destSizeRes = Lznt1Dec(dest, destLenMax, destLen, _inBuf, offs); + _tags[cacheIndex] = cacheTag; + + // some files in Vista have destSize > destLen + if (destSizeRes < destLen) + { + memset(dest, 0, destLenMax); + if (InUse) + return S_FALSE; + } + } + if (size > _curRem) + size = (UInt32)_curRem; + HRESULT res = S_OK; + if (_sparseMode) + memset(data, 0, size); + else + { + res = Stream->Read(data, size, &size); + _physPos += size; + } + if (processedSize != NULL) + *processedSize = size; + _virtPos += size; + _curRem -= size; + return res; +} + +STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + UInt64 newVirtPos = offset; + switch(seekOrigin) + { + case STREAM_SEEK_SET: break; + case STREAM_SEEK_CUR: newVirtPos += _virtPos; break; + case STREAM_SEEK_END: newVirtPos += Size; break; + default: return STG_E_INVALIDFUNCTION; + } + if (_virtPos != newVirtPos) + _curRem = 0; + _virtPos = newVirtPos; + if (newPosition) + *newPosition = newVirtPos; + return S_OK; +} + +class CByteBufStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _virtPos; +public: + CByteBuffer Buf; + void Init() { _virtPos = 0; } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +STDMETHODIMP CByteBufStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + if (_virtPos >= Buf.GetCapacity()) + return (_virtPos == Buf.GetCapacity()) ? S_OK: E_FAIL; + UInt64 rem = Buf.GetCapacity() - _virtPos; + if (rem < size) + size = (UInt32)rem; + memcpy(data, Buf + (size_t)_virtPos, size); + if (processedSize != NULL) + *processedSize = size; + _virtPos += size; + return S_OK; +} + +STDMETHODIMP CByteBufStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = Buf.GetCapacity() + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +static HRESULT DataParseExtents(int clusterSizeLog, const CObjectVector<CAttr> &attrs, + int attrIndex, int attrIndexLim, UInt64 numPhysClusters, CRecordVector<CExtent> &Extents) +{ + CExtent e; + e.Virt = 0; + e.Phy = kEmptyExtent; + Extents.Add(e); + const CAttr &attr0 = attrs[attrIndex]; + + if (attr0.AllocatedSize < attr0.Size || + (attrs[attrIndexLim - 1].HighVcn + 1) != (attr0.AllocatedSize >> clusterSizeLog) || + (attr0.AllocatedSize & ((1 << clusterSizeLog) - 1)) != 0) + return S_FALSE; + + for (int i = attrIndex; i < attrIndexLim; i++) + if (!attrs[i].ParseExtents(Extents, numPhysClusters, attr0.CompressionUnit)) + return S_FALSE; + + UInt64 packSizeCalc = 0; + for (int k = 0; k < Extents.Size(); k++) + { + CExtent &e = Extents[k]; + if (!e.IsEmpty()) + packSizeCalc += (Extents[k + 1].Virt - e.Virt) << clusterSizeLog; + PRF2(printf("\nSize = %4I64X", Extents[k + 1].Virt - e.Virt)); + PRF2(printf(" Pos = %4I64X", e.Phy)); + } + + if (attr0.CompressionUnit != 0) + { + if (packSizeCalc != attr0.PackSize) + return S_FALSE; + } + else + { + if (packSizeCalc != attr0.AllocatedSize) + return S_FALSE; + } + return S_OK; +} + +struct CDataRef +{ + int Start; + int Num; +}; + +static const UInt32 kMagic_FILE = 0x454c4946; +static const UInt32 kMagic_BAAD = 0x44414142; + +struct CMftRec +{ + UInt32 Magic; + // UInt64 Lsn; + UInt16 SeqNumber; + UInt16 Flags; + // UInt16 LinkCount; + // UInt16 NextAttrInstance; + CMftRef BaseMftRef; + // UInt32 ThisRecNumber; + UInt32 MyNumNameLinks; + + CObjectVector<CAttr> DataAttrs; + CObjectVector<CFileNameAttr> FileNames; + CRecordVector<CDataRef> DataRefs; + + CSiAttr SiAttr; + + void MoveAttrsFrom(CMftRec &src) + { + DataAttrs += src.DataAttrs; + FileNames += src.FileNames; + src.DataAttrs.ClearAndFree(); + src.FileNames.ClearAndFree(); + } + + UInt64 GetPackSize() const + { + UInt64 res = 0; + for (int i = 0; i < DataRefs.Size(); i++) + res += DataAttrs[DataRefs[i].Start].GetPackSize(); + return res; + } + + bool Parse(Byte *p, int sectorSizeLog, UInt32 numSectors, UInt32 recNumber, CObjectVector<CAttr> *attrs); + + bool IsEmpty() const { return (Magic <= 2); } + bool IsFILE() const { return (Magic == kMagic_FILE); } + bool IsBAAD() const { return (Magic == kMagic_BAAD); } + + bool InUse() const { return (Flags & 1) != 0; } + bool IsDir() const { return (Flags & 2) != 0; } + + void ParseDataNames(); + HRESULT GetStream(IInStream *mainStream, int dataIndex, + int clusterSizeLog, UInt64 numPhysClusters, IInStream **stream) const; + int GetNumExtents(int dataIndex, int clusterSizeLog, UInt64 numPhysClusters) const; + + UInt64 GetSize(int dataIndex) const { return DataAttrs[DataRefs[dataIndex].Start].GetSize(); } + + CMftRec(): MyNumNameLinks(0) {} +}; + +void CMftRec::ParseDataNames() +{ + DataRefs.Clear(); + DataAttrs.Sort(CompareAttr, 0); + + for (int i = 0; i < DataAttrs.Size();) + { + CDataRef ref; + ref.Start = i; + for (i++; i < DataAttrs.Size(); i++) + if (DataAttrs[ref.Start].Name != DataAttrs[i].Name) + break; + ref.Num = i - ref.Start; + DataRefs.Add(ref); + } +} + +HRESULT CMftRec::GetStream(IInStream *mainStream, int dataIndex, + int clusterSizeLog, UInt64 numPhysClusters, IInStream **destStream) const +{ + *destStream = 0; + CByteBufStream *streamSpec = new CByteBufStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + + if (dataIndex < 0) + return E_FAIL; + + if (dataIndex < DataRefs.Size()) + { + const CDataRef &ref = DataRefs[dataIndex]; + int numNonResident = 0; + int i; + for (i = ref.Start; i < ref.Start + ref.Num; i++) + if (DataAttrs[i].NonResident) + numNonResident++; + + const CAttr &attr0 = DataAttrs[ref.Start]; + + if (numNonResident != 0 || ref.Num != 1) + { + if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported()) + return S_FALSE; + CInStream *streamSpec = new CInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + RINOK(DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, streamSpec->Extents)); + streamSpec->Size = attr0.Size; + streamSpec->InitializedSize = attr0.InitializedSize; + streamSpec->Stream = mainStream; + streamSpec->BlockSizeLog = clusterSizeLog; + streamSpec->InUse = InUse(); + RINOK(streamSpec->InitAndSeek(attr0.CompressionUnit)); + *destStream = streamTemp.Detach(); + return S_OK; + } + streamSpec->Buf = attr0.Data; + } + streamSpec->Init(); + *destStream = streamTemp.Detach(); + return S_OK; +} + +int CMftRec::GetNumExtents(int dataIndex, int clusterSizeLog, UInt64 numPhysClusters) const +{ + if (dataIndex < 0) + return 0; + { + const CDataRef &ref = DataRefs[dataIndex]; + int numNonResident = 0; + int i; + for (i = ref.Start; i < ref.Start + ref.Num; i++) + if (DataAttrs[i].NonResident) + numNonResident++; + + const CAttr &attr0 = DataAttrs[ref.Start]; + + if (numNonResident != 0 || ref.Num != 1) + { + if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported()) + return 0; // error; + CRecordVector<CExtent> extents; + if (DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, extents) != S_OK) + return 0; // error; + return extents.Size() - 1; + } + // if (attr0.Data.GetCapacity() != 0) + // return 1; + return 0; + } +} + +bool CMftRec::Parse(Byte *p, int sectorSizeLog, UInt32 numSectors, UInt32 recNumber, + CObjectVector<CAttr> *attrs) +{ + G32(p, Magic); + if (!IsFILE()) + return IsEmpty() || IsBAAD(); + + UInt32 usaOffset; + UInt32 numUsaItems; + G16(p + 0x04, usaOffset); + G16(p + 0x06, numUsaItems); + + if ((usaOffset & 1) != 0 || usaOffset + numUsaItems * 2 > ((UInt32)1 << sectorSizeLog) - 2 || + numUsaItems == 0 || numUsaItems - 1 != numSectors) + return false; + + UInt16 usn = Get16(p + usaOffset); + // PRF(printf("\nusn = %d", usn)); + for (UInt32 i = 1; i < numUsaItems; i++) + { + void *pp = p + (i << sectorSizeLog) - 2; + if (Get16(pp) != usn) + return false; + SetUi16(pp, Get16(p + usaOffset + i * 2)); + } + + // G64(p + 0x08, Lsn); + G16(p + 0x10, SeqNumber); + // G16(p + 0x12, LinkCount); + // PRF(printf(" L=%d", LinkCount)); + UInt32 attrOffs = Get16(p + 0x14); + G16(p + 0x16, Flags); + PRF(printf(" F=%4X", Flags)); + + UInt32 bytesInUse = Get32(p + 0x18); + UInt32 bytesAlloc = Get32(p + 0x1C); + G64(p + 0x20, BaseMftRef.Val); + if (BaseMftRef.Val != 0) + { + PRF(printf(" BaseRef=%d", (int)BaseMftRef.Val)); + // return false; // Check it; + } + // G16(p + 0x28, NextAttrInstance); + if (usaOffset >= 0x30) + if (Get32(p + 0x2C) != recNumber) // NTFS 3.1+ + return false; + + UInt32 limit = numSectors << sectorSizeLog; + if (attrOffs >= limit || (attrOffs & 7) != 0 || bytesInUse > limit + || bytesAlloc != limit) + return false; + + + for (UInt32 t = attrOffs; t < limit;) + { + CAttr attr; + // PRF(printf("\n %2d:", Attrs.Size())); + PRF(printf("\n")); + UInt32 length = attr.Parse(p + t, limit - t); + if (length == 0 || limit - t < length) + return false; + t += length; + if (attr.Type == 0xFFFFFFFF) + break; + switch(attr.Type) + { + case ATTR_TYPE_FILE_NAME: + { + CFileNameAttr fna; + if (!attr.ParseFileName(fna)) + return false; + FileNames.Add(fna); + PRF(printf(" flags = %4x", (int)fna.NameType)); + PRF(printf("\n %S", fna.Name)); + break; + } + case ATTR_TYPE_STANDARD_INFO: + if (!attr.ParseSi(SiAttr)) + return false; + break; + case ATTR_TYPE_DATA: + DataAttrs.Add(attr); + break; + default: + if (attrs) + attrs->Add(attr); + break; + } + } + + return true; +} + +struct CItem +{ + int RecIndex; + int DataIndex; + CMftRef ParentRef; + UString Name; + UInt32 Attrib; + + bool IsDir() const { return (DataIndex < 0); } +}; + +struct CDatabase +{ + CHeader Header; + CObjectVector<CItem> Items; + CObjectVector<CMftRec> Recs; + CMyComPtr<IInStream> InStream; + IArchiveOpenCallback *OpenCallback; + + CByteBuffer ByteBuf; + + CObjectVector<CAttr> VolAttrs; + + ~CDatabase() { ClearAndClose(); } + + void Clear(); + void ClearAndClose(); + + UString GetItemPath(Int32 index) const; + HRESULT Open(); + HRESULT ReadDir(Int32 parent, UInt32 cluster, int level); + + HRESULT SeekToCluster(UInt64 cluster); + + int FindMtfRec(const CMftRef &ref) const + { + UInt64 val = ref.GetIndex(); + int left = 0, right = Items.Size(); + while (left != right) + { + int mid = (left + right) / 2; + UInt64 midValue = Items[mid].RecIndex; + if (val == midValue) + return mid; + if (val < midValue) + right = mid; + else + left = mid + 1; + } + return -1; + } + +}; + +HRESULT CDatabase::SeekToCluster(UInt64 cluster) +{ + return InStream->Seek(cluster << Header.ClusterSizeLog, STREAM_SEEK_SET, NULL); +} + +void CDatabase::Clear() +{ + Items.Clear(); + Recs.Clear(); +} + +void CDatabase::ClearAndClose() +{ + Clear(); + InStream.Release(); +} + +#define MY_DIR_PREFIX(x) L"[" x L"]" WSTRING_PATH_SEPARATOR + +UString CDatabase::GetItemPath(Int32 index) const +{ + const CItem *item = &Items[index]; + UString name = item->Name; + for (int j = 0; j < 256; j++) + { + CMftRef ref = item->ParentRef; + index = FindMtfRec(ref); + if (ref.GetIndex() == 5) + return name; + if (index < 0 || Recs[Items[index].RecIndex].SeqNumber != ref.GetNumber()) + return MY_DIR_PREFIX(L"UNKNOWN") + name; + item = &Items[index]; + name = item->Name + WCHAR_PATH_SEPARATOR + name; + } + return MY_DIR_PREFIX(L"BAD") + name; +} + +HRESULT CDatabase::Open() +{ + Clear(); + + static const UInt32 kHeaderSize = 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize)); + if (!Header.Parse(buf)) + return S_FALSE; + UInt64 fileSize; + RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize < Header.GetPhySize()) + return S_FALSE; + + SeekToCluster(Header.MftCluster); + + CMftRec mftRec; + UInt32 numSectorsInRec; + int recSizeLog; + CMyComPtr<IInStream> mftStream; + { + UInt32 blockSize = 1 << 12; + ByteBuf.SetCapacity(blockSize); + RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize)); + + UInt32 allocSize = Get32(ByteBuf + 0x1C); + recSizeLog = GetLog(allocSize); + if (recSizeLog < Header.SectorSizeLog) + return false; + numSectorsInRec = 1 << (recSizeLog - Header.SectorSizeLog); + if (!mftRec.Parse(ByteBuf, Header.SectorSizeLog, numSectorsInRec, NULL, 0)) + return S_FALSE; + if (!mftRec.IsFILE()) + return S_FALSE; + mftRec.ParseDataNames(); + if (mftRec.DataRefs.IsEmpty()) + return S_FALSE; + RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream)); + if (!mftStream) + return S_FALSE; + } + + UInt64 mftSize = mftRec.DataAttrs[0].Size; + if ((mftSize >> 4) > Header.GetPhySize()) + return S_FALSE; + + UInt64 numFiles = mftSize >> recSizeLog; + if (numFiles > (1 << 30)) + return S_FALSE; + if (OpenCallback) + { + RINOK(OpenCallback->SetTotal(&numFiles, &mftSize)); + } + const UInt32 kBufSize = (1 << 15); + if (kBufSize < (1 << recSizeLog)) + return S_FALSE; + + ByteBuf.SetCapacity((size_t)kBufSize); + Recs.Reserve((int)numFiles); + for (UInt64 pos64 = 0;;) + { + if (OpenCallback) + { + UInt64 numFiles = Recs.Size(); + if ((numFiles & 0x3FF) == 0) + { + RINOK(OpenCallback->SetCompleted(&numFiles, &pos64)); + } + } + UInt32 readSize = kBufSize; + UInt64 rem = mftSize - pos64; + if (readSize > rem) + readSize = (UInt32)rem; + if (readSize < ((UInt32)1 << recSizeLog)) + break; + RINOK(ReadStream_FALSE(mftStream, ByteBuf, (size_t)readSize)); + pos64 += readSize; + for (int i = 0; ((UInt32)(i + 1) << recSizeLog) <= readSize; i++) + { + PRF(printf("\n---------------------")); + PRF(printf("\n%5d:", Recs.Size())); + Byte *p = ByteBuf + ((UInt32)i << recSizeLog); + CMftRec rec; + if (!rec.Parse(p, Header.SectorSizeLog, numSectorsInRec, (UInt32)Recs.Size(), + (Recs.Size() == kRecIndex_Volume) ? &VolAttrs: NULL)) + return S_FALSE; + Recs.Add(rec); + } + } + + int i; + for (i = 0; i < Recs.Size(); i++) + { + CMftRec &rec = Recs[i]; + if (!rec.BaseMftRef.IsBaseItself()) + { + UInt64 refIndex = rec.BaseMftRef.GetIndex(); + if (refIndex > (UInt32)Recs.Size()) + return S_FALSE; + CMftRec &refRec = Recs[(int)refIndex]; + bool moveAttrs = (refRec.SeqNumber == rec.BaseMftRef.GetNumber() && refRec.BaseMftRef.IsBaseItself()); + if (rec.InUse() && refRec.InUse()) + { + if (!moveAttrs) + return S_FALSE; + } + else if (rec.InUse() || refRec.InUse()) + moveAttrs = false; + if (moveAttrs) + refRec.MoveAttrsFrom(rec); + } + } + + for (i = 0; i < Recs.Size(); i++) + Recs[i].ParseDataNames(); + + for (i = 0; i < Recs.Size(); i++) + { + CMftRec &rec = Recs[i]; + if (!rec.IsFILE() || !rec.BaseMftRef.IsBaseItself()) + continue; + int numNames = 0; + // printf("\n%4d: ", i); + for (int t = 0; t < rec.FileNames.Size(); t++) + { + const CFileNameAttr &fna = rec.FileNames[t]; + // printf("%4d %S | ", (int)fna.NameType, fna.Name); + if (fna.IsDos()) + continue; + int numDatas = rec.DataRefs.Size(); + + // For hard linked files we show substreams only for first Name. + if (numDatas > 1 && numNames > 0) + numDatas = 1; + numNames++; + + if (rec.IsDir()) + { + CItem item; + item.Name = fna.Name; + item.RecIndex = i; + item.DataIndex = -1; + item.ParentRef = fna.ParentDirRef; + item.Attrib = rec.SiAttr.Attrib | 0x10; + // item.Attrib = fna.Attrib; + Items.Add(item); + } + for (int di = 0; di < numDatas; di++) + { + CItem item; + item.Name = fna.Name; + item.Attrib = rec.SiAttr.Attrib; + const UString &subName = rec.DataAttrs[rec.DataRefs[di].Start].Name; + if (!subName.IsEmpty()) + { + // $BadClus:$Bad is sparse file for all clusters. So we skip it. + if (i == kRecIndex_BadClus && subName == L"$Bad") + continue; + item.Name += L":"; + item.Name += subName; + item.Attrib = fna.Attrib; + } + + PRF(printf("\n%3d", i)); + PRF(printf(" attrib=%2x", rec.SiAttr.Attrib)); + PRF(printf(" %S", item.Name)); + + item.RecIndex = i; + item.DataIndex = di; + item.ParentRef = fna.ParentDirRef; + + Items.Add(item); + rec.MyNumNameLinks++; + } + } + rec.FileNames.ClearAndFree(); + } + + return S_OK; +} + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp, + CDatabase +{ +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + IInStream *stream2; + const CItem &item = Items[index]; + const CMftRec &rec = Recs[item.RecIndex]; + HRESULT res = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &stream2); + *stream = (ISequentialInStream *)stream2; + return res; + COM_TRY_END +} + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidLinks, VT_UI4}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidVolumeName, VT_BSTR}, + { NULL, kpidFileSystem, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + + { NULL, kpidSectorSize, VT_UI4}, + { NULL, kpidId, VT_UI8} + // { NULL, kpidSectorsPerTrack, VT_UI4}, + // { NULL, kpidNumHeads, VT_UI4}, + // { NULL, kpidHiddenSectors, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static void NtfsTimeToProp(UInt64 t, NWindows::NCOM::CPropVariant &prop) +{ + FILETIME ft; + ft.dwLowDateTime = (DWORD)t; + ft.dwHighDateTime = (DWORD)(t >> 32); + prop = ft; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + const CMftRec *volRec = (Recs.Size() > kRecIndex_Volume ? &Recs[kRecIndex_Volume] : NULL); + + switch(propID) + { + case kpidClusterSize: prop = Header.ClusterSize(); break; + case kpidPhySize: prop = Header.GetPhySize(); break; + /* + case kpidHeadersSize: + { + UInt64 val = 0; + for (int i = 0; i < kNumSysRecs; i++) + { + printf("\n%2d: %8I64d ", i, Recs[i].GetPackSize()); + if (i == 8) + i = i + val += Recs[i].GetPackSize(); + } + prop = val; + break; + } + */ + case kpidCTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.CTime, prop); break;break; + case kpidVolumeName: + { + for (int i = 0; i < VolAttrs.Size(); i++) + { + const CAttr &attr = VolAttrs[i]; + if (attr.Type == ATTR_TYPE_VOLUME_NAME) + { + UString name; + GetString(attr.Data, (int)attr.Data.GetCapacity() / 2, name); + prop = name; + break; + } + } + break; + } + case kpidFileSystem: + { + AString s = "NTFS"; + for (int i = 0; i < VolAttrs.Size(); i++) + { + const CAttr &attr = VolAttrs[i]; + if (attr.Type == ATTR_TYPE_VOLUME_INFO) + { + CVolInfo vi; + if (attr.ParseVolInfo(vi)) + { + s += ' '; + char temp[16]; + ConvertUInt32ToString(vi.MajorVer, temp); + s += temp; + s += '.'; + ConvertUInt32ToString(vi.MinorVer, temp); + s += temp; + } + break; + } + } + prop = s; + break; + } + case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break; + case kpidId: prop = Header.SerialNumber; break; + // case kpidMediaType: prop = Header.MediaType; break; + // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break; + // case kpidNumHeads: prop = Header.NumHeads; break; + // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break; + } + 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; + const CItem &item = Items[index]; + const CMftRec &rec = Recs[item.RecIndex]; + + const CAttr *data= NULL; + if (item.DataIndex >= 0) + data = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start]; + + switch(propID) + { + case kpidPath: + { + UString name = GetItemPath(index); + const wchar_t *prefix = NULL; + if (!rec.InUse()) + prefix = MY_DIR_PREFIX(L"DELETED"); + else if (item.RecIndex < kNumSysRecs) + prefix = MY_DIR_PREFIX(L"SYSTEM"); + if (prefix) + name = prefix + name; + prop = name; + break; + } + + case kpidIsDir: prop = item.IsDir(); break; + case kpidMTime: NtfsTimeToProp(rec.SiAttr.MTime, prop); break; + + case kpidCTime: NtfsTimeToProp(rec.SiAttr.CTime, prop); break; + case kpidATime: NtfsTimeToProp(rec.SiAttr.ATime, prop); break; + case kpidAttrib: + prop = item.Attrib; + break; + case kpidLinks: prop = rec.MyNumNameLinks; break; + case kpidSize: if (data) prop = data->GetSize(); break; + case kpidPackSize: if (data) prop = data->GetPackSize(); break; + case kpidNumBlocks: if (data) prop = (UInt32)rec.GetNumExtents(item.DataIndex, Header.ClusterSizeLog, Header.NumClusters); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + OpenCallback = callback; + InStream = stream; + HRESULT res; + try + { + res = CDatabase::Open(); + if (res == S_OK) + return S_OK; + } + catch(...) + { + Close(); + throw; + } + Close(); + return res; + } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + ClearAndClose(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = Items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + const CItem &item = Items[allFilesMode ? i : indices[i]]; + const CMftRec &rec = Recs[item.RecIndex]; + if (!rec.IsDir()) + totalSize += rec.GetSize(item.DataIndex); + } + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + CByteBuffer buf; + UInt32 clusterSize = Header.ClusterSize(); + buf.SetCapacity(clusterSize); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + const CItem &item = Items[index]; + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(); + + const CMftRec &rec = Recs[item.RecIndex]; + const CAttr &data = rec.DataAttrs[rec.DataRefs[item.DataIndex].Start]; + + int res = NExtract::NOperationResult::kDataError; + { + CMyComPtr<IInStream> inStream; + HRESULT hres = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kUnSupportedMethod; + else + { + RINOK(hres); + if (inStream) + { + HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (hres != S_OK && hres != S_FALSE) + { + RINOK(hres); + } + if (/* copyCoderSpec->TotalSize == item.GetSize() && */ hres == S_OK) + res = NExtract::NOperationResult::kOK; + } + } + } + totalPackSize += data.GetPackSize(); + totalSize += data.GetSize(); + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = Items.Size(); + return S_OK; +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"NTFS", L"ntfs img", 0, 0xD9, { 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' ', 0 }, 9, false, CreateArc, 0 }; + +REGISTER_ARC(Fat) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/PeHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/PeHandler.cpp new file mode 100644 index 000000000..c64067aa5 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/PeHandler.cpp @@ -0,0 +1,1752 @@ +// PeHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/DynamicBuffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariantUtils.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NPe { + +#define NUM_SCAN_SECTIONS_MAX (1 << 6) + +#define PE_SIG 0x00004550 +#define PE_OptHeader_Magic_32 0x10B +#define PE_OptHeader_Magic_64 0x20B + +static AString GetDecString(UInt32 v) +{ + char sz[32]; + ConvertUInt64ToString(v, sz); + return sz; +} + +struct CVersion +{ + UInt16 Major; + UInt16 Minor; + + void Parse(const Byte *buf); + AString GetString() const { return GetDecString(Major) + '.' + GetDecString(Minor); } +}; + +void CVersion::Parse(const Byte *p) +{ + Major = Get16(p); + Minor = Get16(p + 2); +} + +static const UInt32 kHeaderSize = 4 + 20; + +struct CHeader +{ + UInt16 NumSections; + UInt32 Time; + UInt32 PointerToSymbolTable; + UInt32 NumSymbols; + UInt16 OptHeaderSize; + UInt16 Flags; + UInt16 Machine; + + bool Parse(const Byte *buf); +}; + +bool CHeader::Parse(const Byte *p) +{ + if (Get32(p) != PE_SIG) + return false; + p += 4; + Machine = Get16(p + 0); + NumSections = Get16(p + 2); + Time = Get32(p + 4); + PointerToSymbolTable = Get32(p + 8); + NumSymbols = Get32(p + 12); + OptHeaderSize = Get16(p + 16); + Flags = Get16(p + 18); + return true; +} + +struct CDirLink +{ + UInt32 Va; + UInt32 Size; + void Parse(const Byte *p); +}; + +void CDirLink::Parse(const Byte *p) +{ + Va = Get32(p); + Size = Get32(p + 4); +} + +enum +{ + kDirLink_Certificate = 4, + kDirLink_Debug = 6 +}; + +struct CDebugEntry +{ + UInt32 Flags; + UInt32 Time; + CVersion Ver; + UInt32 Type; + UInt32 Size; + UInt32 Va; + UInt32 Pa; + + void Parse(const Byte *p); +}; + +void CDebugEntry::Parse(const Byte *p) +{ + Flags = Get32(p); + Time = Get32(p + 4); + Ver.Parse(p + 8); + Type = Get32(p + 12); + Size = Get32(p + 16); + Va = Get32(p + 20); + Pa = Get32(p + 24); +} + +static const UInt32 kNumDirItemsMax = 16; + +struct COptHeader +{ + UInt16 Magic; + Byte LinkerVerMajor; + Byte LinkerVerMinor; + + UInt32 CodeSize; + UInt32 InitDataSize; + UInt32 UninitDataSize; + + // UInt32 AddressOfEntryPoint; + // UInt32 BaseOfCode; + // UInt32 BaseOfData32; + UInt64 ImageBase; + + UInt32 SectAlign; + UInt32 FileAlign; + + CVersion OsVer; + CVersion ImageVer; + CVersion SubsysVer; + + UInt32 ImageSize; + UInt32 HeadersSize; + UInt32 CheckSum; + UInt16 SubSystem; + UInt16 DllCharacts; + + UInt64 StackReserve; + UInt64 StackCommit; + UInt64 HeapReserve; + UInt64 HeapCommit; + + UInt32 NumDirItems; + CDirLink DirItems[kNumDirItemsMax]; + + bool Is64Bit() const { return Magic == PE_OptHeader_Magic_64; } + bool Parse(const Byte *p, UInt32 size); + + int GetNumFileAlignBits() const + { + for (int i = 9; i <= 16; i++) + if (((UInt32)1 << i) == FileAlign) + return i; + return -1; + } +}; + +bool COptHeader::Parse(const Byte *p, UInt32 size) +{ + Magic = Get16(p); + switch (Magic) + { + case PE_OptHeader_Magic_32: + case PE_OptHeader_Magic_64: + break; + default: + return false; + } + LinkerVerMajor = p[2]; + LinkerVerMinor = p[3]; + + bool hdr64 = Is64Bit(); + + CodeSize = Get32(p + 4); + InitDataSize = Get32(p + 8); + UninitDataSize = Get32(p + 12); + + // AddressOfEntryPoint = Get32(p + 16); + // BaseOfCode = Get32(p + 20); + // BaseOfData32 = hdr64 ? 0: Get32(p + 24); + ImageBase = hdr64 ? GetUi64(p + 24) : Get32(p + 28); + + SectAlign = Get32(p + 32); + FileAlign = Get32(p + 36); + + OsVer.Parse(p + 40); + ImageVer.Parse(p + 44); + SubsysVer.Parse(p + 48); + + // reserved = Get32(p + 52); + + ImageSize = Get32(p + 56); + HeadersSize = Get32(p + 60); + CheckSum = Get32(p + 64); + SubSystem = Get16(p + 68); + DllCharacts = Get16(p + 70); + + if (hdr64) + { + StackReserve = Get64(p + 72); + StackCommit = Get64(p + 80); + HeapReserve = Get64(p + 88); + HeapCommit = Get64(p + 96); + } + else + { + StackReserve = Get32(p + 72); + StackCommit = Get32(p + 76); + HeapReserve = Get32(p + 80); + HeapCommit = Get32(p + 84); + } + UInt32 pos = (hdr64 ? 108 : 92); + NumDirItems = Get32(p + pos); + pos += 4; + if (pos + 8 * NumDirItems != size) + return false; + for (UInt32 i = 0; i < NumDirItems && i < kNumDirItemsMax; i++) + DirItems[i].Parse(p + pos + i * 8); + return true; +} + +static const UInt32 kSectionSize = 40; + +struct CSection +{ + AString Name; + + UInt32 VSize; + UInt32 Va; + UInt32 PSize; + UInt32 Pa; + UInt32 Flags; + UInt32 Time; + // UInt16 NumRelocs; + bool IsDebug; + bool IsRealSect; + bool IsAdditionalSection; + + CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {} + UInt64 GetPackSize() const { return PSize; } + + void UpdateTotalSize(UInt32 &totalSize) + { + UInt32 t = Pa + PSize; + if (t > totalSize) + totalSize = t; + } + void Parse(const Byte *p); +}; + +static bool operator <(const CSection &a1, const CSection &a2) { return (a1.Pa < a2.Pa) || ((a1.Pa == a2.Pa) && (a1.PSize < a2.PSize)) ; } +static bool operator ==(const CSection &a1, const CSection &a2) { return (a1.Pa == a2.Pa) && (a1.PSize == a2.PSize); } + +static AString GetName(const Byte *name) +{ + const int kNameSize = 8; + AString res; + char *p = res.GetBuffer(kNameSize); + memcpy(p, name, kNameSize); + p[kNameSize] = 0; + res.ReleaseBuffer(); + return res; +} + +void CSection::Parse(const Byte *p) +{ + Name = GetName(p); + VSize = Get32(p + 8); + Va = Get32(p + 12); + PSize = Get32(p + 16); + Pa = Get32(p + 20); + // NumRelocs = Get16(p + 32); + Flags = Get32(p + 36); +} + +static const CUInt32PCharPair g_HeaderCharacts[] = +{ + { 1, "Executable" }, + { 13, "DLL" }, + { 8, "32-bit" }, + { 5, "LargeAddress" }, + { 0, "NoRelocs" }, + { 2, "NoLineNums" }, + { 3, "NoLocalSyms" }, + { 4, "AggressiveWsTrim" }, + { 9, "NoDebugInfo" }, + { 10, "RemovableRun" }, + { 11, "NetRun" }, + { 12, "System" }, + { 14, "UniCPU" }, + { 7, "Little-Endian" }, + { 15, "Big-Endian" } +}; + +static const CUInt32PCharPair g_DllCharacts[] = +{ + { 6, "Relocated" }, + { 7, "Integrity" }, + { 8, "NX-Compatible" }, + { 9, "NoIsolation" }, + { 10, "NoSEH" }, + { 11, "NoBind" }, + { 13, "WDM" }, + { 15, "TerminalServerAware" } +}; + +static const CUInt32PCharPair g_SectFlags[] = +{ + { 3, "NoPad" }, + { 5, "Code" }, + { 6, "InitializedData" }, + { 7, "UninitializedData" }, + { 9, "Comments" }, + { 11, "Remove" }, + { 12, "COMDAT" }, + { 15, "GP" }, + { 24, "ExtendedRelocations" }, + { 25, "Discardable" }, + { 26, "NotCached" }, + { 27, "NotPaged" }, + { 28, "Shared" }, + { 29, "Execute" }, + { 30, "Read" }, + { 31, "Write" } +}; + +static const CUInt32PCharPair g_MachinePairs[] = +{ + { 0x014C, "x86" }, + { 0x0162, "MIPS-R3000" }, + { 0x0166, "MIPS-R4000" }, + { 0x0168, "MIPS-R10000" }, + { 0x0169, "MIPS-V2" }, + { 0x0184, "Alpha" }, + { 0x01A2, "SH3" }, + { 0x01A3, "SH3-DSP" }, + { 0x01A4, "SH3E" }, + { 0x01A6, "SH4" }, + { 0x01A8, "SH5" }, + { 0x01C0, "ARM" }, + { 0x01C2, "ARM-Thumb" }, + { 0x01F0, "PPC" }, + { 0x01F1, "PPC-FP" }, + { 0x0200, "IA-64" }, + { 0x0284, "Alpha-64" }, + { 0x0200, "IA-64" }, + { 0x0366, "MIPSFPU" }, + { 0x8664, "x64" }, + { 0x0EBC, "EFI" } +}; + +static const CUInt32PCharPair g_SubSystems[] = +{ + { 0, "Unknown" }, + { 1, "Native" }, + { 2, "Windows GUI" }, + { 3, "Windows CUI" }, + { 7, "Posix" }, + { 9, "Windows CE" }, + { 10, "EFI" }, + { 11, "EFI Boot" }, + { 12, "EFI Runtime" }, + { 13, "EFI ROM" }, + { 14, "XBOX" } +}; + +static const wchar_t *g_ResTypes[] = +{ + NULL, + L"CURSOR", + L"BITMAP", + L"ICON", + L"MENU", + L"DIALOG", + L"STRING", + L"FONTDIR", + L"FONT", + L"ACCELERATOR", + L"RCDATA", + L"MESSAGETABLE", + L"GROUP_CURSOR", + NULL, + L"GROUP_ICON", + NULL, + L"VERSION", + L"DLGINCLUDE", + NULL, + L"PLUGPLAY", + L"VXD", + L"ANICURSOR", + L"ANIICON", + L"HTML", + L"MANIFEST" +}; + +const UInt32 kFlag = (UInt32)1 << 31; +const UInt32 kMask = ~kFlag; + +struct CTableItem +{ + UInt32 Offset; + UInt32 ID; +}; + + +const UInt32 kBmpHeaderSize = 14; +const UInt32 kIconHeaderSize = 22; + +struct CResItem +{ + UInt32 Type; + UInt32 ID; + UInt32 Lang; + + UInt32 Size; + UInt32 Offset; + + UInt32 HeaderSize; + Byte Header[kIconHeaderSize]; // it must be enough for max size header. + bool Enabled; + + bool IsNameEqual(const CResItem &item) const { return Lang == item.Lang; } + UInt32 GetSize() const { return Size + HeaderSize; } + bool IsBmp() const { return Type == 2; } + bool IsIcon() const { return Type == 3; } + bool IsString() const { return Type == 6; } + bool IsRcData() const { return Type == 10; } + bool IsRcDataOrUnknown() const { return IsRcData() || Type > 64; } +}; + +struct CStringItem +{ + UInt32 Lang; + UInt32 Size; + CByteDynamicBuffer Buf; + + void AddChar(Byte c); + void AddWChar(UInt16 c); +}; + +void CStringItem::AddChar(Byte c) +{ + Buf.EnsureCapacity(Size + 2); + Buf[Size++] = c; + Buf[Size++] = 0; +} + +void CStringItem::AddWChar(UInt16 c) +{ + if (c == '\n') + { + AddChar('\\'); + c = 'n'; + } + Buf.EnsureCapacity(Size + 2); + SetUi16(Buf + Size, c); + Size += 2; +} + +struct CMixItem +{ + int SectionIndex; + int ResourceIndex; + int StringIndex; + + bool IsSectionItem() const { return ResourceIndex < 0 && StringIndex < 0; }; +}; + +struct CUsedBitmap +{ + CByteBuffer Buf; +public: + void Alloc(size_t size) + { + size = (size + 7) / 8; + Buf.SetCapacity(size); + memset(Buf, 0, size); + } + void Free() + { + Buf.SetCapacity(0); + } + bool SetRange(size_t from, int size) + { + for (int i = 0; i < size; i++) + { + size_t pos = (from + i) >> 3; + Byte mask = (Byte)(1 << ((from + i) & 7)); + Byte b = Buf[pos]; + if ((b & mask) != 0) + return false; + Buf[pos] = b | mask; + } + return true; + } +}; + + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + CObjectVector<CSection> _sections; + UInt32 _peOffset; + CHeader _header; + COptHeader _optHeader; + UInt32 _totalSize; + UInt32 _totalSizeLimited; + Int32 _mainSubfile; + + CRecordVector<CResItem> _items; + CObjectVector<CStringItem> _strings; + + CByteBuffer _buf; + bool _oneLang; + UString _resourceFileName; + CUsedBitmap _usedRes; + bool _parseResources; + + CRecordVector<CMixItem> _mixItems; + + HRESULT LoadDebugSections(IInStream *stream, bool &thereIsSection); + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); + bool Parse(const Byte *buf, UInt32 size); + + void AddResNameToString(UString &s, UInt32 id) const; + UString GetLangPrefix(UInt32 lang); + HRESULT ReadString(UInt32 offset, UString &dest) const; + HRESULT ReadTable(UInt32 offset, CRecordVector<CTableItem> &items); + bool ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size); + HRESULT OpenResources(int sectIndex, IInStream *stream, IArchiveOpenCallback *callback); + void CloseResources(); + + + bool CheckItem(const CSection §, const CResItem &item, size_t offset) const + { + return item.Offset >= sect.Va && offset <= _buf.GetCapacity() && _buf.GetCapacity() - offset >= item.Size; + } + +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +bool CHandler::Parse(const Byte *buf, UInt32 size) +{ + UInt32 i; + if (size < 512) + return false; + _peOffset = Get32(buf + 0x3C); + if (_peOffset >= 0x1000 || _peOffset + 512 > size || (_peOffset & 7) != 0) + return false; + + UInt32 pos = _peOffset; + if (!_header.Parse(buf + pos)) + return false; + if (_header.OptHeaderSize > 512 || _header.NumSections > NUM_SCAN_SECTIONS_MAX) + return false; + pos += kHeaderSize; + + if (!_optHeader.Parse(buf + pos, _header.OptHeaderSize)) + return false; + + pos += _header.OptHeaderSize; + _totalSize = pos; + + for (i = 0; i < _header.NumSections; i++, pos += kSectionSize) + { + CSection sect; + if (pos + kSectionSize > size) + return false; + sect.Parse(buf + pos); + sect.IsRealSect = true; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + return true; +} + +enum +{ + kpidSectAlign = kpidUserDefined, + kpidFileAlign, + kpidLinkerVer, + kpidOsVer, + kpidImageVer, + kpidSubsysVer, + kpidCodeSize, + kpidImageSize, + kpidInitDataSize, + kpidUnInitDataSize, + kpidHeadersSizeUnInitDataSize, + kpidSubSystem, + kpidDllCharacts, + kpidStackReserve, + kpidStackCommit, + kpidHeapReserve, + kpidHeapCommit, + kpidImageBase + // kpidAddressOfEntryPoint, + // kpidBaseOfCode, + // kpidBaseOfData32, +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidCpu, VT_BSTR}, + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidPhySize, VT_UI4}, + { NULL, kpidHeadersSize, VT_UI4}, + { NULL, kpidChecksum, VT_UI4}, + { L"Image Size", kpidImageSize, VT_UI4}, + { L"Section Alignment", kpidSectAlign, VT_UI4}, + { L"File Alignment", kpidFileAlign, VT_UI4}, + { L"Code Size", kpidCodeSize, VT_UI4}, + { L"Initialized Data Size", kpidInitDataSize, VT_UI4}, + { L"Uninitialized Data Size", kpidUnInitDataSize, VT_UI4}, + { L"Linker Version", kpidLinkerVer, VT_BSTR}, + { L"OS Version", kpidOsVer, VT_BSTR}, + { L"Image Version", kpidImageVer, VT_BSTR}, + { L"Subsystem Version", kpidSubsysVer, VT_BSTR}, + { L"Subsystem", kpidSubSystem, VT_BSTR}, + { L"DLL Characteristics", kpidDllCharacts, VT_BSTR}, + { L"Stack Reserve", kpidStackReserve, VT_UI8}, + { L"Stack Commit", kpidStackCommit, VT_UI8}, + { L"Heap Reserve", kpidHeapReserve, VT_UI8}, + { L"Heap Commit", kpidHeapCommit, VT_UI8}, + { L"Image Base", kpidImageBase, VT_UI8} + // { L"Address Of Entry Point", kpidAddressOfEntryPoint, VT_UI8}, + // { L"Base Of Code", kpidBaseOfCode, VT_UI8}, + // { L"Base Of Data", kpidBaseOfData32, VT_UI8}, +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidVa, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_WITH_NAME + +static void VerToProp(const CVersion &v, NCOM::CPropVariant &prop) +{ + StringToProp(v.GetString(), prop); +} + +void TimeToProp(UInt32 unixTime, NCOM::CPropVariant &prop) +{ + if (unixTime != 0) + { + FILETIME ft; + NTime::UnixTimeToFileTime(unixTime, ft); + prop = ft; + } +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidSectAlign: prop = _optHeader.SectAlign; break; + case kpidFileAlign: prop = _optHeader.FileAlign; break; + case kpidLinkerVer: + { + CVersion v = { _optHeader.LinkerVerMajor, _optHeader.LinkerVerMinor }; + VerToProp(v, prop); + break; + } + + case kpidOsVer: VerToProp(_optHeader.OsVer, prop); break; + case kpidImageVer: VerToProp(_optHeader.ImageVer, prop); break; + case kpidSubsysVer: VerToProp(_optHeader.SubsysVer, prop); break; + case kpidCodeSize: prop = _optHeader.CodeSize; break; + case kpidInitDataSize: prop = _optHeader.InitDataSize; break; + case kpidUnInitDataSize: prop = _optHeader.UninitDataSize; break; + case kpidImageSize: prop = _optHeader.ImageSize; break; + case kpidPhySize: prop = _totalSize; break; + case kpidHeadersSize: prop = _optHeader.HeadersSize; break; + case kpidChecksum: prop = _optHeader.CheckSum; break; + + case kpidCpu: PAIR_TO_PROP(g_MachinePairs, _header.Machine, prop); break; + case kpidBit64: if (_optHeader.Is64Bit()) prop = true; break; + case kpidSubSystem: PAIR_TO_PROP(g_SubSystems, _optHeader.SubSystem, prop); break; + + case kpidMTime: + case kpidCTime: TimeToProp(_header.Time, prop); break; + case kpidCharacts: FLAGS_TO_PROP(g_HeaderCharacts, _header.Flags, prop); break; + case kpidDllCharacts: FLAGS_TO_PROP(g_DllCharacts, _optHeader.DllCharacts, prop); break; + case kpidStackReserve: prop = _optHeader.StackReserve; break; + case kpidStackCommit: prop = _optHeader.StackCommit; break; + case kpidHeapReserve: prop = _optHeader.HeapReserve; break; + case kpidHeapCommit: prop = _optHeader.HeapCommit; break; + + case kpidImageBase: prop = _optHeader.ImageBase; break; + // case kpidAddressOfEntryPoint: prop = _optHeader.AddressOfEntryPoint; break; + // case kpidBaseOfCode: prop = _optHeader.BaseOfCode; break; + // case kpidBaseOfData32: if (!_optHeader.Is64Bit()) prop = _optHeader.BaseOfData32; break; + + case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +void CHandler::AddResNameToString(UString &s, UInt32 id) const +{ + if ((id & kFlag) != 0) + { + UString name; + if (ReadString(id & kMask, name) == S_OK) + { + if (name.IsEmpty()) + s += L"[]"; + else + { + if (name.Length() > 1 && name[0] == '"' && name.Back() == '"') + name = name.Mid(1, name.Length() - 2); + s += name; + } + return; + } + } + wchar_t sz[32]; + ConvertUInt32ToString(id, sz); + s += sz; +} + +UString CHandler::GetLangPrefix(UInt32 lang) +{ + UString s = _resourceFileName; + s += WCHAR_PATH_SEPARATOR; + if (!_oneLang) + { + AddResNameToString(s, lang); + s += WCHAR_PATH_SEPARATOR; + } + return s; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + const CMixItem &mixItem = _mixItems[index]; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + switch(propID) + { + case kpidPath: prop = GetLangPrefix(item.Lang) + L"string.txt"; break; + case kpidSize: + case kpidPackSize: + prop = (UInt64)item.Size; break; + } + } + else if (mixItem.ResourceIndex < 0) + { + const CSection &item = _sections[mixItem.SectionIndex]; + switch(propID) + { + case kpidPath: StringToProp(item.Name, prop); break; + case kpidSize: prop = (UInt64)item.VSize; break; + case kpidPackSize: prop = (UInt64)item.GetPackSize(); break; + case kpidOffset: prop = item.Pa; break; + case kpidVa: if (item.IsRealSect) prop = item.Va; break; + case kpidMTime: + case kpidCTime: + TimeToProp(item.IsDebug ? item.Time : _header.Time, prop); break; + case kpidCharacts: if (item.IsRealSect) FLAGS_TO_PROP(g_SectFlags, item.Flags, prop); break; + } + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + switch(propID) + { + case kpidPath: + { + UString s = GetLangPrefix(item.Lang); + { + const wchar_t *p = NULL; + if (item.Type < sizeof(g_ResTypes) / sizeof(g_ResTypes[0])) + p = g_ResTypes[item.Type]; + if (p != 0) + s += p; + else + AddResNameToString(s, item.Type); + } + s += WCHAR_PATH_SEPARATOR; + AddResNameToString(s, item.ID); + if (item.HeaderSize != 0) + { + if (item.IsBmp()) + s += L".bmp"; + else if (item.IsIcon()) + s += L".ico"; + } + prop = s; + break; + } + case kpidSize: prop = (UInt64)item.GetSize(); break; + case kpidPackSize: prop = (UInt64)item.Size; break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::LoadDebugSections(IInStream *stream, bool &thereIsSection) +{ + thereIsSection = false; + const CDirLink &debugLink = _optHeader.DirItems[kDirLink_Debug]; + if (debugLink.Size == 0) + return S_OK; + const unsigned kEntrySize = 28; + UInt32 numItems = debugLink.Size / kEntrySize; + if (numItems * kEntrySize != debugLink.Size || numItems > 16) + return S_FALSE; + + UInt64 pa = 0; + int i; + for (i = 0; i < _sections.Size(); i++) + { + const CSection § = _sections[i]; + if (sect.Va < debugLink.Va && debugLink.Va + debugLink.Size <= sect.Va + sect.PSize) + { + pa = sect.Pa + (debugLink.Va - sect.Va); + break; + } + } + if (i == _sections.Size()) + { + return S_OK; + // Exe for ARM requires S_OK + // return S_FALSE; + } + + CByteBuffer buffer; + buffer.SetCapacity(debugLink.Size); + Byte *buf = buffer; + + RINOK(stream->Seek(pa, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf, debugLink.Size)); + + for (i = 0; i < (int)numItems; i++) + { + CDebugEntry de; + de.Parse(buf); + + if (de.Size == 0) + continue; + + CSection sect; + sect.Name = ".debug" + GetDecString(i); + + sect.IsDebug = true; + sect.Time = de.Time; + sect.Va = de.Va; + sect.Pa = de.Pa; + sect.PSize = sect.VSize = de.Size; + UInt32 totalSize = sect.Pa + sect.PSize; + if (totalSize > _totalSize) + { + _totalSize = totalSize; + _sections.Add(sect); + thereIsSection = true; + } + buf += kEntrySize; + } + + return S_OK; +} + +HRESULT CHandler::ReadString(UInt32 offset, UString &dest) const +{ + if ((offset & 1) != 0 || offset >= _buf.GetCapacity()) + return S_FALSE; + size_t rem = _buf.GetCapacity() - offset; + if (rem < 2) + return S_FALSE; + unsigned length = Get16(_buf + offset); + if ((rem - 2) / 2 < length) + return S_FALSE; + dest.Empty(); + offset += 2; + for (unsigned i = 0; i < length; i++) + dest += (wchar_t)Get16(_buf + offset + i * 2); + return S_OK; +} + +HRESULT CHandler::ReadTable(UInt32 offset, CRecordVector<CTableItem> &items) +{ + if ((offset & 3) != 0 || offset >= _buf.GetCapacity()) + return S_FALSE; + size_t rem = _buf.GetCapacity() - offset; + if (rem < 16) + return S_FALSE; + items.Clear(); + unsigned numNameItems = Get16(_buf + offset + 12); + unsigned numIdItems = Get16(_buf + offset + 14); + unsigned numItems = numNameItems + numIdItems; + if ((rem - 16) / 8 < numItems) + return S_FALSE; + if (!_usedRes.SetRange(offset, 16 + numItems * 8)) + return S_FALSE; + offset += 16; + _oneLang = true; + unsigned i; + for (i = 0; i < numItems; i++) + { + CTableItem item; + const Byte *buf = _buf + offset; + offset += 8; + item.ID = Get32(buf + 0); + if (((item.ID & kFlag) != 0) != (i < numNameItems)) + return S_FALSE; + item.Offset = Get32(buf + 4); + items.Add(item); + } + return S_OK; +} + +static const UInt32 kFileSizeMax = (UInt32)1 << 30; +static const int kNumResItemsMax = (UInt32)1 << 23; +static const int kNumStringLangsMax = 128; + +// BITMAPINFOHEADER +struct CBitmapInfoHeader +{ + // UInt32 HeaderSize; + UInt32 XSize; + Int32 YSize; + UInt16 Planes; + UInt16 BitCount; + UInt32 Compression; + UInt32 SizeImage; + + bool Parse(const Byte *p, size_t size); +}; + +static const UInt32 kBitmapInfoHeader_Size = 0x28; + +bool CBitmapInfoHeader::Parse(const Byte *p, size_t size) +{ + if (size < kBitmapInfoHeader_Size || Get32(p) != kBitmapInfoHeader_Size) + return false; + XSize = Get32(p + 4); + YSize = (Int32)Get32(p + 8); + Planes = Get16(p + 12); + BitCount = Get16(p + 14); + Compression = Get32(p + 16); + SizeImage = Get32(p + 20); + return true; +} + +static UInt32 GetImageSize(UInt32 xSize, UInt32 ySize, UInt32 bitCount) +{ + return ((xSize * bitCount + 7) / 8 + 3) / 4 * 4 * ySize; +} + +static UInt32 SetBitmapHeader(Byte *dest, const Byte *src, UInt32 size) +{ + CBitmapInfoHeader h; + if (!h.Parse(src, size)) + return 0; + if (h.YSize < 0) + h.YSize = -h.YSize; + if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || h.BitCount > 32 || + h.Compression != 0) // BI_RGB + return 0; + if (h.SizeImage == 0) + h.SizeImage = GetImageSize(h.XSize, h.YSize, h.BitCount); + UInt32 totalSize = kBmpHeaderSize + size; + UInt32 offBits = totalSize - h.SizeImage; + // BITMAPFILEHEADER + SetUi16(dest, 0x4D42); + SetUi32(dest + 2, totalSize); + SetUi32(dest + 6, 0); + SetUi32(dest + 10, offBits); + return kBmpHeaderSize; +} + +static UInt32 SetIconHeader(Byte *dest, const Byte *src, UInt32 size) +{ + CBitmapInfoHeader h; + if (!h.Parse(src, size)) + return 0; + if (h.YSize < 0) + h.YSize = -h.YSize; + if (h.XSize > (1 << 26) || h.YSize > (1 << 26) || h.Planes != 1 || + h.Compression != 0) // BI_RGB + return 0; + + UInt32 numBitCount = h.BitCount; + if (numBitCount != 1 && + numBitCount != 4 && + numBitCount != 8 && + numBitCount != 24 && + numBitCount != 32) + return 0; + + if ((h.YSize & 1) != 0) + return 0; + h.YSize /= 2; + if (h.XSize > 0x100 || h.YSize > 0x100) + return 0; + + UInt32 imageSize; + // imageSize is not correct if AND mask array contains zeros + // in this case it is equal image1Size + + // UInt32 imageSize = h.SizeImage; + // if (imageSize == 0) + // { + UInt32 image1Size = GetImageSize(h.XSize, h.YSize, h.BitCount); + UInt32 image2Size = GetImageSize(h.XSize, h.YSize, 1); + imageSize = image1Size + image2Size; + // } + UInt32 numColors = 0; + if (numBitCount < 16) + numColors = 1 << numBitCount; + + SetUi16(dest, 0); // Reserved + SetUi16(dest + 2, 1); // RES_ICON + SetUi16(dest + 4, 1); // ResCount + + dest[6] = (Byte)h.XSize; // Width + dest[7] = (Byte)h.YSize; // Height + dest[8] = (Byte)numColors; // ColorCount + dest[9] = 0; // Reserved + + SetUi32(dest + 10, 0); // Reserved1 / Reserved2 + + UInt32 numQuadsBytes = numColors * 4; + UInt32 BytesInRes = kBitmapInfoHeader_Size + numQuadsBytes + imageSize; + SetUi32(dest + 14, BytesInRes); + SetUi32(dest + 18, kIconHeaderSize); + + /* + Description = DWORDToString(xSize) + + kDelimiterChar + DWORDToString(ySize) + + kDelimiterChar + DWORDToString(numBitCount); + */ + return kIconHeaderSize; +} + +bool CHandler::ParseStringRes(UInt32 id, UInt32 lang, const Byte *src, UInt32 size) +{ + if ((size & 1) != 0) + return false; + + int i; + for (i = 0; i < _strings.Size(); i++) + if (_strings[i].Lang == lang) + break; + if (i == _strings.Size()) + { + if (_strings.Size() >= kNumStringLangsMax) + return false; + CStringItem item; + item.Size = 0; + item.Lang = lang; + i = _strings.Add(item); + } + + CStringItem &item = _strings[i]; + id = (id - 1) << 4; + UInt32 pos = 0; + for (i = 0; i < 16; i++) + { + if (size - pos < 2) + return false; + UInt32 len = Get16(src + pos); + pos += 2; + if (len != 0) + { + if (size - pos < len * 2) + return false; + char temp[32]; + ConvertUInt32ToString(id + i, temp); + size_t tempLen = strlen(temp); + size_t j; + for (j = 0; j < tempLen; j++) + item.AddChar(temp[j]); + item.AddChar('\t'); + for (j = 0; j < len; j++, pos += 2) + item.AddWChar(Get16(src + pos)); + item.AddChar(0x0D); + item.AddChar(0x0A); + } + } + return (size == pos); +} + +HRESULT CHandler::OpenResources(int sectionIndex, IInStream *stream, IArchiveOpenCallback *callback) +{ + const CSection § = _sections[sectionIndex]; + size_t fileSize = sect.PSize; // Maybe we need sect.VSize here !!! + if (fileSize > kFileSizeMax) + return S_FALSE; + { + UInt64 fileSize64 = fileSize; + if (callback) + RINOK(callback->SetTotal(NULL, &fileSize64)); + RINOK(stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL)); + _buf.SetCapacity(fileSize); + for (size_t pos = 0; pos < fileSize;) + { + UInt64 offset64 = pos; + if (callback) + RINOK(callback->SetCompleted(NULL, &offset64)) + size_t rem = MyMin(fileSize - pos, (size_t)(1 << 20)); + RINOK(ReadStream_FALSE(stream, _buf + pos, rem)); + pos += rem; + } + } + + _usedRes.Alloc(fileSize); + CRecordVector<CTableItem> specItems; + RINOK(ReadTable(0, specItems)); + + _oneLang = true; + bool stringsOk = true; + size_t maxOffset = 0; + for (int i = 0; i < specItems.Size(); i++) + { + const CTableItem &item1 = specItems[i]; + if ((item1.Offset & kFlag) == 0) + return S_FALSE; + + CRecordVector<CTableItem> specItems2; + RINOK(ReadTable(item1.Offset & kMask, specItems2)); + + for (int j = 0; j < specItems2.Size(); j++) + { + const CTableItem &item2 = specItems2[j]; + if ((item2.Offset & kFlag) == 0) + return S_FALSE; + + CRecordVector<CTableItem> specItems3; + RINOK(ReadTable(item2.Offset & kMask, specItems3)); + + CResItem item; + item.Type = item1.ID; + item.ID = item2.ID; + + for (int k = 0; k < specItems3.Size(); k++) + { + if (_items.Size() >= kNumResItemsMax) + return S_FALSE; + const CTableItem &item3 = specItems3[k]; + if ((item3.Offset & kFlag) != 0) + return S_FALSE; + if (item3.Offset >= _buf.GetCapacity() || _buf.GetCapacity() - item3.Offset < 16) + return S_FALSE; + const Byte *buf = _buf + item3.Offset; + item.Lang = item3.ID; + item.Offset = Get32(buf + 0); + item.Size = Get32(buf + 4); + // UInt32 codePage = Get32(buf + 8); + if (Get32(buf + 12) != 0) + return S_FALSE; + if (!_items.IsEmpty() && _oneLang && !item.IsNameEqual(_items.Back())) + _oneLang = false; + + item.HeaderSize = 0; + + size_t offset = item.Offset - sect.Va; + if (offset > maxOffset) + maxOffset = offset; + if (offset + item.Size > maxOffset) + maxOffset = offset + item.Size; + + if (CheckItem(sect, item, offset)) + { + const Byte *data = _buf + offset; + if (item.IsBmp()) + item.HeaderSize = SetBitmapHeader(item.Header, data, item.Size); + else if (item.IsIcon()) + item.HeaderSize = SetIconHeader(item.Header, data, item.Size); + else if (item.IsString()) + { + if (stringsOk) + stringsOk = ParseStringRes(item.ID, item.Lang, data, item.Size); + } + } + + item.Enabled = true; + _items.Add(item); + } + } + } + + if (stringsOk && !_strings.IsEmpty()) + { + int i; + for (i = 0; i < _items.Size(); i++) + { + CResItem &item = _items[i]; + if (item.IsString()) + item.Enabled = false; + } + for (i = 0; i < _strings.Size(); i++) + { + if (_strings[i].Size == 0) + continue; + CMixItem mixItem; + mixItem.ResourceIndex = -1; + mixItem.StringIndex = i; + mixItem.SectionIndex = sectionIndex; + _mixItems.Add(mixItem); + } + } + + _usedRes.Free(); + + int numBits = _optHeader.GetNumFileAlignBits(); + if (numBits >= 0) + { + UInt32 mask = (1 << numBits) - 1; + size_t end = ((maxOffset + mask) & ~mask); + if (end < sect.VSize && end <= sect.PSize) + { + CSection sect2; + sect2.Flags = 0; + + // we skip Zeros to start of aligned block + size_t i; + for (i = maxOffset; i < end; i++) + if (_buf[i] != 0) + break; + if (i == end) + maxOffset = end; + + sect2.Pa = sect.Pa + (UInt32)maxOffset; + sect2.Va = sect.Va + (UInt32)maxOffset; + sect2.PSize = sect.VSize - (UInt32)maxOffset; + sect2.VSize = sect2.PSize; + sect2.Name = ".rsrc_1"; + sect2.Time = 0; + sect2.IsAdditionalSection = true; + _sections.Add(sect2); + } + } + + return S_OK; +} + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) +{ + const UInt32 kBufSize = 1 << 18; + const UInt32 kSigSize = 2; + + _mainSubfile = -1; + + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + size_t processed = kSigSize; + RINOK(ReadStream_FALSE(stream, buf, processed)); + if (buf[0] != 'M' || buf[1] != 'Z') + return S_FALSE; + processed = kBufSize - kSigSize; + RINOK(ReadStream(stream, buf + kSigSize, &processed)); + processed += kSigSize; + if (!Parse(buf, (UInt32)processed)) + return S_FALSE; + bool thereISDebug; + RINOK(LoadDebugSections(stream, thereISDebug)); + + const CDirLink &certLink = _optHeader.DirItems[kDirLink_Certificate]; + if (certLink.Size != 0) + { + CSection sect; + sect.Name = "CERTIFICATE"; + sect.Va = 0; + sect.Pa = certLink.Va; + sect.PSize = sect.VSize = certLink.Size; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + if (thereISDebug) + { + const UInt32 kAlign = 1 << 12; + UInt32 alignPos = _totalSize & (kAlign - 1); + if (alignPos != 0) + { + UInt32 size = kAlign - alignPos; + RINOK(stream->Seek(_totalSize, STREAM_SEEK_SET, NULL)); + buffer.Free(); + buffer.SetCapacity(kAlign); + Byte *buf = buffer; + size_t processed = size; + RINOK(ReadStream(stream, buf, &processed)); + size_t i; + for (i = 0; i < processed; i++) + { + if (buf[i] != 0) + break; + } + if (processed < size && processed < 100) + _totalSize += (UInt32)processed; + else if (((_totalSize + i) & 0x1FF) == 0 || processed < size) + _totalSize += (UInt32)i; + } + } + + if (_header.NumSymbols > 0 && _header.PointerToSymbolTable >= 512) + { + if (_header.NumSymbols >= (1 << 24)) + return S_FALSE; + CSection sect; + sect.Name = "COFF_SYMBOLS"; + UInt32 size = _header.NumSymbols * 18; + RINOK(stream->Seek((UInt64)_header.PointerToSymbolTable + size, STREAM_SEEK_SET, NULL)); + Byte buf[4]; + RINOK(ReadStream_FALSE(stream, buf, 4)); + UInt32 size2 = Get32(buf); + if (size2 >= (1 << 28)) + return S_FALSE; + size += size2; + + sect.Va = 0; + sect.Pa = _header.PointerToSymbolTable; + sect.PSize = sect.VSize = size; + sect.UpdateTotalSize(_totalSize); + _sections.Add(sect); + } + + UInt64 fileSize; + RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize > _totalSize) + return S_FALSE; + _totalSizeLimited = (_totalSize < fileSize) ? _totalSize : (UInt32)fileSize; + + { + CObjectVector<CSection> sections = _sections; + sections.Sort(); + UInt32 limit = (1 << 12); + int num = 0; + int numSections = sections.Size(); + for (int i = 0; i < numSections; i++) + { + const CSection &s = sections[i]; + if (s.Pa > limit) + { + CSection s2; + s2.Pa = s2.Va = limit; + s2.PSize = s2.VSize = s.Pa - limit; + s2.IsAdditionalSection = true; + s2.Name = '['; + s2.Name += GetDecString(num++); + s2.Name += ']'; + _sections.Add(s2); + limit = s.Pa; + } + UInt32 next = s.Pa + s.PSize; + if (next < s.Pa) + break; + if (next >= limit) + limit = next; + } + } + + _parseResources = true; + + UInt64 mainSize = 0, mainSize2 = 0; + int i; + for (i = 0; i < _sections.Size(); i++) + { + const CSection § = _sections[i]; + CMixItem mixItem; + mixItem.SectionIndex = i; + if (_parseResources && sect.Name == ".rsrc" && _items.IsEmpty()) + { + HRESULT res = OpenResources(i, stream, callback); + if (res == S_OK) + { + _resourceFileName = GetUnicodeString(sect.Name); + for (int j = 0; j < _items.Size(); j++) + { + const CResItem &item = _items[j]; + if (item.Enabled) + { + mixItem.ResourceIndex = j; + mixItem.StringIndex = -1; + if (item.IsRcDataOrUnknown()) + { + if (item.Size >= mainSize) + { + mainSize2 = mainSize; + mainSize = item.Size; + _mainSubfile = _mixItems.Size(); + } + else if (item.Size >= mainSize2) + mainSize2 = item.Size; + } + _mixItems.Add(mixItem); + } + } + if (sect.PSize > sect.VSize) + { + int numBits = _optHeader.GetNumFileAlignBits(); + if (numBits >= 0) + { + UInt32 mask = (1 << numBits) - 1; + UInt32 end = ((sect.VSize + mask) & ~mask); + + if (sect.PSize > end) + { + CSection sect2; + sect2.Flags = 0; + sect2.Pa = sect.Pa + end; + sect2.Va = sect.Va + end; + sect2.PSize = sect.PSize - end; + sect2.VSize = sect2.PSize; + sect2.Name = ".rsrc_2"; + sect2.Time = 0; + sect2.IsAdditionalSection = true; + _sections.Add(sect2); + } + } + } + continue; + } + if (res != S_FALSE) + return res; + CloseResources(); + } + mixItem.StringIndex = -1; + mixItem.ResourceIndex = -1; + if (sect.IsAdditionalSection) + { + if (sect.PSize >= mainSize) + { + mainSize2 = mainSize; + mainSize = sect.PSize; + _mainSubfile = _mixItems.Size(); + } + else + mainSize2 = sect.PSize; + } + _mixItems.Add(mixItem); + } + + if (mainSize2 >= (1 << 20) && mainSize < mainSize2 * 2) + _mainSubfile = -1; + + for (i = 0; i < _mixItems.Size(); i++) + { + const CMixItem &mixItem = _mixItems[i]; + if (mixItem.StringIndex < 0 && mixItem.ResourceIndex < 0 && _sections[mixItem.SectionIndex].Name == "_winzip_") + { + _mainSubfile = i; + break; + } + } + + return S_OK; +} + +HRESULT CalcCheckSum(ISequentialInStream *stream, UInt32 size, UInt32 excludePos, UInt32 &res) +{ + // size &= ~1; + const UInt32 kBufSize = 1 << 23; + CByteBuffer buffer; + buffer.SetCapacity(kBufSize); + Byte *buf = buffer; + + UInt32 sum = 0; + UInt32 pos = 0; + for (;;) + { + UInt32 rem = size - pos; + if (rem > kBufSize) + rem = kBufSize; + if (rem == 0) + break; + size_t processed = rem; + RINOK(ReadStream(stream, buf, &processed)); + + /* + for (; processed < rem; processed++) + buf[processed] = 0; + */ + + if ((processed & 1) != 0) + buf[processed] = 0; + + for (int j = 0; j < 4; j++) + { + UInt32 p = excludePos + j; + if (pos <= p && p < pos + processed) + buf[p - pos] = 0; + } + + for (size_t i = 0; i < processed; i += 2) + { + sum += Get16(buf + i); + sum = (sum + (sum >> 16)) & 0xFFFF; + } + pos += (UInt32)processed; + if (rem != processed) + break; + } + sum += pos; + res = sum; + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(inStream, callback)); + _stream = inStream; + return S_OK; + COM_TRY_END +} + +void CHandler::CloseResources() +{ + _usedRes.Free(); + _items.Clear(); + _strings.Clear(); + _buf.SetCapacity(0); +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + _sections.Clear(); + _mixItems.Clear(); + CloseResources(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _mixItems.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _mixItems.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CMixItem &mixItem = _mixItems[allFilesMode ? i : indices[i]]; + if (mixItem.StringIndex >= 0) + totalSize += _strings[mixItem.StringIndex].Size; + else if (mixItem.ResourceIndex < 0) + totalSize += _sections[mixItem.SectionIndex].GetPackSize(); + else + totalSize += _items[mixItem.ResourceIndex].GetSize(); + } + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + UInt64 currentItemSize; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + bool checkSumOK = true; + if (_optHeader.CheckSum != 0 && (int)numItems == _mixItems.Size()) + { + UInt32 checkSum = 0; + RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); + CalcCheckSum(_stream, _totalSizeLimited, _peOffset + kHeaderSize + 64, checkSum); + checkSumOK = (checkSum == _optHeader.CheckSum); + } + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + const CMixItem &mixItem = _mixItems[index]; + + const CSection § = _sections[mixItem.SectionIndex]; + bool isOk = true; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + currentItemSize = item.Size; + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + if (outStream) + RINOK(WriteStream(outStream, item.Buf, item.Size)); + } + else if (mixItem.ResourceIndex < 0) + { + currentItemSize = sect.GetPackSize(); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(_stream->Seek(sect.Pa, STREAM_SEEK_SET, NULL)); + streamSpec->Init(currentItemSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + isOk = (copyCoderSpec->TotalSize == currentItemSize); + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + currentItemSize = item.GetSize(); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + size_t offset = item.Offset - sect.Va; + if (!CheckItem(sect, item, offset)) + isOk = false; + else if (outStream) + { + if (item.HeaderSize != 0) + RINOK(WriteStream(outStream, item.Header, item.HeaderSize)); + RINOK(WriteStream(outStream, _buf + offset, item.Size)); + } + } + + outStream.Release(); + RINOK(extractCallback->SetOperationResult(isOk ? + checkSumOK ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + + const CMixItem &mixItem = _mixItems[index]; + const CSection § = _sections[mixItem.SectionIndex]; + if (mixItem.IsSectionItem()) + return CreateLimitedInStream(_stream, sect.Pa, sect.PSize, stream); + + CBufInStream *inStreamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> streamTemp = inStreamSpec; + CReferenceBuf *referenceBuf = new CReferenceBuf; + CMyComPtr<IUnknown> ref = referenceBuf; + if (mixItem.StringIndex >= 0) + { + const CStringItem &item = _strings[mixItem.StringIndex]; + referenceBuf->Buf.SetCapacity(item.Size); + memcpy(referenceBuf->Buf, item.Buf, item.Size); + } + else + { + const CResItem &item = _items[mixItem.ResourceIndex]; + size_t offset = item.Offset - sect.Va; + if (!CheckItem(sect, item, offset)) + return S_FALSE; + if (item.HeaderSize == 0) + { + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp2 = streamSpec; + streamSpec->Init(_buf + offset, item.Size, (IInArchive *)this); + *stream = streamTemp2.Detach(); + return S_OK; + } + referenceBuf->Buf.SetCapacity(item.HeaderSize + item.Size); + memcpy(referenceBuf->Buf, item.Header, item.HeaderSize); + memcpy(referenceBuf->Buf + item.HeaderSize, _buf + offset, item.Size); + } + inStreamSpec->Init(referenceBuf); + + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"PE", L"exe dll sys", 0, 0xDD, { 'P', 'E', 0, 0 }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Pe) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/PpmdHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/PpmdHandler.cpp new file mode 100644 index 000000000..9b2ef0482 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/PpmdHandler.cpp @@ -0,0 +1,456 @@ +/* PpmdHandler.c -- PPMd format handler +2010-03-10 : Igor Pavlov : Public domain +This code is based on: + PPMd var.H (2001) / var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" +#include "../../../C/Alloc.h" +#include "../../../C/Ppmd7.h" +#include "../../../C/Ppmd8.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/CWrappers.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +using namespace NWindows; + +namespace NArchive { +namespace NPpmd { + +static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); } +static void SzBigFree(void *, void *address) { BigFree(address); } +static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; + +static const UInt32 kBufSize = (1 << 20); + +struct CBuf +{ + Byte *Buf; + + CBuf(): Buf(0) {} + ~CBuf() { ::MidFree(Buf); } + bool Alloc() + { + if (!Buf) + Buf = (Byte *)::MidAlloc(kBufSize); + return (Buf != 0); + } +}; + +static const UInt32 kHeaderSize = 16; +static const UInt32 kSignature = 0x84ACAF8F; +static const unsigned kNewHeaderVer = 8; + +struct CItem +{ + UInt32 Attrib; + UInt32 Time; + AString Name; + + unsigned Order; + unsigned MemInMB; + unsigned Ver; + unsigned Restor; + + HRESULT ReadHeader(ISequentialInStream *s, UInt32 &headerSize); + bool IsSupported() const { return Ver == 7 || (Ver == 8 && Restor <= 1); } +}; + +HRESULT CItem::ReadHeader(ISequentialInStream *s, UInt32 &headerSize) +{ + Byte h[kHeaderSize]; + RINOK(ReadStream_FALSE(s, h, kHeaderSize)); + if (GetUi32(h) != kSignature) + return S_FALSE; + Attrib = GetUi32(h + 4); + Time = GetUi32(h + 12); + + unsigned info = GetUi16(h + 8); + Order = (info & 0xF) + 1; + MemInMB = ((info >> 4) & 0xFF) + 1; + Ver = info >> 12; + + UInt32 nameLen = GetUi16(h + 10); + Restor = nameLen >> 14; + if (Restor > 2) + return S_FALSE; + if (Ver >= kNewHeaderVer) + nameLen &= 0x3FFF; + if (nameLen > (1 << 9)) + return S_FALSE; + char *name = Name.GetBuffer(nameLen + 1); + HRESULT res = ReadStream_FALSE(s, name, nameLen); + name[nameLen] = 0; + headerSize = kHeaderSize + nameLen; + Name.ReleaseBuffer(); + return res; +} + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public CMyUnknownImp +{ + CItem _item; + UInt32 _headerSize; + UInt64 _packSize; + bool _packSizeDefined; + CMyComPtr<ISequentialInStream> _stream; + +public: + MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq) + INTERFACE_IInArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidMethod, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +static void UIntToString(AString &s, const char *prefix, unsigned value) +{ + s += prefix; + char temp[16]; + ::ConvertUInt32ToString((UInt32)value, temp); + s += temp; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPath: prop = MultiByteToUnicodeString(_item.Name, CP_ACP); break; + case kpidMTime: + { + FILETIME utc; + if (NTime::DosTimeToFileTime(_item.Time, utc)) + prop = utc; + break; + } + case kpidAttrib: prop = _item.Attrib; break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + case kpidMethod: + { + AString s = "PPMd"; + s += (char)('A' + _item.Ver); + UIntToString(s, ":o", _item.Order); + UIntToString(s, ":mem", _item.MemInMB); + s += 'm'; + if (_item.Ver >= kNewHeaderVer && _item.Restor != 0) + UIntToString(s, ":r", _item.Restor); + prop = s; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) +{ + return OpenSeq(stream); +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + COM_TRY_BEGIN + HRESULT res; + try + { + Close(); + res = _item.ReadHeader(stream, _headerSize); + } + catch(...) { res = S_FALSE; } + if (res == S_OK) + _stream = stream; + else + Close(); + return res; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _stream.Release(); + return S_OK; +} + +static const UInt32 kTopValue = (1 << 24); +static const UInt32 kBot = (1 << 15); + +struct CRangeDecoder +{ + IPpmd7_RangeDec s; + UInt32 Range; + UInt32 Code; + UInt32 Low; + CByteInBufWrap *Stream; + +public: + bool Init() + { + Code = 0; + Low = 0; + Range = 0xFFFFFFFF; + for (int i = 0; i < 4; i++) + Code = (Code << 8) | Stream->ReadByte(); + return Code < 0xFFFFFFFF; + } + + void Normalize() + { + while ((Low ^ (Low + Range)) < kTopValue || + Range < kBot && ((Range = (0 - Low) & (kBot - 1)), 1)) + { + Code = (Code << 8) | Stream->ReadByte(); + Range <<= 8; + Low <<= 8; + } + } + + CRangeDecoder(); +}; + + +extern "C" { + +static UInt32 Range_GetThreshold(void *pp, UInt32 total) +{ + CRangeDecoder *p = (CRangeDecoder *)pp; + return p->Code / (p->Range /= total); +} + +static void Range_Decode(void *pp, UInt32 start, UInt32 size) +{ + CRangeDecoder *p = (CRangeDecoder *)pp; + start *= p->Range; + p->Low += start; + p->Code -= start; + p->Range *= size; + p->Normalize(); +} + +static UInt32 Range_DecodeBit(void *pp, UInt32 size0) +{ + CRangeDecoder *p = (CRangeDecoder *)pp; + if (p->Code / (p->Range >>= 14) < size0) + { + Range_Decode(p, 0, size0); + return 0; + } + else + { + Range_Decode(p, size0, (1 << 14) - size0); + return 1; + } +} + +} + +CRangeDecoder::CRangeDecoder() +{ + s.GetThreshold = Range_GetThreshold; + s.Decode = Range_Decode; + s.DecodeBit = Range_DecodeBit; +} + +struct CPpmdCpp +{ + unsigned Ver; + CRangeDecoder _rc; + CPpmd7 _ppmd7; + CPpmd8 _ppmd8; + + CPpmdCpp(unsigned version) + { + Ver = version; + Ppmd7_Construct(&_ppmd7); + Ppmd8_Construct(&_ppmd8); + } + + ~CPpmdCpp() + { + Ppmd7_Free(&_ppmd7, &g_BigAlloc); + Ppmd8_Free(&_ppmd8, &g_BigAlloc); + } + + bool Alloc(UInt32 memInMB) + { + memInMB <<= 20; + if (Ver == 7) + return Ppmd7_Alloc(&_ppmd7, memInMB, &g_BigAlloc) != 0; + return Ppmd8_Alloc(&_ppmd8, memInMB, &g_BigAlloc) != 0; + } + + void Init(unsigned order, unsigned restor) + { + if (Ver == 7) + Ppmd7_Init(&_ppmd7, order); + else + Ppmd8_Init(&_ppmd8, order, restor);; + } + + bool InitRc(CByteInBufWrap *inStream) + { + if (Ver == 7) + { + _rc.Stream = inStream; + return _rc.Init(); + } + else + { + _ppmd8.Stream.In = &inStream->p; + return Ppmd8_RangeDec_Init(&_ppmd8) != 0; + } + } + + bool IsFinishedOK() + { + if (Ver == 7) + return Ppmd7z_RangeDec_IsFinishedOK(&_rc); + return Ppmd8_RangeDec_IsFinishedOK(&_ppmd8); + } +}; + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + // extractCallback->SetTotal(_packSize); + UInt64 currentTotalPacked = 0; + RINOK(extractCallback->SetCompleted(¤tTotalPacked)); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + CByteInBufWrap inBuf; + if (!inBuf.Alloc(1 << 20)) + return E_OUTOFMEMORY; + inBuf.Stream = _stream; + + CBuf outBuf; + if (!outBuf.Alloc()) + return E_OUTOFMEMORY; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + CPpmdCpp ppmd(_item.Ver); + if (!ppmd.Alloc(_item.MemInMB)) + return E_OUTOFMEMORY; + Int32 opRes = NExtract::NOperationResult::kUnSupportedMethod; + if (_item.IsSupported()) + { + opRes = NExtract::NOperationResult::kDataError; + ppmd.Init(_item.Order, _item.Restor); + inBuf.Init(); + UInt64 outSize = 0; + if (ppmd.InitRc(&inBuf) && !inBuf.Extra && inBuf.Res == S_OK) + for (;;) + { + lps->InSize = _packSize = inBuf.GetProcessed(); + lps->OutSize = outSize; + RINOK(lps->SetCur()); + + size_t i; + int sym = 0; + + if (ppmd.Ver == 7) + { + for (i = 0; i < kBufSize; i++) + { + sym = Ppmd7_DecodeSymbol(&ppmd._ppmd7, &ppmd._rc.s); + if (inBuf.Extra || sym < 0) + break; + outBuf.Buf[i] = (Byte)sym; + } + } + else + { + for (i = 0; i < kBufSize; i++) + { + sym = Ppmd8_DecodeSymbol(&ppmd._ppmd8); + if (inBuf.Extra || sym < 0) + break; + outBuf.Buf[i] = (Byte)sym; + } + } + + outSize += i; + _packSize = _headerSize + inBuf.GetProcessed(); + _packSizeDefined = true; + if (realOutStream) + { + RINOK(WriteStream(realOutStream, outBuf.Buf, i)); + } + if (sym < 0) + { + if (sym == -1 && ppmd.IsFinishedOK()) + opRes = NExtract::NOperationResult::kOK; + break; + } + } + RINOK(inBuf.Res); + } + realOutStream.Release(); + return extractCallback->SetOperationResult(opRes); +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Ppmd", L"pmd", 0, 0xD, { 0x8F, 0xAF, 0xAC, 0x84 }, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Ppmd) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.cpp new file mode 100644 index 000000000..5d072d34d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.cpp @@ -0,0 +1,869 @@ +// RarHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/PropVariantUtils.h" +#include "Windows/Time.h" + +#include "../../IPassword.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" +#include "../../Common/MethodId.h" +#include "../../Common/ProgressUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "../../Crypto/Rar20Crypto.h" +#include "../../Crypto/RarAes.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/OutStreamWithCRC.h" + +#include "RarHandler.h" + +using namespace NWindows; +using namespace NTime; + +namespace NArchive { +namespace NRar { + +static const wchar_t *kHostOS[] = +{ + L"MS DOS", + L"OS/2", + L"Win32", + L"Unix", + L"Mac OS", + L"BeOS" +}; + +static const int kNumHostOSes = sizeof(kHostOS) / sizeof(kHostOS[0]); + +static const wchar_t *kUnknownOS = L"Unknown"; + +static const CUInt32PCharPair k_Flags[] = +{ + { 0, "Volume" }, + { 1, "Comment" }, + { 2, "Lock" }, + { 3, "Solid" }, + { 4, "NewVolName" }, // pack_comment in old versuons + { 5, "Authenticity" }, + { 6, "Recovery" }, + { 7, "BlockEncryption" }, + { 8, "FirstVolume" }, + { 9, "EncryptVer" } +}; + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + + { NULL, kpidEncrypted, VT_BOOL}, + { NULL, kpidSolid, VT_BOOL}, + { NULL, kpidCommented, VT_BOOL}, + { NULL, kpidSplitBefore, VT_BOOL}, + { NULL, kpidSplitAfter, VT_BOOL}, + { NULL, kpidCRC, VT_UI4}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidUnpackVer, VT_UI1} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidCharacts, VT_BSTR}, + { NULL, kpidSolid, VT_BOOL}, + { NULL, kpidNumBlocks, VT_UI4}, + // { NULL, kpidEncrypted, VT_BOOL}, + { NULL, kpidIsVolume, VT_BOOL}, + { NULL, kpidNumVolumes, VT_UI4}, + { NULL, kpidPhySize, VT_UI8} + // { NULL, kpidCommented, VT_BOOL} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +UInt64 CHandler::GetPackSize(int refIndex) const +{ + const CRefItem &refItem = _refItems[refIndex]; + UInt64 totalPackSize = 0; + for (int i = 0; i < refItem.NumItems; i++) + totalPackSize += _items[refItem.ItemIndex + i].PackSize; + return totalPackSize; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidSolid: prop = _archiveInfo.IsSolid(); break; + case kpidCharacts: FLAGS_TO_PROP(k_Flags, _archiveInfo.Flags, prop); break; + // case kpidEncrypted: prop = _archiveInfo.IsEncrypted(); break; // it's for encrypted names. + case kpidIsVolume: prop = _archiveInfo.IsVolume(); break; + case kpidNumVolumes: prop = (UInt32)_archives.Size(); break; + case kpidOffset: if (_archiveInfo.StartPosition != 0) prop = _archiveInfo.StartPosition; break; + // case kpidCommented: prop = _archiveInfo.IsCommented(); break; + case kpidNumBlocks: + { + UInt32 numBlocks = 0; + for (int i = 0; i < _refItems.Size(); i++) + if (!IsSolid(i)) + numBlocks++; + prop = (UInt32)numBlocks; + break; + } + case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _refItems.Size(); + return S_OK; +} + +static bool RarTimeToFileTime(const CRarTime &rarTime, FILETIME &result) +{ + if (!DosTimeToFileTime(rarTime.DosTime, result)) + return false; + UInt64 value = (((UInt64)result.dwHighDateTime) << 32) + result.dwLowDateTime; + value += (UInt64)rarTime.LowSecond * 10000000; + value += ((UInt64)rarTime.SubTime[2] << 16) + + ((UInt64)rarTime.SubTime[1] << 8) + + ((UInt64)rarTime.SubTime[0]); + result.dwLowDateTime = (DWORD)value; + result.dwHighDateTime = DWORD(value >> 32); + return true; +} + +static void RarTimeToProp(const CRarTime &rarTime, NWindows::NCOM::CPropVariant &prop) +{ + FILETIME localFileTime, utcFileTime; + if (RarTimeToFileTime(rarTime, localFileTime)) + { + if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime)) + utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; + } + else + utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0; + prop = utcFileTime; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CRefItem &refItem = _refItems[index]; + const CItemEx &item = _items[refItem.ItemIndex]; + switch(propID) + { + case kpidPath: + { + UString u; + if (item.HasUnicodeName() && !item.UnicodeName.IsEmpty()) + u = item.UnicodeName; + else + u = MultiByteToUnicodeString(item.Name, CP_OEMCP); + prop = (const wchar_t *)NItemName::WinNameToOSName(u); + break; + } + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.Size; break; + case kpidPackSize: prop = GetPackSize(index); break; + case kpidMTime: RarTimeToProp(item.MTime, prop); break; + case kpidCTime: if (item.CTimeDefined) RarTimeToProp(item.CTime, prop); break; + case kpidATime: if (item.ATimeDefined) RarTimeToProp(item.ATime, prop); break; + case kpidAttrib: prop = item.GetWinAttributes(); break; + case kpidEncrypted: prop = item.IsEncrypted(); break; + case kpidSolid: prop = IsSolid(index); break; + case kpidCommented: prop = item.IsCommented(); break; + case kpidSplitBefore: prop = item.IsSplitBefore(); break; + case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break; + case kpidCRC: + { + const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC); + break; + } + case kpidUnpackVer: prop = item.UnPackVersion; break; + case kpidMethod: + { + UString method; + if (item.Method >= Byte('0') && item.Method <= Byte('5')) + { + method = L"m"; + wchar_t temp[32]; + ConvertUInt64ToString(item.Method - Byte('0'), temp); + method += temp; + if (!item.IsDir()) + { + method += L":"; + ConvertUInt64ToString(16 + item.GetDictSize(), temp); + method += temp; + } + } + else + { + wchar_t temp[32]; + ConvertUInt64ToString(item.Method, temp); + method += temp; + } + prop = method; + break; + } + case kpidHostOS: prop = (item.HostOS < kNumHostOSes) ? (kHostOS[item.HostOS]) : kUnknownOS; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CVolumeName +{ + bool _first; + bool _newStyle; + UString _unchangedPart; + UString _changedPart; + UString _afterPart; +public: + CVolumeName(): _newStyle(true) {}; + + bool InitName(const UString &name, bool newStyle) + { + _first = true; + _newStyle = newStyle; + int dotPos = name.ReverseFind('.'); + UString basePart = name; + if (dotPos >= 0) + { + UString ext = name.Mid(dotPos + 1); + if (ext.CompareNoCase(L"rar") == 0) + { + _afterPart = name.Mid(dotPos); + basePart = name.Left(dotPos); + } + else if (ext.CompareNoCase(L"exe") == 0) + { + _afterPart = L".rar"; + basePart = name.Left(dotPos); + } + else if (!_newStyle) + { + if (ext.CompareNoCase(L"000") == 0 || + ext.CompareNoCase(L"001") == 0 || + ext.CompareNoCase(L"r00") == 0 || + ext.CompareNoCase(L"r01") == 0) + { + _afterPart.Empty(); + _first = false; + _changedPart = ext; + _unchangedPart = name.Left(dotPos + 1); + return true; + } + } + } + + if (!_newStyle) + { + _afterPart.Empty(); + _unchangedPart = basePart + UString(L"."); + _changedPart = L"r00"; + return true; + } + + int numLetters = 1; + if (basePart.Right(numLetters) == L"1" || basePart.Right(numLetters) == L"0") + { + while (numLetters < basePart.Length()) + { + if (basePart[basePart.Length() - numLetters - 1] != '0') + break; + numLetters++; + } + } + else + return false; + _unchangedPart = basePart.Left(basePart.Length() - numLetters); + _changedPart = basePart.Right(numLetters); + return true; + } + + UString GetNextName() + { + UString newName; + if (_newStyle || !_first) + { + int i; + int numLetters = _changedPart.Length(); + for (i = numLetters - 1; i >= 0; i--) + { + wchar_t c = _changedPart[i]; + if (c == L'9') + { + c = L'0'; + newName = c + newName; + if (i == 0) + newName = UString(L'1') + newName; + continue; + } + c++; + newName = UString(c) + newName; + i--; + for (; i >= 0; i--) + newName = _changedPart[i] + newName; + break; + } + _changedPart = newName; + } + _first = false; + return _unchangedPart + _changedPart + _afterPart; + } +}; + +HRESULT CHandler::Open2(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback) +{ + { + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openCallback; + + CVolumeName seqName; + + UInt64 totalBytes = 0; + UInt64 curBytes = 0; + + if (openCallback) + { + openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback); + openArchiveCallbackWrap.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); + } + + for (;;) + { + CMyComPtr<IInStream> inStream; + if (!_archives.IsEmpty()) + { + if (!openVolumeCallback) + break; + + if (_archives.Size() == 1) + { + if (!_archiveInfo.IsVolume()) + break; + UString baseName; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + break; + baseName = prop.bstrVal; + } + seqName.InitName(baseName, _archiveInfo.HaveNewVolumeName()); + } + + UString fullName = seqName.GetNextName(); + HRESULT result = openVolumeCallback->GetStream(fullName, &inStream); + if (result == S_FALSE) + break; + if (result != S_OK) + return result; + if (!stream) + break; + } + else + inStream = stream; + + UInt64 endPos = 0; + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + if (openCallback) + { + totalBytes += endPos; + RINOK(openCallback->SetTotal(NULL, &totalBytes)); + } + + NArchive::NRar::CInArchive archive; + RINOK(archive.Open(inStream, maxCheckStartPosition)); + + if (_archives.IsEmpty()) + archive.GetArchiveInfo(_archiveInfo); + + CItemEx item; + for (;;) + { + if (archive.m_Position > endPos) + { + AddErrorMessage("Unexpected end of archive"); + break; + } + bool decryptionError; + AString errorMessageLoc; + HRESULT result = archive.GetNextItem(item, getTextPassword, decryptionError, errorMessageLoc); + if (errorMessageLoc) + AddErrorMessage(errorMessageLoc); + if (result == S_FALSE) + { + if (decryptionError && _items.IsEmpty()) + return S_FALSE; + break; + } + RINOK(result); + if (item.IgnoreItem()) + continue; + + bool needAdd = true; + if (item.IsSplitBefore()) + { + if (!_refItems.IsEmpty()) + { + CRefItem &refItem = _refItems.Back(); + refItem.NumItems++; + needAdd = false; + } + } + if (needAdd) + { + CRefItem refItem; + refItem.ItemIndex = _items.Size(); + refItem.NumItems = 1; + refItem.VolumeIndex = _archives.Size(); + _refItems.Add(refItem); + } + _items.Add(item); + if (openCallback && _items.Size() % 100 == 0) + { + UInt64 numFiles = _items.Size(); + UInt64 numBytes = curBytes + item.Position; + RINOK(openCallback->SetCompleted(&numFiles, &numBytes)); + } + } + curBytes += endPos; + _archives.Add(archive); + } + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback) +{ + COM_TRY_BEGIN + Close(); + try + { + HRESULT res = Open2(stream, maxCheckStartPosition, openCallback); + if (res != S_OK) + Close(); + return res; + } + catch(const CInArchiveException &) { Close(); return S_FALSE; } + catch(...) { Close(); throw; } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + COM_TRY_BEGIN + _errorMessage.Empty(); + _refItems.Clear(); + _items.Clear(); + _archives.Clear(); + return S_OK; + COM_TRY_END +} + +struct CMethodItem +{ + Byte RarUnPackVersion; + CMyComPtr<ICompressCoder> Coder; +}; + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + UInt64 censoredTotalUnPacked = 0, + // censoredTotalPacked = 0, + importantTotalUnPacked = 0; + // importantTotalPacked = 0; + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _refItems.Size(); + if (numItems == 0) + return S_OK; + int lastIndex = 0; + CRecordVector<int> importantIndexes; + CRecordVector<bool> extractStatuses; + + for (UInt32 t = 0; t < numItems; t++) + { + int index = allFilesMode ? t : indices[t]; + const CRefItem &refItem = _refItems[index]; + const CItemEx &item = _items[refItem.ItemIndex]; + censoredTotalUnPacked += item.Size; + // censoredTotalPacked += item.PackSize; + int j; + for (j = lastIndex; j <= index; j++) + // if (!_items[_refItems[j].ItemIndex].IsSolid()) + if (!IsSolid(j)) + lastIndex = j; + for (j = lastIndex; j <= index; j++) + { + const CRefItem &refItem = _refItems[j]; + const CItemEx &item = _items[refItem.ItemIndex]; + + // const CItemEx &item = _items[j]; + + importantTotalUnPacked += item.Size; + // importantTotalPacked += item.PackSize; + importantIndexes.Add(j); + extractStatuses.Add(j == index); + } + lastIndex = index + 1; + } + + RINOK(extractCallback->SetTotal(importantTotalUnPacked)); + UInt64 currentImportantTotalUnPacked = 0; + UInt64 currentImportantTotalPacked = 0; + UInt64 currentUnPackSize, currentPackSize; + + CObjectVector<CMethodItem> methodItems; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CFilterCoder *filterStreamSpec = new CFilterCoder; + CMyComPtr<ISequentialInStream> filterStream = filterStreamSpec; + + NCrypto::NRar20::CDecoder *rar20CryptoDecoderSpec = NULL; + CMyComPtr<ICompressFilter> rar20CryptoDecoder; + NCrypto::NRar29::CDecoder *rar29CryptoDecoderSpec = NULL; + CMyComPtr<ICompressFilter> rar29CryptoDecoder; + + CFolderInStream *folderInStreamSpec = NULL; + CMyComPtr<ISequentialInStream> folderInStream; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + bool solidStart = true; + for (int i = 0; i < importantIndexes.Size(); i++, + currentImportantTotalUnPacked += currentUnPackSize, + currentImportantTotalPacked += currentPackSize) + { + lps->InSize = currentImportantTotalPacked; + lps->OutSize = currentImportantTotalUnPacked; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + + Int32 askMode; + if (extractStatuses[i]) + askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + else + askMode = NExtract::NAskMode::kSkip; + + UInt32 index = importantIndexes[i]; + + const CRefItem &refItem = _refItems[index]; + const CItemEx &item = _items[refItem.ItemIndex]; + + currentUnPackSize = item.Size; + + currentPackSize = GetPackSize(index); + + if (item.IgnoreItem()) + continue; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (!IsSolid(index)) + solidStart = true; + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + bool mustBeProcessedAnywhere = false; + if (i < importantIndexes.Size() - 1) + { + // const CRefItem &nextRefItem = _refItems[importantIndexes[i + 1]]; + // const CItemEx &nextItemInfo = _items[nextRefItem.ItemIndex]; + // mustBeProcessedAnywhere = nextItemInfo.IsSolid(); + mustBeProcessedAnywhere = IsSolid(importantIndexes[i + 1]); + } + + if (!mustBeProcessedAnywhere && !testMode && !realOutStream) + continue; + + if (!realOutStream && !testMode) + askMode = NExtract::NAskMode::kSkip; + + RINOK(extractCallback->PrepareOperation(askMode)); + + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + /* + for (int partIndex = 0; partIndex < 1; partIndex++) + { + CMyComPtr<ISequentialInStream> inStream; + + // item redefinition + const CItemEx &item = _items[refItem.ItemIndex + partIndex]; + + NArchive::NRar::CInArchive &archive = _archives[refItem.VolumeIndex + partIndex]; + + inStream.Attach(archive.CreateLimitedStream(item.GetDataPosition(), + item.PackSize)); + */ + if (!folderInStream) + { + folderInStreamSpec = new CFolderInStream; + folderInStream = folderInStreamSpec; + } + + folderInStreamSpec->Init(&_archives, &_items, refItem); + + UInt64 packSize = currentPackSize; + + // packedPos += item.PackSize; + // unpackedPos += 0; + + CMyComPtr<ISequentialInStream> inStream; + if (item.IsEncrypted()) + { + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + if (item.UnPackVersion >= 29) + { + if (!rar29CryptoDecoder) + { + rar29CryptoDecoderSpec = new NCrypto::NRar29::CDecoder; + rar29CryptoDecoder = rar29CryptoDecoderSpec; + // RINOK(rar29CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar29Decoder)); + } + rar29CryptoDecoderSpec->SetRar350Mode(item.UnPackVersion < 36); + CMyComPtr<ICompressSetDecoderProperties2> cryptoProperties; + RINOK(rar29CryptoDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, + &cryptoProperties)); + RINOK(cryptoProperties->SetDecoderProperties2(item.Salt, item.HasSalt() ? sizeof(item.Salt) : 0)); + filterStreamSpec->Filter = rar29CryptoDecoder; + } + else if (item.UnPackVersion >= 20) + { + if (!rar20CryptoDecoder) + { + rar20CryptoDecoderSpec = new NCrypto::NRar20::CDecoder; + rar20CryptoDecoder = rar20CryptoDecoderSpec; + // RINOK(rar20CryptoDecoder.CoCreateInstance(CLSID_CCryptoRar20Decoder)); + } + filterStreamSpec->Filter = rar20CryptoDecoder; + } + else + { + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + RINOK(filterStreamSpec->Filter.QueryInterface(IID_ICryptoSetPassword, + &cryptoSetPassword)); + + if (!getTextPassword) + extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); + if (getTextPassword) + { + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)); + if (item.UnPackVersion >= 29) + { + CByteBuffer buffer; + UString unicodePassword(password); + const UInt32 sizeInBytes = unicodePassword.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < unicodePassword.Length(); i++) + { + wchar_t c = unicodePassword[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + RINOK(cryptoSetPassword->CryptoSetPassword( + (const Byte *)buffer, sizeInBytes)); + } + else + { + AString oemPassword = UnicodeStringToMultiByte( + (const wchar_t *)password, CP_OEMCP); + RINOK(cryptoSetPassword->CryptoSetPassword( + (const Byte *)(const char *)oemPassword, oemPassword.Length())); + } + } + else + { + RINOK(cryptoSetPassword->CryptoSetPassword(0, 0)); + } + filterStreamSpec->SetInStream(folderInStream); + inStream = filterStream; + } + else + { + inStream = folderInStream; + } + CMyComPtr<ICompressCoder> commonCoder; + switch(item.Method) + { + case '0': + { + commonCoder = copyCoder; + break; + } + case '1': + case '2': + case '3': + case '4': + case '5': + { + /* + if (item.UnPackVersion >= 29) + { + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + */ + int m; + for (m = 0; m < methodItems.Size(); m++) + if (methodItems[m].RarUnPackVersion == item.UnPackVersion) + break; + if (m == methodItems.Size()) + { + CMethodItem mi; + mi.RarUnPackVersion = item.UnPackVersion; + + mi.Coder.Release(); + if (item.UnPackVersion <= 30) + { + UInt32 methodID = 0x040300; + if (item.UnPackVersion < 20) + methodID += 1; + else if (item.UnPackVersion < 29) + methodID += 2; + else + methodID += 3; + RINOK(CreateCoder(EXTERNAL_CODECS_VARS methodID, mi.Coder, false)); + } + + if (mi.Coder == 0) + { + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + + m = methodItems.Add(mi); + } + CMyComPtr<ICompressCoder> decoder = methodItems[m].Coder; + + CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties; + RINOK(decoder.QueryInterface(IID_ICompressSetDecoderProperties2, + &compressSetDecoderProperties)); + + Byte isSolid = (Byte)((IsSolid(index) || item.IsSplitBefore()) ? 1: 0); + if (solidStart) + { + isSolid = false; + solidStart = false; + } + + + RINOK(compressSetDecoderProperties->SetDecoderProperties2(&isSolid, 1)); + + commonCoder = decoder; + break; + } + default: + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + continue; + } + HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &item.Size, progress); + if (item.IsEncrypted()) + filterStreamSpec->ReleaseInStream(); + if (result == S_FALSE) + { + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kDataError)); + continue; + } + if (result != S_OK) + return result; + + /* + if (refItem.NumItems == 1 && + !item.IsSplitBefore() && !item.IsSplitAfter()) + */ + { + const CItemEx &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + bool crcOK = outStreamSpec->GetCRC() == lastItem.FileCRC; + outStream.Release(); + RINOK(extractCallback->SetOperationResult(crcOK ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError)); + } + /* + else + { + bool crcOK = true; + for (int partIndex = 0; partIndex < refItem.NumItems; partIndex++) + { + const CItemEx &item = _items[refItem.ItemIndex + partIndex]; + if (item.FileCRC != folderInStreamSpec->CRCs[partIndex]) + { + crcOK = false; + break; + } + } + RINOK(extractCallback->SetOperationResult(crcOK ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kCRCError)); + } + */ + } + return S_OK; + COM_TRY_END +} + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.h new file mode 100644 index 000000000..792668273 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHandler.h @@ -0,0 +1,66 @@ +// Rar/Handler.h + +#ifndef __RAR_HANDLER_H +#define __RAR_HANDLER_H + +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#include "RarIn.h" +#include "RarVolumeInStream.h" + +namespace NArchive { +namespace NRar { + +class CHandler: + public IInArchive, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ + CRecordVector<CRefItem> _refItems; + CObjectVector<CItemEx> _items; + CObjectVector<CInArchive> _archives; + NArchive::NRar::CInArchiveInfo _archiveInfo; + AString _errorMessage; + + DECL_EXTERNAL_CODECS_VARS + + UInt64 GetPackSize(int refIndex) const; + + bool IsSolid(int refIndex) + { + const CItemEx &item = _items[_refItems[refIndex].ItemIndex]; + if (item.UnPackVersion < 20) + { + if (_archiveInfo.IsSolid()) + return (refIndex > 0); + return false; + } + return item.IsSolid(); + } + void AddErrorMessage(const AString &s) + { + if (!_errorMessage.IsEmpty()) + _errorMessage += '\n'; + _errorMessage += s; + } + + HRESULT Open2(IInStream *stream, + const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback); + +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + + DECL_ISetCompressCodecsInfo +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.cpp new file mode 100644 index 000000000..94481e025 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.cpp @@ -0,0 +1,21 @@ +// Archive/Rar/Headers.cpp + +#include "StdAfx.h" + +#include "RarHeader.h" + +namespace NArchive{ +namespace NRar{ +namespace NHeader{ + +Byte kMarker[kMarkerSize] = {0x52 + 1, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}; + +class CMarkerInitializer +{ +public: + CMarkerInitializer() { kMarker[0]--; }; +}; + +static CMarkerInitializer markerInitializer; + +}}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.h new file mode 100644 index 000000000..5c21a2ac0 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarHeader.h @@ -0,0 +1,205 @@ +// Archive/RarHeader.h + +#ifndef __ARCHIVE_RAR_HEADER_H +#define __ARCHIVE_RAR_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NRar { +namespace NHeader { + +const int kMarkerSize = 7; +extern Byte kMarker[kMarkerSize]; + +const int kArchiveSolid = 0x1; + +namespace NBlockType +{ + enum EBlockType + { + kMarker = 0x72, + kArchiveHeader, + kFileHeader, + kCommentHeader, + kOldAuthenticity, + kOldSubBlock, + kRecoveryRecord, + kAuthenticity, + kSubBlock, + kEndOfArchive + }; +} + +namespace NArchive +{ + const UInt16 kVolume = 1; + const UInt16 kComment = 2; + const UInt16 kLock = 4; + const UInt16 kSolid = 8; + const UInt16 kNewVolName = 0x10; // ('volname.partN.rar') + const UInt16 kAuthenticity = 0x20; + const UInt16 kRecovery = 0x40; + const UInt16 kBlockEncryption = 0x80; + const UInt16 kFirstVolume = 0x100; // (set only by RAR 3.0 and later) + const UInt16 kEncryptVer = 0x200; // RAR 3.6 there is EncryptVer Byte in End of MainHeader + + const int kHeaderSizeMin = 7; + + const int kArchiveHeaderSize = 13; + + const int kBlockHeadersAreEncrypted = 0x80; + +} + +namespace NFile +{ + const int kSplitBefore = 1 << 0; + const int kSplitAfter = 1 << 1; + const int kEncrypted = 1 << 2; + const int kComment = 1 << 3; + const int kSolid = 1 << 4; + + const int kDictBitStart = 5; + const int kNumDictBits = 3; + const int kDictMask = (1 << kNumDictBits) - 1; + const int kDictDirectoryValue = 0x7; + + const int kSize64Bits = 1 << 8; + const int kUnicodeName = 1 << 9; + const int kSalt = 1 << 10; + const int kOldVersion = 1 << 11; + const int kExtTime = 1 << 12; + // const int kExtFlags = 1 << 13; + // const int kSkipIfUnknown = 1 << 14; + + const int kLongBlock = 1 << 15; + + /* + struct CBlock + { + // UInt16 HeadCRC; + // Byte Type; + // UInt16 Flags; + // UInt16 HeadSize; + UInt32 PackSize; + UInt32 UnPackSize; + Byte HostOS; + UInt32 FileCRC; + UInt32 Time; + Byte UnPackVersion; + Byte Method; + UInt16 NameSize; + UInt32 Attributes; + }; + */ + + /* + struct CBlock32 + { + UInt16 HeadCRC; + Byte Type; + UInt16 Flags; + UInt16 HeadSize; + UInt32 PackSize; + UInt32 UnPackSize; + Byte HostOS; + UInt32 FileCRC; + UInt32 Time; + Byte UnPackVersion; + Byte Method; + UInt16 NameSize; + UInt32 Attributes; + UInt16 GetRealCRC(const void *aName, UInt32 aNameSize, + bool anExtraDataDefined = false, Byte *anExtraData = 0) const; + }; + struct CBlock64 + { + UInt16 HeadCRC; + Byte Type; + UInt16 Flags; + UInt16 HeadSize; + UInt32 PackSizeLow; + UInt32 UnPackSizeLow; + Byte HostOS; + UInt32 FileCRC; + UInt32 Time; + Byte UnPackVersion; + Byte Method; + UInt16 NameSize; + UInt32 Attributes; + UInt32 PackSizeHigh; + UInt32 UnPackSizeHigh; + UInt16 GetRealCRC(const void *aName, UInt32 aNameSize) const; + }; + */ + + const int kLabelFileAttribute = 0x08; + const int kWinFileDirectoryAttributeMask = 0x10; + + enum CHostOS + { + kHostMSDOS = 0, + kHostOS2 = 1, + kHostWin32 = 2, + kHostUnix = 3, + kHostMacOS = 4, + kHostBeOS = 5 + }; +} + +namespace NBlock +{ + const UInt16 kLongBlock = 1 << 15; + struct CBlock + { + UInt16 CRC; + Byte Type; + UInt16 Flags; + UInt16 HeadSize; + // UInt32 DataSize; + }; +} + +/* +struct CSubBlock +{ + UInt16 HeadCRC; + Byte HeadType; + UInt16 Flags; + UInt16 HeadSize; + UInt32 DataSize; + UInt16 SubType; + Byte Level; // Reserved : Must be 0 +}; + +struct CCommentBlock +{ + UInt16 HeadCRC; + Byte HeadType; + UInt16 Flags; + UInt16 HeadSize; + UInt16 UnpSize; + Byte UnpVer; + Byte Method; + UInt16 CommCRC; +}; + + +struct CProtectHeader +{ + UInt16 HeadCRC; + Byte HeadType; + UInt16 Flags; + UInt16 HeadSize; + UInt32 DataSize; + Byte Version; + UInt16 RecSectors; + UInt32 TotalBlocks; + Byte Mark[8]; +}; +*/ + +}}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.cpp new file mode 100644 index 000000000..e4c23752c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.cpp @@ -0,0 +1,478 @@ +// Archive/RarIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" +#include "../../../../C/CpuArch.h" + +#include "Common/StringConvert.h" +#include "Common/UTFConvert.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/StreamUtils.h" + +#include "../Common/FindSignature.h" + +#include "RarIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +namespace NArchive { +namespace NRar { + +static const char *k_UnexpectedEnd = "Unexpected end of archive"; +static const char *k_DecryptionError = "Decryption Error"; + +void CInArchive::ThrowExceptionWithCode( + CInArchiveException::CCauseType cause) +{ + throw CInArchiveException(cause); +} + +HRESULT CInArchive::Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit) +{ + try + { + Close(); + HRESULT res = Open2(inStream, searchHeaderSizeLimit); + if (res == S_OK) + return res; + Close(); + return res; + } + catch(...) { Close(); throw; } +} + +void CInArchive::Close() +{ + m_Stream.Release(); +} + +HRESULT CInArchive::ReadBytesSpec(void *data, size_t *resSize) +{ + if (m_CryptoMode) + { + size_t size = *resSize; + *resSize = 0; + const Byte *bufData = m_DecryptedDataAligned; + UInt32 bufSize = m_DecryptedDataSize; + size_t i; + for (i = 0; i < size && m_CryptoPos < bufSize; i++) + ((Byte *)data)[i] = bufData[m_CryptoPos++]; + *resSize = i; + return S_OK; + } + return ReadStream(m_Stream, data, resSize); +} + +bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size) +{ + size_t processed = size; + if (ReadBytesSpec(data, &processed) != S_OK) + return false; + return processed == size; +} + +HRESULT CInArchive::Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + m_CryptoMode = false; + RINOK(stream->Seek(0, STREAM_SEEK_SET, &m_StreamStartPosition)); + m_Position = m_StreamStartPosition; + + UInt64 arcStartPos; + RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize, + searchHeaderSizeLimit, arcStartPos)); + m_Position = arcStartPos + NHeader::kMarkerSize; + RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + Byte buf[NHeader::NArchive::kArchiveHeaderSize + 1]; + + RINOK(ReadStream_FALSE(stream, buf, NHeader::NArchive::kArchiveHeaderSize)); + AddToSeekValue(NHeader::NArchive::kArchiveHeaderSize); + + + UInt32 blockSize = Get16(buf + 5); + + _header.EncryptVersion = 0; + _header.Flags = Get16(buf + 3); + + UInt32 headerSize = NHeader::NArchive::kArchiveHeaderSize; + if (_header.IsThereEncryptVer()) + { + if (blockSize <= headerSize) + return S_FALSE; + RINOK(ReadStream_FALSE(stream, buf + NHeader::NArchive::kArchiveHeaderSize, 1)); + AddToSeekValue(1); + _header.EncryptVersion = buf[NHeader::NArchive::kArchiveHeaderSize]; + headerSize += 1; + } + if (blockSize < headerSize || + buf[2] != NHeader::NBlockType::kArchiveHeader || + (UInt32)Get16(buf) != (CrcCalc(buf + 2, headerSize - 2) & 0xFFFF)) + return S_FALSE; + + size_t commentSize = blockSize - headerSize; + _comment.SetCapacity(commentSize); + RINOK(ReadStream_FALSE(stream, _comment, commentSize)); + AddToSeekValue(commentSize); + m_Stream = stream; + _header.StartPosition = arcStartPos; + return S_OK; +} + +void CInArchive::GetArchiveInfo(CInArchiveInfo &archiveInfo) const +{ + archiveInfo = _header; +} + +static void DecodeUnicodeFileName(const char *name, const Byte *encName, + int encSize, wchar_t *unicodeName, int maxDecSize) +{ + int encPos = 0; + int decPos = 0; + int flagBits = 0; + Byte flags = 0; + Byte highByte = encName[encPos++]; + while (encPos < encSize && decPos < maxDecSize) + { + if (flagBits == 0) + { + flags = encName[encPos++]; + flagBits = 8; + } + switch(flags >> 6) + { + case 0: + unicodeName[decPos++] = encName[encPos++]; + break; + case 1: + unicodeName[decPos++] = (wchar_t)(encName[encPos++] + (highByte << 8)); + break; + case 2: + unicodeName[decPos++] = (wchar_t)(encName[encPos] + (encName[encPos + 1] << 8)); + encPos += 2; + break; + case 3: + { + int length = encName[encPos++]; + if (length & 0x80) + { + Byte correction = encName[encPos++]; + for (length = (length & 0x7f) + 2; + length > 0 && decPos < maxDecSize; length--, decPos++) + unicodeName[decPos] = (wchar_t)(((name[decPos] + correction) & 0xff) + (highByte << 8)); + } + else + for (length += 2; length > 0 && decPos < maxDecSize; length--, decPos++) + unicodeName[decPos] = name[decPos]; + } + break; + } + flags <<= 2; + flagBits -= 2; + } + unicodeName[decPos < maxDecSize ? decPos : maxDecSize - 1] = 0; +} + +void CInArchive::ReadName(CItemEx &item, int nameSize) +{ + item.UnicodeName.Empty(); + if (nameSize > 0) + { + m_NameBuffer.EnsureCapacity(nameSize + 1); + char *buffer = (char *)m_NameBuffer; + + for (int i = 0; i < nameSize; i++) + buffer[i] = ReadByte(); + + int mainLen; + for (mainLen = 0; mainLen < nameSize; mainLen++) + if (buffer[mainLen] == '\0') + break; + buffer[mainLen] = '\0'; + item.Name = buffer; + + if(item.HasUnicodeName()) + { + if(mainLen < nameSize) + { + int unicodeNameSizeMax = MyMin(nameSize, (0x400)); + _unicodeNameBuffer.EnsureCapacity(unicodeNameSizeMax + 1); + DecodeUnicodeFileName(buffer, (const Byte *)buffer + mainLen + 1, + nameSize - (mainLen + 1), _unicodeNameBuffer, unicodeNameSizeMax); + item.UnicodeName = _unicodeNameBuffer; + } + else if (!ConvertUTF8ToUnicode(item.Name, item.UnicodeName)) + item.UnicodeName.Empty(); + } + } + else + item.Name.Empty(); +} + +Byte CInArchive::ReadByte() +{ + if (m_CurPos >= m_PosLimit) + throw CInArchiveException(CInArchiveException::kIncorrectArchive); + return m_CurData[m_CurPos++]; +} + +UInt16 CInArchive::ReadUInt16() +{ + UInt16 value = 0; + for (int i = 0; i < 2; i++) + { + Byte b = ReadByte(); + value |= (UInt16(b) << (8 * i)); + } + return value; +} + +UInt32 CInArchive::ReadUInt32() +{ + UInt32 value = 0; + for (int i = 0; i < 4; i++) + { + Byte b = ReadByte(); + value |= (UInt32(b) << (8 * i)); + } + return value; +} + +void CInArchive::ReadTime(Byte mask, CRarTime &rarTime) +{ + rarTime.LowSecond = (Byte)(((mask & 4) != 0) ? 1 : 0); + int numDigits = (mask & 3); + rarTime.SubTime[0] = rarTime.SubTime[1] = rarTime.SubTime[2] = 0; + for (int i = 0; i < numDigits; i++) + rarTime.SubTime[3 - numDigits + i] = ReadByte(); +} + +void CInArchive::ReadHeaderReal(CItemEx &item) +{ + item.Flags = m_BlockHeader.Flags; + item.PackSize = ReadUInt32(); + item.Size = ReadUInt32(); + item.HostOS = ReadByte(); + item.FileCRC = ReadUInt32(); + item.MTime.DosTime = ReadUInt32(); + item.UnPackVersion = ReadByte(); + item.Method = ReadByte(); + int nameSize = ReadUInt16(); + item.Attrib = ReadUInt32(); + + item.MTime.LowSecond = 0; + item.MTime.SubTime[0] = + item.MTime.SubTime[1] = + item.MTime.SubTime[2] = 0; + + if((item.Flags & NHeader::NFile::kSize64Bits) != 0) + { + item.PackSize |= ((UInt64)ReadUInt32() << 32); + item.Size |= ((UInt64)ReadUInt32() << 32); + } + + ReadName(item, nameSize); + + if (item.HasSalt()) + for (int i = 0; i < sizeof(item.Salt); i++) + item.Salt[i] = ReadByte(); + + // some rar archives have HasExtTime flag without field. + if (m_CurPos < m_PosLimit && item.HasExtTime()) + { + Byte accessMask = (Byte)(ReadByte() >> 4); + Byte b = ReadByte(); + Byte modifMask = (Byte)(b >> 4); + Byte createMask = (Byte)(b & 0xF); + if ((modifMask & 8) != 0) + ReadTime(modifMask, item.MTime); + item.CTimeDefined = ((createMask & 8) != 0); + if (item.CTimeDefined) + { + item.CTime.DosTime = ReadUInt32(); + ReadTime(createMask, item.CTime); + } + item.ATimeDefined = ((accessMask & 8) != 0); + if (item.ATimeDefined) + { + item.ATime.DosTime = ReadUInt32(); + ReadTime(accessMask, item.ATime); + } + } + + UInt16 fileHeaderWithNameSize = (UInt16)m_CurPos; + + item.Position = m_Position; + item.MainPartSize = fileHeaderWithNameSize; + item.CommentSize = (UInt16)(m_BlockHeader.HeadSize - fileHeaderWithNameSize); + + if (m_CryptoMode) + item.AlignSize = (UInt16)((16 - ((m_BlockHeader.HeadSize) & 0xF)) & 0xF); + else + item.AlignSize = 0; + AddToSeekValue(m_BlockHeader.HeadSize); +} + +void CInArchive::AddToSeekValue(UInt64 addValue) +{ + m_Position += addValue; +} + +HRESULT CInArchive::GetNextItem(CItemEx &item, ICryptoGetTextPassword *getTextPassword, bool &decryptionError, AString &errorMessage) +{ + decryptionError = false; + for (;;) + { + SeekInArchive(m_Position); + if (!m_CryptoMode && (_header.Flags & + NHeader::NArchive::kBlockHeadersAreEncrypted) != 0) + { + m_CryptoMode = false; + if (getTextPassword == 0) + return S_FALSE; + if (!m_RarAES) + { + m_RarAESSpec = new NCrypto::NRar29::CDecoder; + m_RarAES = m_RarAESSpec; + } + m_RarAESSpec->SetRar350Mode(_header.IsEncryptOld()); + + // Salt + const UInt32 kSaltSize = 8; + Byte salt[kSaltSize]; + if(!ReadBytesAndTestSize(salt, kSaltSize)) + return S_FALSE; + m_Position += kSaltSize; + RINOK(m_RarAESSpec->SetDecoderProperties2(salt, kSaltSize)) + // Password + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)) + UString unicodePassword(password); + + CByteBuffer buffer; + const UInt32 sizeInBytes = unicodePassword.Length() * 2; + buffer.SetCapacity(sizeInBytes); + for (int i = 0; i < unicodePassword.Length(); i++) + { + wchar_t c = unicodePassword[i]; + ((Byte *)buffer)[i * 2] = (Byte)c; + ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8); + } + + RINOK(m_RarAESSpec->CryptoSetPassword((const Byte *)buffer, sizeInBytes)); + + const UInt32 kDecryptedBufferSize = (1 << 12); + if (m_DecryptedData.GetCapacity() == 0) + { + const UInt32 kAlign = 16; + m_DecryptedData.SetCapacity(kDecryptedBufferSize + kAlign); + m_DecryptedDataAligned = (Byte *)((ptrdiff_t)((Byte *)m_DecryptedData + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1)); + } + RINOK(m_RarAES->Init()); + size_t decryptedDataSizeT = kDecryptedBufferSize; + RINOK(ReadStream(m_Stream, m_DecryptedDataAligned, &decryptedDataSizeT)); + m_DecryptedDataSize = (UInt32)decryptedDataSizeT; + m_DecryptedDataSize = m_RarAES->Filter(m_DecryptedDataAligned, m_DecryptedDataSize); + + m_CryptoMode = true; + m_CryptoPos = 0; + } + + m_FileHeaderData.EnsureCapacity(7); + size_t processed = 7; + RINOK(ReadBytesSpec((Byte *)m_FileHeaderData, &processed)); + if (processed != 7) + { + if (processed != 0) + errorMessage = k_UnexpectedEnd; + return S_FALSE; + } + + m_CurData = (Byte *)m_FileHeaderData; + m_CurPos = 0; + m_PosLimit = 7; + m_BlockHeader.CRC = ReadUInt16(); + m_BlockHeader.Type = ReadByte(); + m_BlockHeader.Flags = ReadUInt16(); + m_BlockHeader.HeadSize = ReadUInt16(); + + if (m_BlockHeader.HeadSize < 7) + ThrowExceptionWithCode(CInArchiveException::kIncorrectArchive); + + if (m_BlockHeader.Type == NHeader::NBlockType::kEndOfArchive) + return S_FALSE; + + if (m_BlockHeader.Type == NHeader::NBlockType::kFileHeader) + { + m_FileHeaderData.EnsureCapacity(m_BlockHeader.HeadSize); + m_CurData = (Byte *)m_FileHeaderData; + m_PosLimit = m_BlockHeader.HeadSize; + if (!ReadBytesAndTestSize(m_CurData + m_CurPos, m_BlockHeader.HeadSize - 7)) + { + errorMessage = k_UnexpectedEnd; + return S_FALSE; + } + + ReadHeaderReal(item); + if ((CrcCalc(m_CurData + 2, + m_BlockHeader.HeadSize - item.CommentSize - 2) & 0xFFFF) != m_BlockHeader.CRC) + ThrowExceptionWithCode(CInArchiveException::kFileHeaderCRCError); + + FinishCryptoBlock(); + m_CryptoMode = false; + SeekInArchive(m_Position); // Move Position to compressed Data; + AddToSeekValue(item.PackSize); // m_Position points to next header; + return S_OK; + } + if (m_CryptoMode && m_BlockHeader.HeadSize > (1 << 10)) + { + decryptionError = true; + errorMessage = k_DecryptionError; + return S_FALSE; + } + if ((m_BlockHeader.Flags & NHeader::NBlock::kLongBlock) != 0) + { + m_FileHeaderData.EnsureCapacity(7 + 4); + m_CurData = (Byte *)m_FileHeaderData; + if (!ReadBytesAndTestSize(m_CurData + m_CurPos, 4)) + { + errorMessage = k_UnexpectedEnd; + return S_FALSE; + } + m_PosLimit = 7 + 4; + UInt32 dataSize = ReadUInt32(); + AddToSeekValue(dataSize); + if (m_CryptoMode && dataSize > (1 << 27)) + { + decryptionError = true; + errorMessage = k_DecryptionError; + return S_FALSE; + } + m_CryptoPos = m_BlockHeader.HeadSize; + } + else + m_CryptoPos = 0; + AddToSeekValue(m_BlockHeader.HeadSize); + FinishCryptoBlock(); + m_CryptoMode = false; + } +} + +void CInArchive::SeekInArchive(UInt64 position) +{ + m_Stream->Seek(position, STREAM_SEEK_SET, NULL); +} + +ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) +{ + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + SeekInArchive(position); + streamSpec->SetStream(m_Stream); + streamSpec->Init(size); + return inStream.Detach(); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.h new file mode 100644 index 000000000..a6998db26 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarIn.h @@ -0,0 +1,123 @@ +// RarIn.h + +#ifndef __ARCHIVE_RAR_IN_H +#define __ARCHIVE_RAR_IN_H + +#include "Common/DynamicBuffer.h" +#include "Common/MyCom.h" + +#include "../../ICoder.h" +#include "../../IStream.h" + +#include "../../Common/StreamObjects.h" + +#include "../../Crypto/RarAes.h" + +#include "RarHeader.h" +#include "RarItem.h" + +namespace NArchive { +namespace NRar { + +class CInArchiveException +{ +public: + enum CCauseType + { + kUnexpectedEndOfArchive = 0, + kArchiveHeaderCRCError, + kFileHeaderCRCError, + kIncorrectArchive + } + Cause; + CInArchiveException(CCauseType cause) : Cause(cause) {} +}; + + +struct CInArchiveInfo +{ + UInt32 Flags; + Byte EncryptVersion; + UInt64 StartPosition; + + bool IsSolid() const { return (Flags & NHeader::NArchive::kSolid) != 0; } + bool IsCommented() const { return (Flags & NHeader::NArchive::kComment) != 0; } + bool IsVolume() const { return (Flags & NHeader::NArchive::kVolume) != 0; } + bool HaveNewVolumeName() const { return (Flags & NHeader::NArchive::kNewVolName) != 0; } + bool IsEncrypted() const { return (Flags & NHeader::NArchive::kBlockEncryption) != 0; } + bool IsThereEncryptVer() const { return (Flags & NHeader::NArchive::kEncryptVer) != 0; } + bool IsEncryptOld() const { return (!IsThereEncryptVer() || EncryptVersion < 36); } +}; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + + UInt64 m_StreamStartPosition; + + CInArchiveInfo _header; + CDynamicBuffer<char> m_NameBuffer; + CDynamicBuffer<wchar_t> _unicodeNameBuffer; + + CByteBuffer _comment; + + void ReadName(CItemEx &item, int nameSize); + void ReadHeaderReal(CItemEx &item); + + HRESULT ReadBytesSpec(void *data, size_t *size); + bool ReadBytesAndTestSize(void *data, UInt32 size); + + HRESULT Open2(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + + void ThrowExceptionWithCode(CInArchiveException::CCauseType cause); + void ThrowUnexpectedEndOfArchiveException(); + + void AddToSeekValue(UInt64 addValue); + + CDynamicBuffer<Byte> m_FileHeaderData; + + NHeader::NBlock::CBlock m_BlockHeader; + + NCrypto::NRar29::CDecoder *m_RarAESSpec; + CMyComPtr<ICompressFilter> m_RarAES; + + Byte *m_CurData; // it must point to start of Rar::Block + UInt32 m_CurPos; + UInt32 m_PosLimit; + Byte ReadByte(); + UInt16 ReadUInt16(); + UInt32 ReadUInt32(); + void ReadTime(Byte mask, CRarTime &rarTime); + + CBuffer<Byte> m_DecryptedData; + Byte *m_DecryptedDataAligned; + UInt32 m_DecryptedDataSize; + + bool m_CryptoMode; + UInt32 m_CryptoPos; + void FinishCryptoBlock() + { + if (m_CryptoMode) + while ((m_CryptoPos & 0xF) != 0) + { + m_CryptoPos++; + m_Position++; + } + } + +public: + UInt64 m_Position; + + HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit); + void Close(); + HRESULT GetNextItem(CItemEx &item, ICryptoGetTextPassword *getTextPassword, bool &decryptionError, AString &errorMessage); + + void GetArchiveInfo(CInArchiveInfo &archiveInfo) const; + + void SeekInArchive(UInt64 position); + ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.cpp new file mode 100644 index 000000000..9216ae57b --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.cpp @@ -0,0 +1,55 @@ +// RarItem.cpp + +#include "StdAfx.h" + +#include "RarItem.h" + +namespace NArchive{ +namespace NRar{ + +bool CItem::IgnoreItem() const +{ + switch(HostOS) + { + case NHeader::NFile::kHostMSDOS: + case NHeader::NFile::kHostOS2: + case NHeader::NFile::kHostWin32: + return ((Attrib & NHeader::NFile::kLabelFileAttribute) != 0); + } + return false; +} + +bool CItem::IsDir() const +{ + if (GetDictSize() == NHeader::NFile::kDictDirectoryValue) + return true; + switch(HostOS) + { + case NHeader::NFile::kHostMSDOS: + case NHeader::NFile::kHostOS2: + case NHeader::NFile::kHostWin32: + if ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0) + return true; + } + return false; +} + +UInt32 CItem::GetWinAttributes() const +{ + UInt32 winAttributes; + switch(HostOS) + { + case NHeader::NFile::kHostMSDOS: + case NHeader::NFile::kHostOS2: + case NHeader::NFile::kHostWin32: + winAttributes = Attrib; + break; + default: + winAttributes = 0; // must be converted from unix value; + } + if (IsDir()) + winAttributes |= NHeader::NFile::kWinFileDirectoryAttributeMask; + return winAttributes; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.h new file mode 100644 index 000000000..4aa4d8667 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarItem.h @@ -0,0 +1,79 @@ +// RarItem.h + +#ifndef __ARCHIVE_RAR_ITEM_H +#define __ARCHIVE_RAR_ITEM_H + +#include "Common/Types.h" +#include "Common/MyString.h" + +#include "RarHeader.h" + +namespace NArchive{ +namespace NRar{ + +struct CRarTime +{ + UInt32 DosTime; + Byte LowSecond; + Byte SubTime[3]; +}; + +struct CItem +{ + UInt64 Size; + UInt64 PackSize; + + CRarTime CTime; + CRarTime ATime; + CRarTime MTime; + + UInt32 FileCRC; + UInt32 Attrib; + + UInt16 Flags; + Byte HostOS; + Byte UnPackVersion; + Byte Method; + + bool CTimeDefined; + bool ATimeDefined; + + AString Name; + UString UnicodeName; + + Byte Salt[8]; + + bool IsEncrypted() const { return (Flags & NHeader::NFile::kEncrypted) != 0; } + bool IsSolid() const { return (Flags & NHeader::NFile::kSolid) != 0; } + bool IsCommented() const { return (Flags & NHeader::NFile::kComment) != 0; } + bool IsSplitBefore() const { return (Flags & NHeader::NFile::kSplitBefore) != 0; } + bool IsSplitAfter() const { return (Flags & NHeader::NFile::kSplitAfter) != 0; } + bool HasSalt() const { return (Flags & NHeader::NFile::kSalt) != 0; } + bool HasExtTime() const { return (Flags & NHeader::NFile::kExtTime) != 0; } + bool HasUnicodeName()const { return (Flags & NHeader::NFile::kUnicodeName) != 0; } + bool IsOldVersion() const { return (Flags & NHeader::NFile::kOldVersion) != 0; } + + UInt32 GetDictSize() const { return (Flags >> NHeader::NFile::kDictBitStart) & NHeader::NFile::kDictMask; } + bool IsDir() const; + bool IgnoreItem() const; + UInt32 GetWinAttributes() const; + + CItem(): CTimeDefined(false), ATimeDefined(false) {} +}; + +class CItemEx: public CItem +{ +public: + UInt64 Position; + UInt16 MainPartSize; + UInt16 CommentSize; + UInt16 AlignSize; + UInt64 GetFullSize() const { return MainPartSize + CommentSize + AlignSize + PackSize; }; + // DWORD GetHeaderWithCommentSize() const { return MainPartSize + CommentSize; }; + UInt64 GetCommentPosition() const { return Position + MainPartSize; }; + UInt64 GetDataPosition() const { return GetCommentPosition() + CommentSize + AlignSize; }; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarRegister.cpp new file mode 100644 index 000000000..2bcf569ef --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarRegister.cpp @@ -0,0 +1,13 @@ +// RarRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "RarHandler.h" +static IInArchive *CreateArc() { return new NArchive::NRar::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Rar", L"rar r00", 0, 3, {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}, 7, false, CreateArc, 0, }; + +REGISTER_ARC(Rar) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.cpp new file mode 100644 index 000000000..25194f915 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.cpp @@ -0,0 +1,78 @@ +// RarVolumeInStream.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" + +#include "RarVolumeInStream.h" + +namespace NArchive { +namespace NRar { + +void CFolderInStream::Init( + CObjectVector<CInArchive> *archives, + const CObjectVector<CItemEx> *items, + const CRefItem &refItem) +{ + _archives = archives; + _items = items; + _refItem = refItem; + _curIndex = 0; + CRCs.Clear(); + _fileIsOpen = false; +} + +HRESULT CFolderInStream::OpenStream() +{ + while (_curIndex < _refItem.NumItems) + { + const CItemEx &item = (*_items)[_refItem.ItemIndex + _curIndex]; + _stream.Attach((*_archives)[_refItem.VolumeIndex + _curIndex]. + CreateLimitedStream(item.GetDataPosition(), item.PackSize)); + _curIndex++; + _fileIsOpen = true; + _crc = CRC_INIT_VAL; + return S_OK; + } + return S_OK; +} + +HRESULT CFolderInStream::CloseStream() +{ + CRCs.Add(CRC_GET_DIGEST(_crc)); + _stream.Release(); + _fileIsOpen = false; + return S_OK; +} + +STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize = 0; + while ((_curIndex < _refItem.NumItems || _fileIsOpen) && size > 0) + { + if (_fileIsOpen) + { + UInt32 localProcessedSize; + RINOK(_stream->Read( + ((Byte *)data) + realProcessedSize, size, &localProcessedSize)); + _crc = CrcUpdate(_crc, ((Byte *)data) + realProcessedSize, localProcessedSize); + if (localProcessedSize == 0) + { + RINOK(CloseStream()); + continue; + } + realProcessedSize += localProcessedSize; + size -= localProcessedSize; + break; + } + else + { + RINOK(OpenStream()); + } + } + if (processedSize != 0) + *processedSize = realProcessedSize; + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.h b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.h new file mode 100644 index 000000000..78d95b10f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Rar/RarVolumeInStream.h @@ -0,0 +1,49 @@ +// RarVolumeInStream.h + +#ifndef __RAR_VOLUME_IN_STREAM_H +#define __RAR_VOLUME_IN_STREAM_H + +#include "../../IStream.h" +#include "RarIn.h" + +namespace NArchive { +namespace NRar { + +struct CRefItem +{ + int VolumeIndex; + int ItemIndex; + int NumItems; +}; + +class CFolderInStream: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + +private: + CObjectVector<CInArchive> *_archives; + const CObjectVector<CItemEx> *_items; + CRefItem _refItem; + int _curIndex; + UInt32 _crc; + bool _fileIsOpen; + CMyComPtr<ISequentialInStream> _stream; + + HRESULT OpenStream(); + HRESULT CloseStream(); +public: + void Init(CObjectVector<CInArchive> *archives, + const CObjectVector<CItemEx> *items, + const CRefItem &refItem); + + CRecordVector<UInt32> CRCs; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/RpmHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/RpmHandler.cpp new file mode 100644 index 000000000..1d31d4514 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/RpmHandler.cpp @@ -0,0 +1,292 @@ +// RpmHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +using namespace NWindows; + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) + +namespace NArchive { +namespace NRpm { + +/* Reference: lib/signature.h of rpm package */ +#define RPMSIG_NONE 0 /* Do not change! */ +/* The following types are no longer generated */ +#define RPMSIG_PGP262_1024 1 /* No longer generated */ /* 256 byte */ +/* These are the new-style signatures. They are Header structures. */ +/* Inside them we can put any number of any type of signature we like. */ + +#define RPMSIG_HEADERSIG 5 /* New Header style signature */ + +const UInt32 kLeadSize = 96; +struct CLead +{ + unsigned char Magic[4]; + unsigned char Major; // not supported ver1, only support 2,3 and lator + unsigned char Minor; + UInt16 Type; + UInt16 ArchNum; + char Name[66]; + UInt16 OSNum; + UInt16 SignatureType; + char Reserved[16]; // pad to 96 bytes -- 8 byte aligned + bool MagicCheck() const + { return Magic[0] == 0xed && Magic[1] == 0xab && Magic[2] == 0xee && Magic[3] == 0xdb; }; +}; + +const UInt32 kEntryInfoSize = 16; +/* +struct CEntryInfo +{ + int Tag; + int Type; + int Offset; // Offset from beginning of data segment, only defined on disk + int Count; +}; +*/ + +// case: SignatureType == RPMSIG_HEADERSIG +const UInt32 kCSigHeaderSigSize = 16; +struct CSigHeaderSig +{ + unsigned char Magic[4]; + UInt32 Reserved; + UInt32 IndexLen; // count of index entries + UInt32 DataLen; // number of bytes + bool MagicCheck() + { return Magic[0] == 0x8e && Magic[1] == 0xad && Magic[2] == 0xe8 && Magic[3] == 0x01; }; + UInt32 GetLostHeaderLen() + { return IndexLen * kEntryInfoSize + DataLen; }; +}; + +static HRESULT RedSigHeaderSig(IInStream *inStream, CSigHeaderSig &h) +{ + char dat[kCSigHeaderSigSize]; + char *cur = dat; + RINOK(ReadStream_FALSE(inStream, dat, kCSigHeaderSigSize)); + memcpy(h.Magic, cur, 4); + cur += 4; + cur += 4; + h.IndexLen = Get32(cur); + cur += 4; + h.DataLen = Get32(cur); + return S_OK; +} + +HRESULT OpenArchive(IInStream *inStream) +{ + UInt64 pos; + char leadData[kLeadSize]; + char *cur = leadData; + CLead lead; + RINOK(ReadStream_FALSE(inStream, leadData, kLeadSize)); + memcpy(lead.Magic, cur, 4); + cur += 4; + lead.Major = *cur++; + lead.Minor = *cur++; + lead.Type = Get16(cur); + cur += 2; + lead.ArchNum = Get16(cur); + cur += 2; + memcpy(lead.Name, cur, sizeof(lead.Name)); + cur += sizeof(lead.Name); + lead.OSNum = Get16(cur); + cur += 2; + lead.SignatureType = Get16(cur); + cur += 2; + + if (!lead.MagicCheck() || lead.Major < 3) + return S_FALSE; + + CSigHeaderSig sigHeader, header; + if (lead.SignatureType == RPMSIG_NONE) + { + ; + } + else if (lead.SignatureType == RPMSIG_PGP262_1024) + { + UInt64 pos; + RINOK(inStream->Seek(256, STREAM_SEEK_CUR, &pos)); + } + else if (lead.SignatureType == RPMSIG_HEADERSIG) + { + RINOK(RedSigHeaderSig(inStream, sigHeader)); + if (!sigHeader.MagicCheck()) + return S_FALSE; + UInt32 len = sigHeader.GetLostHeaderLen(); + RINOK(inStream->Seek(len, STREAM_SEEK_CUR, &pos)); + if ((pos % 8) != 0) + { + RINOK(inStream->Seek((pos / 8 + 1) * 8 - pos, + STREAM_SEEK_CUR, &pos)); + } + } + else + return S_FALSE; + + RINOK(RedSigHeaderSig(inStream, header)); + if (!header.MagicCheck()) + return S_FALSE; + int headerLen = header.GetLostHeaderLen(); + if (headerLen == -1) + return S_FALSE; + RINOK(inStream->Seek(headerLen, STREAM_SEEK_CUR, &pos)); + return S_OK; +} + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + UInt64 _pos; + UInt64 _size; + Byte _sig[4]; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) { case kpidMainSubfile: prop = (UInt32)0; break; } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + try + { + Close(); + if (OpenArchive(inStream) != S_OK) + return S_FALSE; + RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_pos)); + RINOK(ReadStream_FALSE(inStream, _sig, sizeof(_sig) / sizeof(_sig[0]))); + UInt64 endPosition; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPosition)); + _size = endPosition - _pos; + _stream = inStream; + return S_OK; + } + catch(...) { return S_FALSE; } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidSize: + case kpidPackSize: + prop = _size; + break; + case kpidExtension: + { + char s[32]; + MyStringCopy(s, "cpio."); + const char *ext; + if (_sig[0] == 0x1F && _sig[1] == 0x8B) + ext = "gz"; + else if (_sig[0] == 'B' && _sig[1] == 'Z' && _sig[2] == 'h') + ext = "bz2"; + else + ext = "lzma"; + MyStringCopy(s + MyStringLen(s), ext); + prop = s; + break; + } + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + RINOK(extractCallback->SetTotal(_size)); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &outStream, askMode)); + if (!testMode && !outStream) + return S_OK; + RINOK(extractCallback->PrepareOperation(askMode)); + + CMyComPtr<ICompressCoder> copyCoder = new NCompress::CCopyCoder; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + RINOK(_stream->Seek(_pos, STREAM_SEEK_SET, NULL)); + RINOK(copyCoder->Code(_stream, outStream, NULL, NULL, progress)); + outStream.Release(); + return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK); + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + return CreateLimitedInStream(_stream, _pos, _size, stream); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NRpm::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Rpm", L"rpm", 0, 0xEB, { 0xED, 0xAB, 0xEE, 0xDB}, 4, false, CreateArc, 0 }; + +REGISTER_ARC(Rpm) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp new file mode 100644 index 000000000..5d84de4ed --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp @@ -0,0 +1,366 @@ +// SplitHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/MultiStream.h" + +using namespace NWindows; + +namespace NArchive { +namespace NSplit { + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8} +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidNumVolumes, VT_UI4} +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + UString _subName; + CObjectVector<CMyComPtr<IInStream> > _streams; + CRecordVector<UInt64> _sizes; + UInt64 _totalSize; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidMainSubfile: prop = (UInt32)0; break; + case kpidNumVolumes: prop = (UInt32)_streams.Size(); break; + } + prop.Detach(value); + return S_OK; +} + +struct CSeqName +{ + UString _unchangedPart; + UString _changedPart; + bool _splitStyle; + + UString GetNextName() + { + UString newName; + if (_splitStyle) + { + int i; + int numLetters = _changedPart.Length(); + for (i = numLetters - 1; i >= 0; i--) + { + wchar_t c = _changedPart[i]; + if (c == 'z') + { + c = 'a'; + newName = c + newName; + continue; + } + else if (c == 'Z') + { + c = 'A'; + newName = c + newName; + continue; + } + c++; + if ((c == 'z' || c == 'Z') && i == 0) + { + _unchangedPart += c; + wchar_t newChar = (c == 'z') ? L'a' : L'A'; + newName.Empty(); + numLetters++; + for (int k = 0; k < numLetters; k++) + newName += newChar; + break; + } + newName = c + newName; + i--; + for (; i >= 0; i--) + newName = _changedPart[i] + newName; + break; + } + } + else + { + int i; + int numLetters = _changedPart.Length(); + for (i = numLetters - 1; i >= 0; i--) + { + wchar_t c = _changedPart[i]; + if (c == L'9') + { + c = L'0'; + newName = c + newName; + if (i == 0) + newName = UString(L'1') + newName; + continue; + } + c++; + newName = c + newName; + i--; + for (; i >= 0; i--) + newName = _changedPart[i] + newName; + break; + } + } + _changedPart = newName; + return _unchangedPart + _changedPart; + } +}; + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + Close(); + if (openArchiveCallback == 0) + return S_FALSE; + // try + { + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback; + if (openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, + &openVolumeCallback) != S_OK) + return S_FALSE; + + UString name; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + return S_FALSE; + name = prop.bstrVal; + } + + int dotPos = name.ReverseFind('.'); + UString prefix, ext; + if (dotPos >= 0) + { + prefix = name.Left(dotPos + 1); + ext = name.Mid(dotPos + 1); + } + else + ext = name; + UString extBig = ext; + extBig.MakeUpper(); + + CSeqName seqName; + + int numLetters = 2; + bool splitStyle = false; + if (extBig.Right(2) == L"AA") + { + splitStyle = true; + while (numLetters < extBig.Length()) + { + if (extBig[extBig.Length() - numLetters - 1] != 'A') + break; + numLetters++; + } + } + else if (ext.Right(2) == L"01") + { + while (numLetters < extBig.Length()) + { + if (extBig[extBig.Length() - numLetters - 1] != '0') + break; + numLetters++; + } + if (numLetters != ext.Length()) + return S_FALSE; + } + else + return S_FALSE; + + _streams.Add(stream); + + seqName._unchangedPart = prefix + ext.Left(extBig.Length() - numLetters); + seqName._changedPart = ext.Right(numLetters); + seqName._splitStyle = splitStyle; + + if (prefix.Length() < 1) + _subName = L"file"; + else + _subName = prefix.Left(prefix.Length() - 1); + + _totalSize = 0; + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + _totalSize += size; + _sizes.Add(size); + + if (openArchiveCallback != NULL) + { + UInt64 numFiles = _streams.Size(); + RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); + } + + for (;;) + { + UString fullName = seqName.GetNextName(); + CMyComPtr<IInStream> nextStream; + HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream); + if (result == S_FALSE) + break; + if (result != S_OK) + return result; + if (!stream) + break; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + _totalSize += size; + _sizes.Add(size); + _streams.Add(nextStream); + if (openArchiveCallback != NULL) + { + UInt64 numFiles = _streams.Size(); + RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); + } + } + } + /* + catch(...) + { + return S_FALSE; + } + */ + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _sizes.Clear(); + _streams.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _streams.IsEmpty() ? 0 : 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPath: prop = _subName; break; + case kpidSize: + case kpidPackSize: + prop = _totalSize; + break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + UInt64 currentTotalSize = 0; + RINOK(extractCallback->SetTotal(_totalSize)); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &outStream, askMode)); + if (!testMode && !outStream) + return S_OK; + RINOK(extractCallback->PrepareOperation(askMode)); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (int i = 0; i < _streams.Size(); i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + IInStream *inStream = _streams[i]; + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + currentTotalSize += copyCoderSpec->TotalSize; + } + outStream.Release(); + return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK); + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + if (index != 0) + return E_INVALIDARG; + *stream = 0; + CMultiStream *streamSpec = new CMultiStream; + CMyComPtr<ISequentialInStream> streamTemp = streamSpec; + for (int i = 0; i < _streams.Size(); i++) + { + CMultiStream::CSubStreamInfo subStreamInfo; + subStreamInfo.Stream = _streams[i]; + subStreamInfo.Size = _sizes[i]; + streamSpec->Streams.Add(subStreamInfo); + } + streamSpec->Init(); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = +{ L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 }; + +REGISTER_ARC(Split) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/SquashfsHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/SquashfsHandler.cpp new file mode 100644 index 000000000..2cc1219ad --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/SquashfsHandler.cpp @@ -0,0 +1,2155 @@ +// SquashfsHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/7zCrc.h" +#include "../../../C/Alloc.h" +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariantUtils.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" +#include "../Compress/LzmaDecoder.h" + +namespace NArchive { +namespace NSquashfs { + +static const UInt32 kNumFilesMax = (1 << 28); +static const unsigned kNumDirLevelsMax = (1 << 10); + +// Layout: Header, Data, inodes, Directories, Fragments, UIDs, GIDs + +/* +#define Get16(p) (be ? GetBe16(p) : GetUi16(p)) +#define Get32(p) (be ? GetBe32(p) : GetUi32(p)) +#define Get64(p) (be ? GetBe64(p) : GetUi64(p)) +*/ + +UInt16 Get16b(const Byte *p, bool be) { return be ? GetBe16(p) : GetUi16(p); } +UInt32 Get32b(const Byte *p, bool be) { return be ? GetBe32(p) : GetUi32(p); } +UInt64 Get64b(const Byte *p, bool be) { return be ? GetBe64(p) : GetUi64(p); } + +#define Get16(p) Get16b(p, be) +#define Get32(p) Get32b(p, be) +#define Get64(p) Get64b(p, be) + +#define LE_16(offs, dest) dest = GetUi16(p + (offs)); +#define LE_32(offs, dest) dest = GetUi32(p + (offs)); +#define LE_64(offs, dest) dest = GetUi64(p + (offs)); + +#define GET_16(offs, dest) dest = Get16(p + (offs)); +#define GET_32(offs, dest) dest = Get32(p + (offs)); +#define GET_64(offs, dest) dest = Get64(p + (offs)); + +static const UInt32 kSignatureSize = 4; +#define SIGNATURE { 'h', 's', 'q', 's' } +static const UInt32 kSignature32_LE = 0x73717368; +static const UInt32 kSignature32_BE = 0x68737173; +static const UInt32 kSignature32_LZ = 0x71736873; + +#define kMethod_ZLIB 1 +#define kMethod_LZMA 2 +#define kMethod_LZO 3 + +static const char *k_Methods[] = +{ + "Unknown", + "ZLIB", + "LZMA", + "LZO" +}; + +static const UInt32 kMetadataBlockSizeLog = 13; +static const UInt32 kMetadataBlockSize = (1 << kMetadataBlockSizeLog); + +#define MY_S_IFIFO 0x1000 +#define MY_S_IFCHR 0x2000 +#define MY_S_IFDIR 0x4000 +#define MY_S_IFBLK 0x6000 +#define MY_S_IFREG 0x8000 +#define MY_S_IFLNK 0xA000 +#define MY_S_IFSOCK 0xC000 + +enum +{ + kType_IPC, + kType_DIR, + kType_FILE, + kType_LNK, + kType_BLK, + kType_CHR, + kType_FIFO, + kType_SOCK +}; + +static const UInt32 k_TypeToMode[] = +{ + 0, + MY_S_IFDIR, MY_S_IFREG, MY_S_IFLNK, MY_S_IFBLK, MY_S_IFCHR, MY_S_IFIFO, MY_S_IFSOCK, + MY_S_IFDIR, MY_S_IFREG, MY_S_IFLNK, MY_S_IFBLK, MY_S_IFCHR, MY_S_IFIFO, MY_S_IFSOCK +}; + + +enum +{ + kFlag_UNC_INODES, + kFlag_UNC_DATA, + kFlag_CHECK, + kFlag_UNC_FRAGS, + kFlag_NO_FRAGS, + kFlag_ALWAYS_FRAG, + kFlag_DUPLICATE, + kFlag_EXPORT +}; + +static const CUInt32PCharPair k_Flags[] = +{ + { kFlag_UNC_INODES, "UNCOMPRESSED_INODES" }, + { kFlag_UNC_DATA, "UNCOMPRESSED_DATA" }, + { kFlag_CHECK, "CHECK" }, + { kFlag_UNC_FRAGS, "UNCOMPRESSED_FRAGMENTS" }, + { kFlag_NO_FRAGS, "NO_FRAGMENTS" }, + { kFlag_ALWAYS_FRAG, "ALWAYS_FRAGMENTS" }, + { kFlag_DUPLICATE, "DUPLICATES_REMOVED" }, + { kFlag_EXPORT, "EXPORTABLE" } +}; + +static const UInt32 kNotCompressedBit16 = (1 << 15); +static const UInt32 kNotCompressedBit32 = (1 << 24); + +#define GET_COMPRESSED_BLOCK_SIZE(size) ((size) & ~kNotCompressedBit32) +#define IS_COMPRESSED_BLOCK(size) (((size) & kNotCompressedBit32) == 0) + +static const UInt32 kHeaderSize1 = 0x33; +static const UInt32 kHeaderSize2 = 0x3F; +static const UInt32 kHeaderSize3 = 0x77; +static const UInt32 kHeaderSize4 = 0x60; + +struct CHeader +{ + bool be; + bool SeveralMethods; + Byte NumUids; + Byte NumGids; + + UInt32 NumInodes; + UInt32 CTime; + UInt32 BlockSize; + UInt32 NumFrags; + UInt16 Method; + UInt16 BlockSizeLog; + UInt16 Flags; + UInt16 NumIDs; + UInt16 Major; + UInt16 Minor; + UInt64 RootInode; + UInt64 Size; + UInt64 UidTable; + UInt64 GidTable; + UInt64 XattrIdTable; + UInt64 InodeTable; + UInt64 DirTable; + UInt64 FragTable; + UInt64 LookupTable; + + void Parse3(const Byte *p) + { + Method = kMethod_ZLIB; + GET_32 (0x08, Size); + GET_32 (0x0C, UidTable); + GET_32 (0x10, GidTable); + GET_32 (0x14, InodeTable); + GET_32 (0x18, DirTable); + GET_16 (0x20, BlockSize); + GET_16 (0x22, BlockSizeLog); + Flags = p[0x24]; + NumUids = p[0x25]; + NumGids = p[0x26]; + GET_32 (0x27, CTime); + GET_64 (0x2B, RootInode); + NumFrags = 0; + FragTable = UidTable; + + if (Major >= 2) + { + GET_32 (0x33, BlockSize); + GET_32 (0x37, NumFrags); + GET_32 (0x3B, FragTable); + if (Major == 3) + { + GET_64 (0x3F, Size); + GET_64 (0x47, UidTable); + GET_64 (0x4F, GidTable); + GET_64 (0x57, InodeTable); + GET_64 (0x5F, DirTable); + GET_64 (0x67, FragTable); + GET_64 (0x6F, LookupTable); + } + } + } + + void Parse4(const Byte *p) + { + LE_32 (0x08, CTime); + LE_32 (0x0C, BlockSize); + LE_32 (0x10, NumFrags); + LE_16 (0x14, Method); + LE_16 (0x16, BlockSizeLog); + LE_16 (0x18, Flags); + LE_16 (0x1A, NumIDs); + LE_64 (0x20, RootInode); + LE_64 (0x28, Size); + LE_64 (0x30, UidTable); + LE_64 (0x38, XattrIdTable); + LE_64 (0x40, InodeTable); + LE_64 (0x48, DirTable); + LE_64 (0x50, FragTable); + LE_64 (0x58, LookupTable); + GidTable = 0; + } + + bool Parse(const Byte *p) + { + be = false; + SeveralMethods = false; + switch (GetUi32(p)) + { + case kSignature32_LE: break; + case kSignature32_BE: be = true; break; + case kSignature32_LZ: SeveralMethods = true; break; + default: return false; + } + GET_32 (4, NumInodes); + GET_16 (0x1C, Major); + GET_16 (0x1E, Minor); + if (Major <= 3) + Parse3(p); + else + { + if (be) + return false; + Parse4(p); + } + return + InodeTable < DirTable && + DirTable <= FragTable && + FragTable <= Size && + UidTable <= Size && + BlockSizeLog >= 12 && + BlockSizeLog < 31 && + BlockSize == ((UInt32)1 << BlockSizeLog); + } + + bool IsSupported() const { return Major > 0 && Major <= 4 && BlockSizeLog <= 23; } + bool IsOldVersion() const { return Major < 4; } + bool NeedCheckData() const { return (Flags & (1 << kFlag_CHECK)) != 0; } + unsigned GetFileNameOffset() const { return Major <= 2 ? 3 : (Major == 3 ? 5 : 8); } + unsigned GetSymLinkOffset() const { return Major <= 1 ? 5: (Major <= 2 ? 6: (Major == 3 ? 18 : 24)); } + unsigned GetSpecGuidIndex() const { return Major <= 1 ? 0xF: 0xFF; } +}; + +static const UInt32 kFrag_Empty = (UInt32)(Int32)-1; +// static const UInt32 kXattr_Empty = (UInt32)(Int32)-1; + +struct CNode +{ + UInt16 Type; + UInt16 Mode; + UInt16 Uid; + UInt16 Gid; + UInt32 Frag; + UInt32 Offset; + // UInt32 MTime; + // UInt32 Number; + // UInt32 NumLinks; + // UInt32 RDev; + // UInt32 Xattr; + // UInt32 Parent; + + UInt64 FileSize; + UInt64 StartBlock; + // UInt64 Sparse; + + UInt32 Parse1(const Byte *p, UInt32 size, const CHeader &_h); + UInt32 Parse2(const Byte *p, UInt32 size, const CHeader &_h); + UInt32 Parse3(const Byte *p, UInt32 size, const CHeader &_h); + UInt32 Parse4(const Byte *p, UInt32 size, const CHeader &_h); + + bool IsDir() const { return (Type == kType_DIR || Type == kType_DIR + 7); } + bool IsLink() const { return (Type == kType_LNK || Type == kType_LNK + 7); } + UInt64 GetSize() const { return IsDir() ? 0 : FileSize; } + + bool ThereAreFrags() const { return Frag != kFrag_Empty; } + UInt64 GetNumBlocks(const CHeader &_h) const + { + return (FileSize >> _h.BlockSizeLog) + + (!ThereAreFrags() && (FileSize & (_h.BlockSize - 1)) != 0); + } +}; + +UInt32 CNode::Parse1(const Byte *p, UInt32 size, const CHeader &_h) +{ + bool be = _h.be; + if (size < 4) + return 0; + UInt16 t = Get16(p); + if (be) + { + Type = t >> 12; + Mode = t & 0xFFF; + Uid = p[2] >> 4; + Gid = p[2] & 0xF; + } + else + { + Type = t & 0xF; + Mode = t >> 4; + Uid = p[2] & 0xF; + Gid = p[2] >> 4; + } + + // Xattr = kXattr_Empty; + // MTime = 0; + FileSize = 0; + StartBlock = 0; + Frag = kFrag_Empty; + + if (Type == 0) + { + Byte t = p[3]; + if (be) + { + Type = t >> 4; + Offset = t & 0xF; + } + else + { + Type = t & 0xF; + Offset = t >> 4; + } + return (Type == kType_FIFO || Type == kType_SOCK) ? 4 : 0; + } + + Type--; + Uid += (Type / 5) * 16; + Type = (Type % 5) + 1; + + if (Type == kType_FILE) + { + if (size < 15) + return 0; + // GET_32 (3, MTime); + GET_32 (7, StartBlock); + UInt32 t; + GET_32 (11, t); + FileSize = t; + UInt32 numBlocks = t >> _h.BlockSizeLog; + if ((t & (_h.BlockSize - 1)) != 0) + numBlocks++; + UInt32 pos = numBlocks * 2 + 15; + return (pos <= size) ? pos : 0; + } + + if (Type == kType_DIR) + { + if (size < 14) + return 0; + UInt32 t = Get32(p + 3); + if (be) + { + FileSize = t >> 13; + Offset = t & 0x1FFF; + } + else + { + FileSize = t & 0x7FFFF; + Offset = t >> 19; + } + // GET_32 (7, MTime); + GET_32 (10, StartBlock); + if (be) + StartBlock &= 0xFFFFFF; + else + StartBlock >>= 8; + return 14; + } + + if (size < 5) + return 0; + + if (Type == kType_LNK) + { + UInt32 len; + GET_16 (3, len); + FileSize = len; + len += 5; + return (len <= size) ? len : 0; + } + + // GET_32 (3, RDev); + return 5; +} + +UInt32 CNode::Parse2(const Byte *p, UInt32 size, const CHeader &_h) +{ + bool be = _h.be; + if (size < 4) + return 0; + UInt16 t = Get16(p); + if (be) + { + Type = t >> 12; + Mode = t & 0xFFF; + } + else + { + Type = t & 0xF; + Mode = t >> 4; + } + Uid = p[2]; + Gid = p[3]; + + // Xattr = kXattr_Empty; + + if (Type == kType_FILE) + { + if (size < 24) + return 0; + // GET_32 (4, MTime); + GET_32 (8, StartBlock); + GET_32 (12, Frag); + GET_32 (16, Offset); + UInt32 t; + GET_32 (20, t); + FileSize = t; + UInt32 numBlocks = t >> _h.BlockSizeLog; + if (!ThereAreFrags() && (t & (_h.BlockSize - 1)) != 0) + numBlocks++; + UInt32 pos = numBlocks * 4 + 24; + return (pos <= size) ? (UInt32)pos : 0; + } + + FileSize = 0; + // MTime = 0; + StartBlock = 0; + Frag = kFrag_Empty; + + if (Type == kType_DIR) + { + if (size < 15) + return 0; + UInt32 t = Get32(p + 4); + if (be) + { + FileSize = t >> 13; + Offset = t & 0x1FFF; + } + else + { + FileSize = t & 0x7FFFF; + Offset = t >> 19; + } + // GET_32 (8, MTime); + GET_32 (11, StartBlock); + if (be) + StartBlock &= 0xFFFFFF; + else + StartBlock >>= 8; + return 15; + } + + if (Type == kType_DIR + 7) + { + if (size < 18) + return 0; + UInt32 t = Get32(p + 4); + UInt32 t2 = Get16(p + 7); + if (be) + { + FileSize = t >> 5; + Offset = t2 & 0x1FFF; + } + else + { + FileSize = t & 0x7FFFFFF; + Offset = t2 >> 3; + } + // GET_32 (9, MTime); + GET_32 (12, StartBlock); + if (be) + StartBlock &= 0xFFFFFF; + else + StartBlock >>= 8; + UInt32 iCount; + GET_16 (16, iCount); + UInt32 pos = 18; + for (UInt32 i = 0; i < iCount; i++) + { + // 27 bits: index + // 29 bits: startBlock + if (pos + 8 > size) + return 0; + pos += 8 + (UInt32)p[pos + 7] + 1; // nameSize + if (pos > size) + return 0; + } + return pos; + } + + if (Type == kType_FIFO || Type == kType_SOCK) + return 4; + + if (size < 6) + return 0; + + if (Type == kType_LNK) + { + UInt32 len; + GET_16 (4, len); + FileSize = len; + len += 6; + return (len <= size) ? len : 0; + } + + if (Type == kType_BLK || Type == kType_CHR) + { + // GET_16 (4, RDev); + return 6; + } + + return 0; +} + +UInt32 CNode::Parse3(const Byte *p, UInt32 size, const CHeader &_h) +{ + bool be = _h.be; + if (size < 12) + return 0; + UInt16 t = Get16(p); + if (be) + { + Type = t >> 12; + Mode = t & 0xFFF; + } + else + { + Type = t & 0xF; + Mode = t >> 4; + } + Uid = p[2]; + Gid = p[3]; + // GET_32 (4, MTime); + // GET_32 (8, Number); + // Xattr = kXattr_Empty; + FileSize = 0; + StartBlock = 0; + + if (Type == kType_FILE || Type == kType_FILE + 7) + { + UInt32 offset; + if (Type == kType_FILE) + { + if (size < 32) + return 0; + GET_64 (12, StartBlock); + GET_32 (20, Frag); + GET_32 (24, Offset); + GET_32 (28, FileSize); + offset = 32; + } + else + { + if (size < 40) + return 0; + // GET_32 (12, NumLinks); + GET_64 (16, StartBlock); + GET_32 (24, Frag); + GET_32 (28, Offset); + GET_64 (32, FileSize); + offset = 40; + } + UInt64 pos = GetNumBlocks(_h) * 4 + offset; + return (pos <= size) ? (UInt32)pos : 0; + } + + if (size < 16) + return 0; + // GET_32 (12, NumLinks); + + if (Type == kType_DIR) + { + if (size < 28) + return 0; + UInt32 t = Get32(p + 16); + if (be) + { + FileSize = t >> 13; + Offset = t & 0x1FFF; + } + else + { + FileSize = t & 0x7FFFF; + Offset = t >> 19; + } + GET_32 (20, StartBlock); + // GET_32 (24, Parent); + return 28; + } + + if (Type == kType_DIR + 7) + { + if (size < 31) + return 0; + UInt32 t = Get32(p + 16); + UInt32 t2 = Get16(p + 19); + if (be) + { + FileSize = t >> 5; + Offset = t2 & 0x1FFF; + } + else + { + FileSize = t & 0x7FFFFFF; + Offset = t2 >> 3; + } + GET_32 (21, StartBlock); + UInt32 iCount; + GET_16 (25, iCount); + // GET_32 (27, Parent); + UInt32 pos = 31; + for (UInt32 i = 0; i < iCount; i++) + { + // UInt32 index + // UInt32 startBlock + if (pos + 9 > size) + return 0; + pos += 9 + (unsigned)p[pos + 8] + 1; // nameSize + if (pos > size) + return 0; + } + return pos; + } + + if (Type == kType_FIFO || Type == kType_SOCK) + return 16; + + if (size < 18) + return 0; + if (Type == kType_LNK) + { + UInt32 len; + GET_16 (16, len); + FileSize = len; + len += 18; + return (len <= size) ? len : 0; + } + + if (Type == kType_BLK || Type == kType_CHR) + { + // GET_16 (16, RDev); + return 18; + } + + return 0; +} + +UInt32 CNode::Parse4(const Byte *p, UInt32 size, const CHeader &_h) +{ + if (size < 20) + return 0; + LE_16 (0, Type); + LE_16 (2, Mode); + LE_16 (4, Uid); + LE_16 (6, Gid); + // LE_32 (8, MTime); + // LE_32 (12, Number); + + // Xattr = kXattr_Empty; + FileSize = 0; + StartBlock = 0; + + if (Type == kType_FILE || Type == kType_FILE + 7) + { + UInt32 offset; + if (Type == kType_FILE) + { + if (size < 32) + return 0; + LE_32 (16, StartBlock); + LE_32 (20, Frag); + LE_32 (24, Offset); + LE_32 (28, FileSize); + offset = 32; + } + else + { + if (size < 56) + return 0; + LE_64 (16, StartBlock); + LE_64 (24, FileSize); + // LE_64 (32, Sparse); + // LE_32 (40, NumLinks); + LE_32 (44, Frag); + LE_32 (48, Offset); + // LE_32 (52, Xattr); + offset = 56; + } + UInt64 pos = GetNumBlocks(_h) * 4 + offset; + return (pos <= size) ? (UInt32)pos : 0; + } + + if (Type == kType_DIR) + { + if (size < 32) + return 0; + LE_32 (16, StartBlock); + // LE_32 (20, NumLinks); + LE_16 (24, FileSize); + LE_16 (26, Offset); + // LE_32 (28, Parent); + return 32; + } + + // LE_32 (16, NumLinks); + + if (Type == kType_DIR + 7) + { + if (size < 40) + return 0; + LE_32 (20, FileSize); + LE_32 (24, StartBlock); + // LE_32 (28, Parent); + UInt32 iCount; + LE_16 (32, iCount); + LE_16 (34, Offset); + // LE_32 (36, Xattr); + + UInt32 pos = 40; + for (UInt32 i = 0; i < iCount; i++) + { + // UInt32 index + // UInt32 startBlock + if (pos + 12 > size) + return 0; + UInt32 nameLen = GetUi32(p + pos + 8); + pos += 12 + nameLen + 1; + if (pos > size || nameLen > (1 << 10)) + return 0; + } + return pos; + } + + unsigned offset = 20; + switch(Type) + { + case kType_FIFO: case kType_FIFO + 7: + case kType_SOCK: case kType_SOCK + 7: + break; + case kType_LNK: case kType_LNK + 7: + { + if (size < 24) + return 0; + UInt32 len; + LE_32 (20, len); + FileSize = len; + offset = len + 24; + if (size < offset || len > (1 << 30)) + return 0; + break; + } + case kType_BLK: case kType_BLK + 7: + case kType_CHR: case kType_CHR + 7: + if (size < 24) + return 0; + // LE_32 (20, RDev); + offset = 24; + break; + default: + return 0; + } + + if (Type >= 8) + { + if (size < offset + 4) + return 0; + // LE_32 (offset, Xattr); + offset += 4; + } + return offset; +} + +struct CItem +{ + int Node; + int Parent; + UInt32 Ptr; +}; + +struct CData +{ + CByteBuffer Data; + CRecordVector<UInt32> PackPos; + CRecordVector<UInt32> UnpackPos; // additional item at the end contains TotalUnpackSize + + UInt32 GetNumBlocks() const { return PackPos.Size(); } + void Clear() + { + Data.Free(); + PackPos.Clear(); + UnpackPos.Clear(); + } +}; + +struct CFrag +{ + UInt64 StartBlock; + UInt32 Size; +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CRecordVector<CItem> _items; + CRecordVector<CNode> _nodes; + CRecordVector<UInt32> _nodesPos; + CRecordVector<UInt32> _blockToNode; + CData _inodesData; + CData _dirs; + CRecordVector<CFrag> _frags; + // CByteBuffer _uids; + // CByteBuffer _gids; + CHeader _h; + + CMyComPtr<IInStream> _stream; + UInt64 _sizeCalculated; + + IArchiveOpenCallback *_openCallback; + + int _nodeIndex; + CRecordVector<bool> _blockCompressed; + CRecordVector<UInt64> _blockOffsets; + + CByteBuffer _cachedBlock; + UInt64 _cachedBlockStartPos; + UInt32 _cachedPackBlockSize; + UInt32 _cachedUnpackBlockSize; + + CLimitedSequentialInStream *_limitedInStreamSpec; + CMyComPtr<ISequentialInStream> _limitedInStream; + + CBufPtrSeqOutStream *_outStreamSpec; + CMyComPtr<ISequentialOutStream> _outStream; + + NCompress::NLzma::CDecoder *_lzmaDecoderSpec; + CMyComPtr<ICompressCoder> _lzmaDecoder; + + NCompress::NZlib::CDecoder *_zlibDecoderSpec; + CMyComPtr<ICompressCoder> _zlibDecoder; + + CByteBuffer _inputBuffer; + + CDynBufSeqOutStream *_dynOutStreamSpec; + CMyComPtr<ISequentialOutStream> _dynOutStream; + + void ClearCache() + { + _cachedBlockStartPos = 0; + _cachedPackBlockSize = 0; + _cachedUnpackBlockSize = 0; + } + + HRESULT Decompress(ISequentialOutStream *outStream, Byte *outBuf, bool *outBufWasWritten, UInt32 *outBufWasWrittenSize, + UInt32 inSize, UInt32 outSizeMax); + HRESULT ReadMetadataBlock(UInt32 &packSize); + HRESULT ReadData(CData &data, UInt64 start, UInt64 end); + + HRESULT OpenDir(int parent, UInt32 startBlock, UInt32 offset, unsigned level, int &nodeIndex); + HRESULT ScanInodes(UInt64 ptr); + // HRESULT ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids); + HRESULT Open2(IInStream *inStream); + AString GetPath(int index) const; + bool GetPackSize(int index, UInt64 &res, bool fillOffsets); + +public: + CHandler(); + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + + HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize); +}; + +CHandler::CHandler() +{ + _limitedInStreamSpec = new CLimitedSequentialInStream; + _limitedInStream = _limitedInStreamSpec; + + _outStreamSpec = new CBufPtrSeqOutStream(); + _outStream = _outStreamSpec; + + _dynOutStreamSpec = new CDynBufSeqOutStream; + _dynOutStream = _dynOutStreamSpec; +} + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidPosixAttrib, VT_UI4} + // { NULL, kpidUser, VT_BSTR}, + // { NULL, kpidGroup, VT_BSTR}, + // { NULL, kpidLinks, VT_UI4}, + // { NULL, kpidOffset, VT_UI4} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidFileSystem, VT_BSTR}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidBlock, VT_UI4}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8}, + { NULL, kpidBigEndian, VT_BOOL}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidCharacts, VT_BSTR} + // { NULL, kpidNumBlocks, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static HRESULT LzoDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen) +{ + SizeT destRem = *destLen; + SizeT srcRem = *srcLen; + *destLen = 0; + *srcLen = 0; + const Byte *destStart = dest; + const Byte *srcStart = src; + unsigned mode = 2; + + { + if (srcRem == 0) + return S_FALSE; + UInt32 b = *src; + if (b > 17) + { + src++; + srcRem--; + b -= 17; + mode = (b < 4 ? 0 : 1); + if (b > srcRem || b > destRem) + return S_FALSE; + srcRem -= b; + destRem -= b; + do + *dest++ = *src++; + while (--b); + } + } + + for (;;) + { + if (srcRem < 3) + return S_FALSE; + UInt32 b = *src++; + srcRem--; + UInt32 len, back; + if (b >= 64) + { + srcRem--; + back = ((b >> 2) & 7) + ((UInt32)*src++ << 3); + len = (b >> 5) + 1; + } + else if (b < 16) + { + if (mode == 2) + { + if (b == 0) + { + for (b = 15;; b += 255) + { + if (srcRem == 0) + return S_FALSE; + UInt32 b2 = *src++; + srcRem--; + if (b2 != 0) + { + b += b2; + break; + } + } + } + b += 3; + if (b > srcRem || b > destRem) + return S_FALSE; + srcRem -= b; + destRem -= b; + mode = 1; + do + *dest++ = *src++; + while (--b); + continue; + } + srcRem--; + back = (b >> 2) + (*src++ << 2); + len = 2; + if (mode == 1) + { + back += (1 << 11); + len = 3; + } + } + else + { + UInt32 bOld = b; + b = (b < 32 ? 7 : 31); + len = bOld & b; + if (len == 0) + { + for (len = b;; len += 255) + { + if (srcRem == 0) + return S_FALSE; + UInt32 b2 = *src++; + srcRem--; + if (b2 != 0) + { + len += b2; + break; + } + } + } + len += 2; + if (srcRem < 2) + return S_FALSE; + b = *src; + back = (b >> 2) + ((UInt32)src[1] << 6); + src += 2; + srcRem -= 2; + if (bOld < 32) + { + if (back == 0) + { + *destLen = dest - destStart; + *srcLen = src - srcStart; + return S_OK; + } + back += ((bOld & 8) << 11) + (1 << 14) - 1; + } + } + back++; + if (len > destRem || (size_t)(dest - destStart) < back) + return S_FALSE; + destRem -= len; + Byte *destTemp = dest - back; + dest += len; + do + { + *(destTemp + back) = *destTemp; + destTemp++; + } + while (--len); + b &= 3; + if (b == 0) + { + mode = 2; + continue; + } + if (b > srcRem || b > destRem) + return S_FALSE; + srcRem -= b; + destRem -= b; + mode = 0; + *dest++ = *src++; + if (b > 1) + { + *dest++ = *src++; + if (b > 2) + *dest++ = *src++; + } + } +} + +HRESULT CHandler::Decompress(ISequentialOutStream *outStream, Byte *outBuf, bool *outBufWasWritten, UInt32 *outBufWasWrittenSize, UInt32 inSize, UInt32 outSizeMax) +{ + if (outBuf) + { + *outBufWasWritten = false; + *outBufWasWrittenSize = 0; + } + UInt32 method = _h.Method; + if (_h.SeveralMethods) + { + Byte props[1]; + RINOK(ReadStream_FALSE(_stream, props, 1)); + method = (props[0] == 0x5D ? kMethod_LZMA : kMethod_ZLIB); + RINOK(_stream->Seek(-1, STREAM_SEEK_CUR, NULL)); + } + + if (method == kMethod_LZO) + { + if (_inputBuffer.GetCapacity() < inSize) + { + _inputBuffer.Free(); + _inputBuffer.SetCapacity(inSize); + } + RINOK(ReadStream_FALSE(_stream, _inputBuffer, inSize)); + + Byte *dest = outBuf; + if (!outBuf) + { + dest = _dynOutStreamSpec->GetBufPtrForWriting(outSizeMax); + if (!dest) + return E_OUTOFMEMORY; + } + SizeT destLen = outSizeMax, srcLen = inSize; + RINOK(LzoDecode(dest, &destLen, _inputBuffer, &srcLen)); + if (inSize != srcLen) + return S_FALSE; + if (outBuf) + { + *outBufWasWritten = true; + *outBufWasWrittenSize = (UInt32)destLen; + } + else + _dynOutStreamSpec->UpdateSize(destLen); + } + else if (method == kMethod_LZMA) + { + if (!_lzmaDecoder) + { + _lzmaDecoderSpec = new NCompress::NLzma::CDecoder(); + _lzmaDecoderSpec->FinishStream = true; + _lzmaDecoder = _lzmaDecoderSpec; + } + const UInt32 kPropsSize = 5 + 8; + Byte props[kPropsSize]; + ReadStream_FALSE(_limitedInStream, props, kPropsSize); + RINOK(_lzmaDecoderSpec->SetDecoderProperties2(props, 5)); + UInt64 outSize = GetUi64(props + 5); + if (outSize > outSizeMax) + return S_FALSE; + RINOK(_lzmaDecoder->Code(_limitedInStream, outStream, NULL, &outSize, NULL)); + if (inSize != kPropsSize + _lzmaDecoderSpec->GetInputProcessedSize()) + return S_FALSE; + } + else + { + if (!_zlibDecoder) + { + _zlibDecoderSpec = new NCompress::NZlib::CDecoder(); + _zlibDecoder = _zlibDecoderSpec; + } + RINOK(_zlibDecoder->Code(_limitedInStream, outStream, NULL, NULL, NULL)); + if (inSize != _zlibDecoderSpec->GetInputProcessedSize()) + return S_FALSE; + } + return S_OK; +} + +HRESULT CHandler::ReadMetadataBlock(UInt32 &packSize) +{ + Byte temp[3]; + unsigned offset = _h.NeedCheckData() ? 3 : 2; + if (offset > packSize) + return S_FALSE; + RINOK(ReadStream_FALSE(_stream, temp, offset)); + // if (NeedCheckData && Major < 4) checkByte must be = 0xFF + bool be = _h.be; + UInt32 size = Get16(temp); + bool isCompressed = ((size & kNotCompressedBit16) == 0); + if (size != kNotCompressedBit16) + size &= ~kNotCompressedBit16; + + if (size > kMetadataBlockSize || offset + size > packSize) + return S_FALSE; + packSize = offset + size; + if (isCompressed) + { + _limitedInStreamSpec->Init(size); + RINOK(Decompress(_dynOutStream, NULL, NULL, NULL, size, kMetadataBlockSize)); + } + else + { + // size != 0 here + Byte *buf = _dynOutStreamSpec->GetBufPtrForWriting(size); + if (!buf) + return E_OUTOFMEMORY; + RINOK(ReadStream_FALSE(_stream, buf, size)); + _dynOutStreamSpec->UpdateSize(size); + } + return S_OK; +} + +HRESULT CHandler::ReadData(CData &data, UInt64 start, UInt64 end) +{ + if (end < start || end - start >= ((UInt64)1 << 32)) + return S_FALSE; + UInt32 size = (UInt32)(end - start); + RINOK(_stream->Seek(start, STREAM_SEEK_SET, NULL)); + _dynOutStreamSpec->Init(); + UInt32 packPos = 0; + while (packPos != size) + { + data.PackPos.Add(packPos); + data.UnpackPos.Add((UInt32)_dynOutStreamSpec->GetSize()); + if (packPos > size) + return S_FALSE; + UInt32 packSize = size - packPos; + RINOK(ReadMetadataBlock(packSize)); + if (_dynOutStreamSpec->GetSize() >= ((UInt64)1 << 32)) + return S_FALSE; + packPos += packSize; + } + data.UnpackPos.Add((UInt32)_dynOutStreamSpec->GetSize()); + _dynOutStreamSpec->CopyToBuffer(data.Data); + return S_OK; +} + +struct CTempItem +{ + UInt32 StartBlock; + // UInt32 iNodeNumber1; + UInt32 Offset; + // UInt16 iNodeNumber2; + UInt16 Type; +}; + +HRESULT CHandler::OpenDir(int parent, UInt32 startBlock, UInt32 offset, unsigned level, int &nodeIndex) +{ + if (level > kNumDirLevelsMax) + return S_FALSE; + + int blockIndex = _inodesData.PackPos.FindInSorted(startBlock); + if (blockIndex < 0) + return S_FALSE; + UInt32 unpackPos = _inodesData.UnpackPos[blockIndex] + offset; + if (unpackPos < offset) + return S_FALSE; + + nodeIndex = _nodesPos.FindInSorted(unpackPos, _blockToNode[blockIndex], _blockToNode[blockIndex + 1]); + // nodeIndex = _nodesPos.FindInSorted(unpackPos); + if (nodeIndex < 0) + return S_FALSE; + + const CNode &n = _nodes[nodeIndex]; + if (!n.IsDir()) + return S_OK; + blockIndex = _dirs.PackPos.FindInSorted((UInt32)n.StartBlock); + if (blockIndex < 0) + return S_FALSE; + unpackPos = _dirs.UnpackPos[blockIndex] + n.Offset; + if (unpackPos < n.Offset || unpackPos > _dirs.Data.GetCapacity()) + return S_FALSE; + + UInt32 rem = (UInt32)_dirs.Data.GetCapacity() - unpackPos; + const Byte *p = _dirs.Data + unpackPos; + UInt32 fileSize = (UInt32)n.FileSize; + + if (fileSize > rem) + return S_FALSE; + rem = fileSize; + if (_h.Major >= 3) + { + if (rem < 3) + return S_FALSE; + rem -= 3; + } + + CRecordVector<CTempItem> tempItems; + while (rem != 0) + { + bool be = _h.be; + UInt32 count; + CTempItem tempItem; + if (_h.Major <= 2) + { + if (rem < 4) + return S_FALSE; + count = p[0]; + tempItem.StartBlock = Get32(p); + if (be) + tempItem.StartBlock &= 0xFFFFFF; + else + tempItem.StartBlock >>= 8; + p += 4; + rem -= 4; + } + else + { + if (_h.Major == 3) + { + if (rem < 9) + return S_FALSE; + count = p[0]; + p += 1; + rem -= 1; + } + else + { + if (rem < 12) + return S_FALSE; + count = GetUi32(p); + p += 4; + rem -= 4; + } + GET_32 (0, tempItem.StartBlock); + // GET_32 (4, tempItem.iNodeNumber1); + p += 8; + rem -= 8; + } + count++; + + for (UInt32 i = 0; i < count; i++) + { + if (rem == 0) + return S_FALSE; + + UInt32 nameOffset = _h.GetFileNameOffset(); + if (rem < nameOffset) + return S_FALSE; + + if ((UInt32)_items.Size() >= kNumFilesMax) + return S_FALSE; + if (_openCallback) + { + UInt64 numFiles = _items.Size(); + if ((numFiles & 0xFFFF) == 0) + { + RINOK(_openCallback->SetCompleted(&numFiles, NULL)); + } + } + + CItem item; + item.Ptr = (UInt32)(p - _dirs.Data); + + UInt32 size; + if (_h.IsOldVersion()) + { + UInt32 t = Get16(p); + if (be) + { + tempItem.Offset = t >> 3; + tempItem.Type = (UInt16)(t & 0x7); + } + else + { + tempItem.Offset = t & 0x1FFF; + tempItem.Type = (UInt16)(t >> 13); + } + size = (UInt32)p[2]; + /* + if (_h.Major > 2) + tempItem.iNodeNumber2 = Get16(p + 3); + */ + } + else + { + GET_16 (0, tempItem.Offset); + // GET_16 (2, tempItem.iNodeNumber2); + GET_16 (4, tempItem.Type); + GET_16 (6, size); + } + p += nameOffset; + rem -= nameOffset; + size++; + if (rem < size) + return S_FALSE; + p += size; + rem -= size; + item.Parent = parent; + _items.Add(item); + tempItems.Add(tempItem); + } + } + + int startItemIndex = _items.Size() - tempItems.Size(); + for (int i = 0; i < tempItems.Size(); i++) + { + const CTempItem &tempItem = tempItems[i]; + int index = startItemIndex + i; + CItem &item = _items[index]; + RINOK(OpenDir(index, tempItem.StartBlock, tempItem.Offset, level + 1, item.Node)); + } + + return S_OK; +} + +/* +HRESULT CHandler::ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids) +{ + size_t size = num * 4; + ids.SetCapacity(size); + RINOK(_stream->Seek(start, STREAM_SEEK_SET, NULL)); + return ReadStream_FALSE(_stream, ids, size); +} +*/ + +HRESULT CHandler::Open2(IInStream *inStream) +{ + { + Byte buf[kHeaderSize3]; + RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize3)); + if (!_h.Parse(buf)) + return S_FALSE; + if (!_h.IsSupported()) + return E_NOTIMPL; + + switch (_h.Method) + { + case kMethod_ZLIB: + case kMethod_LZMA: + case kMethod_LZO: + break; + default: + return E_NOTIMPL; + } + } + + _stream = inStream; + + if (_h.NumFrags != 0) + { + if (_h.NumFrags > kNumFilesMax) + return S_FALSE; + _frags.Reserve(_h.NumFrags); + CByteBuffer data; + unsigned bigFrag = (_h.Major > 2); + + unsigned fragPtrsInBlockLog = kMetadataBlockSizeLog - (3 + bigFrag); + UInt32 numBlocks = (_h.NumFrags + (1 << fragPtrsInBlockLog) - 1) >> fragPtrsInBlockLog; + size_t numBlocksBytes = (size_t)numBlocks << (2 + bigFrag); + data.SetCapacity(numBlocksBytes); + RINOK(inStream->Seek(_h.FragTable, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(inStream, data, numBlocksBytes)); + bool be = _h.be; + + for (UInt32 i = 0; i < numBlocks; i++) + { + UInt64 offset = bigFrag ? Get64(data + i * 8) : Get32(data + i * 4); + RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL)); + _dynOutStreamSpec->Init(); + UInt32 packSize = kMetadataBlockSize + 3; + RINOK(ReadMetadataBlock(packSize)); + UInt32 unpackSize = (UInt32)_dynOutStreamSpec->GetSize(); + if (unpackSize != kMetadataBlockSize) + if (i != numBlocks - 1 || unpackSize != ((_h.NumFrags << (3 + bigFrag)) & (kMetadataBlockSize - 1))) + return S_FALSE; + const Byte *buf = _dynOutStreamSpec->GetBuffer(); + for (UInt32 j = 0; j < kMetadataBlockSize && j < unpackSize;) + { + CFrag frag; + if (bigFrag) + { + frag.StartBlock = Get64(buf + j); + frag.Size = Get32(buf + j + 8); + // some archives contain nonzero in unused (buf + j + 12) + j += 16; + } + else + { + frag.StartBlock = Get32(buf + j); + frag.Size = Get32(buf + j + 4); + j += 8; + } + _frags.Add(frag); + } + } + if ((UInt32)_frags.Size() != _h.NumFrags) + return S_FALSE; + } + + // RINOK(inStream->Seek(_h.InodeTable, STREAM_SEEK_SET, NULL)); + + RINOK(ReadData(_inodesData, _h.InodeTable, _h.DirTable)); + RINOK(ReadData(_dirs, _h.DirTable, _h.FragTable)); + + UInt64 absOffset = _h.RootInode >> 16; + if (absOffset >= ((UInt64)1 << 32)) + return S_FALSE; + { + UInt32 pos = 0; + UInt32 totalSize = (UInt32)_inodesData.Data.GetCapacity(); + _nodesPos.Reserve(_h.NumInodes); + _nodes.Reserve(_h.NumInodes); + // we use _blockToNode for binary search seed optimizations + _blockToNode.Reserve(_inodesData.GetNumBlocks() + 1); + int curBlock = 0; + for (UInt32 i = 0; i < _h.NumInodes; i++) + { + CNode n; + const Byte *p = _inodesData.Data + pos; + UInt32 size = totalSize - pos; + + switch(_h.Major) + { + case 1: size = n.Parse1(p, size, _h); break; + case 2: size = n.Parse2(p, size, _h); break; + case 3: size = n.Parse3(p, size, _h); break; + default: size = n.Parse4(p, size, _h); break; + } + if (size == 0) + return S_FALSE; + while (pos >= _inodesData.UnpackPos[curBlock]) + { + _blockToNode.Add(_nodesPos.Size()); + curBlock++; + } + _nodesPos.Add(pos); + _nodes.Add(n); + pos += size; + } + _blockToNode.Add(_nodesPos.Size()); + if (pos != totalSize) + return S_FALSE; + } + int rootNodeIndex; + RINOK(OpenDir(-1, (UInt32)absOffset, (UInt32)_h.RootInode & 0xFFFF, 0, rootNodeIndex)); + + /* + if (_h.Major < 4) + { + RINOK(ReadUids(_h.UidTable, _h.NumUids, _uids)); + RINOK(ReadUids(_h.GidTable, _h.NumGids, _gids)); + } + else + { + UInt32 size = _h.NumIDs * 4; + _uids.SetCapacity(size); + + UInt32 numBlocks = (size + kMetadataBlockSize - 1) / kMetadataBlockSize; + UInt32 numBlocksBytes = numBlocks << 3; + CByteBuffer data; + data.SetCapacity(numBlocksBytes); + RINOK(inStream->Seek(_h.UidTable, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(inStream, data, numBlocksBytes)); + + for (UInt32 i = 0; i < numBlocks; i++) + { + UInt64 offset = GetUi64(data + i * 8); + UInt32 unpackSize, packSize; + RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL)); + RINOK(ReadMetadataBlock(NULL, _uids + kMetadataBlockSize * i, packSize, unpackSize)); + if (unpackSize != kMetadataBlockSize) + if (i != numBlocks - 1 || unpackSize != (size & (kMetadataBlockSize - 1))) + return S_FALSE; + } + } + */ + + { + const UInt32 alignSize = 1 << 12; + Byte buf[alignSize]; + RINOK(inStream->Seek(_h.Size, STREAM_SEEK_SET, NULL)); + UInt32 rem = (UInt32)(0 - _h.Size) & (alignSize - 1); + _sizeCalculated = _h.Size; + if (rem != 0) + { + if (ReadStream_FALSE(_stream, buf, rem) == S_OK) + { + size_t i; + for (i = 0; i < rem && buf[i] == 0; i++); + if (i == rem) + _sizeCalculated = _h.Size + rem; + } + } + } + return S_OK; +} + +AString CHandler::GetPath(int index) const +{ + unsigned len = 0; + int indexMem = index; + bool be = _h.be; + do + { + const CItem &item = _items[index]; + index = item.Parent; + const Byte *p = _dirs.Data + item.Ptr; + unsigned size = (_h.IsOldVersion() ? (unsigned)p[2] : (unsigned)Get16(p + 6)) + 1; + p += _h.GetFileNameOffset(); + unsigned i; + for (i = 0; i < size && p[i]; i++); + len += i + 1; + } + while (index >= 0); + len--; + + AString path; + char *dest = path.GetBuffer(len) + len; + index = indexMem; + for (;;) + { + const CItem &item = _items[index]; + index = item.Parent; + + const Byte *p = _dirs.Data + item.Ptr; + unsigned size = (_h.IsOldVersion() ? (unsigned)p[2] : (unsigned)Get16(p + 6)) + 1; + p += _h.GetFileNameOffset(); + unsigned i; + for (i = 0; i < size && p[i]; i++); + dest -= i; + memcpy(dest, p, i); + if (index < 0) + break; + *(--dest) = CHAR_PATH_SEPARATOR; + } + path.ReleaseBuffer(len); + return path; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + Close(); + _limitedInStreamSpec->SetStream(stream); + HRESULT res; + try + { + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + _openCallback = callback; + res = Open2(stream); + } + catch(...) + { + Close(); + throw; + } + if (res != S_OK) + { + Close(); + return res; + } + _stream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _limitedInStreamSpec->ReleaseStream(); + _stream.Release(); + + _items.Clear(); + _nodes.Clear(); + _nodesPos.Clear(); + _blockToNode.Clear(); + _frags.Clear(); + _inodesData.Clear(); + _dirs.Clear(); + + // _uids.Free(); + // _gids.Free();; + + _cachedBlock.Free(); + ClearCache(); + + return S_OK; +} + +bool CHandler::GetPackSize(int index, UInt64 &totalPack, bool fillOffsets) +{ + totalPack = 0; + const CItem &item = _items[index]; + const CNode &node = _nodes[item.Node]; + UInt32 ptr = _nodesPos[item.Node]; + const Byte *p = _inodesData.Data + ptr; + bool be = _h.be; + + UInt32 type = node.Type; + UInt32 offset; + if (node.IsLink() || node.FileSize == 0) + { + totalPack = node.FileSize; + return true; + } + + UInt32 numBlocks = (UInt32)node.GetNumBlocks(_h); + + if (fillOffsets) + { + _blockOffsets.Clear(); + _blockCompressed.Clear(); + _blockOffsets.Add(totalPack); + } + + if (_h.Major <= 1) + { + offset = 15; + p += offset; + + for (UInt32 i = 0; i < numBlocks; i++) + { + UInt32 t = Get16(p + i * 2); + if (fillOffsets) + _blockCompressed.Add((t & kNotCompressedBit16) == 0); + if (t != kNotCompressedBit16) + t &= ~kNotCompressedBit16; + totalPack += t; + if (fillOffsets) + _blockOffsets.Add(totalPack); + } + } + else + { + if (_h.Major <= 2) + offset = 24; + else if (type == kType_FILE) + offset = 32; + else if (type == kType_FILE + 7) + offset = (_h.Major <= 3 ? 40 : 56); + else + return false; + + p += offset; + + for (UInt64 i = 0; i < numBlocks; i++) + { + UInt32 t = Get32(p + i * 4); + if (fillOffsets) + _blockCompressed.Add(IS_COMPRESSED_BLOCK(t)); + UInt32 size = GET_COMPRESSED_BLOCK_SIZE(t); + if (size > _h.BlockSize) + return false; + totalPack += size; + if (fillOffsets) + _blockOffsets.Add(totalPack); + } + + if (node.ThereAreFrags()) + { + if (node.Frag >= (UInt32)_frags.Size()) + return false; + const CFrag &frag = _frags[node.Frag]; + if (node.Offset == 0) + { + UInt32 size = GET_COMPRESSED_BLOCK_SIZE(frag.Size); + if (size > _h.BlockSize) + return false; + totalPack += size; + } + } + } + return true; +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidMethod: + { + const char *s; + if (_h.SeveralMethods) + s = "LZMA ZLIB"; + else + { + s = k_Methods[0]; + if (_h.Method < sizeof(k_Methods) / sizeof(k_Methods[0])) + s = k_Methods[_h.Method]; + } + prop = s; + break; + } + case kpidFileSystem: + { + AString res = "SquashFS"; + if (_h.SeveralMethods) + res += "-LZMA"; + res += ' '; + char s[16]; + ConvertUInt32ToString(_h.Major, s); + res += s; + res += '.'; + ConvertUInt32ToString(_h.Minor, s); + res += s; + prop = res; + break; + } + case kpidBlock: prop = _h.BlockSize; break; + case kpidBigEndian: prop = _h.be; break; + case kpidCTime: + if (_h.CTime != 0) + { + FILETIME ft; + NWindows::NTime::UnixTimeToFileTime(_h.CTime, ft); + prop = ft; + } + break; + case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break; + // case kpidNumBlocks: prop = _h.NumFrags; break; + case kpidPhySize: prop = _sizeCalculated; break; + case kpidHeadersSize: + if (_sizeCalculated >= _h.InodeTable) + prop = _sizeCalculated - _h.InodeTable; + break; + } + 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; + const CItem &item = _items[index]; + const CNode &node = _nodes[item.Node]; + bool isDir = node.IsDir(); + bool be = _h.be; + + switch(propID) + { + case kpidPath: prop = MultiByteToUnicodeString(GetPath(index), CP_OEMCP); break; + case kpidIsDir: prop = isDir; break; + // case kpidOffset: if (!node.IsLink()) prop = (UInt64)node.StartBlock; break; + case kpidSize: if (!isDir) prop = node.GetSize(); break; + case kpidPackSize: + if (!isDir) + { + UInt64 size; + if (GetPackSize(index, size, false)) + prop = size; + } + break; + case kpidMTime: + { + UInt32 offset = 0; + switch(_h.Major) + { + case 1: + if (node.Type == kType_FILE) + offset = 3; + else if (node.Type == kType_DIR) + offset = 7; + break; + case 2: + if (node.Type == kType_FILE) + offset = 4; + else if (node.Type == kType_DIR) + offset = 8; + else if (node.Type == kType_DIR + 7) + offset = 9; + break; + case 3: offset = 4; break; + case 4: offset = 8; break; + } + if (offset != 0) + { + const Byte *p = _inodesData.Data + _nodesPos[item.Node] + offset; + FILETIME ft; + NWindows::NTime::UnixTimeToFileTime(Get32(p), ft); + prop = ft; + } + break; + } + case kpidPosixAttrib: + { + if (node.Type != 0 && node.Type < sizeof(k_TypeToMode) / sizeof(k_TypeToMode[0])) + prop = (UInt32)(node.Mode & 0xFFF) | k_TypeToMode[node.Type]; + break; + } + /* + case kpidUser: + { + UInt32 offset = node.Uid * 4; + if (offset < _uids.GetCapacity()) + prop = (UInt32)Get32(_uids + offset); + break; + } + case kpidGroup: + { + if (_h.Major == 4 || node.Gid == _h.GetSpecGuidIndex()) + { + UInt32 offset = node.Uid * 4; + if (offset < _uids.GetCapacity()) + prop = (UInt32)Get32(_uids + offset); + } + else + { + UInt32 offset = node.Gid * 4; + if (offset < _gids.GetCapacity()) + prop = (UInt32)Get32(_gids + offset); + } + break; + } + */ + /* + case kpidLinks: + if (_h.Major >= 3 && node.Type != kType_FILE) + prop = node.NumLinks; + break; + */ + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CSquashfsInStream: public CCachedInStream +{ + HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize); +public: + CHandler *Handler; +}; + +HRESULT CSquashfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) +{ + return Handler->ReadBlock(blockIndex, dest, blockSize); +} + +HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) +{ + const CNode &node = _nodes[_nodeIndex]; + UInt64 blockOffset; + UInt32 packBlockSize; + UInt32 offsetInBlock = 0; + bool compressed; + if (blockIndex < _blockCompressed.Size()) + { + compressed = _blockCompressed[(int)blockIndex]; + blockOffset = _blockOffsets[(int)blockIndex]; + packBlockSize = (UInt32)(_blockOffsets[(int)blockIndex + 1] - blockOffset); + blockOffset += node.StartBlock; + } + else + { + if (!node.ThereAreFrags()) + return S_FALSE; + const CFrag &frag = _frags[node.Frag]; + offsetInBlock = node.Offset; + blockOffset = frag.StartBlock; + packBlockSize = GET_COMPRESSED_BLOCK_SIZE(frag.Size); + compressed = IS_COMPRESSED_BLOCK(frag.Size); + } + + if (packBlockSize == 0) + { + // sparse file ??? + memset(dest, 0, blockSize); + return S_OK; + } + + if (blockOffset != _cachedBlockStartPos || + packBlockSize != _cachedPackBlockSize) + { + ClearCache(); + RINOK(_stream->Seek(blockOffset, STREAM_SEEK_SET, NULL)); + _limitedInStreamSpec->Init(packBlockSize); + + if (compressed) + { + _outStreamSpec->Init((Byte *)_cachedBlock, _h.BlockSize); + bool outBufWasWritten; + UInt32 outBufWasWrittenSize; + HRESULT res = Decompress(_outStream, _cachedBlock, &outBufWasWritten, &outBufWasWrittenSize, packBlockSize, _h.BlockSize); + if (outBufWasWritten) + _cachedUnpackBlockSize = outBufWasWrittenSize; + else + _cachedUnpackBlockSize = (UInt32)_outStreamSpec->GetPos(); + RINOK(res); + } + else + { + RINOK(ReadStream_FALSE(_limitedInStream, _cachedBlock, packBlockSize)); + _cachedUnpackBlockSize = packBlockSize; + } + _cachedBlockStartPos = blockOffset; + _cachedPackBlockSize = packBlockSize; + } + if (offsetInBlock + blockSize > _cachedUnpackBlockSize) + return S_FALSE; + memcpy(dest, _cachedBlock + offsetInBlock, blockSize); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CItem &item = _items[allFilesMode ? i : indices[i]]; + const CNode &node = _nodes[item.Node]; + totalSize += node.GetSize(); + } + extractCallback->SetTotal(totalSize); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CItem &item = _items[index]; + const CNode &node = _nodes[item.Node]; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + // const Byte *p = _data + item.Offset; + + if (node.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + UInt64 unpackSize = node.GetSize(); + totalSize += unpackSize; + UInt64 packSize; + if (GetPackSize(index, packSize, false)) + totalPackSize += packSize; + + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + int res = NExtract::NOperationResult::kDataError; + { + CMyComPtr<ISequentialInStream> inSeqStream; + CMyComPtr<IInStream> inStream; + HRESULT hres = GetStream(index, &inSeqStream); + if (inSeqStream) + inSeqStream.QueryInterface(IID_IInStream, &inStream); + if (hres == S_FALSE || !inStream) + { + if (hres == E_OUTOFMEMORY) + return hres; + res = NExtract::NOperationResult::kUnSupportedMethod; + } + else + { + RINOK(hres); + if (inStream) + { + HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (hres != S_OK && hres != S_FALSE) + { + RINOK(hres); + } + if (copyCoderSpec->TotalSize == unpackSize && hres == S_OK) + res = NExtract::NOperationResult::kOK; + else + { + res = res; + } + } + } + } + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + + const CItem &item = _items[index]; + const CNode &node = _nodes[item.Node]; + + if (node.IsDir()) + return E_FAIL; + + const Byte *p = _inodesData.Data + _nodesPos[item.Node]; + + if (node.FileSize == 0 || node.IsLink()) + { + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + if (node.IsLink()) + streamSpec->Init(p + _h.GetSymLinkOffset(), (size_t)node.FileSize); + else + streamSpec->Init(NULL, 0); + *stream = streamTemp.Detach(); + return S_OK; + } + + UInt64 packSize; + if (!GetPackSize(index, packSize, true)) + return S_FALSE; + + _nodeIndex = item.Node; + + size_t cacheSize = _h.BlockSize; + if (_cachedBlock.GetCapacity() != cacheSize) + { + ClearCache(); + _cachedBlock.SetCapacity(cacheSize); + } + + CSquashfsInStream *streamSpec = new CSquashfsInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + streamSpec->Handler = this; + unsigned cacheSizeLog = 22; + if (cacheSizeLog <= _h.BlockSizeLog) + cacheSizeLog = _h.BlockSizeLog + 1; + if (!streamSpec->Alloc(_h.BlockSizeLog, cacheSizeLog - _h.BlockSizeLog)) + return E_OUTOFMEMORY; + streamSpec->Init(node.FileSize); + *stream = streamTemp.Detach(); + + return S_OK; + + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NSquashfs::CHandler; } + +static CArcInfo g_ArcInfo = + { L"SquashFS", L"squashfs", 0, 0xD2, SIGNATURE, kSignatureSize, false, CreateArc, 0 }; + +REGISTER_ARC(Cramfs) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp new file mode 100644 index 000000000..dfc0326d1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/SwfHandler.cpp @@ -0,0 +1,706 @@ +// SwfHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/InBuffer.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" +#include "../Compress/ZlibEncoder.h" + +#include "Common/DummyOutStream.h" + +#include "DeflateProps.h" + +using namespace NWindows; + +namespace NArchive { +namespace NSwfc { + +static const UInt32 kHeaderSize = 8; + +static const Byte SWF_UNCOMPRESSED = 'F'; +static const Byte SWF_COMPRESSED = 'C'; +static const Byte SWF_MIN_COMPRESSED_VER = 6; + +struct CItem +{ + Byte Buf[kHeaderSize]; + + UInt32 GetSize() const { return GetUi32(Buf + 4); } + bool IsSwf(Byte c) const { return (Buf[0] == c && Buf[1] == 'W' && Buf[2] == 'S' && Buf[3] < 32); } + bool IsUncompressed() const { return IsSwf(SWF_UNCOMPRESSED); } + bool IsCompressed() const { return IsSwf(SWF_COMPRESSED); } + + void MakeUncompressed() { Buf[0] = SWF_UNCOMPRESSED; } + void MakeCompressed() + { + Buf[0] = SWF_COMPRESSED; + if (Buf[3] < SWF_MIN_COMPRESSED_VER) + Buf[3] = SWF_MIN_COMPRESSED_VER; + } + + HRESULT ReadHeader(ISequentialInStream *stream) { return ReadStream_FALSE(stream, Buf, kHeaderSize); } + HRESULT WriteHeader(ISequentialOutStream *stream) { return WriteStream(stream, Buf, kHeaderSize); } +}; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public IOutArchive, + public ISetProperties, + public CMyUnknownImp +{ + CItem _item; + UInt64 _packSize; + bool _packSizeDefined; + CMyComPtr<ISequentialInStream> _seqStream; + CMyComPtr<IInStream> _stream; + + CDeflateProps _method; + +public: + MY_UNKNOWN_IMP4(IInArchive, IArchiveOpenSeq, IOutArchive, ISetProperties) + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidSize: prop = (UInt64)_item.GetSize(); break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *) +{ + RINOK(OpenSeq(stream)); + _stream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + HRESULT res = _item.ReadHeader(stream); + if (res == S_OK) + if (_item.IsCompressed()) + _seqStream = stream; + else + res = S_FALSE; + return res; +} + +STDMETHODIMP CHandler::Close() +{ + _packSizeDefined = false; + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + extractCallback->SetTotal(_item.GetSize()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + NCompress::NZlib::CDecoder *_decoderSpec = new NCompress::NZlib::CDecoder; + CMyComPtr<ICompressCoder> _decoder = _decoderSpec; + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + lps->InSize = kHeaderSize; + lps->OutSize = outStreamSpec->GetSize(); + RINOK(lps->SetCur()); + + CItem item = _item; + item.MakeUncompressed(); + RINOK(item.WriteHeader(outStream)); + if (_stream) + RINOK(_stream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL)); + HRESULT result = _decoderSpec->Code(_seqStream, outStream, NULL, NULL, progress); + Int32 opRes = NExtract::NOperationResult::kDataError; + if (result == S_OK) + { + if (_item.GetSize() == outStreamSpec->GetSize()) + { + _packSizeDefined = true; + _packSize = _decoderSpec->GetInputProcessedSize() + kHeaderSize; + opRes = NExtract::NOperationResult::kOK; + } + } + else if (result != S_FALSE) + return result; + + outStream.Release(); + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +static HRESULT UpdateArchive(ISequentialOutStream *outStream, + UInt64 size, CDeflateProps &deflateProps, + IArchiveUpdateCallback *updateCallback) +{ + UInt64 complexity = 0; + RINOK(updateCallback->SetTotal(size)); + RINOK(updateCallback->SetCompleted(&complexity)); + + CMyComPtr<ISequentialInStream> fileInStream; + RINOK(updateCallback->GetStream(0, &fileInStream)); + + CItem item; + HRESULT res = item.ReadHeader(fileInStream); + if (res == S_FALSE) + return E_INVALIDARG; + RINOK(res); + if (!item.IsUncompressed() || size != item.GetSize()) + return E_INVALIDARG; + + item.MakeCompressed(); + item.WriteHeader(outStream); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + NCompress::NZlib::CEncoder *encoderSpec = new NCompress::NZlib::CEncoder; + CMyComPtr<ICompressCoder> encoder = encoderSpec; + encoderSpec->Create(); + RINOK(deflateProps.SetCoderProperties(encoderSpec->DeflateEncoderSpec)); + RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, progress)); + if (encoderSpec->GetInputProcessedSize() + kHeaderSize != size) + return E_INVALIDARG; + return updateCallback->SetOperationResult(NUpdate::NOperationResult::kOK); +} + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kUnix; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + if (numItems != 1) + return E_INVALIDARG; + + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); + + if (IntToBool(newProps)) + { + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); + if (prop.vt == VT_BOOL) + { + if (prop.boolVal != VARIANT_FALSE) + return E_INVALIDARG; + } + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + } + } + + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + return UpdateArchive(outStream, size, _method, updateCallback); + } + + if (indexInArchive != 0) + return E_INVALIDARG; + + if (!_seqStream) + return E_NOTIMPL; + + if (_stream) + { + RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + else + _item.WriteHeader(outStream); + return NCompress::CopyStream(_seqStream, outStream, NULL); +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + return _method.SetProperties(names, values, numProps); +} + +static IInArchive *CreateArc() { return new CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"SWFc", L"swf", L"~.swf", 0xD8, { 'C', 'W', 'S' }, 3, true, CreateArc, CreateArcOut }; + +REGISTER_ARC(Swfc) + +} + +namespace NSwf { + +static const UInt32 kFileSizeMax = (UInt32)1 << 30; +static const int kNumTagsMax = (UInt32)1 << 23; + +struct CTag +{ + UInt32 Type; + CByteBuffer Buf; +}; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public CMyUnknownImp +{ + CObjectVector<CTag> _tags; + NSwfc::CItem _item; + UInt64 _packSize; + + HRESULT OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback); + HRESULT OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback); +public: + MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq) + INTERFACE_IInArchive(;) + + STDMETHOD(OpenSeq)(ISequentialInStream *stream); +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidComment, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO_Table + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _tags.Size(); + return S_OK; +} + +static const char *g_TagDesc[92] = +{ + "End", + "ShowFrame", + "DefineShape", + NULL, + "PlaceObject", + "RemoveObject", + "DefineBits", + "DefineButton", + "JPEGTables", + "SetBackgroundColor", + "DefineFont", + "DefineText", + "DoAction", + "DefineFontInfo", + "DefineSound", + "StartSound", + NULL, + "DefineButtonSound", + "SoundStreamHead", + "SoundStreamBlock", + "DefineBitsLossless", + "DefineBitsJPEG2", + "DefineShape2", + "DefineButtonCxform", + "Protect", + NULL, + "PlaceObject2", + NULL, + "RemoveObject2", + NULL, + NULL, + NULL, + "DefineShape3", + "DefineText2", + "DefineButton2", + "DefineBitsJPEG3", + "DefineBitsLossless2", + "DefineEditText", + NULL, + "DefineSprite", + NULL, + "41", + NULL, + "FrameLabel", + NULL, + "SoundStreamHead2", + "DefineMorphShape", + NULL, + "DefineFont2", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "ExportAssets", + "ImportAssets", + "EnableDebugger", + "DoInitAction", + "DefineVideoStream", + "VideoFrame", + "DefineFontInfo2", + NULL, + "EnableDebugger2", + "ScriptLimits", + "SetTabIndex", + NULL, + NULL, + "FileAttributes", + "PlaceObject3", + "ImportAssets2", + NULL, + "DefineFontAlignZones", + "CSMTextSettings", + "DefineFont3", + "SymbolClass", + "Metadata", + "DefineScalingGrid", + NULL, + NULL, + NULL, + "DoABC", + "DefineShape4", + "DefineMorphShape2", + NULL, + "DefineSceneAndFrameLabelData", + "DefineBinaryData", + "DefineFontName", + "StartSound2", + "DefineBitsJPEG4", + "DefineFont4" +}; + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + const CTag &tag = _tags[index]; + switch(propID) + { + case kpidPath: + { + char s[32]; + ConvertUInt32ToString(index, s); + size_t i = strlen(s); + s[i++] = '.'; + ConvertUInt32ToString(tag.Type, s + i); + prop = s; + break; + } + case kpidSize: + case kpidPackSize: + prop = (UInt64)tag.Buf.GetCapacity(); break; + case kpidComment: + if (tag.Type < sizeof(g_TagDesc) / sizeof(g_TagDesc[0])) + { + const char *s = g_TagDesc[tag.Type]; + if (s != NULL) + prop = s; + } + break; + } + prop.Detach(value); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + return OpenSeq2(stream, callback); +} + +static UInt16 Read16(CInBuffer &stream) +{ + UInt16 res = 0; + for (int i = 0; i < 2; i++) + { + Byte b; + if (!stream.ReadByte(b)) + throw 1; + res |= (UInt16)b << (i * 8); + } + return res; +} + +static UInt32 Read32(CInBuffer &stream) +{ + UInt32 res = 0; + for (int i = 0; i < 4; i++) + { + Byte b; + if (!stream.ReadByte(b)) + throw 1; + res |= (UInt32)b << (i * 8); + } + return res; +} + +struct CBitReader +{ + CInBuffer *stream; + unsigned NumBits; + Byte Val; + + CBitReader(): NumBits(0), Val(0) {} + + UInt32 ReadBits(unsigned numBits); +}; + +UInt32 CBitReader::ReadBits(unsigned numBits) +{ + UInt32 res = 0; + while (numBits > 0) + { + if (NumBits == 0) + { + Val = stream->ReadByte(); + NumBits = 8; + } + if (numBits <= NumBits) + { + res <<= numBits; + NumBits -= numBits; + res |= (Val >> NumBits); + Val &= (1 << NumBits) - 1; + break; + } + else + { + res <<= NumBits; + res |= Val; + numBits -= NumBits; + NumBits = 0; + } + } + return res; +} + +HRESULT CHandler::OpenSeq3(ISequentialInStream *stream, IArchiveOpenCallback *callback) +{ + RINOK(_item.ReadHeader(stream)) + if (!_item.IsUncompressed()) + return S_FALSE; + + CInBuffer s; + if (!s.Create(1 << 20)) + return E_OUTOFMEMORY; + s.SetStream(stream); + s.Init(); + { + CBitReader br; + br.stream = &s; + unsigned numBits = br.ReadBits(5); + /* UInt32 xMin = */ br.ReadBits(numBits); + /* UInt32 xMax = */ br.ReadBits(numBits); + /* UInt32 yMin = */ br.ReadBits(numBits); + /* UInt32 yMax = */ br.ReadBits(numBits); + } + /* UInt32 frameDelay = */ Read16(s); + /* UInt32 numFrames = */ Read16(s); + + _tags.Clear(); + UInt64 offsetPrev = 0; + for (;;) + { + UInt32 pair = Read16(s); + UInt32 type = pair >> 6; + UInt32 length = pair & 0x3F; + if (length == 0x3F) + length = Read32(s); + if (type == 0) + break; + UInt64 offset = s.GetProcessedSize() + NSwfc::kHeaderSize + length; + if (offset > kFileSizeMax || _tags.Size() >= kNumTagsMax) + return S_FALSE; + _tags.Add(CTag()); + CTag &tag = _tags.Back(); + tag.Type = type; + tag.Buf.SetCapacity(length); + if (s.ReadBytes(tag.Buf, length) != length) + return S_FALSE; + if (callback && offset >= offsetPrev + (1 << 20)) + { + UInt64 numItems = _tags.Size(); + RINOK(callback->SetCompleted(&numItems, &offset)); + offsetPrev = offset; + } + } + _packSize = s.GetProcessedSize() + NSwfc::kHeaderSize; + return S_OK; +} + +HRESULT CHandler::OpenSeq2(ISequentialInStream *stream, IArchiveOpenCallback *callback) +{ + HRESULT res; + try { res = OpenSeq3(stream, callback); } + catch(...) { res = S_FALSE; } + return res; +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + return OpenSeq2(stream, NULL); +} + +STDMETHODIMP CHandler::Close() +{ + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _tags.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _tags[allFilesMode ? i : indices[i]].Buf.GetCapacity(); + extractCallback->SetTotal(totalSize); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + totalSize = 0; + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = totalSize; + RINOK(lps->SetCur()); + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + const CByteBuffer &buf = _tags[index].Buf; + totalSize += buf.GetCapacity(); + + CMyComPtr<ISequentialOutStream> outStream; + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + if (!testMode && !outStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + if (outStream) + RINOK(WriteStream(outStream, buf, buf.GetCapacity())); + outStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"SWF", L"swf", 0, 0xD7, { 'F', 'W', 'S' }, 3, true, CreateArc, 0 }; + +REGISTER_ARC(Swf) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.cpp new file mode 100644 index 000000000..4db0cae82 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.cpp @@ -0,0 +1,386 @@ +// TarHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../Common/ItemNameUtils.h" + +#include "TarHandler.h" +#include "TarIn.h" + +using namespace NWindows; + +namespace NArchive { +namespace NTar { + +static const char *kUnexpectedEnd = "Unexpected end of archive"; + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidPosixAttrib, VT_UI4}, + { NULL, kpidUser, VT_BSTR}, + { NULL, kpidGroup, VT_BSTR}, + { NULL, kpidLink, VT_BSTR} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + NCOM::CPropVariant prop; + switch(propID) + { + case kpidPhySize: if (_phySizeDefined) prop = _phySize; break; + case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break; + case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break; + } + prop.Detach(value); + return S_OK; +} + +HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item) +{ + item.HeaderPos = _phySize; + RINOK(ReadItem(stream, filled, item, _errorMessage)); + _phySize += item.HeaderSize; + _headersSize += item.HeaderSize; + return S_OK; +} + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) +{ + UInt64 endPos = 0; + { + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + + _phySizeDefined = true; + for (;;) + { + CItemEx item; + bool filled; + RINOK(ReadItem2(stream, filled, item)); + if (!filled) + break; + _items.Add(item); + + RINOK(stream->Seek(item.GetPackSize(), STREAM_SEEK_CUR, &_phySize)); + if (_phySize > endPos) + { + _errorMessage = kUnexpectedEnd; + break; + } + /* + if (_phySize == endPos) + { + _errorMessage = "There are no trailing zero-filled records"; + break; + } + */ + if (callback != NULL) + { + if (_items.Size() == 1) + { + RINOK(callback->SetTotal(NULL, &endPos)); + } + if (_items.Size() % 100 == 0) + { + UInt64 numFiles = _items.Size(); + RINOK(callback->SetCompleted(&numFiles, &_phySize)); + } + } + } + + if (_items.Size() == 0) + { + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + if (!callback) + return S_FALSE; + callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); + if (!openVolumeCallback) + return S_FALSE; + NCOM::CPropVariant prop; + if (openVolumeCallback->GetProperty(kpidName, &prop) != S_OK) + return S_FALSE; + if (prop.vt != VT_BSTR) + return S_FALSE; + UString baseName = prop.bstrVal; + baseName = baseName.Right(4); + if (baseName.CompareNoCase(L".tar") != 0) + return S_FALSE; + } + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + { + Close(); + RINOK(Open2(stream, openArchiveCallback)); + _stream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + _seqStream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + _errorMessage.Empty(); + _phySizeDefined = false; + _phySize = 0; + _headersSize = 0; + _curIndex = 0; + _latestIsRead = false; + _items.Clear(); + _seqStream.Release(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = (_stream ? _items.Size() : (UInt32)(Int32)-1); + return S_OK; +} + +CHandler::CHandler() +{ + copyCoderSpec = new NCompress::CCopyCoder(); + copyCoder = copyCoderSpec; +} + +HRESULT CHandler::SkipTo(UInt32 index) +{ + while (_curIndex < index || !_latestIsRead) + { + if (_latestIsRead) + { + UInt64 packSize = _latestItem.GetPackSize(); + RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL)); + _phySize += copyCoderSpec->TotalSize; + if (copyCoderSpec->TotalSize != packSize) + { + _errorMessage = kUnexpectedEnd; + return S_FALSE; + } + _latestIsRead = false; + _curIndex++; + } + else + { + bool filled; + RINOK(ReadItem2(_seqStream, filled, _latestItem)); + if (!filled) + { + _phySizeDefined = true; + return E_INVALIDARG; + } + _latestIsRead = true; + } + } + return S_OK; +} + +static UString TarStringToUnicode(const AString &s) +{ + return MultiByteToUnicodeString(s, CP_OEMCP); +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + const CItemEx *item; + if (_stream) + item = &_items[index]; + else + { + if (index < _curIndex) + return E_INVALIDARG; + else + { + RINOK(SkipTo(index)); + item = &_latestItem; + } + } + + switch(propID) + { + case kpidPath: prop = NItemName::GetOSName2(TarStringToUnicode(item->Name)); break; + case kpidIsDir: prop = item->IsDir(); break; + case kpidSize: prop = item->GetUnpackSize(); break; + case kpidPackSize: prop = item->GetPackSize(); break; + case kpidMTime: + if (item->MTime != 0) + { + FILETIME ft; + NTime::UnixTimeToFileTime(item->MTime, ft); + prop = ft; + } + break; + case kpidPosixAttrib: prop = item->Mode; break; + case kpidUser: prop = TarStringToUnicode(item->User); break; + case kpidGroup: prop = TarStringToUnicode(item->Group); break; + case kpidLink: prop = TarStringToUnicode(item->LinkName); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + ISequentialInStream *stream = _seqStream; + bool seqMode = (_stream == NULL); + if (!seqMode) + stream = _stream; + + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (_stream && numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize(); + extractCallback->SetTotal(totalSize); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(stream); + + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + for (i = 0; i < numItems || seqMode; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CItemEx *item; + if (seqMode) + { + HRESULT res = SkipTo(index); + if (res == E_INVALIDARG) + break; + RINOK(res); + item = &_latestItem; + } + else + item = &_items[index]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + UInt64 unpackSize = item->GetUnpackSize(); + totalSize += unpackSize; + totalPackSize += item->GetPackSize(); + if (item->IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + bool skipMode = false; + if (!testMode && !realOutStream) + { + if (!seqMode) + continue; + skipMode = true; + askMode = NExtract::NAskMode::kSkip; + } + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(skipMode ? 0 : unpackSize, true); + + if (item->IsLink()) + { + RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Length())); + } + else + { + if (!seqMode) + { + RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL)); + } + streamSpec->Init(item->GetPackSize()); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + } + if (seqMode) + { + _latestIsRead = false; + _curIndex++; + } + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(outStreamSpec->GetRem() == 0 ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CItemEx &item = _items[index]; + if (item.IsLink()) + { + CBufInStream *streamSpec = new CBufInStream; + CMyComPtr<IInStream> streamTemp = streamSpec; + streamSpec->Init((const Byte *)(const char *)item.LinkName, item.LinkName.Length(), (IInArchive *)this); + *stream = streamTemp.Detach(); + return S_OK; + } + return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream); + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.h new file mode 100644 index 000000000..b19670616 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandler.h @@ -0,0 +1,61 @@ +// TarHandler.h + +#ifndef __TAR_HANDLER_H +#define __TAR_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" + +#include "../../Compress/CopyCoder.h" + +#include "TarItem.h" + +namespace NArchive { +namespace NTar { + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + public IInArchiveGetStream, + public IOutArchive, + public CMyUnknownImp +{ + CObjectVector<CItemEx> _items; + CMyComPtr<IInStream> _stream; + CMyComPtr<ISequentialInStream> _seqStream; + + UInt32 _curIndex; + bool _latestIsRead; + CItemEx _latestItem; + + UInt64 _phySize; + UInt64 _headersSize; + bool _phySizeDefined; + AString _errorMessage; + + NCompress::CCopyCoder *copyCoderSpec; + CMyComPtr<ICompressCoder> copyCoder; + + HRESULT ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo); + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback); + HRESULT SkipTo(UInt32 index); + +public: + MY_UNKNOWN_IMP4( + IInArchive, + IArchiveOpenSeq, + IInArchiveGetStream, + IOutArchive + ) + + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + + CHandler(); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandlerOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandlerOut.cpp new file mode 100644 index 000000000..ffdf2b136 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHandlerOut.cpp @@ -0,0 +1,122 @@ +// TarHandlerOut.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "TarHandler.h" +#include "TarUpdate.h" + +using namespace NWindows; + +namespace NArchive { +namespace NTar { + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) +{ + *type = NFileTimeType::kUnix; + return S_OK; +} + +static HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId, AString &res) +{ + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(index, propId, &prop)); + if (prop.vt == VT_BSTR) + res = UnicodeStringToMultiByte(prop.bstrVal, CP_OEMCP); + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *callback) +{ + COM_TRY_BEGIN + if ((_stream && !_errorMessage.IsEmpty()) || _seqStream) + return E_NOTIMPL; + CObjectVector<CUpdateItem> updateItems; + for (UInt32 i = 0; i < numItems; i++) + { + CUpdateItem ui; + Int32 newData; + Int32 newProps; + UInt32 indexInArchive; + if (!callback) + return E_FAIL; + RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); + ui.NewProps = IntToBool(newProps); + ui.NewData = IntToBool(newData); + ui.IndexInArchive = indexInArchive; + ui.IndexInClient = i; + + if (IntToBool(newProps)) + { + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidIsDir, &prop)); + if (prop.vt == VT_EMPTY) + ui.IsDir = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + ui.IsDir = (prop.boolVal != VARIANT_FALSE); + } + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidPosixAttrib, &prop)); + if (prop.vt == VT_EMPTY) + ui.Mode = 0777 | (ui.IsDir ? 0040000 : 0100000); + else if (prop.vt != VT_UI4) + return E_INVALIDARG; + else + ui.Mode = prop.ulVal; + } + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidMTime, &prop)); + if (prop.vt == VT_EMPTY) + ui.Time = 0; + else if (prop.vt != VT_FILETIME) + return E_INVALIDARG; + else if (!NTime::FileTimeToUnixTime(prop.filetime, ui.Time)) + ui.Time = 0; + } + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidPath, &prop)); + if (prop.vt == VT_BSTR) + ui.Name = UnicodeStringToMultiByte(NItemName::MakeLegalName(prop.bstrVal), CP_OEMCP); + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + if (ui.IsDir) + ui.Name += '/'; + } + RINOK(GetPropString(callback, i, kpidUser, ui.User)); + RINOK(GetPropString(callback, i, kpidGroup, ui.Group)); + } + if (IntToBool(newData)) + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + ui.Size = prop.uhVal.QuadPart; + /* + // now we support GNU extension for big files + if (ui.Size >= ((UInt64)1 << 33)) + return E_INVALIDARG; + */ + } + updateItems.Add(ui); + } + return UpdateArchive(_stream, outStream, _items, updateItems, callback); + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.cpp new file mode 100644 index 000000000..3275b284c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.cpp @@ -0,0 +1,25 @@ +// Archive/Tar/Header.h + +#include "StdAfx.h" + +#include "TarHeader.h" + +namespace NArchive { +namespace NTar { +namespace NFileHeader { + + // The checksum field is filled with this while the checksum is computed. + const char *kCheckSumBlanks = " "; // 8 blanks, no null + + const char *kLongLink = "././@LongLink"; + const char *kLongLink2 = "@LongLink"; + + // The magic field is filled with this if uname and gname are valid. + namespace NMagic + { + const char *kUsTar = "ustar"; // 5 chars + const char *kGNUTar = "GNUtar "; // 7 chars and a null + const char *kEmpty = "\0\0\0\0\0\0\0\0"; // 7 chars and a null + } + +}}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.h new file mode 100644 index 000000000..0b78bdc26 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarHeader.h @@ -0,0 +1,108 @@ +// Archive/Tar/Header.h + +#ifndef __ARCHIVE_TAR_HEADER_H +#define __ARCHIVE_TAR_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NTar { + +namespace NFileHeader +{ + const int kRecordSize = 512; + const int kNameSize = 100; + const int kUserNameSize = 32; + const int kGroupNameSize = 32; + const int kPrefixSize = 155; + + /* + struct CHeader + { + char Name[kNameSize]; + char Mode[8]; + char UID[8]; + char GID[8]; + char Size[12]; + char ModificationTime[12]; + char CheckSum[8]; + char LinkFlag; + char LinkName[kNameSize]; + char Magic[8]; + char UserName[kUserNameSize]; + char GroupName[kGroupNameSize]; + char DeviceMajor[8]; + char DeviceMinor[8]; + char Prefix[155]; + }; + union CRecord + { + CHeader Header; + Byte Padding[kRecordSize]; + }; + */ + + namespace NMode + { + const int kSetUID = 04000; // Set UID on execution + const int kSetGID = 02000; // Set GID on execution + const int kSaveText = 01000; // Save text (sticky bit) + } + + namespace NFilePermissions + { + const int kUserRead = 00400; // read by owner + const int kUserWrite = 00200; // write by owner + const int kUserExecute = 00100; // execute/search by owner + const int kGroupRead = 00040; // read by group + const int kGroupWrite = 00020; // write by group + const int kGroupExecute = 00010; // execute/search by group + const int kOtherRead = 00004; // read by other + const int kOtherWrite = 00002; // write by other + const int kOtherExecute = 00001; // execute/search by other + } + + + // The linkflag defines the type of file + namespace NLinkFlag + { + const char kOldNormal = '\0'; // Normal disk file, Unix compatible + const char kNormal = '0'; // Normal disk file + const char kLink = '1'; // Link to previously dumped file + const char kSymbolicLink = '2'; // Symbolic link + const char kCharacter = '3'; // Character special file + const char kBlock = '4'; // Block special file + const char kDirectory = '5'; // Directory + const char kFIFO = '6'; // FIFO special file + const char kContiguous = '7'; // Contiguous file + + const char kDumpDir = 'D'; /* GNUTYPE_DUMPDIR. + data: list of files created by the --incremental (-G) option + Each file name is preceded by either + - 'Y' (file should be in this archive) + - 'N' (file is a directory, or is not stored in the archive.) + Each file name is terminated by a null + an additional null after + the last file name. */ + + } + // Further link types may be defined later. + + // The checksum field is filled with this while the checksum is computed. + extern const char *kCheckSumBlanks;// = " "; // 8 blanks, no null + + extern const char *kLongLink; // = "././@LongLink"; + extern const char *kLongLink2; // = "@LongLink"; + + // The magic field is filled with this if uname and gname are valid. + namespace NMagic + { + extern const char *kUsTar; // = "ustar"; // 5 chars + extern const char *kGNUTar; // = "GNUtar "; // 7 chars and a null + extern const char *kEmpty; // = "GNUtar "; // 7 chars and a null + } + +} + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.cpp new file mode 100644 index 000000000..5ceaa509d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.cpp @@ -0,0 +1,207 @@ +// TarIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/StringToInt.h" + +#include "../../Common/StreamUtils.h" + +#include "TarIn.h" + +namespace NArchive { +namespace NTar { + +static void MyStrNCpy(char *dest, const char *src, int size) +{ + for (int i = 0; i < size; i++) + { + char c = src[i]; + dest[i] = c; + if (c == 0) + break; + } +} + +static bool OctalToNumber(const char *srcString, int size, UInt64 &res) +{ + char sz[32]; + MyStrNCpy(sz, srcString, size); + sz[size] = 0; + const char *end; + int i; + for (i = 0; sz[i] == ' '; i++); + res = ConvertOctStringToUInt64(sz + i, &end); + return (*end == ' ' || *end == 0); +} + +static bool OctalToNumber32(const char *srcString, int size, UInt32 &res) +{ + UInt64 res64; + if (!OctalToNumber(srcString, size, res64)) + return false; + res = (UInt32)res64; + return (res64 <= 0xFFFFFFFF); +} + +#define RIF(x) { if (!(x)) return S_FALSE; } + +static bool IsRecordLast(const char *buf) +{ + for (int i = 0; i < NFileHeader::kRecordSize; i++) + if (buf[i] != 0) + return false; + return true; +} + +static void ReadString(const char *s, int size, AString &result) +{ + char temp[NFileHeader::kRecordSize + 1]; + MyStrNCpy(temp, s, size); + temp[size] = '\0'; + result = temp; +} + +static HRESULT GetNextItemReal(ISequentialInStream *stream, bool &filled, CItemEx &item, AString &error) +{ + char buf[NFileHeader::kRecordSize]; + char *p = buf; + + error.Empty(); + filled = false; + + bool thereAreEmptyRecords = false; + for (;;) + { + size_t processedSize = NFileHeader::kRecordSize; + RINOK(ReadStream(stream, buf, &processedSize)); + if (processedSize == 0) + { + if (!thereAreEmptyRecords ) + error = "There are no trailing zero-filled records"; + return S_OK; + } + if (processedSize != NFileHeader::kRecordSize) + { + error = "There is no correct record at the end of archive"; + return S_OK; + } + item.HeaderSize += NFileHeader::kRecordSize; + if (!IsRecordLast(buf)) + break; + thereAreEmptyRecords = true; + } + if (thereAreEmptyRecords) + { + error = "There are data after end of archive"; + return S_OK; + } + + ReadString(p, NFileHeader::kNameSize, item.Name); p += NFileHeader::kNameSize; + + RIF(OctalToNumber32(p, 8, item.Mode)); p += 8; + + if (!OctalToNumber32(p, 8, item.UID)) item.UID = 0; p += 8; + if (!OctalToNumber32(p, 8, item.GID)) item.GID = 0; p += 8; + + if (GetBe32(p) == (UInt32)1 << 31) + { + // GNU extension + item.Size = GetBe64(p + 4); + } + else + { + RIF(OctalToNumber(p, 12, item.Size)); + } + p += 12; + RIF(OctalToNumber32(p, 12, item.MTime)); p += 12; + + UInt32 checkSum; + RIF(OctalToNumber32(p, 8, checkSum)); + memcpy(p, NFileHeader::kCheckSumBlanks, 8); p += 8; + + item.LinkFlag = *p++; + + ReadString(p, NFileHeader::kNameSize, item.LinkName); p += NFileHeader::kNameSize; + + memcpy(item.Magic, p, 8); p += 8; + + ReadString(p, NFileHeader::kUserNameSize, item.User); p += NFileHeader::kUserNameSize; + ReadString(p, NFileHeader::kGroupNameSize, item.Group); p += NFileHeader::kGroupNameSize; + + item.DeviceMajorDefined = (p[0] != 0); RIF(OctalToNumber32(p, 8, item.DeviceMajor)); p += 8; + item.DeviceMinorDefined = (p[0] != 0); RIF(OctalToNumber32(p, 8, item.DeviceMinor)); p += 8; + + AString prefix; + ReadString(p, NFileHeader::kPrefixSize, prefix); + p += NFileHeader::kPrefixSize; + if (!prefix.IsEmpty() && item.IsMagic() && + (item.LinkFlag != 'L' /* || prefix != "00000000000" */ )) + item.Name = prefix + AString('/') + item.Name; + + if (item.LinkFlag == NFileHeader::NLinkFlag::kLink) + item.Size = 0; + + UInt32 checkSumReal = 0; + for (int i = 0; i < NFileHeader::kRecordSize; i++) + checkSumReal += (Byte)buf[i]; + + if (checkSumReal != checkSum) + return S_FALSE; + + filled = true; + return S_OK; +} + +HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &item, AString &error) +{ + item.HeaderSize = 0; + bool flagL = false; + bool flagK = false; + AString nameL; + AString nameK; + for (;;) + { + RINOK(GetNextItemReal(stream, filled, item, error)); + if (!filled) + return S_OK; + if (item.LinkFlag == 'L' || // NEXT file has a long name + item.LinkFlag == 'K') // NEXT file has a long linkname + { + AString *name; + if (item.LinkFlag == 'L') + { if (flagL) return S_FALSE; flagL = true; name = &nameL; } + else + { if (flagK) return S_FALSE; flagK = true; name = &nameK; } + + if (item.Name.Compare(NFileHeader::kLongLink) != 0 && + item.Name.Compare(NFileHeader::kLongLink2) != 0) + return S_FALSE; + if (item.Size > (1 << 14)) + return S_FALSE; + int packSize = (int)item.GetPackSize(); + char *buf = name->GetBuffer(packSize); + RINOK(ReadStream_FALSE(stream, buf, packSize)); + item.HeaderSize += packSize; + buf[(size_t)item.Size] = '\0'; + name->ReleaseBuffer(); + continue; + } + if (item.LinkFlag == 'g' || item.LinkFlag == 'x' || item.LinkFlag == 'X') + { + // pax Extended Header + } + else if (item.LinkFlag == NFileHeader::NLinkFlag::kDumpDir) + { + // GNU Extensions to the Archive Format + } + else if (item.LinkFlag > '7' || (item.LinkFlag < '0' && item.LinkFlag != 0)) + return S_FALSE; + if (flagL) item.Name = nameL; + if (flagK) item.LinkName = nameK; + return S_OK; + } +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.h new file mode 100644 index 000000000..a5491ebe4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarIn.h @@ -0,0 +1,17 @@ +// TarIn.h + +#ifndef __ARCHIVE_TAR_IN_H +#define __ARCHIVE_TAR_IN_H + +#include "../../IStream.h" + +#include "TarItem.h" + +namespace NArchive { +namespace NTar { + +HRESULT ReadItem(ISequentialInStream *stream, bool &filled, CItemEx &itemInfo, AString &error); + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarItem.h new file mode 100644 index 000000000..859e66dd8 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarItem.h @@ -0,0 +1,72 @@ +// TarItem.h + +#ifndef __ARCHIVE_TAR_ITEM_H +#define __ARCHIVE_TAR_ITEM_H + +#include "../Common/ItemNameUtils.h" + +#include "TarHeader.h" + +namespace NArchive { +namespace NTar { + +struct CItem +{ + AString Name; + UInt64 Size; + + UInt32 Mode; + UInt32 UID; + UInt32 GID; + UInt32 MTime; + UInt32 DeviceMajor; + UInt32 DeviceMinor; + + AString LinkName; + AString User; + AString Group; + + char Magic[8]; + char LinkFlag; + bool DeviceMajorDefined; + bool DeviceMinorDefined; + + bool IsLink() const { return LinkFlag == NFileHeader::NLinkFlag::kSymbolicLink && (Size == 0); } + UInt64 GetUnpackSize() const { return IsLink() ? LinkName.Length() : Size; } + + bool IsDir() const + { + switch(LinkFlag) + { + case NFileHeader::NLinkFlag::kDirectory: + case NFileHeader::NLinkFlag::kDumpDir: + return true; + case NFileHeader::NLinkFlag::kOldNormal: + case NFileHeader::NLinkFlag::kNormal: + return NItemName::HasTailSlash(Name, CP_OEMCP); + } + return false; + } + + bool IsMagic() const + { + for (int i = 0; i < 5; i++) + if (Magic[i] != NFileHeader::NMagic::kUsTar[i]) + return false; + return true; + } + + UInt64 GetPackSize() const { return (Size + 0x1FF) & (~((UInt64)0x1FF)); } +}; + +struct CItemEx: public CItem +{ + UInt64 HeaderPos; + unsigned HeaderSize; + UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; } + UInt64 GetFullSize() const { return HeaderSize + Size; } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.cpp new file mode 100644 index 000000000..e542a3b2f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.cpp @@ -0,0 +1,187 @@ +// TarOut.cpp + +#include "StdAfx.h" + +#include "Common/IntToString.h" + +#include "../../Common/StreamUtils.h" + +#include "TarOut.h" + +namespace NArchive { +namespace NTar { + +HRESULT COutArchive::WriteBytes(const void *buffer, UInt32 size) +{ + return WriteStream(m_Stream, buffer, size); +} + +void COutArchive::Create(ISequentialOutStream *outStream) +{ + m_Stream = outStream; +} + +static AString MakeOctalString(UInt64 value) +{ + char s[32]; + ConvertUInt64ToString(value, s, 8); + return AString(s) + ' '; +} + +static void MyStrNCpy(char *dest, const char *src, int size) +{ + for (int i = 0; i < size; i++) + { + char c = src[i]; + dest[i] = c; + if (c == 0) + break; + } +} + +static bool MakeOctalString8(char *s, UInt32 value) +{ + AString tempString = MakeOctalString(value); + + const int kMaxSize = 8; + if (tempString.Length() >= kMaxSize) + return false; + int numSpaces = kMaxSize - (tempString.Length() + 1); + for(int i = 0; i < numSpaces; i++) + s[i] = ' '; + MyStringCopy(s + numSpaces, (const char *)tempString); + return true; +} + +static void MakeOctalString12(char *s, UInt64 value) +{ + AString tempString = MakeOctalString(value); + const int kMaxSize = 12; + if (tempString.Length() > kMaxSize) + { + // GNU extension; + s[0] = (char)(Byte)0x80; + s[1] = s[2] = s[3] = 0; + for (int i = 0; i < 8; i++, value <<= 8) + s[4 + i] = (char)(value >> 56); + return; + } + int numSpaces = kMaxSize - tempString.Length(); + for(int i = 0; i < numSpaces; i++) + s[i] = ' '; + memmove(s + numSpaces, (const char *)tempString, tempString.Length()); +} + +static bool CopyString(char *dest, const AString &src, int maxSize) +{ + if (src.Length() >= maxSize) + return false; + MyStringCopy(dest, (const char *)src); + return true; +} + +#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; } + +HRESULT COutArchive::WriteHeaderReal(const CItem &item) +{ + char record[NFileHeader::kRecordSize]; + char *cur = record; + int i; + for (i = 0; i < NFileHeader::kRecordSize; i++) + record[i] = 0; + + // RETURN_IF_NOT_TRUE(CopyString(header.Name, item.Name, NFileHeader::kNameSize)); + if (item.Name.Length() > NFileHeader::kNameSize) + return E_FAIL; + MyStrNCpy(cur, item.Name, NFileHeader::kNameSize); + cur += NFileHeader::kNameSize; + + RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.Mode)); cur += 8; + RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.UID)); cur += 8; + RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.GID)); cur += 8; + + MakeOctalString12(cur, item.Size); cur += 12; + MakeOctalString12(cur, item.MTime); cur += 12; + + memmove(cur, NFileHeader::kCheckSumBlanks, 8); + cur += 8; + + *cur++ = item.LinkFlag; + + RETURN_IF_NOT_TRUE(CopyString(cur, item.LinkName, NFileHeader::kNameSize)); + cur += NFileHeader::kNameSize; + + memmove(cur, item.Magic, 8); + cur += 8; + + RETURN_IF_NOT_TRUE(CopyString(cur, item.User, NFileHeader::kUserNameSize)); + cur += NFileHeader::kUserNameSize; + RETURN_IF_NOT_TRUE(CopyString(cur, item.Group, NFileHeader::kGroupNameSize)); + cur += NFileHeader::kGroupNameSize; + + + if (item.DeviceMajorDefined) + RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.DeviceMajor)); + cur += 8; + + if (item.DeviceMinorDefined) + RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.DeviceMinor)); + cur += 8; + + + UInt32 checkSumReal = 0; + for(i = 0; i < NFileHeader::kRecordSize; i++) + checkSumReal += Byte(record[i]); + + RETURN_IF_NOT_TRUE(MakeOctalString8(record + 148, checkSumReal)); + + return WriteBytes(record, NFileHeader::kRecordSize); +} + +HRESULT COutArchive::WriteHeader(const CItem &item) +{ + int nameSize = item.Name.Length(); + if (nameSize < NFileHeader::kNameSize) + return WriteHeaderReal(item); + + CItem modifiedItem = item; + int nameStreamSize = nameSize + 1; + modifiedItem.Size = nameStreamSize; + modifiedItem.LinkFlag = 'L'; + modifiedItem.Name = NFileHeader::kLongLink; + modifiedItem.LinkName.Empty(); + RINOK(WriteHeaderReal(modifiedItem)); + RINOK(WriteBytes(item.Name, nameStreamSize)); + RINOK(FillDataResidual(nameStreamSize)); + + modifiedItem = item; + modifiedItem.Name = item.Name.Left(NFileHeader::kNameSize - 1); + return WriteHeaderReal(modifiedItem); +} + +HRESULT COutArchive::FillDataResidual(UInt64 dataSize) +{ + UInt32 lastRecordSize = UInt32(dataSize & (NFileHeader::kRecordSize - 1)); + if (lastRecordSize == 0) + return S_OK; + UInt32 residualSize = NFileHeader::kRecordSize - lastRecordSize; + Byte residualBytes[NFileHeader::kRecordSize]; + for (UInt32 i = 0; i < residualSize; i++) + residualBytes[i] = 0; + return WriteBytes(residualBytes, residualSize); +} + +HRESULT COutArchive::WriteFinishHeader() +{ + Byte record[NFileHeader::kRecordSize]; + int i; + for (i = 0; i < NFileHeader::kRecordSize; i++) + record[i] = 0; + for (i = 0; i < 2; i++) + { + RINOK(WriteBytes(record, NFileHeader::kRecordSize)); + } + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.h new file mode 100644 index 000000000..ef837869b --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarOut.h @@ -0,0 +1,28 @@ +// Archive/TarOut.h + +#ifndef __ARCHIVE_TAR_OUT_H +#define __ARCHIVE_TAR_OUT_H + +#include "TarItem.h" + +#include "Common/MyCom.h" +#include "../../IStream.h" + +namespace NArchive { +namespace NTar { + +class COutArchive +{ + CMyComPtr<ISequentialOutStream> m_Stream; + HRESULT WriteBytes(const void *buffer, UInt32 size); +public: + void Create(ISequentialOutStream *outStream); + HRESULT WriteHeaderReal(const CItem &item); + HRESULT WriteHeader(const CItem &item); + HRESULT FillDataResidual(UInt64 dataSize); + HRESULT WriteFinishHeader(); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarRegister.cpp new file mode 100644 index 000000000..e21c0aac4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarRegister.cpp @@ -0,0 +1,18 @@ +// TarRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "TarHandler.h" +static IInArchive *CreateArc() { return new NArchive::NTar::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::NTar::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = +{ L"tar", L"tar", 0, 0xEE, { 'u', 's', 't', 'a', 'r' }, 5, false, CreateArc, CreateArcOut }; + +REGISTER_ARC(Tar) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.cpp new file mode 100644 index 000000000..c16332189 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.cpp @@ -0,0 +1,139 @@ +// TarUpdate.cpp + +#include "StdAfx.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "TarOut.h" +#include "TarUpdate.h" + +namespace NArchive { +namespace NTar { + +HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, + const CObjectVector<NArchive::NTar::CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + IArchiveUpdateCallback *updateCallback) +{ + COutArchive outArchive; + outArchive.Create(outStream); + + UInt64 complexity = 0; + + int i; + for(i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (ui.NewData) + complexity += ui.Size; + else + complexity += inputItems[ui.IndexInArchive].GetFullSize(); + } + + RINOK(updateCallback->SetTotal(complexity)); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec); + streamSpec->SetStream(inStream); + + complexity = 0; + + for(i = 0; i < updateItems.Size(); i++) + { + lps->InSize = lps->OutSize = complexity; + RINOK(lps->SetCur()); + + const CUpdateItem &ui = updateItems[i]; + CItem item; + if (ui.NewProps) + { + item.Mode = ui.Mode; + item.Name = ui.Name; + item.User = ui.User; + item.Group = ui.Group; + if (ui.IsDir) + { + item.LinkFlag = NFileHeader::NLinkFlag::kDirectory; + item.Size = 0; + } + else + { + item.LinkFlag = NFileHeader::NLinkFlag::kNormal; + item.Size = ui.Size; + } + item.MTime = ui.Time; + item.DeviceMajorDefined = false; + item.DeviceMinorDefined = false; + item.UID = 0; + item.GID = 0; + memmove(item.Magic, NFileHeader::NMagic::kEmpty, 8); + } + else + item = inputItems[ui.IndexInArchive]; + + if (ui.NewData) + { + item.Size = ui.Size; + if (item.Size == (UInt64)(Int64)-1) + return E_INVALIDARG; + } + else + item.Size = inputItems[ui.IndexInArchive].Size; + + if (ui.NewData) + { + CMyComPtr<ISequentialInStream> fileInStream; + HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); + if (res != S_FALSE) + { + RINOK(res); + RINOK(outArchive.WriteHeader(item)); + if (!ui.IsDir) + { + RINOK(copyCoder->Code(fileInStream, outStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize != item.Size) + return E_FAIL; + RINOK(outArchive.FillDataResidual(item.Size)); + } + } + complexity += ui.Size; + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + } + else + { + const CItemEx &existItem = inputItems[ui.IndexInArchive]; + UInt64 size; + if (ui.NewProps) + { + RINOK(outArchive.WriteHeader(item)); + RINOK(inStream->Seek(existItem.GetDataPosition(), STREAM_SEEK_SET, NULL)); + size = existItem.Size; + } + else + { + RINOK(inStream->Seek(existItem.HeaderPos, STREAM_SEEK_SET, NULL)); + size = existItem.GetFullSize(); + } + streamSpec->Init(size); + + RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize != size) + return E_FAIL; + RINOK(outArchive.FillDataResidual(existItem.Size)); + complexity += size; + } + } + return outArchive.WriteFinishHeader(); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.h b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.h new file mode 100644 index 000000000..fb217d196 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Tar/TarUpdate.h @@ -0,0 +1,34 @@ +// TarUpdate.h + +#ifndef __TAR_UPDATE_H +#define __TAR_UPDATE_H + +#include "../IArchive.h" +#include "TarItem.h" + +namespace NArchive { +namespace NTar { + +struct CUpdateItem +{ + int IndexInArchive; + int IndexInClient; + UInt32 Time; + UInt32 Mode; + UInt64 Size; + AString Name; + AString User; + AString Group; + bool NewData; + bool NewProps; + bool IsDir; +}; + +HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream, + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + IArchiveUpdateCallback *updateCallback); + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.cpp new file mode 100644 index 000000000..c70852728 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.cpp @@ -0,0 +1,451 @@ +// UdfHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" + +#include "../../Compress/CopyCoder.h" + +#include "UdfHandler.h" + +namespace NArchive { +namespace NUdf { + +void UdfTimeToFileTime(const CTime &t, NWindows::NCOM::CPropVariant &prop) +{ + UInt64 numSecs; + const Byte *d = t.Data; + if (!NWindows::NTime::GetSecondsSince1601(t.GetYear(), d[4], d[5], d[6], d[7], d[8], numSecs)) + return; + if (t.IsLocal()) + numSecs -= t.GetMinutesOffset() * 60; + FILETIME ft; + UInt64 v = (((numSecs * 100 + d[9]) * 100 + d[10]) * 100 + d[11]) * 10; + ft.dwLowDateTime = (UInt32)v; + ft.dwHighDateTime = (UInt32)(v >> 32); + prop = ft; +} + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME} +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidCTime, VT_FILETIME} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidComment: + { + UString comment = _archive.GetComment(); + if (!comment.IsEmpty()) + prop = comment; + break; + } + + case kpidClusterSize: + if (_archive.LogVols.Size() > 0) + { + UInt32 blockSize = _archive.LogVols[0].BlockSize; + int i; + for (i = 1; i < _archive.LogVols.Size(); i++) + if (_archive.LogVols[i].BlockSize != blockSize) + break; + if (i == _archive.LogVols.Size()) + prop = blockSize; + } + break; + + case kpidCTime: + if (_archive.LogVols.Size() == 1) + { + const CLogVol &vol = _archive.LogVols[0]; + if (vol.FileSets.Size() >= 1) + UdfTimeToFileTime(vol.FileSets[0].RecodringTime, prop); + } + break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> _callback; + UInt64 _numFiles; + UInt64 _numBytes; +public: + HRESULT SetTotal(UInt64 numBytes); + HRESULT SetCompleted(UInt64 numFiles, UInt64 numBytes); + HRESULT SetCompleted(); + CProgressImp(IArchiveOpenCallback *callback): _callback(callback), _numFiles(0), _numBytes(0) {} +}; + +HRESULT CProgressImp::SetTotal(UInt64 numBytes) +{ + if (_callback) + return _callback->SetTotal(NULL, &numBytes); + return S_OK; +} + +HRESULT CProgressImp::SetCompleted(UInt64 numFiles, UInt64 numBytes) +{ + _numFiles = numFiles; + _numBytes = numBytes; + return SetCompleted(); +} + +HRESULT CProgressImp::SetCompleted() +{ + if (_callback) + return _callback->SetCompleted(&_numFiles, &_numBytes); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + Close(); + CProgressImp progressImp(callback); + RINOK(_archive.Open(stream, &progressImp)); + bool showVolName = (_archive.LogVols.Size() > 1); + for (int volIndex = 0; volIndex < _archive.LogVols.Size(); volIndex++) + { + const CLogVol &vol = _archive.LogVols[volIndex]; + bool showFileSetName = (vol.FileSets.Size() > 1); + for (int fsIndex = 0; fsIndex < vol.FileSets.Size(); fsIndex++) + { + const CFileSet &fs = vol.FileSets[fsIndex]; + for (int i = ((showVolName || showFileSetName) ? 0 : 1); i < fs.Refs.Size(); i++) + { + CRef2 ref2; + ref2.Vol = volIndex; + ref2.Fs = fsIndex; + ref2.Ref = i; + _refs2.Add(ref2); + } + } + } + _inStream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _archive.Clear(); + _refs2.Clear(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _refs2.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + { + const CRef2 &ref2 = _refs2[index]; + const CLogVol &vol = _archive.LogVols[ref2.Vol]; + const CRef &ref = vol.FileSets[ref2.Fs].Refs[ref2.Ref]; + const CFile &file = _archive.Files[ref.FileIndex]; + const CItem &item = _archive.Items[file.ItemIndex]; + switch(propID) + { + case kpidPath: prop = _archive.GetItemPath(ref2.Vol, ref2.Fs, ref2.Ref, + _archive.LogVols.Size() > 1, vol.FileSets.Size() > 1); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: if (!item.IsDir()) prop = (UInt64)item.Size; break; + case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumLogBlockRecorded * vol.BlockSize; break; + case kpidMTime: UdfTimeToFileTime(item.MTime, prop); break; + case kpidATime: UdfTimeToFileTime(item.ATime, prop); break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +struct CSeekExtent +{ + UInt64 Phy; + UInt64 Virt; +}; + +class CExtentsStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _phyPos; + UInt64 _virtPos; + bool _needStartSeek; + + HRESULT SeekToPhys() { return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL); } + +public: + CMyComPtr<IInStream> Stream; + CRecordVector<CSeekExtent> Extents; + + MY_UNKNOWN_IMP1(IInStream) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + void ReleaseStream() { Stream.Release(); } + + void Init() + { + _virtPos = 0; + _phyPos = 0; + _needStartSeek = true; + } + +}; + + +STDMETHODIMP CExtentsStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size > 0) + { + UInt64 totalSize = Extents.Back().Virt; + if (_virtPos >= totalSize) + return (_virtPos == totalSize) ? S_OK : E_FAIL; + int left = 0, right = Extents.Size() - 1; + for (;;) + { + int mid = (left + right) / 2; + if (mid == left) + break; + if (_virtPos < Extents[mid].Virt) + right = mid; + else + left = mid; + } + + const CSeekExtent &extent = Extents[left]; + UInt64 phyPos = extent.Phy + (_virtPos - extent.Virt); + if (_needStartSeek || _phyPos != phyPos) + { + _needStartSeek = false; + _phyPos = phyPos; + RINOK(SeekToPhys()); + } + + UInt64 rem = Extents[left + 1].Virt - _virtPos; + if (size > rem) + size = (UInt32)rem; + + HRESULT res = Stream->Read(data, size, &size); + _phyPos += size; + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; + } + return S_OK; +} + +STDMETHODIMP CExtentsStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = Extents.Back().Virt + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + *stream = 0; + + const CRef2 &ref2 = _refs2[index]; + const CLogVol &vol = _archive.LogVols[ref2.Vol]; + const CRef &ref = vol.FileSets[ref2.Fs].Refs[ref2.Ref]; + const CFile &file = _archive.Files[ref.FileIndex]; + const CItem &item = _archive.Items[file.ItemIndex]; + UInt64 size = item.Size; + + if (!item.IsRecAndAlloc() || !item.CheckChunkSizes() || ! _archive.CheckItemExtents(ref2.Vol, item)) + return E_NOTIMPL; + + if (item.IsInline) + { + CBufInStream *inStreamSpec = new CBufInStream; + CMyComPtr<ISequentialInStream> inStream = inStreamSpec; + CReferenceBuf *referenceBuf = new CReferenceBuf; + CMyComPtr<IUnknown> ref = referenceBuf; + referenceBuf->Buf = item.InlineData; + inStreamSpec->Init(referenceBuf); + *stream = inStream.Detach(); + return S_OK; + } + + CExtentsStream *extentStreamSpec = new CExtentsStream(); + CMyComPtr<ISequentialInStream> extentStream = extentStreamSpec; + + extentStreamSpec->Stream = _inStream; + + UInt64 virtOffset = 0; + for (int extentIndex = 0; extentIndex < item.Extents.Size(); extentIndex++) + { + const CMyExtent &extent = item.Extents[extentIndex]; + UInt32 len = extent.GetLen(); + if (len == 0) + continue; + if (size < len) + return S_FALSE; + + int partitionIndex = vol.PartitionMaps[extent.PartitionRef].PartitionIndex; + UInt32 logBlockNumber = extent.Pos; + const CPartition &partition = _archive.Partitions[partitionIndex]; + UInt64 offset = ((UInt64)partition.Pos << _archive.SecLogSize) + + (UInt64)logBlockNumber * vol.BlockSize; + + CSeekExtent se; + se.Phy = offset; + se.Virt = virtOffset; + virtOffset += len; + extentStreamSpec->Extents.Add(se); + + size -= len; + } + if (size != 0) + return S_FALSE; + CSeekExtent se; + se.Phy = 0; + se.Virt = virtOffset; + extentStreamSpec->Extents.Add(se); + extentStreamSpec->Init(); + *stream = extentStream.Detach(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _refs2.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + + for (i = 0; i < numItems; i++) + { + UInt32 index = (allFilesMode ? i : indices[i]); + const CRef2 &ref2 = _refs2[index]; + const CRef &ref = _archive.LogVols[ref2.Vol].FileSets[ref2.Fs].Refs[ref2.Ref]; + const CFile &file = _archive.Files[ref.FileIndex]; + const CItem &item = _archive.Items[file.ItemIndex]; + if (!item.IsDir()) + totalSize += item.Size; + } + extractCallback->SetTotal(totalSize); + + UInt64 currentTotalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + for (i = 0; i < numItems; i++) + { + lps->InSize = lps->OutSize = currentTotalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + UInt32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + const CRef2 &ref2 = _refs2[index]; + const CRef &ref = _archive.LogVols[ref2.Vol].FileSets[ref2.Fs].Refs[ref2.Ref]; + const CFile &file = _archive.Files[ref.FileIndex]; + const CItem &item = _archive.Items[file.ItemIndex]; + + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + currentTotalSize += item.Size; + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(item.Size); + Int32 opRes; + CMyComPtr<ISequentialInStream> udfInStream; + HRESULT res = GetStream(index, &udfInStream); + if (res == E_NOTIMPL) + opRes = NExtract::NOperationResult::kUnSupportedMethod; + else if (res != S_OK) + opRes = NExtract::NOperationResult::kDataError; + else + { + RINOK(copyCoder->Code(udfInStream, outStream, NULL, NULL, progress)); + opRes = outStreamSpec->IsFinishedOK() ? + NExtract::NOperationResult::kOK: + NExtract::NOperationResult::kDataError; + } + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + return S_OK; + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.h new file mode 100644 index 000000000..f513727d7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfHandler.h @@ -0,0 +1,37 @@ +// Udf/Handler.h + +#ifndef __UDF_HANDLER_H +#define __UDF_HANDLER_H + +#include "Common/MyCom.h" +#include "../IArchive.h" + +#include "UdfIn.h" + +namespace NArchive { +namespace NUdf { + +struct CRef2 +{ + int Vol; + int Fs; + int Ref; +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _inStream; + CInArchive _archive; + CRecordVector<CRef2> _refs2; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.cpp new file mode 100644 index 000000000..60d5fc2ad --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.cpp @@ -0,0 +1,876 @@ +// Archive/UdfIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "../../Common/StreamUtils.h" + +#include "UdfIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +namespace NArchive { +namespace NUdf { + +const int kNumPartitionsMax = 64; +const int kNumLogVolumesMax = 64; +const int kNumRecureseLevelsMax = 1 << 10; +const int kNumItemsMax = 1 << 27; +const int kNumFilesMax = 1 << 28; +const int kNumRefsMax = 1 << 28; +const UInt32 kNumExtentsMax = (UInt32)1 << 30; +const UInt64 kFileNameLengthTotalMax = (UInt64)1 << 33; +const UInt64 kInlineExtentsSizeMax = (UInt64)1 << 33; + +void MY_FAST_CALL Crc16GenerateTable(void); + +#define CRC16_INIT_VAL 0 +#define CRC16_GET_DIGEST(crc) (crc) +#define CRC16_UPDATE_BYTE(crc, b) (g_Crc16Table[(((crc) >> 8) ^ (b)) & 0xFF] ^ ((crc) << 8)) + +#define kCrc16Poly 0x1021 +UInt16 g_Crc16Table[256]; + +void MY_FAST_CALL Crc16GenerateTable(void) +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = (i << 8); + for (int j = 8; j > 0; j--) + r = ((r & 0x8000) ? ((r << 1) ^ kCrc16Poly) : (r << 1)) & 0xFFFF; + g_Crc16Table[i] = (UInt16)r; + } +} + +UInt16 MY_FAST_CALL Crc16_Update(UInt16 v, const void *data, size_t size) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 ; size--, p++) + v = CRC16_UPDATE_BYTE(v, *p); + return v; +} + +UInt16 MY_FAST_CALL Crc16Calc(const void *data, size_t size) +{ + return Crc16_Update(CRC16_INIT_VAL, data, size); +} + +struct CCrc16TableInit { CCrc16TableInit() { Crc16GenerateTable(); } } g_Crc16TableInit; + +void CDString128::Parse(const Byte *buf) { memcpy(Data, buf, sizeof(Data)); } + +void CDString::Parse(const Byte *p, unsigned size) +{ + Data.SetCapacity(size); + memcpy(Data, p, size); +} + +static UString ParseDString(const Byte *data, int size) +{ + UString res; + wchar_t *p; + if (size > 0) + { + Byte type = data[0]; + if (type == 8) + { + p = res.GetBuffer((int)size + 1); + for (int i = 1; i < size; i++) + { + wchar_t c = data[i]; + if (c == 0) + break; + *p++ = c; + } + } + else if (type == 16) + { + p = res.GetBuffer((int)size / 2 + 1); + for (int i = 1; i + 2 <= size; i += 2) + { + wchar_t c = ((wchar_t)data[i] << 8) | data[i + 1]; + if (c == 0) + break; + *p++ = c; + } + } + else + return L"[unknow]"; + *p++ = 0; + res.ReleaseBuffer(); + } + return res; +} + +UString CDString:: GetString() const { return ParseDString(Data, (int)Data.GetCapacity()); } +UString CDString128::GetString() const +{ + int size = Data[sizeof(Data) - 1]; + return ParseDString(Data, MyMin(size, (int)(sizeof(Data) - 1))); +} + +void CTime::Parse(const Byte *buf) { memcpy(Data, buf, sizeof(Data)); } + +/* +void CRegId::Parse(const Byte *buf) +{ + Flags = buf[0]; + memcpy(Id, buf + 1, sizeof(Id)); + memcpy(Suffix, buf + 24, sizeof(Suffix)); +} +*/ + +// ECMA 3/7.1 + +struct CExtent +{ + UInt32 Len; + UInt32 Pos; + + void Parse(const Byte *buf); +}; + +void CExtent::Parse(const Byte *buf) +{ + Len = Get32(buf); + Pos = Get32(buf + 4); +} + +// ECMA 3/7.2 + +struct CTag +{ + UInt16 Id; + UInt16 Version; + // Byte Checksum; + // UInt16 SerialNumber; + // UInt16 Crc; + // UInt16 CrcLen; + // UInt32 TagLocation; + + HRESULT Parse(const Byte *buf, size_t size); +}; + +HRESULT CTag::Parse(const Byte *buf, size_t size) +{ + if (size < 16) + return S_FALSE; + Byte sum = 0; + int i; + for (i = 0; i < 4; i++) sum = sum + buf[i]; + for (i = 5; i < 16; i++) sum = sum + buf[i]; + if (sum != buf[4] || buf[5] != 0) return S_FALSE; + + Id = Get16(buf); + Version = Get16(buf + 2); + // SerialNumber = Get16(buf + 6); + UInt16 crc = Get16(buf + 8); + UInt16 crcLen = Get16(buf + 10); + // TagLocation = Get32(buf + 12); + + if (size >= 16 + (size_t)crcLen) + if (crc == Crc16Calc(buf + 16, crcLen)) + return S_OK; + return S_FALSE; +} + +// ECMA 3/7.2.1 + +enum EDescriptorType +{ + DESC_TYPE_SpoaringTable = 0, // UDF + DESC_TYPE_PrimVol = 1, + DESC_TYPE_AnchorVolPtr = 2, + DESC_TYPE_VolPtr = 3, + DESC_TYPE_ImplUseVol = 4, + DESC_TYPE_Partition = 5, + DESC_TYPE_LogicalVol = 6, + DESC_TYPE_UnallocSpace = 7, + DESC_TYPE_Terminating = 8, + DESC_TYPE_LogicalVolIntegrity = 9, + DESC_TYPE_FileSet = 256, + DESC_TYPE_FileId = 257, + DESC_TYPE_AllocationExtent = 258, + DESC_TYPE_Indirect = 259, + DESC_TYPE_Terminal = 260, + DESC_TYPE_File = 261, + DESC_TYPE_ExtendedAttrHeader = 262, + DESC_TYPE_UnallocatedSpace = 263, + DESC_TYPE_SpaceBitmap = 264, + DESC_TYPE_PartitionIntegrity = 265, + DESC_TYPE_ExtendedFile = 266 +}; + + +void CLogBlockAddr::Parse(const Byte *buf) +{ + Pos = Get32(buf); + PartitionRef = Get16(buf + 4); +} + +void CShortAllocDesc::Parse(const Byte *buf) +{ + Len = Get32(buf); + Pos = Get32(buf + 4); +} + +/* +void CADImpUse::Parse(const Byte *buf) +{ + Flags = Get16(buf); + UdfUniqueId = Get32(buf + 2); +} +*/ + +void CLongAllocDesc::Parse(const Byte *buf) +{ + Len = Get32(buf); + Location.Parse(buf + 4); + // memcpy(ImplUse, buf + 10, sizeof(ImplUse)); + // adImpUse.Parse(ImplUse); +} + +bool CInArchive::CheckExtent(int volIndex, int partitionRef, UInt32 blockPos, UInt32 len) const +{ + const CLogVol &vol = LogVols[volIndex]; + const CPartition &partition = Partitions[vol.PartitionMaps[partitionRef].PartitionIndex]; + UInt64 offset = ((UInt64)partition.Pos << SecLogSize) + (UInt64)blockPos * vol.BlockSize; + return (offset + len) <= (((UInt64)partition.Pos + partition.Len) << SecLogSize); +} + +bool CInArchive::CheckItemExtents(int volIndex, const CItem &item) const +{ + for (int i = 0; i < item.Extents.Size(); i++) + { + const CMyExtent &e = item.Extents[i]; + if (!CheckExtent(volIndex, e.PartitionRef, e.Pos, e.GetLen())) + return false; + } + return true; +} + +HRESULT CInArchive::Read(int volIndex, int partitionRef, UInt32 blockPos, UInt32 len, Byte *buf) +{ + if (!CheckExtent(volIndex, partitionRef, blockPos, len)) + return S_FALSE; + const CLogVol &vol = LogVols[volIndex]; + const CPartition &partition = Partitions[vol.PartitionMaps[partitionRef].PartitionIndex]; + RINOK(_stream->Seek(((UInt64)partition.Pos << SecLogSize) + + (UInt64)blockPos * vol.BlockSize, STREAM_SEEK_SET, NULL)); + return ReadStream_FALSE(_stream, buf, len); +} + +HRESULT CInArchive::Read(int volIndex, const CLongAllocDesc &lad, Byte *buf) +{ + return Read(volIndex, lad.Location.PartitionRef, lad.Location.Pos, lad.GetLen(), (Byte *)buf); +} + +HRESULT CInArchive::ReadFromFile(int volIndex, const CItem &item, CByteBuffer &buf) +{ + if (item.Size >= (UInt32)1 << 30) + return S_FALSE; + if (item.IsInline) + { + buf = item.InlineData; + return S_OK; + } + buf.SetCapacity((size_t)item.Size); + size_t pos = 0; + for (int i = 0; i < item.Extents.Size(); i++) + { + const CMyExtent &e = item.Extents[i]; + UInt32 len = e.GetLen(); + RINOK(Read(volIndex, e.PartitionRef, e.Pos, len, (Byte *)buf + pos)); + pos += len; + } + return S_OK; +} + + +void CIcbTag::Parse(const Byte *p) +{ + // PriorDirectNum = Get32(p); + // StrategyType = Get16(p + 4); + // StrategyParam = Get16(p + 6); + // MaxNumOfEntries = Get16(p + 8); + FileType = p[11]; + // ParentIcb.Parse(p + 12); + Flags = Get16(p + 18); +} + +void CItem::Parse(const Byte *p) +{ + // Uid = Get32(p + 36); + // Gid = Get32(p + 40); + // Permissions = Get32(p + 44); + // FileLinkCount = Get16(p + 48); + // RecordFormat = p[50]; + // RecordDisplayAttr = p[51]; + // RecordLen = Get32(p + 52); + Size = Get64(p + 56); + NumLogBlockRecorded = Get64(p + 64); + ATime.Parse(p + 72); + MTime.Parse(p + 84); + // AttrtTime.Parse(p + 96); + // CheckPoint = Get32(p + 108); + // ExtendedAttrIcb.Parse(p + 112); + // ImplId.Parse(p + 128); + // UniqueId = Get64(p + 160); +} + +// 4/14.4 +struct CFileId +{ + // UInt16 FileVersion; + Byte FileCharacteristics; + // CByteBuffer ImplUse; + CDString Id; + CLongAllocDesc Icb; + + bool IsItLinkParent() const { return (FileCharacteristics & FILEID_CHARACS_Parent) != 0; } + HRESULT Parse(const Byte *p, size_t size, size_t &processed); +}; + +HRESULT CFileId::Parse(const Byte *p, size_t size, size_t &processed) +{ + processed = 0; + if (size < 38) + return S_FALSE; + CTag tag; + RINOK(tag.Parse(p, size)); + if (tag.Id != DESC_TYPE_FileId) + return S_FALSE; + // FileVersion = Get16(p + 16); + FileCharacteristics = p[18]; + unsigned idLen = p[19]; + Icb.Parse(p + 20); + unsigned impLen = Get16(p + 36); + if (size < 38 + idLen + impLen) + return S_FALSE; + // ImplUse.SetCapacity(impLen); + processed = 38; + // memcpy(ImplUse, p + processed, impLen); + processed += impLen; + Id.Parse(p + processed, idLen); + processed += idLen; + for (;(processed & 3) != 0; processed++) + if (p[processed] != 0) + return S_FALSE; + return (processed <= size) ? S_OK : S_FALSE; +} + +HRESULT CInArchive::ReadFileItem(int volIndex, int fsIndex, const CLongAllocDesc &lad, int numRecurseAllowed) +{ + if (Files.Size() % 100 == 0) + RINOK(_progress->SetCompleted(Files.Size(), _processedProgressBytes)); + if (numRecurseAllowed-- == 0) + return S_FALSE; + CFile &file = Files.Back(); + const CLogVol &vol = LogVols[volIndex]; + CPartition &partition = Partitions[vol.PartitionMaps[lad.Location.PartitionRef].PartitionIndex]; + + UInt32 key = lad.Location.Pos; + UInt32 value; + const UInt32 kRecursedErrorValue = (UInt32)(Int32)-1; + if (partition.Map.Find(key, value)) + { + if (value == kRecursedErrorValue) + return S_FALSE; + file.ItemIndex = value; + } + else + { + value = Items.Size(); + file.ItemIndex = (int)value; + if (partition.Map.Set(key, kRecursedErrorValue)) + return S_FALSE; + RINOK(ReadItem(volIndex, fsIndex, lad, numRecurseAllowed)); + if (!partition.Map.Set(key, value)) + return S_FALSE; + } + return S_OK; +} + +HRESULT CInArchive::ReadItem(int volIndex, int fsIndex, const CLongAllocDesc &lad, int numRecurseAllowed) +{ + if (Items.Size() > kNumItemsMax) + return S_FALSE; + Items.Add(CItem()); + CItem &item = Items.Back(); + + const CLogVol &vol = LogVols[volIndex]; + + if (lad.GetLen() != vol.BlockSize) + return S_FALSE; + + CByteBuffer buf; + size_t size = lad.GetLen(); + buf.SetCapacity(size); + RINOK(Read(volIndex, lad, buf)); + + CTag tag; + const Byte *p = buf; + RINOK(tag.Parse(p, size)); + if (tag.Id != DESC_TYPE_File) + return S_FALSE; + + item.IcbTag.Parse(p + 16); + if (item.IcbTag.FileType != ICB_FILE_TYPE_DIR && + item.IcbTag.FileType != ICB_FILE_TYPE_FILE) + return S_FALSE; + + item.Parse(p); + + _processedProgressBytes += (UInt64)item.NumLogBlockRecorded * vol.BlockSize + size; + + UInt32 extendedAttrLen = Get32(p + 168); + UInt32 allocDescriptorsLen = Get32(p + 172); + + if ((extendedAttrLen & 3) != 0) + return S_FALSE; + int pos = 176; + if (extendedAttrLen > size - pos) + return S_FALSE; + /* + if (extendedAttrLen != 16) + { + if (extendedAttrLen < 24) + return S_FALSE; + CTag attrTag; + RINOK(attrTag.Parse(p + pos, size)); + if (attrTag.Id != DESC_TYPE_ExtendedAttrHeader) + return S_FALSE; + // UInt32 implAttrLocation = Get32(p + pos + 16); + // UInt32 applicationlAttrLocation = Get32(p + pos + 20); + } + */ + pos += extendedAttrLen; + + int desctType = item.IcbTag.GetDescriptorType(); + if (allocDescriptorsLen > size - pos) + return S_FALSE; + if (desctType == ICB_DESC_TYPE_INLINE) + { + item.IsInline = true; + item.InlineData.SetCapacity(allocDescriptorsLen); + memcpy(item.InlineData, p + pos, allocDescriptorsLen); + } + else + { + item.IsInline = false; + if (desctType != ICB_DESC_TYPE_SHORT && desctType != ICB_DESC_TYPE_LONG) + return S_FALSE; + for (UInt32 i = 0; i < allocDescriptorsLen;) + { + CMyExtent e; + if (desctType == ICB_DESC_TYPE_SHORT) + { + if (i + 8 > allocDescriptorsLen) + return S_FALSE; + CShortAllocDesc sad; + sad.Parse(p + pos + i); + e.Pos = sad.Pos; + e.Len = sad.Len; + e.PartitionRef = lad.Location.PartitionRef; + i += 8; + } + else + { + if (i + 16 > allocDescriptorsLen) + return S_FALSE; + CLongAllocDesc ladNew; + ladNew.Parse(p + pos + i); + e.Pos = ladNew.Location.Pos; + e.PartitionRef = ladNew.Location.PartitionRef; + e.Len = ladNew.Len; + i += 16; + } + item.Extents.Add(e); + } + } + + if (item.IcbTag.IsDir()) + { + if (!item.CheckChunkSizes() || !CheckItemExtents(volIndex, item)) + return S_FALSE; + CByteBuffer buf; + RINOK(ReadFromFile(volIndex, item, buf)); + item.Size = 0; + item.Extents.ClearAndFree(); + item.InlineData.Free(); + + const Byte *p = buf; + size = buf.GetCapacity(); + size_t processedTotal = 0; + for (; processedTotal < size;) + { + size_t processedCur; + CFileId fileId; + RINOK(fileId.Parse(p + processedTotal, size - processedTotal, processedCur)); + if (!fileId.IsItLinkParent()) + { + CFile file; + // file.FileVersion = fileId.FileVersion; + // file.FileCharacteristics = fileId.FileCharacteristics; + // file.ImplUse = fileId.ImplUse; + file.Id = fileId.Id; + + _fileNameLengthTotal += file.Id.Data.GetCapacity(); + if (_fileNameLengthTotal > kFileNameLengthTotalMax) + return S_FALSE; + + item.SubFiles.Add(Files.Size()); + if (Files.Size() > kNumFilesMax) + return S_FALSE; + Files.Add(file); + RINOK(ReadFileItem(volIndex, fsIndex, fileId.Icb, numRecurseAllowed)); + } + processedTotal += processedCur; + } + } + else + { + if ((UInt32)item.Extents.Size() > kNumExtentsMax - _numExtents) + return S_FALSE; + _numExtents += item.Extents.Size(); + + if (item.InlineData.GetCapacity() > kInlineExtentsSizeMax - _inlineExtentsSize) + return S_FALSE; + _inlineExtentsSize += item.InlineData.GetCapacity(); + } + + return S_OK; +} + +HRESULT CInArchive::FillRefs(CFileSet &fs, int fileIndex, int parent, int numRecurseAllowed) +{ + if (_numRefs % 10000 == 0) + { + RINOK(_progress->SetCompleted()); + } + if (numRecurseAllowed-- == 0) + return S_FALSE; + if (_numRefs >= kNumRefsMax) + return S_FALSE; + _numRefs++; + CRef ref; + ref.FileIndex = fileIndex; + ref.Parent = parent; + parent = fs.Refs.Size(); + fs.Refs.Add(ref); + const CItem &item = Items[Files[fileIndex].ItemIndex]; + for (int i = 0; i < item.SubFiles.Size(); i++) + { + RINOK(FillRefs(fs, item.SubFiles[i], parent, numRecurseAllowed)); + } + return S_OK; +} + +HRESULT CInArchive::Open2() +{ + Clear(); + + UInt64 fileSize; + RINOK(_stream->Seek(0, STREAM_SEEK_END, &fileSize)); + + // Some UDFs contain additional 2 KB of zeros, so we also check 12, corrected to 11. + const int kSecLogSizeMax = 12; + Byte buf[1 << kSecLogSizeMax]; + Byte kSizesLog[] = { 11, 8, 12 }; + + for (int i = 0;; i++) + { + if (i == sizeof(kSizesLog) / sizeof(kSizesLog[0])) + return S_FALSE; + SecLogSize = kSizesLog[i]; + Int32 bufSize = 1 << SecLogSize; + if (bufSize > fileSize) + return S_FALSE; + RINOK(_stream->Seek(-bufSize, STREAM_SEEK_END, NULL)); + RINOK(ReadStream_FALSE(_stream, buf, bufSize)); + CTag tag; + if (tag.Parse(buf, bufSize) == S_OK) + if (tag.Id == DESC_TYPE_AnchorVolPtr) + break; + } + if (SecLogSize == 12) + SecLogSize = 11; + + CExtent extentVDS; + extentVDS.Parse(buf + 16); + + for (UInt32 location = extentVDS.Pos; ; location++) + { + size_t bufSize = 1 << SecLogSize; + size_t pos = 0; + RINOK(_stream->Seek((UInt64)location << SecLogSize, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(_stream, buf, bufSize)); + CTag tag; + RINOK(tag.Parse(buf + pos, bufSize - pos)); + if (tag.Id == DESC_TYPE_Terminating) + break; + if (tag.Id == DESC_TYPE_Partition) + { + if (Partitions.Size() >= kNumPartitionsMax) + return S_FALSE; + CPartition partition; + // UInt32 volDescSeqNumer = Get32(buf + 16); + // partition.Flags = Get16(buf + 20); + partition.Number = Get16(buf + 22); + // partition.ContentsId.Parse(buf + 24); + + // memcpy(partition.ContentsUse, buf + 56, sizeof(partition.ContentsUse)); + // ContentsUse is Partition Header Description. + + // partition.AccessType = Get32(buf + 184); + partition.Pos = Get32(buf + 188); + partition.Len = Get32(buf + 192); + // partition.ImplId.Parse(buf + 196); + // memcpy(partition.ImplUse, buf + 228, sizeof(partition.ImplUse)); + + Partitions.Add(partition); + } + else if (tag.Id == DESC_TYPE_LogicalVol) + { + if (LogVols.Size() >= kNumLogVolumesMax) + return S_FALSE; + CLogVol vol; + vol.Id.Parse(buf + 84); + vol.BlockSize = Get32(buf + 212); + // vol.DomainId.Parse(buf + 216); + + if (vol.BlockSize < 512 || vol.BlockSize > ((UInt32)1 << 30)) + return S_FALSE; + + // memcpy(vol.ContentsUse, buf + 248, sizeof(vol.ContentsUse)); + vol.FileSetLocation.Parse(buf + 248); + + // UInt32 mapTableLength = Get32(buf + 264); + UInt32 numPartitionMaps = Get32(buf + 268); + if (numPartitionMaps > kNumPartitionsMax) + return S_FALSE; + // vol.ImplId.Parse(buf + 272); + // memcpy(vol.ImplUse, buf + 128, sizeof(vol.ImplUse)); + size_t pos = 440; + for (UInt32 i = 0; i < numPartitionMaps; i++) + { + if (pos + 2 > bufSize) + return S_FALSE; + CPartitionMap pm; + pm.Type = buf[pos]; + // pm.Length = buf[pos + 1]; + Byte len = buf[pos + 1]; + + if (pos + len > bufSize) + return S_FALSE; + + // memcpy(pm.Data, buf + pos + 2, pm.Length - 2); + if (pm.Type == 1) + { + if (pos + 6 > bufSize) + return S_FALSE; + // pm.VolSeqNumber = Get16(buf + pos + 2); + pm.PartitionNumber = Get16(buf + pos + 4); + } + else + return S_FALSE; + pos += len; + vol.PartitionMaps.Add(pm); + } + LogVols.Add(vol); + } + } + + UInt64 totalSize = 0; + + int volIndex; + for (volIndex = 0; volIndex < LogVols.Size(); volIndex++) + { + CLogVol &vol = LogVols[volIndex]; + for (int pmIndex = 0; pmIndex < vol.PartitionMaps.Size(); pmIndex++) + { + CPartitionMap &pm = vol.PartitionMaps[pmIndex]; + int i; + for (i = 0; i < Partitions.Size(); i++) + { + CPartition &part = Partitions[i]; + if (part.Number == pm.PartitionNumber) + { + if (part.VolIndex >= 0) + return S_FALSE; + pm.PartitionIndex = i; + part.VolIndex = volIndex; + + totalSize += (UInt64)part.Len << SecLogSize; + break; + } + } + if (i == Partitions.Size()) + return S_FALSE; + } + } + + RINOK(_progress->SetTotal(totalSize)); + + for (volIndex = 0; volIndex < LogVols.Size(); volIndex++) + { + CLogVol &vol = LogVols[volIndex]; + + CLongAllocDesc nextExtent = vol.FileSetLocation; + // while (nextExtent.ExtentLen != 0) + // for (int i = 0; i < 1; i++) + { + if (nextExtent.GetLen() < 512) + return S_FALSE; + CByteBuffer buf; + buf.SetCapacity(nextExtent.GetLen()); + RINOK(Read(volIndex, nextExtent, buf)); + const Byte *p = buf; + size_t size = nextExtent.GetLen(); + + CTag tag; + RINOK(tag.Parse(p, size)); + if (tag.Id != DESC_TYPE_FileSet) + return S_FALSE; + + CFileSet fs; + fs.RecodringTime.Parse(p + 16); + // fs.InterchangeLevel = Get16(p + 18); + // fs.MaxInterchangeLevel = Get16(p + 20); + // fs.FileSetNumber = Get32(p + 40); + // fs.FileSetDescNumber = Get32(p + 44); + + // fs.Id.Parse(p + 304); + // fs.CopyrightId.Parse(p + 336); + // fs.AbstractId.Parse(p + 368); + + fs.RootDirICB.Parse(p + 400); + // fs.DomainId.Parse(p + 416); + + // fs.SystemStreamDirICB.Parse(p + 464); + + vol.FileSets.Add(fs); + + // nextExtent.Parse(p + 448); + } + + for (int fsIndex = 0; fsIndex < vol.FileSets.Size(); fsIndex++) + { + CFileSet &fs = vol.FileSets[fsIndex]; + int fileIndex = Files.Size(); + Files.Add(CFile()); + RINOK(ReadFileItem(volIndex, fsIndex, fs.RootDirICB, kNumRecureseLevelsMax)); + RINOK(FillRefs(fs, fileIndex, -1, kNumRecureseLevelsMax)); + } + } + + return S_OK; +} + +HRESULT CInArchive::Open(IInStream *inStream, CProgressVirt *progress) +{ + _progress = progress; + _stream = inStream; + HRESULT res; + try { res = Open2(); } + catch(...) { Clear(); res = S_FALSE; } + _stream.Release(); + return res; +} + +void CInArchive::Clear() +{ + Partitions.Clear(); + LogVols.Clear(); + Items.Clear(); + Files.Clear(); + _fileNameLengthTotal = 0; + _numRefs = 0; + _numExtents = 0; + _inlineExtentsSize = 0; + _processedProgressBytes = 0; +} + +UString CInArchive::GetComment() const +{ + UString res; + for (int i = 0; i < LogVols.Size(); i++) + { + if (i > 0) + res += L" "; + res += LogVols[i].GetName(); + } + return res; +} + +static UString GetSpecName(const UString &name) +{ + UString name2 = name; + name2.Trim(); + if (name2.IsEmpty()) + { + /* + wchar_t s[32]; + ConvertUInt64ToString(id, s); + return L"[" + (UString)s + L"]"; + */ + return L"[]"; + } + return name; +} + +static void UpdateWithName(UString &res, const UString &addString) +{ + if (res.IsEmpty()) + res = addString; + else + res = addString + WCHAR_PATH_SEPARATOR + res; +} + +UString CInArchive::GetItemPath(int volIndex, int fsIndex, int refIndex, + bool showVolName, bool showFsName) const +{ + // showVolName = true; + const CLogVol &vol = LogVols[volIndex]; + const CFileSet &fs = vol.FileSets[fsIndex]; + + UString name; + + for (;;) + { + const CRef &ref = fs.Refs[refIndex]; + refIndex = ref.Parent; + if (refIndex < 0) + break; + UpdateWithName(name, GetSpecName(Files[ref.FileIndex].GetName())); + } + + if (showFsName) + { + wchar_t s[32]; + ConvertUInt64ToString(fsIndex, s); + UString newName = L"File Set "; + newName += s; + UpdateWithName(name, newName); + } + + if (showVolName) + { + wchar_t s[32]; + ConvertUInt64ToString(volIndex, s); + UString newName = s; + UString newName2 = vol.GetName(); + if (newName2.IsEmpty()) + newName2 = L"Volume"; + newName += L'-'; + newName += newName2; + UpdateWithName(name, newName); + } + return name; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.h new file mode 100644 index 000000000..46b9a7e85 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfIn.h @@ -0,0 +1,375 @@ +// Archive/UdfIn.h -- UDF / ECMA-167 + +#ifndef __ARCHIVE_UDF_IN_H +#define __ARCHIVE_UDF_IN_H + +#include "Common/MyCom.h" +#include "Common/IntToString.h" +#include "Common/Buffer.h" +#include "Common/MyString.h" +#include "Common/MyMap.h" + +#include "../../IStream.h" + +namespace NArchive { +namespace NUdf { + +// ---------- ECMA Part 1 ---------- + +// ECMA 1/7.2.12 + +/* +struct CDString32 +{ + Byte Data[32]; + void Parse(const Byte *buf); + // UString GetString() const; +}; +*/ + +struct CDString128 +{ + Byte Data[128]; + void Parse(const Byte *buf); + UString GetString() const; +}; + +struct CDString +{ + CByteBuffer Data; + void Parse(const Byte *p, unsigned size); + UString GetString() const; +}; + + +// ECMA 1/7.3 + +struct CTime +{ + Byte Data[12]; + + unsigned GetType() const { return Data[1] >> 4; } + bool IsLocal() const { return GetType() == 1; } + int GetMinutesOffset() const + { + int t = (Data[0] | ((UInt16)Data[1] << 8)) & 0xFFF; + if ((t >> 11) != 0) + t -= (1 << 12); + return (t > (60 * 24) || t < -(60 * 24)) ? 0 : t; + } + unsigned GetYear() const { return (Data[2] | ((UInt16)Data[3] << 8)); } + void Parse(const Byte *buf); +}; + + +// ECMA 1/7.4 + +/* +struct CRegId +{ + Byte Flags; + char Id[23]; + char Suffix[8]; + + void Parse(const Byte *buf); +}; +*/ + +// ---------- ECMA Part 3: Volume Structure ---------- + +// ECMA 3/10.5 + +struct CPartition +{ + // UInt16 Flags; + UInt16 Number; + // CRegId ContentsId; + // Byte ContentsUse[128]; + // UInt32 AccessType; + + UInt32 Pos; + UInt32 Len; + + // CRegId ImplId; + // Byte ImplUse[128]; + + int VolIndex; + CMap32 Map; + + CPartition(): VolIndex(-1) {} + + // bool IsNsr() const { return (strncmp(ContentsId.Id, "+NSR0", 5) == 0); } + // bool IsAllocated() const { return ((Flags & 1) != 0); } +}; + +struct CLogBlockAddr +{ + UInt32 Pos; + UInt16 PartitionRef; + + void Parse(const Byte *buf); +}; + +enum EShortAllocDescType +{ + SHORT_ALLOC_DESC_TYPE_RecordedAndAllocated = 0, + SHORT_ALLOC_DESC_TYPE_NotRecordedButAllocated = 1, + SHORT_ALLOC_DESC_TYPE_NotRecordedAndNotAllocated = 2, + SHORT_ALLOC_DESC_TYPE_NextExtent = 3 +}; + +struct CShortAllocDesc +{ + UInt32 Len; + UInt32 Pos; + + // 4/14.14.1 + // UInt32 GetLen() const { return Len & 0x3FFFFFFF; } + // UInt32 GetType() const { return Len >> 30; } + // bool IsRecAndAlloc() const { return GetType() == SHORT_ALLOC_DESC_TYPE_RecordedAndAllocated; } + void Parse(const Byte *buf); +}; + +/* +struct CADImpUse +{ + UInt16 Flags; + UInt32 UdfUniqueId; + void Parse(const Byte *buf); +}; +*/ + +struct CLongAllocDesc +{ + UInt32 Len; + CLogBlockAddr Location; + + // Byte ImplUse[6]; + // CADImpUse adImpUse; // UDF + + UInt32 GetLen() const { return Len & 0x3FFFFFFF; } + UInt32 GetType() const { return Len >> 30; } + bool IsRecAndAlloc() const { return GetType() == SHORT_ALLOC_DESC_TYPE_RecordedAndAllocated; } + void Parse(const Byte *buf); +}; + +struct CPartitionMap +{ + Byte Type; + // Byte Len; + + // Type - 1 + // UInt16 VolSeqNumber; + UInt16 PartitionNumber; + + // Byte Data[256]; + + int PartitionIndex; +}; + +// ECMA 4/14.6 + +enum EIcbFileType +{ + ICB_FILE_TYPE_DIR = 4, + ICB_FILE_TYPE_FILE = 5 +}; + +enum EIcbDescriptorType +{ + ICB_DESC_TYPE_SHORT = 0, + ICB_DESC_TYPE_LONG = 1, + ICB_DESC_TYPE_EXTENDED = 2, + ICB_DESC_TYPE_INLINE = 3 +}; + +struct CIcbTag +{ + // UInt32 PriorDirectNum; + // UInt16 StrategyType; + // UInt16 StrategyParam; + // UInt16 MaxNumOfEntries; + Byte FileType; + // CLogBlockAddr ParentIcb; + UInt16 Flags; + + bool IsDir() const { return FileType == ICB_FILE_TYPE_DIR; } + int GetDescriptorType() const { return Flags & 3; } + void Parse(const Byte *p); +}; + +// const Byte FILEID_CHARACS_Existance = (1 << 0); +const Byte FILEID_CHARACS_Parent = (1 << 3); + +struct CFile +{ + // UInt16 FileVersion; + // Byte FileCharacteristics; + // CByteBuffer ImplUse; + CDString Id; + + CFile(): /* FileVersion(0), FileCharacteristics(0), */ ItemIndex(-1) {} + int ItemIndex; + UString GetName() const { return Id.GetString(); } +}; + +struct CMyExtent +{ + UInt32 Pos; + UInt32 Len; + int PartitionRef; + + UInt32 GetLen() const { return Len & 0x3FFFFFFF; } + UInt32 GetType() const { return Len >> 30; } + bool IsRecAndAlloc() const { return GetType() == SHORT_ALLOC_DESC_TYPE_RecordedAndAllocated; } +}; + +struct CItem +{ + CIcbTag IcbTag; + + // UInt32 Uid; + // UInt32 Gid; + // UInt32 Permissions; + // UInt16 FileLinkCount; + // Byte RecordFormat; + // Byte RecordDisplayAttr; + // UInt32 RecordLen; + UInt64 Size; + UInt64 NumLogBlockRecorded; + CTime ATime; + CTime MTime; + // CTime AttrtTime; + // UInt32 CheckPoint; + // CLongAllocDesc ExtendedAttrIcb; + // CRegId ImplId; + // UInt64 UniqueId; + + bool IsInline; + CByteBuffer InlineData; + CRecordVector<CMyExtent> Extents; + CRecordVector<int> SubFiles; + + void Parse(const Byte *buf); + + bool IsRecAndAlloc() const + { + for (int i = 0; i < Extents.Size(); i++) + if (!Extents[i].IsRecAndAlloc()) + return false; + return true; + } + + UInt64 GetChunksSumSize() const + { + if (IsInline) + return InlineData.GetCapacity(); + UInt64 size = 0; + for (int i = 0; i < Extents.Size(); i++) + size += Extents[i].GetLen(); + return size; + } + + bool CheckChunkSizes() const { return GetChunksSumSize() == Size; } + + bool IsDir() const { return IcbTag.IsDir(); } +}; + +struct CRef +{ + int Parent; + int FileIndex; +}; + + +// ECMA 4 / 14.1 +struct CFileSet +{ + CTime RecodringTime; + // UInt16 InterchangeLevel; + // UInt16 MaxInterchangeLevel; + // UInt32 FileSetNumber; + // UInt32 FileSetDescNumber; + // CDString32 Id; + // CDString32 CopyrightId; + // CDString32 AbstractId; + + CLongAllocDesc RootDirICB; + // CRegId DomainId; + // CLongAllocDesc SystemStreamDirICB; + + CRecordVector<CRef> Refs; +}; + + +// ECMA 3/10.6 + +struct CLogVol +{ + CDString128 Id; + UInt32 BlockSize; + // CRegId DomainId; + + // Byte ContentsUse[16]; + CLongAllocDesc FileSetLocation; // UDF + + // CRegId ImplId; + // Byte ImplUse[128]; + + CObjectVector<CPartitionMap> PartitionMaps; + CObjectVector<CFileSet> FileSets; + + UString GetName() const { return Id.GetString(); } +}; + +struct CProgressVirt +{ + virtual HRESULT SetTotal(UInt64 numBytes) PURE; + virtual HRESULT SetCompleted(UInt64 numFiles, UInt64 numBytes) PURE; + virtual HRESULT SetCompleted() PURE; +}; + +class CInArchive +{ + CMyComPtr<IInStream> _stream; + CProgressVirt *_progress; + + HRESULT Read(int volIndex, int partitionRef, UInt32 blockPos, UInt32 len, Byte *buf); + HRESULT Read(int volIndex, const CLongAllocDesc &lad, Byte *buf); + HRESULT ReadFromFile(int volIndex, const CItem &item, CByteBuffer &buf); + + HRESULT ReadFileItem(int volIndex, int fsIndex, const CLongAllocDesc &lad, int numRecurseAllowed); + HRESULT ReadItem(int volIndex, int fsIndex, const CLongAllocDesc &lad, int numRecurseAllowed); + + HRESULT Open2(); + HRESULT FillRefs(CFileSet &fs, int fileIndex, int parent, int numRecurseAllowed); + + UInt64 _processedProgressBytes; + + UInt64 _fileNameLengthTotal; + int _numRefs; + UInt32 _numExtents; + UInt64 _inlineExtentsSize; + bool CheckExtent(int volIndex, int partitionRef, UInt32 blockPos, UInt32 len) const; +public: + HRESULT Open(IInStream *inStream, CProgressVirt *progress); + void Clear(); + + CObjectVector<CPartition> Partitions; + CObjectVector<CLogVol> LogVols; + + CObjectVector<CItem> Items; + CObjectVector<CFile> Files; + + int SecLogSize; + + UString GetComment() const; + UString GetItemPath(int volIndex, int fsIndex, int refIndex, + bool showVolName, bool showFsName) const; + + bool CheckItemExtents(int volIndex, const CItem &item) const; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfRegister.cpp new file mode 100644 index 000000000..1b08d120b --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Udf/UdfRegister.cpp @@ -0,0 +1,13 @@ +// UdfRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "UdfHandler.h" +static IInArchive *CreateArc() { return new NArchive::NUdf::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Udf", L"iso img", 0, 0xE0, { 0, 'N', 'S', 'R', '0' }, 5, false, CreateArc, 0 }; + +REGISTER_ARC(Udf) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/VhdHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/VhdHandler.cpp new file mode 100644 index 000000000..9d1c928e6 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/VhdHandler.cpp @@ -0,0 +1,734 @@ +// VhdHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyString.h" + +#include "Windows/PropVariant.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +#define G32(p, dest) dest = Get32(p); +#define G64(p, dest) dest = Get64(p); + +using namespace NWindows; + +namespace NArchive { +namespace NVhd { + +static const UInt32 kUnusedBlock = 0xFFFFFFFF; + +static const UInt32 kDiskType_Fixed = 2; +static const UInt32 kDiskType_Dynamic = 3; +static const UInt32 kDiskType_Diff = 4; + +static const char *kDiskTypes[] = +{ + "0", + "1", + "Fixed", + "Dynamic", + "Differencing" +}; + +struct CFooter +{ + // UInt32 Features; + // UInt32 FormatVersion; + UInt64 DataOffset; + UInt32 CTime; + UInt32 CreatorApp; + UInt32 CreatorVersion; + UInt32 CreatorHostOS; + // UInt64 OriginalSize; + UInt64 CurrentSize; + UInt32 DiskGeometry; + UInt32 Type; + Byte Id[16]; + Byte SavedState; + + bool IsFixed() const { return Type == kDiskType_Fixed; } + bool ThereIsDynamic() const { return Type == kDiskType_Dynamic || Type == kDiskType_Diff; } + // bool IsSupported() const { return Type == kDiskType_Fixed || Type == kDiskType_Dynamic || Type == kDiskType_Diff; } + UInt32 NumCyls() const { return DiskGeometry >> 16; } + UInt32 NumHeads() const { return (DiskGeometry >> 8) & 0xFF; } + UInt32 NumSectorsPerTrack() const { return DiskGeometry & 0xFF; } + AString GetTypeString() const; + bool Parse(const Byte *p); +}; + +AString CFooter::GetTypeString() const +{ + if (Type < sizeof(kDiskTypes) / sizeof(kDiskTypes[0])) + return kDiskTypes[Type]; + char s[16]; + ConvertUInt32ToString(Type, s); + return s; +} + +static bool CheckBlock(const Byte *p, unsigned size, unsigned checkSumOffset, unsigned zeroOffset) +{ + UInt32 sum = 0; + unsigned i; + for (i = 0; i < checkSumOffset; i++) + sum += p[i]; + for (i = checkSumOffset + 4; i < size; i++) + sum += p[i]; + if (~sum != Get32(p + checkSumOffset)) + return false; + for (i = zeroOffset; i < size; i++) + if (p[i] != 0) + return false; + return true; +} + +bool CFooter::Parse(const Byte *p) +{ + if (memcmp(p, "conectix", 8) != 0) + return false; + // G32(p + 0x08, Features); + // G32(p + 0x0C, FormatVersion); + G64(p + 0x10, DataOffset); + G32(p + 0x18, CTime); + G32(p + 0x1C, CreatorApp); + G32(p + 0x20, CreatorVersion); + G32(p + 0x24, CreatorHostOS); + // G64(p + 0x28, OriginalSize); + G64(p + 0x30, CurrentSize); + G32(p + 0x38, DiskGeometry); + G32(p + 0x3C, Type); + memcpy(Id, p + 0x44, 16); + SavedState = p[0x54]; + return CheckBlock(p, 512, 0x40, 0x55); +} + +/* +struct CParentLocatorEntry +{ + UInt32 Code; + UInt32 DataSpace; + UInt32 DataLen; + UInt64 DataOffset; + + bool Parse(const Byte *p); +}; +bool CParentLocatorEntry::Parse(const Byte *p) +{ + G32(p + 0x00, Code); + G32(p + 0x04, DataSpace); + G32(p + 0x08, DataLen); + G32(p + 0x10, DataOffset); + return (Get32(p + 0x0C) == 0); // Resrved +} +*/ + +struct CDynHeader +{ + // UInt64 DataOffset; + UInt64 TableOffset; + // UInt32 HeaderVersion; + UInt32 NumBlocks; + int BlockSizeLog; + UInt32 ParentTime; + Byte ParentId[16]; + UString ParentName; + // CParentLocatorEntry ParentLocators[8]; + + bool Parse(const Byte *p); + UInt32 NumBitMapSectors() const + { + UInt32 numSectorsInBlock = (1 << (BlockSizeLog - 9)); + return (numSectorsInBlock + 512 * 8 - 1) / (512 * 8); + } +}; + +static int GetLog(UInt32 num) +{ + for (int i = 0; i < 31; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + +bool CDynHeader::Parse(const Byte *p) +{ + if (memcmp(p, "cxsparse", 8) != 0) + return false; + // G64(p + 0x08, DataOffset); + G64(p + 0x10, TableOffset); + // G32(p + 0x18, HeaderVersion); + G32(p + 0x1C, NumBlocks); + BlockSizeLog = GetLog(Get32(p + 0x20)); + if (BlockSizeLog < 9 || BlockSizeLog > 30) + return false; + G32(p + 0x38, ParentTime); + if (Get32(p + 0x3C) != 0) // reserved + return false; + memcpy(ParentId, p + 0x28, 16); + { + const int kNameLength = 256; + wchar_t *s = ParentName.GetBuffer(kNameLength); + for (unsigned i = 0; i < kNameLength; i++) + s[i] = Get16(p + 0x40 + i * 2); + s[kNameLength] = 0; + ParentName.ReleaseBuffer(); + } + /* + for (int i = 0; i < 8; i++) + if (!ParentLocators[i].Parse(p + 0x240 + i * 24)) + return false; + */ + return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24); +} + +class CHandler: + public IInStream, + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ + UInt64 _virtPos; + UInt64 _phyPos; + UInt64 _phyLimit; + + CFooter Footer; + CDynHeader Dyn; + CRecordVector<UInt32> Bat; + CByteBuffer BitMap; + UInt32 BitMapTag; + UInt32 NumUsedBlocks; + CMyComPtr<IInStream> Stream; + CMyComPtr<IInStream> ParentStream; + CHandler *Parent; + + HRESULT Seek(UInt64 offset); + HRESULT InitAndSeek(); + HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size); + + bool NeedParent() const { return Footer.Type == kDiskType_Diff; } + UInt64 GetPackSize() const + { return Footer.ThereIsDynamic() ? ((UInt64)NumUsedBlocks << Dyn.BlockSizeLog) : Footer.CurrentSize; } + + UString GetParentName() const + { + const CHandler *p = this; + UString res; + while (p && p->NeedParent()) + { + if (!res.IsEmpty()) + res += L" -> "; + res += p->Dyn.ParentName; + p = p->Parent; + } + return res; + } + + bool IsOK() const + { + const CHandler *p = this; + while (p->NeedParent()) + { + p = p->Parent; + if (p == 0) + return false; + } + return true; + } + + HRESULT Open3(); + HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level); + +public: + MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream) + + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); } + +HRESULT CHandler::InitAndSeek() +{ + if (ParentStream) + { + RINOK(Parent->InitAndSeek()); + } + _virtPos = _phyPos = 0; + BitMapTag = kUnusedBlock; + BitMap.SetCapacity(Dyn.NumBitMapSectors() << 9); + return Seek(0); +} + +HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size) +{ + if (offset + size > _phyLimit) + return S_FALSE; + if (offset != _phyPos) + { + _phyPos = offset; + RINOK(Seek(offset)); + } + HRESULT res = ReadStream_FALSE(Stream, data, size); + _phyPos += size; + return res; +} + +HRESULT CHandler::Open3() +{ + RINOK(Stream->Seek(0, STREAM_SEEK_END, &_phyPos)); + if (_phyPos < 512) + return S_FALSE; + const UInt32 kDynSize = 1024; + Byte buf[kDynSize]; + + _phyLimit = _phyPos; + RINOK(ReadPhy(_phyLimit - 512, buf, 512)); + if (!Footer.Parse(buf)) + return S_FALSE; + _phyLimit -= 512; + + if (!Footer.ThereIsDynamic()) + return S_OK; + + RINOK(ReadPhy(0, buf + 512, 512)); + if (memcmp(buf, buf + 512, 512) != 0) + return S_FALSE; + + RINOK(ReadPhy(Footer.DataOffset, buf, kDynSize)); + if (!Dyn.Parse(buf)) + return S_FALSE; + + if (Dyn.NumBlocks >= (UInt32)1 << 31) + return S_FALSE; + if (Footer.CurrentSize == 0) + { + if (Dyn.NumBlocks != 0) + return S_FALSE; + } + else if (((Footer.CurrentSize - 1) >> Dyn.BlockSizeLog) + 1 != Dyn.NumBlocks) + return S_FALSE; + + Bat.Reserve(Dyn.NumBlocks); + while ((UInt32)Bat.Size() < Dyn.NumBlocks) + { + RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, 512)); + for (UInt32 j = 0; j < 512; j += 4) + { + UInt32 v = Get32(buf + j); + if (v != kUnusedBlock) + NumUsedBlocks++; + Bat.Add(v); + if ((UInt32)Bat.Size() >= Dyn.NumBlocks) + break; + } + } + return S_OK; +} + +STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + if (_virtPos >= Footer.CurrentSize) + return (Footer.CurrentSize == _virtPos) ? S_OK: E_FAIL; + UInt64 rem = Footer.CurrentSize - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (size == 0) + return S_OK; + UInt32 blockIndex = (UInt32)(_virtPos >> Dyn.BlockSizeLog); + UInt32 blockSectIndex = Bat[blockIndex]; + UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog; + UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1); + size = MyMin(blockSize - offsetInBlock, size); + + HRESULT res = S_OK; + if (blockSectIndex == kUnusedBlock) + { + if (ParentStream) + { + RINOK(ParentStream->Seek(_virtPos, STREAM_SEEK_SET, NULL)); + res = ParentStream->Read(data, size, &size); + } + else + memset(data, 0, size); + } + else + { + UInt64 newPos = (UInt64)blockSectIndex << 9; + if (BitMapTag != blockIndex) + { + RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.GetCapacity())); + BitMapTag = blockIndex; + } + RINOK(ReadPhy(newPos + BitMap.GetCapacity() + offsetInBlock, data, size)); + for (UInt32 cur = 0; cur < size;) + { + UInt32 rem = MyMin(0x200 - (offsetInBlock & 0x1FF), size - cur); + UInt32 bmi = offsetInBlock >> 9; + if (((BitMap[bmi >> 3] >> (7 - (bmi & 7))) & 1) == 0) + { + if (ParentStream) + { + RINOK(ParentStream->Seek(_virtPos + cur, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(ParentStream, (Byte *)data + cur, rem)); + } + else + { + const Byte *p = (const Byte *)data + cur; + for (UInt32 i = 0; i < rem; i++) + if (p[i] != 0) + return S_FALSE; + } + } + offsetInBlock += rem; + cur += rem; + } + } + if (processedSize != NULL) + *processedSize = size; + _virtPos += size; + return res; +} + +STDMETHODIMP CHandler::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = Footer.CurrentSize + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +enum +{ + kpidParent = kpidUserDefined, + kpidSavedState +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidClusterSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR}, + { L"Parent", kpidParent, VT_BSTR}, + { NULL, kpidCreatorApp, VT_BSTR}, + { NULL, kpidHostOS, VT_BSTR}, + { L"Saved State", kpidSavedState, VT_BOOL}, + { NULL, kpidId, VT_BSTR} + }; + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME} + + /* + { NULL, kpidNumCyls, VT_UI4}, + { NULL, kpidNumHeads, VT_UI4}, + { NULL, kpidSectorsPerTrack, VT_UI4} + */ +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_WITH_NAME + +// VHD start time: 2000-01-01 +static const UInt64 kVhdTimeStartValue = (UInt64)3600 * 24 * (399 * 365 + 24 * 4); + +static void VhdTimeToFileTime(UInt32 vhdTime, NCOM::CPropVariant &prop) +{ + FILETIME ft, utc; + UInt64 v = (kVhdTimeStartValue + vhdTime) * 10000000; + ft.dwLowDateTime = (DWORD)v; + ft.dwHighDateTime = (DWORD)(v >> 32); + // specification says that it's UTC time, but Virtual PC 6 writes local time. Why? + LocalFileTimeToFileTime(&ft, &utc); + prop = utc; +} + +static void StringToAString(char *dest, UInt32 s) +{ + for (int i = 24; i >= 0; i -= 8) + { + Byte b = (Byte)((s >> i) & 0xFF); + if (b < 0x20 || b > 0x7F) + break; + *dest++ = b; + } + *dest = 0; +} + +static void ConvertByteToHex(unsigned value, char *s) +{ + for (int i = 0; i < 2; i++) + { + unsigned t = value & 0xF; + value >>= 4; + s[1 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10))); + } +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch(propID) + { + case kpidMainSubfile: prop = (UInt32)0; break; + case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break; + case kpidClusterSize: if (Footer.ThereIsDynamic()) prop = (UInt32)1 << Dyn.BlockSizeLog; break; + case kpidMethod: + { + AString s = Footer.GetTypeString(); + if (NeedParent()) + { + s += " -> "; + const CHandler *p = this; + while (p != 0 && p->NeedParent()) + p = p->Parent; + if (p == 0) + s += '?'; + else + s += p->Footer.GetTypeString(); + } + prop = s; + break; + } + case kpidCreatorApp: + { + char s[16]; + StringToAString(s, Footer.CreatorApp); + AString res = s; + res.Trim(); + ConvertUInt32ToString(Footer.CreatorVersion >> 16, s); + res += ' '; + res += s; + res += '.'; + ConvertUInt32ToString(Footer.CreatorVersion & 0xFFFF, s); + res += s; + prop = res; + break; + } + case kpidHostOS: + { + if (Footer.CreatorHostOS == 0x5769326b) + prop = "Windows"; + else + { + char s[16]; + StringToAString(s, Footer.CreatorHostOS); + prop = s; + } + break; + } + case kpidId: + { + char s[32 + 4]; + for (int i = 0; i < 16; i++) + ConvertByteToHex(Footer.Id[i], s + i * 2); + s[32] = 0; + prop = s; + break; + } + case kpidSavedState: prop = Footer.SavedState ? true : false; break; + case kpidParent: if (NeedParent()) prop = GetParentName(); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level) +{ + Close(); + Stream = stream; + if (level > 32) + return S_FALSE; + RINOK(Open3()); + if (child && memcmp(child->Dyn.ParentId, Footer.Id, 16) != 0) + return S_FALSE; + if (Footer.Type != kDiskType_Diff) + return S_OK; + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + if (openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback) != S_OK) + return S_FALSE; + CMyComPtr<IInStream> nextStream; + HRESULT res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream); + if (res == S_FALSE) + return S_OK; + RINOK(res); + + Parent = new CHandler; + ParentStream = Parent; + return Parent->Open2(nextStream, this, openArchiveCallback, level + 1); +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * openArchiveCallback) +{ + COM_TRY_BEGIN + { + HRESULT res; + try + { + res = Open2(stream, NULL, openArchiveCallback, 0); + if (res == S_OK) + return S_OK; + } + catch(...) + { + Close(); + throw; + } + Close(); + return res; + } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + Bat.Clear(); + NumUsedBlocks = 0; + Parent = 0; + Stream.Release(); + ParentStream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + switch(propID) + { + case kpidSize: prop = Footer.CurrentSize; break; + case kpidPackSize: prop = GetPackSize(); break; + case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break; + /* + case kpidNumCyls: prop = Footer.NumCyls(); break; + case kpidNumHeads: prop = Footer.NumHeads(); break; + case kpidSectorsPerTrack: prop = Footer.NumSectorsPerTrack(); break; + */ + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + RINOK(extractCallback->SetTotal(Footer.CurrentSize)); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &outStream, askMode)); + if (!testMode && !outStream) + return S_OK; + RINOK(extractCallback->PrepareOperation(askMode)); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + int res = NExtract::NOperationResult::kDataError; + CMyComPtr<ISequentialInStream> inStream; + HRESULT hres = GetStream(0, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kUnSupportedMethod; + else + { + RINOK(hres); + HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (hres == S_OK) + { + if (copyCoderSpec->TotalSize == Footer.CurrentSize) + res = NExtract::NOperationResult::kOK; + } + else + { + if (hres != S_FALSE) + { + RINOK(hres); + } + } + } + outStream.Release(); + return extractCallback->SetOperationResult(res); + COM_TRY_END +} + +STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + if (Footer.IsFixed()) + { + CLimitedInStream *streamSpec = new CLimitedInStream; + CMyComPtr<ISequentialInStream> streamTemp = streamSpec; + streamSpec->SetStream(Stream); + streamSpec->InitAndSeek(0, Footer.CurrentSize); + RINOK(streamSpec->SeekToStart()); + *stream = streamTemp.Detach(); + return S_OK; + } + if (!Footer.ThereIsDynamic() || !IsOK()) + return S_FALSE; + CMyComPtr<ISequentialInStream> streamTemp = this; + RINOK(InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"VHD", L"vhd", L".mbr", 0xDC, { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 }, 10, false, CreateArc, 0 }; + +REGISTER_ARC(Vhd) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp new file mode 100644 index 000000000..eaad1e7ca --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.cpp @@ -0,0 +1,660 @@ +// WimHandler.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/StringToInt.h" +#include "Common/UTFConvert.h" + +#include "Windows/PropVariant.h" + +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamUtils.h" + +#include "WimHandler.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NWim { + +#define WIM_DETAILS + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidShortName, VT_BSTR} + + #ifdef WIM_DETAILS + , { NULL, kpidVolume, VT_UI4} + , { NULL, kpidOffset, VT_UI8} + , { NULL, kpidLinks, VT_UI4} + #endif +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidUnpackVer, VT_BSTR}, + { NULL, kpidIsVolume, VT_BOOL}, + { NULL, kpidVolume, VT_UI4}, + { NULL, kpidNumVolumes, VT_UI4} +}; + +static bool ParseNumber64(const AString &s, UInt64 &res) +{ + const char *end; + if (s.Left(2) == "0x") + { + if (s.Length() == 2) + return false; + res = ConvertHexStringToUInt64((const char *)s + 2, &end); + } + else + { + if (s.IsEmpty()) + return false; + res = ConvertStringToUInt64(s, &end); + } + return *end == 0; +} + +static bool ParseNumber32(const AString &s, UInt32 &res) +{ + UInt64 res64; + if (!ParseNumber64(s, res64) || res64 >= ((UInt64)1 << 32)) + return false; + res = (UInt32)res64; + return true; +} + +bool ParseTime(const CXmlItem &item, FILETIME &ft, const char *tag) +{ + int index = item.FindSubTag(tag); + if (index >= 0) + { + const CXmlItem &timeItem = item.SubItems[index]; + UInt32 low = 0, high = 0; + if (ParseNumber32(timeItem.GetSubStringForTag("LOWPART"), low) && + ParseNumber32(timeItem.GetSubStringForTag("HIGHPART"), high)) + { + ft.dwLowDateTime = low; + ft.dwHighDateTime = high; + return true; + } + } + return false; +} + +void CImageInfo::Parse(const CXmlItem &item) +{ + CTimeDefined = ParseTime(item, CTime, "CREATIONTIME"); + MTimeDefined = ParseTime(item, MTime, "LASTMODIFICATIONTIME"); + NameDefined = ConvertUTF8ToUnicode(item.GetSubStringForTag("NAME"), Name); + // IndexDefined = ParseNumber32(item.GetPropertyValue("INDEX"), Index); +} + +void CXml::ToUnicode(UString &s) +{ + size_t size = Data.GetCapacity(); + if (size < 2 || (size & 1) != 0 || size > (1 << 24)) + return; + const Byte *p = Data; + if (Get16(p) != 0xFEFF) + return; + wchar_t *chars = s.GetBuffer((int)size / 2); + for (size_t i = 2; i < size; i += 2) + *chars++ = (wchar_t)Get16(p + i); + *chars = 0; + s.ReleaseBuffer(); +} + +void CXml::Parse() +{ + UString s; + ToUnicode(s); + AString utf; + if (!ConvertUnicodeToUTF8(s, utf)) + return; + ::CXml xml; + if (!xml.Parse(utf)) + return; + if (xml.Root.Name != "WIM") + return; + + for (int i = 0; i < xml.Root.SubItems.Size(); i++) + { + const CXmlItem &item = xml.Root.SubItems[i]; + if (item.IsTagged("IMAGE")) + { + CImageInfo imageInfo; + imageInfo.Parse(item); + Images.Add(imageInfo); + } + } +} + +static const char *kMethodLZX = "LZX"; +static const char *kMethodXpress = "XPress"; +static const char *kMethodCopy = "Copy"; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + const CImageInfo *image = NULL; + if (_xmls.Size() == 1) + { + const CXml &xml = _xmls[0]; + if (xml.Images.Size() == 1) + image = &xml.Images[0]; + } + + switch(propID) + { + case kpidSize: prop = _db.GetUnpackSize(); break; + case kpidPackSize: prop = _db.GetPackSize(); break; + + case kpidCTime: + if (_xmls.Size() == 1) + { + const CXml &xml = _xmls[0]; + int index = -1; + for (int i = 0; i < xml.Images.Size(); i++) + { + const CImageInfo &image = xml.Images[i]; + if (image.CTimeDefined) + if (index < 0 || ::CompareFileTime(&image.CTime, &xml.Images[index].CTime) < 0) + index = i; + } + if (index >= 0) + prop = xml.Images[index].CTime; + } + break; + + case kpidMTime: + if (_xmls.Size() == 1) + { + const CXml &xml = _xmls[0]; + int index = -1; + for (int i = 0; i < xml.Images.Size(); i++) + { + const CImageInfo &image = xml.Images[i]; + if (image.MTimeDefined) + if (index < 0 || ::CompareFileTime(&image.MTime, &xml.Images[index].MTime) > 0) + index = i; + } + if (index >= 0) + prop = xml.Images[index].MTime; + } + break; + + case kpidComment: + if (image != NULL) + { + if (_xmlInComments) + { + UString s; + _xmls[0].ToUnicode(s); + prop = s; + } + else if (image->NameDefined) + prop = image->Name; + } + break; + + case kpidUnpackVer: + { + UInt32 ver1 = _version >> 16; + UInt32 ver2 = (_version >> 8) & 0xFF; + UInt32 ver3 = (_version) & 0xFF; + + char s[16]; + ConvertUInt32ToString(ver1, s); + AString res = s; + res += '.'; + ConvertUInt32ToString(ver2, s); + res += s; + if (ver3 != 0) + { + res += '.'; + ConvertUInt32ToString(ver3, s); + res += s; + } + prop = res; + break; + } + + case kpidIsVolume: + if (_xmls.Size() > 0) + { + UInt16 volIndex = _xmls[0].VolIndex; + if (volIndex < _volumes.Size()) + prop = (_volumes[volIndex].Header.NumParts > 1); + } + break; + case kpidVolume: + if (_xmls.Size() > 0) + { + UInt16 volIndex = _xmls[0].VolIndex; + if (volIndex < _volumes.Size()) + prop = (UInt32)_volumes[volIndex].Header.PartNumber; + } + break; + case kpidNumVolumes: if (_volumes.Size() > 0) prop = (UInt32)(_volumes.Size() - 1); break; + case kpidMethod: + { + bool lzx = false, xpress = false, copy = false; + for (int i = 0; i < _xmls.Size(); i++) + { + const CHeader &header = _volumes[_xmls[i].VolIndex].Header; + if (header.IsCompressed()) + if (header.IsLzxMode()) + lzx = true; + else + xpress = true; + else + copy = true; + } + AString res; + if (lzx) + res = kMethodLZX; + if (xpress) + { + if (!res.IsEmpty()) + res += ' '; + res += kMethodXpress; + } + if (copy) + { + if (!res.IsEmpty()) + res += ' '; + res += kMethodCopy; + } + prop = res; + } + } + 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; + if (index < (UInt32)_db.SortedItems.Size()) + { + int realIndex = _db.SortedItems[index]; + const CItem &item = _db.Items[realIndex]; + const CStreamInfo *si = NULL; + const CVolume *vol = NULL; + if (item.StreamIndex >= 0) + { + si = &_db.Streams[item.StreamIndex]; + vol = &_volumes[si->PartNumber]; + } + + switch(propID) + { + case kpidPath: + if (item.HasMetadata) + prop = _db.GetItemPath(realIndex); + else + { + char sz[16]; + ConvertUInt32ToString(item.StreamIndex, sz); + AString s = sz; + while (s.Length() < _nameLenForStreams) + s = '0' + s; + /* + if (si->Resource.IsFree()) + prefix = "[Free]"; + */ + s = "[Files]" STRING_PATH_SEPARATOR + s; + prop = s; + } + break; + case kpidShortName: if (item.HasMetadata) prop = item.ShortName; break; + + case kpidIsDir: prop = item.IsDir(); break; + case kpidAttrib: if (item.HasMetadata) prop = item.Attrib; break; + case kpidCTime: if (item.HasMetadata) prop = item.CTime; break; + case kpidATime: if (item.HasMetadata) prop = item.ATime; break; + case kpidMTime: if (item.HasMetadata) prop = item.MTime; break; + case kpidPackSize: prop = si ? si->Resource.PackSize : (UInt64)0; break; + case kpidSize: prop = si ? si->Resource.UnpackSize : (UInt64)0; break; + case kpidMethod: if (si) prop = si->Resource.IsCompressed() ? + (vol->Header.IsLzxMode() ? kMethodLZX : kMethodXpress) : kMethodCopy; break; + #ifdef WIM_DETAILS + case kpidVolume: if (si) prop = (UInt32)si->PartNumber; break; + case kpidOffset: if (si) prop = (UInt64)si->Resource.Offset; break; + case kpidLinks: prop = si ? (UInt32)si->RefCount : (UInt32)0; break; + #endif + } + } + else + { + index -= _db.SortedItems.Size(); + { + switch(propID) + { + case kpidPath: + { + char sz[16]; + ConvertUInt32ToString(_xmls[index].VolIndex, sz); + prop = (AString)"[" + (AString)sz + "].xml"; + break; + } + case kpidIsDir: prop = false; break; + case kpidPackSize: + case kpidSize: prop = (UInt64)_xmls[index].Data.GetCapacity(); break; + case kpidMethod: prop = kMethodCopy; break; + } + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CVolumeName +{ + // UInt32 _volIndex; + UString _before; + UString _after; +public: + CVolumeName() {}; + + void InitName(const UString &name) + { + // _volIndex = 1; + int dotPos = name.ReverseFind('.'); + if (dotPos < 0) + dotPos = name.Length(); + _before = name.Left(dotPos); + _after = name.Mid(dotPos); + } + + UString GetNextName(UInt32 index) + { + wchar_t s[16]; + ConvertUInt32ToString(index, s); + return _before + (UString)s + _after; + } +}; + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback *openArchiveCallback) +{ + COM_TRY_BEGIN + Close(); + { + CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; + + CVolumeName seqName; + if (openArchiveCallback != NULL) + openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback); + + UInt32 numVolumes = 1; + int firstVolumeIndex = -1; + for (UInt32 i = 1; i <= numVolumes; i++) + { + CMyComPtr<IInStream> curStream; + if (i != 1) + { + UString fullName = seqName.GetNextName(i); + HRESULT result = openVolumeCallback->GetStream(fullName, &curStream); + if (result == S_FALSE) + continue; + if (result != S_OK) + return result; + if (!curStream) + break; + } + else + curStream = inStream; + CHeader header; + HRESULT res = NWim::ReadHeader(curStream, header); + if (res != S_OK) + { + if (i == 1) + return res; + if (res == S_FALSE) + continue; + return res; + } + _version = header.Version; + _isOldVersion = header.IsOldVersion(); + if (firstVolumeIndex >= 0) + if (!header.AreFromOnArchive(_volumes[firstVolumeIndex].Header)) + break; + if (_volumes.Size() > header.PartNumber && _volumes[header.PartNumber].Stream) + break; + CXml xml; + xml.VolIndex = header.PartNumber; + res = _db.Open(curStream, header, xml.Data, openArchiveCallback); + if (res != S_OK) + { + if (i == 1) + return res; + if (res == S_FALSE) + continue; + return res; + } + + while (_volumes.Size() <= header.PartNumber) + _volumes.Add(CVolume()); + CVolume &volume = _volumes[header.PartNumber]; + volume.Header = header; + volume.Stream = curStream; + + firstVolumeIndex = header.PartNumber; + + bool needAddXml = true; + if (_xmls.Size() != 0) + if (xml.Data == _xmls[0].Data) + needAddXml = false; + if (needAddXml) + { + xml.Parse(); + _xmls.Add(xml); + } + + if (i == 1) + { + if (header.PartNumber != 1) + break; + if (!openVolumeCallback) + break; + numVolumes = header.NumParts; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + break; + seqName.InitName(prop.bstrVal); + } + } + } + + _db.DetectPathMode(); + RINOK(_db.Sort(_db.SkipRoot)); + + wchar_t sz[16]; + ConvertUInt32ToString(_db.Streams.Size(), sz); + _nameLenForStreams = MyStringLen(sz); + + _xmlInComments = (_xmls.Size() == 1 && !_db.ShowImageNumber); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _db.Clear(); + _volumes.Clear(); + _xmls.Clear(); + _nameLenForStreams = 0; + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + + if (allFilesMode) + numItems = _db.SortedItems.Size() + _xmls.Size(); + if (numItems == 0) + return S_OK; + + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + UInt32 index = allFilesMode ? i : indices[i]; + if (index < (UInt32)_db.SortedItems.Size()) + { + int streamIndex = _db.Items[_db.SortedItems[index]].StreamIndex; + if (streamIndex >= 0) + { + const CStreamInfo &si = _db.Streams[streamIndex]; + totalSize += si.Resource.UnpackSize; + } + } + else + totalSize += _xmls[index - (UInt32)_db.SortedItems.Size()].Data.GetCapacity(); + } + + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 currentTotalPacked = 0; + UInt64 currentTotalUnPacked = 0; + UInt64 currentItemUnPacked, currentItemPacked; + + int prevSuccessStreamIndex = -1; + + CUnpacker unpacker; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; currentTotalUnPacked += currentItemUnPacked, + currentTotalPacked += currentItemPacked) + { + currentItemUnPacked = 0; + currentItemPacked = 0; + + lps->InSize = currentTotalPacked; + lps->OutSize = currentTotalUnPacked; + + RINOK(lps->SetCur()); + UInt32 index = allFilesMode ? i : indices[i]; + i++; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + + CMyComPtr<ISequentialOutStream> realOutStream; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + if (index >= (UInt32)_db.SortedItems.Size()) + { + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + const CByteBuffer &data = _xmls[index - (UInt32)_db.SortedItems.Size()].Data; + currentItemUnPacked = data.GetCapacity(); + if (realOutStream) + { + RINOK(WriteStream(realOutStream, (const Byte *)data, data.GetCapacity())); + realOutStream.Release(); + } + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + const CItem &item = _db.Items[_db.SortedItems[index]]; + int streamIndex = item.StreamIndex; + if (streamIndex < 0) + { + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(item.HasStream() ? + NExtract::NOperationResult::kDataError : + NExtract::NOperationResult::kOK)); + continue; + } + + const CStreamInfo &si = _db.Streams[streamIndex]; + currentItemUnPacked = si.Resource.UnpackSize; + currentItemPacked = si.Resource.PackSize; + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + Int32 opRes = NExtract::NOperationResult::kOK; + if (streamIndex != prevSuccessStreamIndex || realOutStream) + { + Byte digest[20]; + const CVolume &vol = _volumes[si.PartNumber]; + HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header.IsLzxMode(), + realOutStream, progress, digest); + if (res == S_OK) + { + if (memcmp(digest, si.Hash, kHashSize) == 0) + prevSuccessStreamIndex = streamIndex; + else + opRes = NExtract::NOperationResult::kCRCError; + } + else if (res == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else + return res; + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _db.SortedItems.Size(); + if (!_xmlInComments) + *numItems += _xmls.Size(); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.h new file mode 100644 index 000000000..aa92069a5 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandler.h @@ -0,0 +1,77 @@ +// WimHandler.h + +#ifndef __ARCHIVE_WIM_HANDLER_H +#define __ARCHIVE_WIM_HANDLER_H + +#include "Common/MyCom.h" +#include "Common/MyXml.h" + +#include "WimIn.h" + +namespace NArchive { +namespace NWim { + +struct CVolume +{ + CHeader Header; + CMyComPtr<IInStream> Stream; +}; + +struct CImageInfo +{ + bool CTimeDefined; + bool MTimeDefined; + bool NameDefined; + // bool IndexDefined; + + FILETIME CTime; + FILETIME MTime; + UString Name; + // UInt32 Index; + + CImageInfo(): CTimeDefined(false), MTimeDefined(false), NameDefined(false) + // , IndexDefined(false) + {} + void Parse(const CXmlItem &item); +}; + +struct CXml +{ + CByteBuffer Data; + UInt16 VolIndex; + CObjectVector<CImageInfo> Images; + + void ToUnicode(UString &s); + void Parse(); +}; + + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CDatabase _db; + UInt32 _version; + bool _isOldVersion; + CObjectVector<CVolume> _volumes; + CObjectVector<CXml> _xmls; + int _nameLenForStreams; + bool _xmlInComments; + +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +class COutHandler: + public IOutArchive, + public CMyUnknownImp +{ +public: + MY_UNKNOWN_IMP1(IOutArchive) + INTERFACE_IOutArchive(;) +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandlerOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandlerOut.cpp new file mode 100644 index 000000000..50b879e79 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimHandlerOut.cpp @@ -0,0 +1,639 @@ +// WimHandlerOut.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamUtils.h" + +#include "../../Crypto/RandGen.h" +#include "../../Crypto/Sha1.h" + +#include "WimHandler.h" + +using namespace NWindows; + +namespace NArchive { +namespace NWim { + +struct CSha1Hash +{ + Byte Hash[kHashSize]; +}; + +struct CHashList +{ + CRecordVector<CSha1Hash> Digests; + CIntVector Sorted; + + int AddUnique(const CSha1Hash &h); +}; + +int CHashList::AddUnique(const CSha1Hash &h) +{ + int left = 0, right = Sorted.Size(); + while (left != right) + { + int mid = (left + right) / 2; + int index = Sorted[mid]; + UInt32 i; + const Byte *hash2 = Digests[index].Hash; + for (i = 0; i < kHashSize; i++) + if (h.Hash[i] != hash2[i]) + break; + if (i == kHashSize) + return index; + if (h.Hash[i] < hash2[i]) + right = mid; + else + left = mid + 1; + } + Sorted.Insert(left, Digests.Add(h)); + return -1; +} + +struct CUpdateItem +{ + UString Name; + UInt64 Size; + FILETIME CTime; + FILETIME ATime; + FILETIME MTime; + UInt32 Attrib; + bool IsDir; + int HashIndex; + + CUpdateItem(): HashIndex(-1) {} +}; + +struct CDir +{ + int Index; + UString Name; + CObjectVector<CDir> Dirs; + CIntVector Files; + + CDir(): Index(-1) {} + bool IsLeaf() const { return Index >= 0; } + UInt64 GetNumDirs() const; + UInt64 GetNumFiles() const; + CDir* AddDir(CObjectVector<CUpdateItem> &items, const UString &name, int index); +}; + +UInt64 CDir::GetNumDirs() const +{ + UInt64 num = Dirs.Size(); + for (int i = 0; i < Dirs.Size(); i++) + num += Dirs[i].GetNumDirs(); + return num; +} + +UInt64 CDir::GetNumFiles() const +{ + UInt64 num = Files.Size(); + for (int i = 0; i < Dirs.Size(); i++) + num += Dirs[i].GetNumFiles(); + return num; +} + +CDir* CDir::AddDir(CObjectVector<CUpdateItem> &items, const UString &name, int index) +{ + int left = 0, right = Dirs.Size(); + while (left != right) + { + int mid = (left + right) / 2; + CDir &d = Dirs[mid]; + int compare = name.CompareNoCase(d.IsLeaf() ? items[Dirs[mid].Index].Name : d.Name); + if (compare == 0) + { + if (index >= 0) + d.Index = index; + return &d; + } + if (compare < 0) + right = mid; + else + left = mid + 1; + } + Dirs.Insert(left, CDir()); + CDir &d = Dirs[left]; + d.Index = index; + if (index < 0) + d.Name = name; + return &d; +} + + +STDMETHODIMP COutHandler::GetFileTimeType(UInt32 *type) +{ + *type = NFileTimeType::kWindows; + return S_OK; +} + +static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &ft) +{ + ft.dwLowDateTime = ft.dwHighDateTime = 0; + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(index, propID, &prop)); + if (prop.vt == VT_FILETIME) + ft = prop.filetime; + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +#define Set16(p, d) SetUi16(p, d) +#define Set32(p, d) SetUi32(p, d) +#define Set64(p, d) SetUi64(p, d) + +void CResource::WriteTo(Byte *p) const +{ + Set64(p, PackSize); + p[7] = Flags; + Set64(p + 8, Offset); + Set64(p + 16, UnpackSize); +} + +void CHeader::WriteTo(Byte *p) const +{ + memcpy(p, kSignature, kSignatureSize); + Set32(p + 8, kHeaderSizeMax); + Set32(p + 0xC, Version); + Set32(p + 0x10, Flags); + Set32(p + 0x14, ChunkSize); + memcpy(p + 0x18, Guid, 16); + Set16(p + 0x28, PartNumber); + Set16(p + 0x2A, NumParts); + Set32(p + 0x2C, NumImages); + OffsetResource.WriteTo(p + 0x30); + XmlResource.WriteTo(p + 0x48); + MetadataResource.WriteTo(p + 0x60); + IntegrityResource.WriteTo(p + 0x7C); + Set32(p + 0x78, BootIndex); + memset(p + 0x94, 0, 60); +} + +void CStreamInfo::WriteTo(Byte *p) const +{ + Resource.WriteTo(p); + Set16(p + 0x18, PartNumber); + Set32(p + 0x1A, RefCount); + memcpy(p + 0x1E, Hash, kHashSize); +} + +class CInStreamWithSha1: + public ISequentialInStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialInStream> _stream; + UInt64 _size; + NCrypto::NSha1::CContext _sha; +public: + MY_UNKNOWN_IMP1(IInStream) + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + + void SetStream(ISequentialInStream *stream) { _stream = stream; } + void Init() + { + _size = 0; + _sha.Init(); + } + void ReleaseStream() { _stream.Release(); } + UInt64 GetSize() const { return _size; } + void Final(Byte *digest) { _sha.Final(digest); } +}; + +STDMETHODIMP CInStreamWithSha1::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + UInt32 realProcessedSize; + HRESULT result = _stream->Read(data, size, &realProcessedSize); + _size += realProcessedSize; + _sha.Update((const Byte *)data, realProcessedSize); + if (processedSize != NULL) + *processedSize = realProcessedSize; + return result; +} + +static void SetFileTimeToMem(Byte *p, const FILETIME &ft) +{ + Set32(p, ft.dwLowDateTime); + Set32(p + 4, ft.dwHighDateTime); +} + +static size_t WriteItem(const CUpdateItem &item, Byte *p, const Byte *hash) +{ + int fileNameLen = item.Name.Length() * 2; + int fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); + + size_t totalLen = ((kDirRecordSize + fileNameLen2 + 6) & ~7); + if (p) + { + memset(p, 0, totalLen); + Set64(p, totalLen); + Set64(p + 8, item.Attrib); + Set32(p + 0xC, (UInt32)(Int32)-1); // item.SecurityId + // Set64(p + 0x10, 0); // subdirOffset + SetFileTimeToMem(p + 0x28, item.CTime); + SetFileTimeToMem(p + 0x30, item.ATime); + SetFileTimeToMem(p + 0x38, item.MTime); + if (hash) + memcpy(p + 0x40, hash, kHashSize); + /* + else + memset(p + 0x40, 0, kHashSize); + */ + // Set16(p + 98, 0); // shortNameLen + Set16(p + 100, (UInt16)fileNameLen); + for (int i = 0; i * 2 < fileNameLen; i++) + Set16(p + kDirRecordSize + i * 2, item.Name[i]); + } + return totalLen; +} + +static void WriteTree(const CDir &tree, CRecordVector<CSha1Hash> &digests, + CUpdateItem &defaultDirItem, + CObjectVector<CUpdateItem> &updateItems, Byte *dest, size_t &pos) +{ + int i; + for (i = 0; i < tree.Files.Size(); i++) + { + const CUpdateItem &ui = updateItems[tree.Files[i]]; + pos += WriteItem(ui, dest ? dest + pos : NULL, + ui.HashIndex >= 0 ? digests[ui.HashIndex].Hash : NULL); + } + + size_t posStart = pos; + for (i = 0; i < tree.Dirs.Size(); i++) + { + const CDir &subfolder = tree.Dirs[i]; + CUpdateItem *item = &defaultDirItem; + if (subfolder.IsLeaf()) + item = &updateItems[subfolder.Index]; + else + defaultDirItem.Name = subfolder.Name; + pos += WriteItem(*item, NULL, NULL); + } + + if (dest) + Set64(dest + pos, 0); + + pos += 8; + + for (i = 0; i < tree.Dirs.Size(); i++) + { + const CDir &subfolder = tree.Dirs[i]; + if (dest) + { + CUpdateItem *item = &defaultDirItem; + if (subfolder.IsLeaf()) + item = &updateItems[subfolder.Index]; + else + defaultDirItem.Name = subfolder.Name; + size_t len = WriteItem(*item, dest + posStart, NULL); + Set64(dest + posStart + 0x10, pos); + posStart += len; + } + WriteTree(subfolder, digests, defaultDirItem, updateItems, dest, pos); + } +} + +static void AddTag(AString &s, const char *name, const AString &value) +{ + s += "<"; + s += name; + s += ">"; + s += value; + s += "</"; + s += name; + s += ">"; +} + +static void AddTagUInt64(AString &s, const char *name, UInt64 value) +{ + char temp[32]; + ConvertUInt64ToString(value, temp); + AddTag(s, name, temp); +} + +static AString TimeToXml(FILETIME &ft) +{ + AString res; + char temp[16] = { '0', 'x' }; + ConvertUInt32ToHexWithZeros(ft.dwHighDateTime, temp + 2); + AddTag(res, "HIGHPART", temp); + ConvertUInt32ToHexWithZeros(ft.dwLowDateTime, temp + 2); + AddTag(res, "LOWPART", temp); + return res; +} + +void CHeader::SetDefaultFields(bool useLZX) +{ + Version = kWimVersion; + Flags = NHeaderFlags::kRpFix; + ChunkSize = 0; + if (useLZX) + { + Flags |= NHeaderFlags::kCompression | NHeaderFlags::kLZX; + ChunkSize = kChunkSize; + } + g_RandomGenerator.Generate(Guid, 16); + PartNumber = 1; + NumParts = 1; + NumImages = 1; + BootIndex = 0; + OffsetResource.Clear(); + XmlResource.Clear(); + MetadataResource.Clear(); + IntegrityResource.Clear(); +} + +static HRESULT UpdateArchive(ISequentialOutStream *seqOutStream, + CDir &rootFolder, + CObjectVector<CUpdateItem> &updateItems, + IArchiveUpdateCallback *callback) +{ + CMyComPtr<IOutStream> outStream; + RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream)); + if (!outStream) + return E_NOTIMPL; + + UInt64 complexity = 0; + + int i; + for (i = 0; i < updateItems.Size(); i++) + complexity += updateItems[i].Size; + + RINOK(callback->SetTotal(complexity)); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(callback, true); + + complexity = 0; + + bool useCompression = false; + + CHeader header; + header.SetDefaultFields(useCompression); + Byte buf[kHeaderSizeMax]; + header.WriteTo(buf); + RINOK(WriteStream(outStream, buf, kHeaderSizeMax)); + + CHashList hashes; + CObjectVector<CStreamInfo> streams; + + UInt64 curPos = kHeaderSizeMax; + UInt64 unpackTotalSize = 0; + for (i = 0; i < updateItems.Size(); i++) + { + lps->InSize = lps->OutSize = complexity; + RINOK(lps->SetCur()); + + CUpdateItem &ui = updateItems[i]; + if (ui.IsDir || ui.Size == 0) + continue; + + CInStreamWithSha1 *inShaStreamSpec = new CInStreamWithSha1; + CMyComPtr<ISequentialInStream> inShaStream = inShaStreamSpec; + + { + CMyComPtr<ISequentialInStream> fileInStream; + HRESULT res = callback->GetStream(i, &fileInStream); + if (res != S_FALSE) + { + RINOK(res); + inShaStreamSpec->SetStream(fileInStream); + fileInStream.Release(); + inShaStreamSpec->Init(); + UInt64 offsetBlockSize = 0; + if (useCompression) + { + for (UInt64 t = kChunkSize; t < ui.Size; t += kChunkSize) + { + Byte buf[8]; + SetUi32(buf, (UInt32)t); + RINOK(WriteStream(outStream, buf, 4)); + offsetBlockSize += 4; + } + } + + RINOK(copyCoder->Code(inShaStream, outStream, NULL, NULL, progress)); + ui.Size = copyCoderSpec->TotalSize; + + CSha1Hash hash; + unpackTotalSize += ui.Size; + UInt64 packSize = offsetBlockSize + ui.Size; + inShaStreamSpec->Final(hash.Hash); + int index = hashes.AddUnique(hash); + if (index >= 0) + { + ui.HashIndex = index; + streams[index].RefCount++; + outStream->Seek(-(Int64)packSize, STREAM_SEEK_CUR, &curPos); + outStream->SetSize(curPos); + } + else + { + ui.HashIndex = hashes.Digests.Size() - 1; + CStreamInfo s; + s.Resource.PackSize = packSize; + s.Resource.Offset = curPos; + s.Resource.UnpackSize = ui.Size; + s.Resource.Flags = 0; + if (useCompression) + s.Resource.Flags = NResourceFlags::Compressed; + s.PartNumber = 1; + s.RefCount = 1; + memcpy(s.Hash, hash.Hash, kHashSize); + streams.Add(s); + curPos += packSize; + } + } + fileInStream.Release(); + complexity += ui.Size; + RINOK(callback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + } + } + + + CUpdateItem ri; + FILETIME ft; + NTime::GetCurUtcFileTime(ft); + ri.MTime = ri.ATime = ri.CTime = ft; + ri.Attrib = FILE_ATTRIBUTE_DIRECTORY; + + const UInt32 kSecuritySize = 8; + size_t pos = kSecuritySize; + WriteTree(rootFolder, hashes.Digests, ri, updateItems, NULL, pos); + + CByteBuffer meta; + meta.SetCapacity(pos); + + // we can write 0 here only if there is no security data, imageX does it, + // but some programs expect size = 8 + Set32((Byte *)meta, 8); // size of security data + Set32((Byte *)meta + 4, 0); // num security entries + + pos = kSecuritySize; + WriteTree(rootFolder, hashes.Digests, ri, updateItems, (Byte *)meta, pos); + + { + NCrypto::NSha1::CContext sha; + sha.Init(); + sha.Update((const Byte *)meta, pos); + CSha1Hash digest; + sha.Final(digest.Hash); + + CStreamInfo s; + s.Resource.PackSize = pos; + s.Resource.Offset = curPos; + s.Resource.UnpackSize = pos; + s.Resource.Flags = NResourceFlags::kMetadata; + s.PartNumber = 1; + s.RefCount = 1; + memcpy(s.Hash, digest.Hash, kHashSize); + streams.Add(s); + RINOK(WriteStream(outStream, (const Byte *)meta, pos)); + meta.Free(); + curPos += pos; + } + + + header.OffsetResource.UnpackSize = header.OffsetResource.PackSize = (UInt64)streams.Size() * kStreamInfoSize; + header.OffsetResource.Offset = curPos; + header.OffsetResource.Flags = NResourceFlags::kMetadata; + + for (i = 0; i < streams.Size(); i++) + { + Byte buf[kStreamInfoSize]; + streams[i].WriteTo(buf); + RINOK(WriteStream(outStream, buf, kStreamInfoSize)); + curPos += kStreamInfoSize; + } + + AString xml = "<WIM>"; + AddTagUInt64(xml, "TOTALBYTES", curPos); + xml += "<IMAGE INDEX=\"1\"><NAME>1</NAME>"; + AddTagUInt64(xml, "DIRCOUNT", rootFolder.GetNumDirs()); + AddTagUInt64(xml, "FILECOUNT", rootFolder.GetNumFiles()); + AddTagUInt64(xml, "TOTALBYTES", unpackTotalSize); + NTime::GetCurUtcFileTime(ft); + AddTag(xml, "CREATIONTIME", TimeToXml(ft)); + AddTag(xml, "LASTMODIFICATIONTIME", TimeToXml(ft)); + xml += "</IMAGE></WIM>"; + + size_t xmlSize = (xml.Length() + 1) * 2; + meta.SetCapacity(xmlSize); + Set16((Byte *)meta, 0xFEFF); + for (i = 0; i < xml.Length(); i++) + Set16((Byte *)meta + 2 + i * 2, xml[i]); + RINOK(WriteStream(outStream, (const Byte *)meta, xmlSize)); + meta.Free(); + + header.XmlResource.UnpackSize = header.XmlResource.PackSize = xmlSize; + header.XmlResource.Offset = curPos; + header.XmlResource.Flags = NResourceFlags::kMetadata; + + outStream->Seek(0, STREAM_SEEK_SET, NULL); + header.WriteTo(buf); + return WriteStream(outStream, buf, kHeaderSizeMax); +} + +STDMETHODIMP COutHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *callback) +{ + COM_TRY_BEGIN + CObjectVector<CUpdateItem> updateItems; + CDir tree; + tree.Dirs.Add(CDir()); + CDir &rootFolder = tree.Dirs.Back(); + + for (UInt32 i = 0; i < numItems; i++) + { + CUpdateItem ui; + Int32 newData, newProps; + UInt32 indexInArchive; + if (!callback) + return E_FAIL; + RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidIsDir, &prop)); + if (prop.vt == VT_EMPTY) + ui.IsDir = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + ui.IsDir = (prop.boolVal != VARIANT_FALSE); + } + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidAttrib, &prop)); + if (prop.vt == VT_EMPTY) + ui.Attrib = (ui.IsDir ? FILE_ATTRIBUTE_DIRECTORY : 0); + else if (prop.vt != VT_UI4) + return E_INVALIDARG; + else + ui.Attrib = prop.ulVal; + } + + RINOK(GetTime(callback, i, kpidCTime, ui.CTime)); + RINOK(GetTime(callback, i, kpidATime, ui.ATime)); + RINOK(GetTime(callback, i, kpidMTime, ui.MTime)); + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + ui.Size = prop.uhVal.QuadPart; + } + + UString path; + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidPath, &prop)); + if (prop.vt == VT_BSTR) + path = prop.bstrVal; + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + + CDir *curItem = &rootFolder; + int len = path.Length(); + UString fileName; + for (int j = 0; j < len; j++) + { + wchar_t c = path[j]; + if (c == WCHAR_PATH_SEPARATOR || c == L'/') + { + curItem = curItem->AddDir(updateItems, fileName, -1); + fileName.Empty(); + } + else + fileName += c; + } + + ui.Name = fileName; + updateItems.Add(ui); + if (ui.IsDir) + curItem->AddDir(updateItems, fileName, (int)i); + else + curItem->Files.Add(i); + } + return UpdateArchive(outStream, tree, updateItems, callback); + COM_TRY_END +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.cpp new file mode 100644 index 000000000..c210804df --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.cpp @@ -0,0 +1,855 @@ +// Archive/WimIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/IntToString.h" + +#include "../../Common/StreamUtils.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/LimitedStreams.h" + +#include "../Common/OutStreamWithSha1.h" + +#include "WimIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +namespace NArchive { +namespace NWim { + +namespace NXpress { + +class CDecoderFlusher +{ + CDecoder *m_Decoder; +public: + bool NeedFlush; + CDecoderFlusher(CDecoder *decoder): m_Decoder(decoder), NeedFlush(true) {} + ~CDecoderFlusher() + { + if (NeedFlush) + m_Decoder->Flush(); + m_Decoder->ReleaseStreams(); + } +}; + +HRESULT CDecoder::CodeSpec(UInt32 outSize) +{ + { + Byte levels[kMainTableSize]; + for (unsigned i = 0; i < kMainTableSize; i += 2) + { + Byte b = m_InBitStream.DirectReadByte(); + levels[i] = b & 0xF; + levels[i + 1] = b >> 4; + } + if (!m_MainDecoder.SetCodeLengths(levels)) + return S_FALSE; + } + + while (outSize > 0) + { + UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream); + if (number < 256) + { + m_OutWindowStream.PutByte((Byte)number); + outSize--; + } + else + { + if (number >= kMainTableSize) + return S_FALSE; + UInt32 posLenSlot = number - 256; + UInt32 posSlot = posLenSlot / kNumLenSlots; + UInt32 len = posLenSlot % kNumLenSlots; + UInt32 distance = (1 << posSlot) - 1 + m_InBitStream.ReadBits(posSlot); + + if (len == kNumLenSlots - 1) + { + len = m_InBitStream.DirectReadByte(); + if (len == 0xFF) + { + len = m_InBitStream.DirectReadByte(); + len |= (UInt32)m_InBitStream.DirectReadByte() << 8; + } + else + len += kNumLenSlots - 1; + } + + len += kMatchMinLen; + UInt32 locLen = (len <= outSize ? len : outSize); + + if (!m_OutWindowStream.CopyBlock(distance, locLen)) + return S_FALSE; + + len -= locLen; + outSize -= locLen; + if (len != 0) + return S_FALSE; + } + } + return S_OK; +} + +const UInt32 kDictSize = (1 << kNumPosSlots); + +HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize) +{ + if (!m_OutWindowStream.Create(kDictSize) || !m_InBitStream.Create(1 << 16)) + return E_OUTOFMEMORY; + + CDecoderFlusher flusher(this); + + m_InBitStream.SetStream(inStream); + m_OutWindowStream.SetStream(outStream); + m_InBitStream.Init(); + m_OutWindowStream.Init(false); + + RINOK(CodeSpec(outSize)); + + flusher.NeedFlush = false; + return Flush(); +} + +HRESULT CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize) +{ + try { return CodeReal(inStream, outStream, outSize); } + catch(const CInBufferException &e) { return e.ErrorCode; } \ + catch(const CLzOutWindowException &e) { return e.ErrorCode; } + catch(...) { return S_FALSE; } +} + +} + +HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, + ISequentialOutStream *outStream, ICompressProgressInfo *progress) +{ + RINOK(inStream->Seek(resource.Offset, STREAM_SEEK_SET, NULL)); + + CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream(); + CMyComPtr<ISequentialInStream> limitedStream = limitedStreamSpec; + limitedStreamSpec->SetStream(inStream); + + if (!copyCoder) + { + copyCoderSpec = new NCompress::CCopyCoder; + copyCoder = copyCoderSpec; + } + if (!resource.IsCompressed()) + { + if (resource.PackSize != resource.UnpackSize) + return S_FALSE; + limitedStreamSpec->Init(resource.PackSize); + return copyCoder->Code(limitedStreamSpec, outStream, NULL, NULL, progress); + } + if (resource.UnpackSize == 0) + return S_OK; + UInt64 numChunks = (resource.UnpackSize + kChunkSize - 1) >> kChunkSizeBits; + unsigned entrySize = ((resource.UnpackSize > (UInt64)1 << 32) ? 8 : 4); + UInt64 sizesBufSize64 = entrySize * (numChunks - 1); + size_t sizesBufSize = (size_t)sizesBufSize64; + if (sizesBufSize != sizesBufSize64) + return E_OUTOFMEMORY; + if (sizesBufSize > sizesBuf.GetCapacity()) + { + sizesBuf.Free(); + sizesBuf.SetCapacity(sizesBufSize); + } + RINOK(ReadStream_FALSE(inStream, (Byte *)sizesBuf, sizesBufSize)); + const Byte *p = (const Byte *)sizesBuf; + + if (lzxMode && !lzxDecoder) + { + lzxDecoderSpec = new NCompress::NLzx::CDecoder(true); + lzxDecoder = lzxDecoderSpec; + RINOK(lzxDecoderSpec->SetParams(kChunkSizeBits)); + } + + UInt64 baseOffset = resource.Offset + sizesBufSize64; + UInt64 outProcessed = 0; + for (UInt32 i = 0; i < (UInt32)numChunks; i++) + { + UInt64 offset = 0; + if (i > 0) + { + offset = (entrySize == 4) ? Get32(p): Get64(p); + p += entrySize; + } + UInt64 nextOffset = resource.PackSize - sizesBufSize64; + if (i + 1 < (UInt32)numChunks) + nextOffset = (entrySize == 4) ? Get32(p): Get64(p); + if (nextOffset < offset) + return S_FALSE; + + RINOK(inStream->Seek(baseOffset + offset, STREAM_SEEK_SET, NULL)); + UInt64 inSize = nextOffset - offset; + limitedStreamSpec->Init(inSize); + + if (progress) + { + RINOK(progress->SetRatioInfo(&offset, &outProcessed)); + } + + UInt32 outSize = kChunkSize; + if (outProcessed + outSize > resource.UnpackSize) + outSize = (UInt32)(resource.UnpackSize - outProcessed); + UInt64 outSize64 = outSize; + if (inSize == outSize) + { + RINOK(copyCoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); + } + else + { + if (lzxMode) + { + lzxDecoderSpec->SetKeepHistory(false); + RINOK(lzxDecoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); + } + else + { + RINOK(xpressDecoder.Code(limitedStreamSpec, outStream, outSize)); + } + } + outProcessed += outSize; + } + return S_OK; +} + +HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, + ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest) +{ + COutStreamWithSha1 *shaStreamSpec = new COutStreamWithSha1(); + CMyComPtr<ISequentialOutStream> shaStream = shaStreamSpec; + shaStreamSpec->SetStream(outStream); + shaStreamSpec->Init(digest != NULL); + HRESULT result = Unpack(inStream, resource, lzxMode, shaStream, progress); + if (digest) + shaStreamSpec->Final(digest); + return result; +} + +static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool lzxMode, CByteBuffer &buf, Byte *digest) +{ + size_t size = (size_t)resource.UnpackSize; + if (size != resource.UnpackSize) + return E_OUTOFMEMORY; + buf.Free(); + buf.SetCapacity(size); + + CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream(); + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->Init((Byte *)buf, size); + + CUnpacker unpacker; + return unpacker.Unpack(inStream, resource, lzxMode, outStream, NULL, digest); +} + +void CResource::Parse(const Byte *p) +{ + Flags = p[7]; + PackSize = Get64(p) & (((UInt64)1 << 56) - 1); + Offset = Get64(p + 8); + UnpackSize = Get64(p + 16); +} + +#define GetResource(p, res) res.Parse(p) + +static void GetStream(bool oldVersion, const Byte *p, CStreamInfo &s) +{ + s.Resource.Parse(p); + if (oldVersion) + { + s.PartNumber = 1; + s.Id = Get32(p + 24); + s.RefCount = Get32(p + 28); + memcpy(s.Hash, p + 32, kHashSize); + } + else + { + s.PartNumber = Get16(p + 24); + s.RefCount = Get32(p + 26); + memcpy(s.Hash, p + 30, kHashSize); + } +} + +static const wchar_t *kLongPath = L"[LongPath]"; + +UString CDatabase::GetItemPath(const int index1) const +{ + int size = 0; + int index = index1; + int newLevel; + for (newLevel = 0;; newLevel = 1) + { + const CItem &item = Items[index]; + index = item.Parent; + if (index >= 0 || !SkipRoot) + size += item.Name.Length() + newLevel; + if (index < 0) + break; + if ((UInt32)size >= ((UInt32)1 << 16)) + return kLongPath; + } + + wchar_t temp[16]; + int imageLen = 0; + if (ShowImageNumber) + { + ConvertUInt32ToString(-1 - index, temp); + imageLen = MyStringLen(temp); + size += imageLen + 1; + } + if ((UInt32)size >= ((UInt32)1 << 16)) + return kLongPath; + + UString path; + wchar_t *s = path.GetBuffer(size); + s[size] = 0; + if (ShowImageNumber) + { + memcpy(s, temp, imageLen * sizeof(wchar_t)); + s[imageLen] = WCHAR_PATH_SEPARATOR; + } + + index = index1; + + for (newLevel = 0;; newLevel = 1) + { + const CItem &item = Items[index]; + index = item.Parent; + if (index >= 0 || !SkipRoot) + { + if (newLevel) + s[--size] = WCHAR_PATH_SEPARATOR; + size -= item.Name.Length(); + memcpy(s + size, item.Name, sizeof(wchar_t) * item.Name.Length()); + } + if (index < 0) + { + path.ReleaseBuffer(); + return path; + } + } +} + +static void GetFileTimeFromMem(const Byte *p, FILETIME *ft) +{ + ft->dwLowDateTime = Get32(p); + ft->dwHighDateTime = Get32(p + 4); +} + +static HRESULT ReadName(const Byte *p, int size, UString &dest) +{ + if (size == 0) + return S_OK; + if (Get16(p + size) != 0) + return S_FALSE; + wchar_t *s = dest.GetBuffer(size / 2); + for (int i = 0; i <= size; i += 2) + *s++ = Get16(p + i); + dest.ReleaseBuffer(); + return S_OK; +} + +HRESULT CDatabase::ParseDirItem(size_t pos, int parent) +{ + if ((pos & 7) != 0) + return S_FALSE; + + int prevIndex = -1; + for (int numItems = 0;; numItems++) + { + if (OpenCallback) + { + UInt64 numFiles = Items.Size(); + if ((numFiles & 0x3FF) == 0) + { + RINOK(OpenCallback->SetCompleted(&numFiles, NULL)); + } + } + size_t rem = DirSize - pos; + if (pos < DirStartOffset || pos > DirSize || rem < 8) + return S_FALSE; + const Byte *p = DirData + pos; + UInt64 len = Get64(p); + if (len == 0) + { + if (parent < 0 && numItems != 1) + SkipRoot = false; + DirProcessed += 8; + return S_OK; + } + if ((len & 7) != 0 || rem < len) + return S_FALSE; + if (!IsOldVersion) + if (len < 0x28) + return S_FALSE; + DirProcessed += (size_t)len; + if (DirProcessed > DirSize) + return S_FALSE; + int extraOffset = 0; + if (IsOldVersion) + { + if (len < 0x40 || (/* Get32(p + 12) == 0 && */ Get32(p + 0x14) != 0)) + { + extraOffset = 0x10; + } + } + else if (Get64(p + 8) == 0) + extraOffset = 0x24; + if (extraOffset) + { + if (prevIndex == -1) + return S_FALSE; + UInt32 fileNameLen = Get16(p + extraOffset); + if ((fileNameLen & 1) != 0) + return S_FALSE; + /* Probably different versions of ImageX can use different number of + additional ZEROs. So we don't use exact check. */ + UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); + if (((extraOffset + 2 + fileNameLen2 + 6) & ~7) > len) + return S_FALSE; + + UString name; + RINOK(ReadName(p + extraOffset + 2, fileNameLen, name)); + + CItem &prevItem = Items[prevIndex]; + if (name.IsEmpty() && !prevItem.HasStream()) + { + if (IsOldVersion) + prevItem.Id = Get32(p + 8); + else + memcpy(prevItem.Hash, p + 0x10, kHashSize); + } + else + { + CItem item; + item.Name = prevItem.Name + L':' + name; + item.CTime = prevItem.CTime; + item.ATime = prevItem.ATime; + item.MTime = prevItem.MTime; + if (IsOldVersion) + { + item.Id = Get32(p + 8); + memset(item.Hash, 0, kHashSize); + } + else + memcpy(item.Hash, p + 0x10, kHashSize); + item.Attrib = 0; + item.Order = Order++; + item.Parent = parent; + Items.Add(item); + } + pos += (size_t)len; + continue; + } + + UInt32 dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize; + if (len < dirRecordSize) + return S_FALSE; + + CItem item; + item.Attrib = Get32(p + 8); + // item.SecurityId = Get32(p + 0xC); + UInt64 subdirOffset = Get64(p + 0x10); + UInt32 timeOffset = IsOldVersion ? 0x18: 0x28; + GetFileTimeFromMem(p + timeOffset, &item.CTime); + GetFileTimeFromMem(p + timeOffset + 8, &item.ATime); + GetFileTimeFromMem(p + timeOffset + 16, &item.MTime); + if (IsOldVersion) + { + item.Id = Get32(p + 0x10); + memset(item.Hash, 0, kHashSize); + } + else + { + memcpy(item.Hash, p + 0x40, kHashSize); + } + // UInt32 numStreams = Get16(p + dirRecordSize - 6); + UInt32 shortNameLen = Get16(p + dirRecordSize - 4); + UInt32 fileNameLen = Get16(p + dirRecordSize - 2); + + if ((shortNameLen & 1) != 0 || (fileNameLen & 1) != 0) + return S_FALSE; + + UInt32 shortNameLen2 = (shortNameLen == 0 ? shortNameLen : shortNameLen + 2); + UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); + + if (((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) > len) + return S_FALSE; + p += dirRecordSize; + + RINOK(ReadName(p, fileNameLen, item.Name)); + RINOK(ReadName(p + fileNameLen2, shortNameLen, item.ShortName)); + + if (parent < 0 && (shortNameLen || fileNameLen || !item.IsDir())) + SkipRoot = false; + + /* + // there are some extra data for some files. + p -= dirRecordSize; + p += ((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7); + if (((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) != len) + p = p; + */ + + /* + if (parent >= 0) + { + UString s = GetItemPath(parent) + L"\\" + item.Name; + printf("\n%s %8x %S", item.IsDir() ? "D" : " ", (int)subdirOffset, (const wchar_t *)s); + } + */ + + if (fileNameLen == 0 && item.IsDir() && !item.HasStream()) + item.Attrib = 0x10; // some swm archives have system/hidden attributes for root + + item.Parent = parent; + prevIndex = Items.Add(item); + if (item.IsDir() && subdirOffset != 0) + { + RINOK(ParseDirItem((size_t)subdirOffset, prevIndex)); + } + Items[prevIndex].Order = Order++; + pos += (size_t)len; + } +} + +HRESULT CDatabase::ParseImageDirs(const CByteBuffer &buf, int parent) +{ + DirData = buf; + DirSize = buf.GetCapacity(); + + size_t pos = 0; + if (DirSize < 8) + return S_FALSE; + const Byte *p = DirData; + UInt32 totalLength = Get32(p); + if (IsOldVersion) + { + for (pos = 4;; pos += 8) + { + if (pos + 4 > DirSize) + return S_FALSE; + UInt32 n = Get32(p + pos); + if (n == 0) + break; + if (pos + 8 > DirSize) + return S_FALSE; + totalLength += Get32(p + pos + 4); + if (totalLength > DirSize) + return S_FALSE; + } + pos += totalLength + 4; + pos = (pos + 7) & ~(size_t)7; + if (pos > DirSize) + return S_FALSE; + } + else + { + + // UInt32 numEntries = Get32(p + 4); + pos += 8; + { + /* + CRecordVector<UInt64> entryLens; + UInt64 sum = 0; + for (UInt32 i = 0; i < numEntries; i++) + { + if (pos + 8 > DirSize) + return S_FALSE; + UInt64 len = Get64(p + pos); + entryLens.Add(len); + sum += len; + pos += 8; + } + pos += (size_t)sum; // skip security descriptors + while ((pos & 7) != 0) + pos++; + if (pos != totalLength) + return S_FALSE; + */ + if (totalLength == 0) + pos = 8; + else if (totalLength < 8) + return S_FALSE; + else + pos = totalLength; + } + } + DirStartOffset = DirProcessed = pos; + RINOK(ParseDirItem(pos, parent)); + if (DirProcessed == DirSize) + return S_OK; + /* Original program writes additional 8 bytes (END_OF_ROOT_FOLDER), but + reference to that folder is empty */ + if (DirProcessed == DirSize - 8 && DirProcessed - DirStartOffset == 112 && + Get64(p + DirSize - 8) == 0) + return S_OK; + return S_FALSE; +} + +HRESULT CHeader::Parse(const Byte *p) +{ + UInt32 headerSize = Get32(p + 8); + Version = Get32(p + 0x0C); + Flags = Get32(p + 0x10); + if (!IsSupported()) + return S_FALSE; + ChunkSize = Get32(p + 0x14); + if (ChunkSize != kChunkSize && ChunkSize != 0) + return S_FALSE; + int offset; + if (IsOldVersion()) + { + if (headerSize != 0x60) + return S_FALSE; + memset(Guid, 0, 16); + offset = 0x18; + PartNumber = 1; + NumParts = 1; + } + else + { + if (headerSize < 0x74) + return S_FALSE; + memcpy(Guid, p + 0x18, 16); + PartNumber = Get16(p + 0x28); + NumParts = Get16(p + 0x2A); + offset = 0x2C; + if (IsNewVersion()) + { + NumImages = Get32(p + offset); + offset += 4; + } + } + GetResource(p + offset, OffsetResource); + GetResource(p + offset + 0x18, XmlResource); + GetResource(p + offset + 0x30, MetadataResource); + if (IsNewVersion()) + { + if (headerSize < 0xD0) + return S_FALSE; + BootIndex = Get32(p + 0x48); + IntegrityResource.Parse(p + offset + 0x4C); + } + return S_OK; +} + +const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }; + +HRESULT ReadHeader(IInStream *inStream, CHeader &h) +{ + Byte p[kHeaderSizeMax]; + RINOK(ReadStream_FALSE(inStream, p, kHeaderSizeMax)); + if (memcmp(p, kSignature, kSignatureSize) != 0) + return S_FALSE; + return h.Parse(p); +} + +static HRESULT ReadStreams(bool oldVersion, IInStream *inStream, const CHeader &h, CDatabase &db) +{ + CByteBuffer offsetBuf; + RINOK(UnpackData(inStream, h.OffsetResource, h.IsLzxMode(), offsetBuf, NULL)); + size_t i; + size_t streamInfoSize = oldVersion ? kStreamInfoSize + 2 : kStreamInfoSize; + for (i = 0; offsetBuf.GetCapacity() - i >= streamInfoSize; i += streamInfoSize) + { + CStreamInfo s; + GetStream(oldVersion, (const Byte *)offsetBuf + i, s); + if (s.PartNumber == h.PartNumber) + db.Streams.Add(s); + } + return (i == offsetBuf.GetCapacity()) ? S_OK : S_FALSE; +} + +static bool IsEmptySha(const Byte *data) +{ + for (int i = 0; i < kHashSize; i++) + if (data[i] != 0) + return false; + return true; +} + +HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, CByteBuffer &xml, IArchiveOpenCallback *openCallback) +{ + OpenCallback = openCallback; + IsOldVersion = h.IsOldVersion(); + RINOK(UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL)); + RINOK(ReadStreams(h.IsOldVersion(), inStream, h, *this)); + bool needBootMetadata = !h.MetadataResource.IsEmpty(); + Order = 0; + if (h.PartNumber == 1) + { + int imageIndex = 1; + for (int i = 0; i < Streams.Size(); i++) + { + // if (imageIndex > 1) break; + const CStreamInfo &si = Streams[i]; + if (!si.Resource.IsMetadata() || si.PartNumber != h.PartNumber) + continue; + Byte hash[kHashSize]; + CByteBuffer metadata; + RINOK(UnpackData(inStream, si.Resource, h.IsLzxMode(), metadata, hash)); + if (memcmp(hash, si.Hash, kHashSize) != 0 && + !(h.IsOldVersion() && IsEmptySha(si.Hash))) + return S_FALSE; + NumImages++; + RINOK(ParseImageDirs(metadata, -(int)(++imageIndex))); + if (needBootMetadata) + if (h.MetadataResource.Offset == si.Resource.Offset) + needBootMetadata = false; + } + } + + if (needBootMetadata) + { + CByteBuffer metadata; + RINOK(UnpackData(inStream, h.MetadataResource, h.IsLzxMode(), metadata, NULL)); + RINOK(ParseImageDirs(metadata, -1)); + NumImages++; + } + return S_OK; +} + + +static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, void * /* param */) +{ + int res = MyCompare(p1->PartNumber, p2->PartNumber); + if (res != 0) + return res; + return MyCompare(p1->Resource.Offset, p2->Resource.Offset); +} + +static int CompareIDs(const int *p1, const int *p2, void *param) +{ + const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param; + return MyCompare(streams[*p1].Id, streams[*p2].Id); +} + +static int CompareHashRefs(const int *p1, const int *p2, void *param) +{ + const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param; + return memcmp(streams[*p1].Hash, streams[*p2].Hash, kHashSize); +} + +static int FindId(const CRecordVector<CStreamInfo> &streams, + const CIntVector &sortedByHash, UInt32 id) +{ + int left = 0, right = streams.Size(); + while (left != right) + { + int mid = (left + right) / 2; + int streamIndex = sortedByHash[mid]; + UInt32 id2 = streams[streamIndex].Id; + if (id == id2) + return streamIndex; + if (id < id2) + right = mid; + else + left = mid + 1; + } + return -1; +} + +static int FindHash(const CRecordVector<CStreamInfo> &streams, + const CIntVector &sortedByHash, const Byte *hash) +{ + int left = 0, right = streams.Size(); + while (left != right) + { + int mid = (left + right) / 2; + int streamIndex = sortedByHash[mid]; + UInt32 i; + const Byte *hash2 = streams[streamIndex].Hash; + for (i = 0; i < kHashSize; i++) + if (hash[i] != hash2[i]) + break; + if (i == kHashSize) + return streamIndex; + if (hash[i] < hash2[i]) + right = mid; + else + left = mid + 1; + } + return -1; +} + +static int CompareItems(const int *a1, const int *a2, void *param) +{ + const CObjectVector<CItem> &items = ((CDatabase *)param)->Items; + const CItem &i1 = items[*a1]; + const CItem &i2 = items[*a2]; + + if (i1.IsDir() != i2.IsDir()) + return i1.IsDir() ? 1 : -1; + int res = MyCompare(i1.StreamIndex, i2.StreamIndex); + if (res != 0) + return res; + return MyCompare(i1.Order, i2.Order); +} + +HRESULT CDatabase::Sort(bool skipRootDir) +{ + Streams.Sort(CompareStreamsByPos, NULL); + + { + CIntVector sortedByHash; + { + for (int i = 0; i < Streams.Size(); i++) + sortedByHash.Add(i); + if (IsOldVersion) + sortedByHash.Sort(CompareIDs, &Streams); + else + sortedByHash.Sort(CompareHashRefs, &Streams); + } + + for (int i = 0; i < Items.Size(); i++) + { + CItem &item = Items[i]; + item.StreamIndex = -1; + if (item.HasStream()) + if (IsOldVersion) + item.StreamIndex = FindId(Streams, sortedByHash, item.Id); + else + item.StreamIndex = FindHash(Streams, sortedByHash, item.Hash); + } + } + + { + CRecordVector<bool> used; + int i; + for (i = 0; i < Streams.Size(); i++) + { + const CStreamInfo &s = Streams[i]; + used.Add(s.Resource.IsMetadata() && s.PartNumber == 1); + // used.Add(false); + } + for (i = 0; i < Items.Size(); i++) + { + CItem &item = Items[i]; + if (item.StreamIndex >= 0) + used[item.StreamIndex] = true; + } + for (i = 0; i < Streams.Size(); i++) + if (!used[i]) + { + CItem item; + item.StreamIndex = i; + item.HasMetadata = false; + Items.Add(item); + } + } + + SortedItems.Reserve(Items.Size()); + for (int i = (skipRootDir ? 1 : 0); i < Items.Size(); i++) + SortedItems.Add(i); + SortedItems.Sort(CompareItems, this); + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.h new file mode 100644 index 000000000..da3e28a56 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimIn.h @@ -0,0 +1,297 @@ +// Archive/WimIn.h + +#ifndef __ARCHIVE_WIM_IN_H +#define __ARCHIVE_WIM_IN_H + +#include "Common/Buffer.h" +#include "Common/MyString.h" + +#include "../../Compress/CopyCoder.h" +#include "../../Compress/LzxDecoder.h" + +#include "../IArchive.h" + +namespace NArchive { +namespace NWim { + +namespace NXpress { + +class CBitStream +{ + CInBuffer m_Stream; + UInt32 m_Value; + unsigned m_BitPos; +public: + bool Create(UInt32 bufferSize) { return m_Stream.Create(bufferSize); } + void SetStream(ISequentialInStream *s) { m_Stream.SetStream(s); } + void ReleaseStream() { m_Stream.ReleaseStream(); } + + void Init() { m_Stream.Init(); m_BitPos = 0; } + // UInt64 GetProcessedSize() const { return m_Stream.GetProcessedSize() - m_BitPos / 8; } + Byte DirectReadByte() { return m_Stream.ReadByte(); } + + void Normalize() + { + if (m_BitPos < 16) + { + Byte b0 = m_Stream.ReadByte(); + Byte b1 = m_Stream.ReadByte(); + m_Value = (m_Value << 8) | b1; + m_Value = (m_Value << 8) | b0; + m_BitPos += 16; + } + } + + UInt32 GetValue(unsigned numBits) + { + Normalize(); + return (m_Value >> (m_BitPos - numBits)) & ((1 << numBits) - 1); + } + + void MovePos(unsigned numBits) { m_BitPos -= numBits; } + + UInt32 ReadBits(unsigned numBits) + { + UInt32 res = GetValue(numBits); + m_BitPos -= numBits; + return res; + } +}; + +const unsigned kNumHuffmanBits = 16; +const UInt32 kMatchMinLen = 3; +const UInt32 kNumLenSlots = 16; +const UInt32 kNumPosSlots = 16; +const UInt32 kNumPosLenSlots = kNumPosSlots * kNumLenSlots; +const UInt32 kMainTableSize = 256 + kNumPosLenSlots; + +class CDecoder +{ + CBitStream m_InBitStream; + CLzOutWindow m_OutWindowStream; + NCompress::NHuffman::CDecoder<kNumHuffmanBits, kMainTableSize> m_MainDecoder; + + HRESULT CodeSpec(UInt32 size); + HRESULT CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize); +public: + void ReleaseStreams() + { + m_OutWindowStream.ReleaseStream(); + m_InBitStream.ReleaseStream(); + } + HRESULT Flush() { return m_OutWindowStream.Flush(); } + HRESULT Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize); +}; + +} + +namespace NResourceFlags +{ + const Byte kFree = 1; + const Byte kMetadata = 2; + const Byte Compressed = 4; + const Byte Spanned = 4; +} + +struct CResource +{ + UInt64 PackSize; + UInt64 Offset; + UInt64 UnpackSize; + Byte Flags; + + void Clear() + { + PackSize = 0; + Offset = 0; + UnpackSize = 0; + Flags = 0; + } + void Parse(const Byte *p); + void WriteTo(Byte *p) const; + bool IsFree() const { return (Flags & NResourceFlags::kFree) != 0; } + bool IsMetadata() const { return (Flags & NResourceFlags::kMetadata) != 0; } + bool IsCompressed() const { return (Flags & NResourceFlags::Compressed) != 0; } + bool IsEmpty() const { return (UnpackSize == 0); } +}; + +namespace NHeaderFlags +{ + const UInt32 kCompression = 2; + const UInt32 kSpanned = 8; + const UInt32 kRpFix = 0x80; + const UInt32 kXPRESS = 0x20000; + const UInt32 kLZX = 0x40000; +} + +const UInt32 kWimVersion = 0x010D00; +const UInt32 kHeaderSizeMax = 0xD0; +const UInt32 kSignatureSize = 8; +extern const Byte kSignature[kSignatureSize]; +const unsigned kChunkSizeBits = 15; +const UInt32 kChunkSize = (1 << kChunkSizeBits); + +struct CHeader +{ + UInt32 Version; + UInt32 Flags; + UInt32 ChunkSize; + Byte Guid[16]; + UInt16 PartNumber; + UInt16 NumParts; + UInt32 NumImages; + + CResource OffsetResource; + CResource XmlResource; + CResource MetadataResource; + CResource IntegrityResource; + UInt32 BootIndex; + + void SetDefaultFields(bool useLZX); + + void WriteTo(Byte *p) const; + HRESULT Parse(const Byte *p); + bool IsCompressed() const { return (Flags & NHeaderFlags::kCompression) != 0; } + bool IsSupported() const { return (!IsCompressed() || (Flags & NHeaderFlags::kLZX) != 0 || (Flags & NHeaderFlags::kXPRESS) != 0 ) ; } + bool IsLzxMode() const { return (Flags & NHeaderFlags::kLZX) != 0; } + bool IsSpanned() const { return (!IsCompressed() || (Flags & NHeaderFlags::kSpanned) != 0); } + bool IsOldVersion() const { return (Version <= 0x010A00); } + bool IsNewVersion() const { return (Version > 0x010C00); } + + bool AreFromOnArchive(const CHeader &h) + { + return (memcmp(Guid, h.Guid, sizeof(Guid)) == 0) && (h.NumParts == NumParts); + } +}; + +const UInt32 kHashSize = 20; +const UInt32 kStreamInfoSize = 24 + 2 + 4 + kHashSize; + +struct CStreamInfo +{ + CResource Resource; + UInt16 PartNumber; + UInt32 RefCount; + UInt32 Id; + BYTE Hash[kHashSize]; + + void WriteTo(Byte *p) const; +}; + +const UInt32 kDirRecordSizeOld = 62; +const UInt32 kDirRecordSize = 102; + +struct CItem +{ + UString Name; + UString ShortName; + UInt32 Attrib; + // UInt32 SecurityId; + BYTE Hash[kHashSize]; + UInt32 Id; + FILETIME CTime; + FILETIME ATime; + FILETIME MTime; + // UInt32 ReparseTag; + // UInt64 HardLink; + // UInt16 NumStreams; + int StreamIndex; + int Parent; + unsigned Order; + bool HasMetadata; + CItem(): HasMetadata(true), StreamIndex(-1), Id(0) {} + bool IsDir() const { return HasMetadata && ((Attrib & 0x10) != 0); } + bool HasStream() const + { + for (unsigned i = 0; i < kHashSize; i++) + if (Hash[i] != 0) + return true; + return Id != 0; + } +}; + +class CDatabase +{ + const Byte *DirData; + size_t DirSize; + size_t DirProcessed; + size_t DirStartOffset; + int Order; + IArchiveOpenCallback *OpenCallback; + + HRESULT ParseDirItem(size_t pos, int parent); + HRESULT ParseImageDirs(const CByteBuffer &buf, int parent); + +public: + CRecordVector<CStreamInfo> Streams; + CObjectVector<CItem> Items; + CIntVector SortedItems; + int NumImages; + bool SkipRoot; + bool ShowImageNumber; + + bool IsOldVersion; + + UInt64 GetUnpackSize() const + { + UInt64 res = 0; + for (int i = 0; i < Streams.Size(); i++) + res += Streams[i].Resource.UnpackSize; + return res; + } + + UInt64 GetPackSize() const + { + UInt64 res = 0; + for (int i = 0; i < Streams.Size(); i++) + res += Streams[i].Resource.PackSize; + return res; + } + + void Clear() + { + Streams.Clear(); + Items.Clear(); + SortedItems.Clear(); + NumImages = 0; + + SkipRoot = true; + ShowImageNumber = true; + IsOldVersion = false; + } + + UString GetItemPath(int index) const; + + HRESULT Open(IInStream *inStream, const CHeader &h, CByteBuffer &xml, IArchiveOpenCallback *openCallback); + + void DetectPathMode() + { + ShowImageNumber = (NumImages != 1); + } + + HRESULT Sort(bool skipRootDir); +}; + +HRESULT ReadHeader(IInStream *inStream, CHeader &header); + +class CUnpacker +{ + NCompress::CCopyCoder *copyCoderSpec; + CMyComPtr<ICompressCoder> copyCoder; + + NCompress::NLzx::CDecoder *lzxDecoderSpec; + CMyComPtr<ICompressCoder> lzxDecoder; + + NXpress::CDecoder xpressDecoder; + + CByteBuffer sizesBuf; + HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode, + ISequentialOutStream *outStream, ICompressProgressInfo *progress); +public: + HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode, + ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimRegister.cpp new file mode 100644 index 000000000..8da914360 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Wim/WimRegister.cpp @@ -0,0 +1,18 @@ +// WimRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "WimHandler.h" +static IInArchive *CreateArc() { return new NArchive::NWim::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::NWim::COutHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"wim", L"wim swm", 0, 0xE6, { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }, 8, false, CreateArc, CreateArcOut }; + +REGISTER_ARC(Wim) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/XarHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/XarHandler.cpp new file mode 100644 index 000000000..e7d88b6c7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/XarHandler.cpp @@ -0,0 +1,588 @@ +// XarHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/CpuArch.h" + +#include "Common/ComTry.h" +#include "Common/MyXml.h" +#include "Common/StringToInt.h" +#include "Common/UTFConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/BZip2Decoder.h" +#include "../Compress/CopyCoder.h" +#include "../Compress/ZlibDecoder.h" + +#include "Common/OutStreamWithSha1.h" + +#define XAR_SHOW_RAW + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +namespace NArchive { +namespace NXar { + +struct CFile +{ + AString Name; + AString Method; + UInt64 Size; + UInt64 PackSize; + UInt64 Offset; + + // UInt32 mode; + UInt64 CTime; + UInt64 MTime; + UInt64 ATime; + + bool IsDir; + bool HasData; + + bool Sha1IsDefined; + Byte Sha1[20]; + // bool packSha1IsDefined; + // Byte packSha1[20]; + + int Parent; + + CFile(): IsDir(false), HasData(false), Sha1IsDefined(false), + /* packSha1IsDefined(false), */ + Parent(-1), Size(0), PackSize(0), CTime(0), MTime(0), ATime(0) {} +}; + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + UInt64 _dataStartPos; + CMyComPtr<IInStream> _inStream; + AString _xml; + CObjectVector<CFile> _files; + + HRESULT Open2(IInStream *stream); + HRESULT Extract(IInStream *stream); +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +const UInt32 kXmlSizeMax = ((UInt32)1 << 30) - (1 << 14); + +STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidMethod, VT_BSTR} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +static bool ParseNumber(const char *s, int size, UInt32 &res) +{ + const char *end; + res = (UInt32)ConvertStringToUInt64(s, &end); + return (end - s == size); +} + +static bool ParseUInt64(const CXmlItem &item, const char *name, UInt64 &res) +{ + AString s = item.GetSubStringForTag(name); + const char *end; + res = ConvertStringToUInt64(s, &end); + return (end - (const char *)s == s.Length()); +} + +static UInt64 ParseTime(const CXmlItem &item, const char *name) +{ + AString s = item.GetSubStringForTag(name); + if (s.Length() < 20) + return 0; + const char *p = s; + if (p[ 4] != '-' || p[ 7] != '-' || p[10] != 'T' || + p[13] != ':' || p[16] != ':' || p[19] != 'Z') + return 0; + UInt32 year, month, day, hour, min, sec; + if (!ParseNumber(p, 4, year )) return 0; + if (!ParseNumber(p + 5, 2, month)) return 0; + if (!ParseNumber(p + 8, 2, day )) return 0; + if (!ParseNumber(p + 11, 2, hour )) return 0; + if (!ParseNumber(p + 14, 2, min )) return 0; + if (!ParseNumber(p + 17, 2, sec )) return 0; + + UInt64 numSecs; + if (!NWindows::NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs)) + return 0; + return numSecs * 10000000; +} + +static bool HexToByte(char c, Byte &res) +{ + if (c >= '0' && c <= '9') res = c - '0'; + else if (c >= 'A' && c <= 'F') res = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') res = c - 'a' + 10; + else return false; + return true; +} + +#define METHOD_NAME_ZLIB "zlib" + +static bool ParseSha1(const CXmlItem &item, const char *name, Byte *digest) +{ + int index = item.FindSubTag(name); + if (index < 0) + return false; + const CXmlItem &checkItem = item.SubItems[index]; + AString style = checkItem.GetPropertyValue("style"); + if (style == "SHA1") + { + AString s = checkItem.GetSubString(); + if (s.Length() != 40) + return false; + for (int i = 0; i < s.Length(); i += 2) + { + Byte b0, b1; + if (!HexToByte(s[i], b0) || !HexToByte(s[i + 1], b1)) + return false; + digest[i / 2] = (b0 << 4) | b1; + } + return true; + } + return false; +} + +static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int parent) +{ + if (!item.IsTag) + return true; + if (item.Name == "file") + { + CFile file; + file.Parent = parent; + parent = files.Size(); + file.Name = item.GetSubStringForTag("name"); + AString type = item.GetSubStringForTag("type"); + if (type == "directory") + file.IsDir = true; + else if (type == "file") + file.IsDir = false; + else + return false; + + int dataIndex = item.FindSubTag("data"); + if (dataIndex >= 0 && !file.IsDir) + { + file.HasData = true; + const CXmlItem &dataItem = item.SubItems[dataIndex]; + if (!ParseUInt64(dataItem, "size", file.Size)) + return false; + if (!ParseUInt64(dataItem, "length", file.PackSize)) + return false; + if (!ParseUInt64(dataItem, "offset", file.Offset)) + return false; + file.Sha1IsDefined = ParseSha1(dataItem, "extracted-checksum", file.Sha1); + // file.packSha1IsDefined = ParseSha1(dataItem, "archived-checksum", file.packSha1); + int encodingIndex = dataItem.FindSubTag("encoding"); + if (encodingIndex >= 0) + { + const CXmlItem &encodingItem = dataItem.SubItems[encodingIndex]; + if (encodingItem.IsTag) + { + AString s = encodingItem.GetPropertyValue("style"); + if (s.Length() >= 0) + { + AString appl = "application/"; + if (s.Left(appl.Length()) == appl) + { + s = s.Mid(appl.Length()); + AString xx = "x-"; + if (s.Left(xx.Length()) == xx) + { + s = s.Mid(xx.Length()); + if (s == "gzip") + s = METHOD_NAME_ZLIB; + } + } + file.Method = s; + } + } + } + } + + file.CTime = ParseTime(item, "ctime"); + file.MTime = ParseTime(item, "mtime"); + file.ATime = ParseTime(item, "atime"); + files.Add(file); + } + for (int i = 0; i < item.SubItems.Size(); i++) + if (!AddItem(item.SubItems[i], files, parent)) + return false; + return true; +} + +HRESULT CHandler::Open2(IInStream *stream) +{ + UInt64 archiveStartPos; + RINOK(stream->Seek(0, STREAM_SEEK_SET, &archiveStartPos)); + + const UInt32 kHeaderSize = 0x1C; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); + + UInt32 size = Get16(buf + 4); + // UInt32 ver = Get16(buf + 6); // == 0 + if (Get32(buf) != 0x78617221 || size != kHeaderSize) + return S_FALSE; + + UInt64 packSize = Get64(buf + 8); + UInt64 unpackSize = Get64(buf + 0x10); + // UInt32 checkSumAlogo = Get32(buf + 0x18); + + if (unpackSize >= kXmlSizeMax) + return S_FALSE; + + _dataStartPos = archiveStartPos + kHeaderSize + packSize; + + char *ss = _xml.GetBuffer((int)unpackSize + 1); + + NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); + CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; + + CLimitedSequentialInStream *inStreamLimSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStreamLim(inStreamLimSpec); + inStreamLimSpec->SetStream(stream); + inStreamLimSpec->Init(packSize); + + CBufPtrSeqOutStream *outStreamLimSpec = new CBufPtrSeqOutStream; + CMyComPtr<ISequentialOutStream> outStreamLim(outStreamLimSpec); + outStreamLimSpec->Init((Byte *)ss, (size_t)unpackSize); + + RINOK(zlibCoder->Code(inStreamLim, outStreamLim, NULL, NULL, NULL)); + + if (outStreamLimSpec->GetPos() != (size_t)unpackSize) + return S_FALSE; + + ss[(size_t)unpackSize] = 0; + _xml.ReleaseBuffer(); + + CXml xml; + if (!xml.Parse(_xml)) + return S_FALSE; + + if (!xml.Root.IsTagged("xar") || xml.Root.SubItems.Size() != 1) + return S_FALSE; + const CXmlItem &toc = xml.Root.SubItems[0]; + if (!toc.IsTagged("toc")) + return S_FALSE; + if (!AddItem(toc, _files, -1)) + return S_FALSE; + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + { + Close(); + if (Open2(stream) != S_OK) + return S_FALSE; + _inStream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _inStream.Release(); + _files.Clear(); + _xml.Empty(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _files.Size() + #ifdef XAR_SHOW_RAW + + 1 + #endif + ; + return S_OK; +} + +static void TimeToProp(UInt64 t, NWindows::NCOM::CPropVariant &prop) +{ + if (t != 0) + { + FILETIME ft; + ft.dwLowDateTime = (UInt32)(t); + ft.dwHighDateTime = (UInt32)(t >> 32); + prop = ft; + } +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + #ifdef XAR_SHOW_RAW + if ((int)index == _files.Size()) + { + switch(propID) + { + case kpidPath: prop = L"[TOC].xml"; break; + case kpidSize: + case kpidPackSize: prop = (UInt64)_xml.Length(); break; + } + } + else + #endif + { + const CFile &item = _files[index]; + switch(propID) + { + case kpidMethod: + { + UString name; + if (!item.Method.IsEmpty() && ConvertUTF8ToUnicode(item.Method, name)) + prop = name; + break; + } + case kpidPath: + { + AString path; + int cur = index; + do + { + const CFile &item = _files[cur]; + AString s = item.Name; + if (s.IsEmpty()) + s = "unknown"; + if (path.IsEmpty()) + path = s; + else + path = s + CHAR_PATH_SEPARATOR + path; + cur = item.Parent; + } + while (cur >= 0); + + UString name; + if (ConvertUTF8ToUnicode(path, name)) + prop = name; + break; + } + + case kpidIsDir: prop = item.IsDir; break; + case kpidSize: if (!item.IsDir) prop = item.Size; break; + case kpidPackSize: if (!item.IsDir) prop = item.PackSize; break; + + case kpidMTime: TimeToProp(item.MTime, prop); break; + case kpidCTime: TimeToProp(item.CTime, prop); break; + case kpidATime: TimeToProp(item.ATime, prop); break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = _files.Size(); + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + { + int index = (int)(allFilesMode ? i : indices[i]); + #ifdef XAR_SHOW_RAW + if (index == _files.Size()) + totalSize += _xml.Length(); + else + #endif + totalSize += _files[index].Size; + } + extractCallback->SetTotal(totalSize); + + UInt64 currentPackTotal = 0; + UInt64 currentUnpTotal = 0; + UInt64 currentPackSize = 0; + UInt64 currentUnpSize = 0; + + const UInt32 kZeroBufSize = (1 << 14); + CByteBuffer zeroBuf; + zeroBuf.SetCapacity(kZeroBufSize); + memset(zeroBuf, 0, kZeroBufSize); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder(); + CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec; + + NCompress::NBZip2::CDecoder *bzip2CoderSpec = new NCompress::NBZip2::CDecoder(); + CMyComPtr<ICompressCoder> bzip2Coder = bzip2CoderSpec; + + NCompress::NDeflate::NDecoder::CCOMCoder *deflateCoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder(); + CMyComPtr<ICompressCoder> deflateCoder = deflateCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *inStreamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(inStreamSpec); + inStreamSpec->SetStream(_inStream); + + + CLimitedSequentialOutStream *outStreamLimSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamLimSpec); + + COutStreamWithSha1 *outStreamSha1Spec = new COutStreamWithSha1; + { + CMyComPtr<ISequentialOutStream> outStreamSha1(outStreamSha1Spec); + outStreamLimSpec->SetStream(outStreamSha1); + } + + for (i = 0; i < numItems; i++, currentPackTotal += currentPackSize, currentUnpTotal += currentUnpSize) + { + lps->InSize = currentPackTotal; + lps->OutSize = currentUnpTotal; + currentPackSize = 0; + currentUnpSize = 0; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + if (index < _files.Size()) + { + const CFile &item = _files[index]; + if (item.IsDir) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + } + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSha1Spec->SetStream(realOutStream); + realOutStream.Release(); + + Int32 opRes = NExtract::NOperationResult::kOK; + #ifdef XAR_SHOW_RAW + if (index == _files.Size()) + { + outStreamSha1Spec->Init(false); + outStreamLimSpec->Init(_xml.Length()); + RINOK(WriteStream(outStream, (const char *)_xml, _xml.Length())); + currentPackSize = currentUnpSize = _xml.Length(); + } + else + #endif + { + const CFile &item = _files[index]; + if (item.HasData) + { + currentPackSize = item.PackSize; + currentUnpSize = item.Size; + + RINOK(_inStream->Seek(_dataStartPos + item.Offset, STREAM_SEEK_SET, NULL)); + inStreamSpec->Init(item.PackSize); + outStreamSha1Spec->Init(item.Sha1IsDefined); + outStreamLimSpec->Init(item.Size); + HRESULT res = S_OK; + + ICompressCoder *coder = NULL; + if (item.Method.IsEmpty() || item.Method == "octet-stream") + if (item.PackSize == item.Size) + coder = copyCoder; + else + opRes = NExtract::NOperationResult::kUnSupportedMethod; + else if (item.Method == METHOD_NAME_ZLIB) + coder = zlibCoder; + else if (item.Method == "bzip2") + coder = bzip2Coder; + else + opRes = NExtract::NOperationResult::kUnSupportedMethod; + + if (coder) + res = coder->Code(inStream, outStream, NULL, NULL, progress); + + if (res != S_OK) + { + if (!outStreamLimSpec->IsFinishedOK()) + opRes = NExtract::NOperationResult::kDataError; + else if (res != S_FALSE) + return res; + if (opRes == NExtract::NOperationResult::kOK) + opRes = NExtract::NOperationResult::kDataError; + } + + if (opRes == NExtract::NOperationResult::kOK) + { + if (outStreamLimSpec->IsFinishedOK() && + outStreamSha1Spec->GetSize() == item.Size) + { + if (!outStreamLimSpec->IsFinishedOK()) + { + opRes = NExtract::NOperationResult::kDataError; + } + else if (item.Sha1IsDefined) + { + Byte digest[NCrypto::NSha1::kDigestSize]; + outStreamSha1Spec->Final(digest); + if (memcmp(digest, item.Sha1, NCrypto::NSha1::kDigestSize) != 0) + opRes = NExtract::NOperationResult::kCRCError; + } + } + else + opRes = NExtract::NOperationResult::kDataError; + } + } + } + outStreamSha1Spec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + return S_OK; + COM_TRY_END +} + +static IInArchive *CreateArc() { return new NArchive::NXar::CHandler; } + +static CArcInfo g_ArcInfo = + { L"Xar", L"xar", 0, 0xE1, { 'x', 'a', 'r', '!', 0, 0x1C }, 6, false, CreateArc, 0 }; + +REGISTER_ARC(Xar) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp new file mode 100644 index 000000000..64b7a5863 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp @@ -0,0 +1,707 @@ +// XzHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/Alloc.h" +#include "../../../C/XzCrc64.h" +#include "../../../C/XzEnc.h" + +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" + +#include "../ICoder.h" + +#include "../Common/CWrappers.h" +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "IArchive.h" + +#include "Common/HandlerOut.h" + +using namespace NWindows; + +namespace NCompress { +namespace NLzma2 { + +HRESULT SetLzma2Prop(PROPID propID, const PROPVARIANT &prop, CLzma2EncProps &lzma2Props); + +}} + +static void *SzAlloc(void *, size_t size) { return MyAlloc(size); } +static void SzFree(void *, void *address) { MyFree(address); } +static ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +namespace NArchive { +namespace NXz { + +struct CCrc64Gen { CCrc64Gen() { Crc64GenerateTable(); } } g_Crc64TableInit; + +class CHandler: + public IInArchive, + public IArchiveOpenSeq, + #ifndef EXTRACT_ONLY + public IOutArchive, + public ISetProperties, + public COutHandler, + #endif + public CMyUnknownImp +{ + Int64 _startPosition; + UInt64 _packSize; + UInt64 _unpackSize; + UInt64 _numBlocks; + AString _methodsString; + bool _useSeq; + UInt64 _unpackSizeDefined; + UInt64 _packSizeDefined; + + CMyComPtr<IInStream> _stream; + CMyComPtr<ISequentialInStream> _seqStream; + + UInt32 _crcSize; + + void Init() + { + _crcSize = 4; + COutHandler::Init(); + } + + HRESULT Open2(IInStream *inStream, IArchiveOpenCallback *callback); + +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + MY_QUERYINTERFACE_ENTRY(IArchiveOpenSeq) + #ifndef EXTRACT_ONLY + MY_QUERYINTERFACE_ENTRY(IOutArchive) + MY_QUERYINTERFACE_ENTRY(ISetProperties) + #endif + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + STDMETHOD(OpenSeq)(ISequentialInStream *stream); + + #ifndef EXTRACT_ONLY + INTERFACE_IOutArchive(;) + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProps); + #endif + + CHandler(); +}; + +CHandler::CHandler() +{ + Init(); +} + +STATPROPSTG kProps[] = +{ + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMethod, VT_BSTR} +}; + +STATPROPSTG kArcProps[] = +{ + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static char GetHex(Byte value) +{ + return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); +} + +static inline void AddHexToString(AString &res, Byte value) +{ + res += GetHex((Byte)(value >> 4)); + res += GetHex((Byte)(value & 0xF)); +} + +static AString ConvertUInt32ToString(UInt32 value) +{ + char temp[32]; + ::ConvertUInt32ToString(value, temp); + return temp; +} + +static AString Lzma2PropToString(int prop) +{ + if ((prop & 1) == 0) + return ConvertUInt32ToString(prop / 2 + 12); + AString res; + char c; + + UInt32 size = (2 | ((prop) & 1)) << ((prop) / 2 + 1); + + if (prop > 17) + { + res = ConvertUInt32ToString(size >> 10); + c = 'm'; + } + else + { + res = ConvertUInt32ToString(size); + c = 'k'; + } + return res + c; +} + +struct CMethodNamePair +{ + UInt32 Id; + const char *Name; +}; + +static CMethodNamePair g_NamePairs[] = +{ + { XZ_ID_Subblock, "SB" }, + { XZ_ID_Delta, "Delta" }, + { XZ_ID_X86, "x86" }, + { XZ_ID_PPC, "PPC" }, + { XZ_ID_IA64, "IA64" }, + { XZ_ID_ARM, "ARM" }, + { XZ_ID_ARMT, "ARMT" }, + { XZ_ID_SPARC, "SPARC" }, + { XZ_ID_LZMA2, "LZMA2" } +}; + +static AString GetMethodString(const CXzFilter &f) +{ + AString s; + + for (int i = 0; i < sizeof(g_NamePairs) / sizeof(g_NamePairs[i]); i++) + if (g_NamePairs[i].Id == f.id) + s = g_NamePairs[i].Name; + if (s.IsEmpty()) + { + char temp[32]; + ::ConvertUInt64ToString(f.id, temp); + s = temp; + } + + if (f.propsSize > 0) + { + s += ':'; + if (f.id == XZ_ID_LZMA2 && f.propsSize == 1) + s += Lzma2PropToString(f.props[0]); + else if (f.id == XZ_ID_Delta && f.propsSize == 1) + s += ConvertUInt32ToString((UInt32)f.props[0] + 1); + else + { + s += '['; + for (UInt32 bi = 0; bi < f.propsSize; bi++) + AddHexToString(s, f.props[bi]); + s += ']'; + } + } + return s; +} + +static void AddString(AString &dest, const AString &src) +{ + if (!dest.IsEmpty()) + dest += ' '; + dest += src; +} + +static const char *kChecks[] = +{ + "NoCheck", + "CRC32", + NULL, + NULL, + "CRC64", + NULL, + NULL, + NULL, + NULL, + NULL, + "SHA256", + NULL, + NULL, + NULL, + NULL, + NULL +}; + +static AString GetCheckString(const CXzs &xzs) +{ + size_t i; + UInt32 mask = 0; + for (i = 0; i < xzs.num; i++) + mask |= ((UInt32)1 << XzFlags_GetCheckType(xzs.streams[i].flags)); + AString s; + for (i = 0; i <= XZ_CHECK_MASK; i++) + if (((mask >> i) & 1) != 0) + { + AString s2; + if (kChecks[i]) + s2 = kChecks[i]; + else + s2 = "Check-" + ConvertUInt32ToString((UInt32)i); + AddString(s, s2); + } + return s; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidNumBlocks: if (!_useSeq) prop = _numBlocks; break; + case kpidPhySize: if (_packSizeDefined) prop = _packSize; break; + case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidSize: if (_unpackSizeDefined) prop = _unpackSize; break; + case kpidPackSize: if (_packSizeDefined) prop = _packSize; break; + case kpidMethod: if (!_methodsString.IsEmpty()) prop = _methodsString; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +struct COpenCallbackWrap +{ + ICompressProgress p; + IArchiveOpenCallback *OpenCallback; + HRESULT Res; + COpenCallbackWrap(IArchiveOpenCallback *progress); +}; + +static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) +{ + COpenCallbackWrap *p = (COpenCallbackWrap *)pp; + p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); + return (SRes)p->Res; +} + +COpenCallbackWrap::COpenCallbackWrap(IArchiveOpenCallback *callback) +{ + p.Progress = OpenCallbackProgress; + OpenCallback = callback; + Res = SZ_OK; +} + +struct CXzsCPP +{ + CXzs p; + CXzsCPP() { Xzs_Construct(&p); } + ~CXzsCPP() { Xzs_Free(&p, &g_Alloc); } +}; + +HRESULT CHandler::Open2(IInStream *inStream, IArchiveOpenCallback *callback) +{ + CSeekInStreamWrap inStreamImp(inStream); + + CLookToRead lookStream; + LookToRead_CreateVTable(&lookStream, True); + lookStream.realStream = &inStreamImp.p; + LookToRead_Init(&lookStream); + + COpenCallbackWrap openWrap(callback); + RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize)); + RINOK(callback->SetTotal(NULL, &_packSize)); + + CXzsCPP xzs; + SRes res = Xzs_ReadBackward(&xzs.p, &lookStream.s, &_startPosition, &openWrap.p, &g_Alloc); + if (res == SZ_ERROR_NO_ARCHIVE && xzs.p.num > 0) + res = SZ_OK; + if (res == SZ_OK) + { + _packSize -= _startPosition; + _unpackSize = Xzs_GetUnpackSize(&xzs.p); + _unpackSizeDefined = _packSizeDefined = true; + _numBlocks = (UInt64)Xzs_GetNumBlocks(&xzs.p); + + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + CXzStreamFlags st; + CSeqInStreamWrap inStreamWrap(inStream); + SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p); + + if (res2 == SZ_OK) + { + CXzBlock block; + Bool isIndex; + UInt32 headerSizeRes; + res2 = XzBlock_ReadHeader(&block, &inStreamWrap.p, &isIndex, &headerSizeRes); + if (res2 == SZ_OK && !isIndex) + { + int numFilters = XzBlock_GetNumFilters(&block); + for (int i = 0; i < numFilters; i++) + AddString(_methodsString, GetMethodString(block.filters[i])); + } + } + AddString(_methodsString, GetCheckString(xzs.p)); + } + + if (res != SZ_OK || _startPosition != 0) + { + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + CXzStreamFlags st; + CSeqInStreamWrap inStreamWrap(inStream); + SRes res2 = Xz_ReadHeader(&st, &inStreamWrap.p); + if (res2 == SZ_OK) + { + res = res2; + _startPosition = 0; + _useSeq = True; + _unpackSizeDefined = _packSizeDefined = false; + } + } + if (res == SZ_ERROR_NO_ARCHIVE) + return S_FALSE; + RINOK(SResToHRESULT(res)); + _stream = inStream; + _seqStream = inStream; + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + try + { + Close(); + return Open2(inStream, callback); + } + catch(...) { return S_FALSE; } + COM_TRY_END +} + +STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream) +{ + Close(); + _seqStream = stream; + return S_OK; +} + +STDMETHODIMP CHandler::Close() +{ + _numBlocks = 0; + _useSeq = true; + _unpackSizeDefined = _packSizeDefined = false; + _methodsString.Empty(); + _stream.Release(); + _seqStream.Release(); + return S_OK; +} + +class CSeekToSeqStream: + public IInStream, + public CMyUnknownImp +{ +public: + CMyComPtr<ISequentialInStream> Stream; + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +STDMETHODIMP CSeekToSeqStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + return Stream->Read(data, size, processedSize); +} + +STDMETHODIMP CSeekToSeqStream::Seek(Int64, UInt32, UInt64 *) { return E_NOTIMPL; } + +struct CXzUnpackerCPP +{ + Byte *InBuf; + Byte *OutBuf; + CXzUnpacker p; + CXzUnpackerCPP(): InBuf(0), OutBuf(0) {} + ~CXzUnpackerCPP() + { + XzUnpacker_Free(&p); + MyFree(InBuf); + MyFree(OutBuf); + } +}; + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + extractCallback->SetTotal(_packSize); + UInt64 currentTotalPacked = 0; + RINOK(extractCallback->SetCompleted(¤tTotalPacked)); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + if (_stream) + { + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + } + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + CCompressProgressWrap progressWrap(progress); + + SRes res; + + const UInt32 kInBufSize = 1 << 15; + const UInt32 kOutBufSize = 1 << 21; + + UInt32 inPos = 0; + UInt32 inSize = 0; + UInt32 outPos = 0; + CXzUnpackerCPP xzu; + res = XzUnpacker_Create(&xzu.p, &g_Alloc); + if (res == SZ_OK) + { + xzu.InBuf = (Byte *)MyAlloc(kInBufSize); + xzu.OutBuf = (Byte *)MyAlloc(kOutBufSize); + if (xzu.InBuf == 0 || xzu.OutBuf == 0) + res = SZ_ERROR_MEM; + } + if (res == SZ_OK) + for (;;) + { + if (inPos == inSize) + { + inPos = inSize = 0; + RINOK(_seqStream->Read(xzu.InBuf, kInBufSize, &inSize)); + } + + SizeT inLen = inSize - inPos; + SizeT outLen = kOutBufSize - outPos; + ECoderStatus status; + res = XzUnpacker_Code(&xzu.p, + xzu.OutBuf + outPos, &outLen, + xzu.InBuf + inPos, &inLen, + (inSize == 0 ? CODER_FINISH_END : CODER_FINISH_ANY), &status); + + // printf("\n_inPos = %6d inLen = %5d, outLen = %5d", inPos, inLen, outLen); + + inPos += (UInt32)inLen; + outPos += (UInt32)outLen; + lps->InSize += inLen; + lps->OutSize += outLen; + + bool finished = (((inLen == 0) && (outLen == 0)) || res != SZ_OK); + + if (outPos == kOutBufSize || finished) + { + if (realOutStream && outPos > 0) + { + RINOK(WriteStream(realOutStream, xzu.OutBuf, outPos)); + } + outPos = 0; + } + if (finished) + { + _packSize = lps->InSize; + _unpackSize = lps->OutSize; + _packSizeDefined = _unpackSizeDefined = true; + if (res == SZ_OK) + { + if (status == CODER_STATUS_NEEDS_MORE_INPUT) + { + if (XzUnpacker_IsStreamWasFinished(&xzu.p)) + _packSize -= xzu.p.padSize; + else + res = SZ_ERROR_DATA; + } + else + res = SZ_ERROR_DATA; + } + break; + } + RINOK(lps->SetCur()); + } + + Int32 opRes; + switch(res) + { + case SZ_OK: + opRes = NExtract::NOperationResult::kOK; break; + case SZ_ERROR_UNSUPPORTED: + opRes = NExtract::NOperationResult::kUnSupportedMethod; break; + case SZ_ERROR_CRC: + opRes = NExtract::NOperationResult::kCRCError; break; + case SZ_ERROR_DATA: + case SZ_ERROR_ARCHIVE: + case SZ_ERROR_NO_ARCHIVE: + opRes = NExtract::NOperationResult::kDataError; break; + default: + return SResToHRESULT(res); + } + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(opRes)); + return S_OK; + COM_TRY_END +} + +#ifndef EXTRACT_ONLY + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kUnix; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *updateCallback) +{ + CSeqOutStreamWrap seqOutStream(outStream); + + if (numItems == 0) + { + SRes res = Xz_EncodeEmpty(&seqOutStream.p); + return SResToHRESULT(res); + } + + if (numItems != 1) + return E_INVALIDARG; + + Int32 newData, newProps; + UInt32 indexInArchive; + if (!updateCallback) + return E_FAIL; + RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive)); + + if (IntToBool(newProps)) + { + { + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop)); + if (prop.vt != VT_EMPTY) + if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE) + return E_INVALIDARG; + } + } + + if (IntToBool(newData)) + { + { + UInt64 size; + NCOM::CPropVariant prop; + RINOK(updateCallback->GetProperty(0, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + RINOK(updateCallback->SetTotal(size)); + } + + CLzma2EncProps lzma2Props; + Lzma2EncProps_Init(&lzma2Props); + + lzma2Props.lzmaProps.level = _level; + + CMyComPtr<ISequentialInStream> fileInStream; + RINOK(updateCallback->GetStream(0, &fileInStream)); + + CSeqInStreamWrap seqInStream(fileInStream); + + for (int i = 0; i < _methods.Size(); i++) + { + COneMethodInfo &m = _methods[i]; + SetCompressionMethod2(m + #ifndef _7ZIP_ST + , _numThreads + #endif + ); + if (m.IsLzma()) + { + for (int j = 0; j < m.Props.Size(); j++) + { + const CProp &prop = m.Props[j]; + RINOK(NCompress::NLzma2::SetLzma2Prop(prop.Id, prop.Value, lzma2Props)); + } + } + } + + #ifndef _7ZIP_ST + lzma2Props.numTotalThreads = _numThreads; + #endif + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CCompressProgressWrap progressWrap(progress); + SRes res = Xz_Encode(&seqOutStream.p, &seqInStream.p, &lzma2Props, False, &progressWrap.p); + if (res == SZ_OK) + return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK); + return SResToHRESULT(res); + } + if (indexInArchive != 0) + return E_INVALIDARG; + if (_stream) + RINOK(_stream->Seek(_startPosition, STREAM_SEEK_SET, NULL)); + return NCompress::CopyStream(_stream, outStream, 0); +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProps) +{ + COM_TRY_BEGIN + BeforeSetProperty(); + for (int i = 0; i < numProps; i++) + { + RINOK(SetProperty(names[i], values[i])); + } + return S_OK; + COM_TRY_END +} + +#endif + +static IInArchive *CreateArc() { return new NArchive::NXz::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::NXz::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"xz", L"xz txz", L"* .tar", 0xC, {0xFD, '7' , 'z', 'X', 'Z', '\0'}, 6, true, CreateArc, CreateArcOut }; + +REGISTER_ARC(xz) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/ZHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/ZHandler.cpp new file mode 100644 index 000000000..49b76a116 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/ZHandler.cpp @@ -0,0 +1,161 @@ +// ZHandler.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/ZDecoder.h" + +#include "Common/DummyOutStream.h" + +namespace NArchive { +namespace NZ { + +class CHandler: + public IInArchive, + public CMyUnknownImp +{ + CMyComPtr<IInStream> _stream; + UInt64 _streamStartPosition; + UInt64 _packSize; + Byte _properties; +public: + MY_UNKNOWN_IMP1(IInArchive) + INTERFACE_IInArchive(;) +}; + +STATPROPSTG kProps[] = +{ + { NULL, kpidPackSize, VT_UI8} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidPackSize: prop = _packSize; break; + } + prop.Detach(value); + return S_OK; +} + +static const int kSignatureSize = 3; + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + { + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_streamStartPosition)); + Byte buffer[kSignatureSize]; + RINOK(ReadStream_FALSE(stream, buffer, kSignatureSize)); + if (buffer[0] != 0x1F || buffer[1] != 0x9D) + return S_FALSE; + _properties = buffer[2]; + + UInt64 endPosition; + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPosition)); + _packSize = endPosition - _streamStartPosition - kSignatureSize; + + _stream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _stream.Release(); + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + extractCallback->SetTotal(_packSize); + + UInt64 currentTotalPacked = 0; + + RINOK(extractCallback->SetCompleted(¤tTotalPacked)); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + + RINOK(extractCallback->GetStream(0, &realOutStream, askMode)); + + if (!testMode && !realOutStream) + return S_OK; + + extractCallback->PrepareOperation(askMode); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(); + realOutStream.Release(); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, true); + + RINOK(_stream->Seek(_streamStartPosition + kSignatureSize, STREAM_SEEK_SET, NULL)); + + CMyComPtr<ICompressCoder> decoder; + NCompress::NZ::CDecoder *decoderSpec = new NCompress::NZ::CDecoder; + decoder = decoderSpec; + + HRESULT result = decoderSpec->SetDecoderProperties2(&_properties, 1); + + int opResult; + if (result != S_OK) + opResult = NExtract::NOperationResult::kUnSupportedMethod; + else + { + result = decoder->Code(_stream, outStream, NULL, NULL, progress); + if (result == S_FALSE) + opResult = NExtract::NOperationResult::kDataError; + else + { + RINOK(result); + opResult = NExtract::NOperationResult::kOK; + } + } + outStream.Release(); + return extractCallback->SetOperationResult(opResult); + COM_TRY_END +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"Z", L"z taz", L"* .tar", 5, { 0x1F, 0x9D }, 2, false, CreateArc, 0 }; + +REGISTER_ARC(Z) + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.cpp new file mode 100644 index 000000000..4c5fd38d1 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.cpp @@ -0,0 +1,379 @@ +// ZipAddCommon.cpp + +#include "StdAfx.h" + +#include "../../../../C/7zCrc.h" + +#include "Windows/PropVariant.h" + +#include "../../ICoder.h" +#include "../../IPassword.h" +#include "../../MyVersion.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/LzmaEncoder.h" +#include "../../Compress/PpmdZip.h" + +#include "../Common/InStreamWithCRC.h" + +#include "ZipAddCommon.h" +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +static const CMethodId kMethodId_ZipBase = 0x040100; +static const CMethodId kMethodId_BZip2 = 0x040202; + +static const UInt32 kLzmaPropsSize = 5; +static const UInt32 kLzmaHeaderSize = 4 + kLzmaPropsSize; + +class CLzmaEncoder: + public ICompressCoder, + public CMyUnknownImp +{ + NCompress::NLzma::CEncoder *EncoderSpec; + CMyComPtr<ICompressCoder> Encoder; + Byte Header[kLzmaHeaderSize]; +public: + STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); + HRESULT SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps); + + MY_UNKNOWN_IMP +}; + +HRESULT CLzmaEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps) +{ + if (!Encoder) + { + EncoderSpec = new NCompress::NLzma::CEncoder; + Encoder = EncoderSpec; + } + CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + outStreamSpec->Init(Header + 4, kLzmaPropsSize); + RINOK(EncoderSpec->SetCoderProperties(propIDs, props, numProps)); + RINOK(EncoderSpec->WriteCoderProperties(outStream)); + if (outStreamSpec->GetPos() != kLzmaPropsSize) + return E_FAIL; + Header[0] = MY_VER_MAJOR; + Header[1] = MY_VER_MINOR; + Header[2] = kLzmaPropsSize; + Header[3] = 0; + return S_OK; +} + +HRESULT CLzmaEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress) +{ + RINOK(WriteStream(outStream, Header, kLzmaHeaderSize)); + return Encoder->Code(inStream, outStream, inSize, outSize, progress); +} + + +CAddCommon::CAddCommon(const CCompressionMethodMode &options): + _options(options), + _copyCoderSpec(NULL), + _cryptoStreamSpec(0) + {} + +static HRESULT GetStreamCRC(ISequentialInStream *inStream, UInt32 &resultCRC) +{ + UInt32 crc = CRC_INIT_VAL; + const UInt32 kBufferSize = (1 << 14); + Byte buffer[kBufferSize]; + for (;;) + { + UInt32 realProcessedSize; + RINOK(inStream->Read(buffer, kBufferSize, &realProcessedSize)); + if (realProcessedSize == 0) + { + resultCRC = CRC_GET_DIGEST(crc); + return S_OK; + } + crc = CrcUpdate(crc, buffer, (size_t)realProcessedSize); + } +} + +HRESULT CAddCommon::Compress( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, IOutStream *outStream, + ICompressProgressInfo *progress, CCompressingResult &opRes) +{ + CSequentialInStreamWithCRC *inSecCrcStreamSpec = 0; + CInStreamWithCRC *inCrcStreamSpec = 0; + CMyComPtr<ISequentialInStream> inCrcStream; + { + CMyComPtr<IInStream> inStream2; + // we don't support stdin, since stream from stdin can require 64-bit size header + RINOK(inStream->QueryInterface(IID_IInStream, (void **)&inStream2)); + if (inStream2) + { + inCrcStreamSpec = new CInStreamWithCRC; + inCrcStream = inCrcStreamSpec; + inCrcStreamSpec->SetStream(inStream2); + inCrcStreamSpec->Init(); + } + else + { + inSecCrcStreamSpec = new CSequentialInStreamWithCRC; + inCrcStream = inSecCrcStreamSpec; + inSecCrcStreamSpec->SetStream(inStream); + inSecCrcStreamSpec->Init(); + } + } + + int numTestMethods = _options.MethodSequence.Size(); + if (numTestMethods > 1 || _options.PasswordIsDefined) + { + if (inCrcStreamSpec == 0) + { + if (_options.PasswordIsDefined) + return E_NOTIMPL; + numTestMethods = 1; + } + } + Byte method = 0; + COutStreamReleaser outStreamReleaser; + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Default; + for (int i = 0; i < numTestMethods; i++) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Default; + if (inCrcStreamSpec != 0) + RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(outStream->SetSize(0)); + RINOK(outStream->Seek(0, STREAM_SEEK_SET, NULL)); + if (_options.PasswordIsDefined) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_ZipCrypto; + + if (!_cryptoStream) + { + _cryptoStreamSpec = new CFilterCoder; + _cryptoStream = _cryptoStreamSpec; + } + if (_options.IsAesMode) + { + opRes.ExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_Aes; + if (!_cryptoStreamSpec->Filter) + { + _cryptoStreamSpec->Filter = _filterAesSpec = new NCrypto::NWzAes::CEncoder; + _filterAesSpec->SetKeyMode(_options.AesKeyMode); + RINOK(_filterAesSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Length())); + } + RINOK(_filterAesSpec->WriteHeader(outStream)); + } + else + { + if (!_cryptoStreamSpec->Filter) + { + _cryptoStreamSpec->Filter = _filterSpec = new NCrypto::NZip::CEncoder; + _filterSpec->CryptoSetPassword((const Byte *)(const char *)_options.Password, _options.Password.Length()); + } + UInt32 crc = 0; + RINOK(GetStreamCRC(inStream, crc)); + RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(_filterSpec->WriteHeader(outStream, crc)); + } + RINOK(_cryptoStreamSpec->SetOutStream(outStream)); + outStreamReleaser.FilterCoder = _cryptoStreamSpec; + } + + method = _options.MethodSequence[i]; + switch(method) + { + case NFileHeader::NCompressionMethod::kStored: + { + if (_copyCoderSpec == NULL) + { + _copyCoderSpec = new NCompress::CCopyCoder; + _copyCoder = _copyCoderSpec; + } + CMyComPtr<ISequentialOutStream> outStreamNew; + if (_options.PasswordIsDefined) + outStreamNew = _cryptoStream; + else + outStreamNew = outStream; + RINOK(_copyCoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress)); + break; + } + default: + { + if (!_compressEncoder) + { + if (method == NFileHeader::NCompressionMethod::kLZMA) + { + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_LZMA; + CLzmaEncoder *_lzmaEncoder = new CLzmaEncoder(); + _compressEncoder = _lzmaEncoder; + NWindows::NCOM::CPropVariant props[] = + { + #ifndef _7ZIP_ST + _options.NumThreads, + #endif + _options.Algo, + _options.DicSize, + _options.NumFastBytes, + const_cast<BSTR>((const wchar_t *)_options.MatchFinder), + _options.NumMatchFinderCycles + }; + PROPID propIDs[] = + { + #ifndef _7ZIP_ST + NCoderPropID::kNumThreads, + #endif + NCoderPropID::kAlgorithm, + NCoderPropID::kDictionarySize, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinder, + NCoderPropID::kMatchFinderCycles + }; + int numProps = sizeof(propIDs) / sizeof(propIDs[0]); + if (!_options.NumMatchFinderCyclesDefined) + numProps--; + RINOK(_lzmaEncoder->SetCoderProperties(propIDs, props, numProps)); + } + else if (method == NFileHeader::NCompressionMethod::kPPMd) + { + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_PPMd; + NCompress::NPpmdZip::CEncoder *encoder = new NCompress::NPpmdZip::CEncoder(); + _compressEncoder = encoder; + NWindows::NCOM::CPropVariant props[] = + { + _options.Algo, + _options.MemSize, + _options.Order + + }; + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kUsedMemorySize, + NCoderPropID::kOrder + }; + RINOK(encoder->SetCoderProperties(propIDs, props, sizeof(propIDs) / sizeof(propIDs[0]))); + } + else + { + CMethodId methodId; + switch(method) + { + case NFileHeader::NCompressionMethod::kBZip2: + methodId = kMethodId_BZip2; + _compressExtractVersion = NFileHeader::NCompressionMethod::kExtractVersion_BZip2; + break; + default: + _compressExtractVersion = ((method == NFileHeader::NCompressionMethod::kDeflated64) ? + NFileHeader::NCompressionMethod::kExtractVersion_Deflate64 : + NFileHeader::NCompressionMethod::kExtractVersion_Deflate); + methodId = kMethodId_ZipBase + method; + break; + } + RINOK(CreateCoder( + EXTERNAL_CODECS_LOC_VARS + methodId, _compressEncoder, true)); + if (!_compressEncoder) + return E_NOTIMPL; + + if (method == NFileHeader::NCompressionMethod::kDeflated || + method == NFileHeader::NCompressionMethod::kDeflated64) + { + NWindows::NCOM::CPropVariant props[] = + { + _options.Algo, + _options.NumPasses, + _options.NumFastBytes, + _options.NumMatchFinderCycles + }; + PROPID propIDs[] = + { + NCoderPropID::kAlgorithm, + NCoderPropID::kNumPasses, + NCoderPropID::kNumFastBytes, + NCoderPropID::kMatchFinderCycles + }; + int numProps = sizeof(propIDs) / sizeof(propIDs[0]); + if (!_options.NumMatchFinderCyclesDefined) + numProps--; + CMyComPtr<ICompressSetCoderProperties> setCoderProperties; + _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties); + if (setCoderProperties) + { + RINOK(setCoderProperties->SetCoderProperties(propIDs, props, numProps)); + } + } + else if (method == NFileHeader::NCompressionMethod::kBZip2) + { + NWindows::NCOM::CPropVariant props[] = + { + _options.DicSize, + _options.NumPasses + #ifndef _7ZIP_ST + , _options.NumThreads + #endif + }; + PROPID propIDs[] = + { + NCoderPropID::kDictionarySize, + NCoderPropID::kNumPasses + #ifndef _7ZIP_ST + , NCoderPropID::kNumThreads + #endif + }; + CMyComPtr<ICompressSetCoderProperties> setCoderProperties; + _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties); + if (setCoderProperties) + { + RINOK(setCoderProperties->SetCoderProperties(propIDs, props, sizeof(propIDs) / sizeof(propIDs[0]))); + } + } + } + } + CMyComPtr<ISequentialOutStream> outStreamNew; + if (_options.PasswordIsDefined) + outStreamNew = _cryptoStream; + else + outStreamNew = outStream; + if (_compressExtractVersion > opRes.ExtractVersion) + opRes.ExtractVersion = _compressExtractVersion; + RINOK(_compressEncoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress)); + break; + } + } + + RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &opRes.PackSize)); + + if (inCrcStreamSpec != 0) + { + opRes.CRC = inCrcStreamSpec->GetCRC(); + opRes.UnpackSize = inCrcStreamSpec->GetSize(); + } + else + { + opRes.CRC = inSecCrcStreamSpec->GetCRC(); + opRes.UnpackSize = inSecCrcStreamSpec->GetSize(); + } + + if (_options.PasswordIsDefined) + { + if (opRes.PackSize < opRes.UnpackSize + + (_options.IsAesMode ? _filterAesSpec->GetHeaderSize() : NCrypto::NZip::kHeaderSize)) + break; + } + else if (opRes.PackSize < opRes.UnpackSize) + break; + } + if (_options.IsAesMode) + { + RINOK(_filterAesSpec->WriteFooter(outStream)); + RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &opRes.PackSize)); + } + opRes.Method = method; + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.h new file mode 100644 index 000000000..e4c02db3f --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipAddCommon.h @@ -0,0 +1,56 @@ +// ZipAddCommon.h + +#ifndef __ZIP_ADD_COMMON_H +#define __ZIP_ADD_COMMON_H + +#include "../../ICoder.h" +#include "../../IProgress.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/FilterCoder.h" + +#include "../../Compress/CopyCoder.h" + +#include "../../Crypto/ZipCrypto.h" +#include "../../Crypto/WzAes.h" + +#include "ZipCompressionMode.h" + +namespace NArchive { +namespace NZip { + +struct CCompressingResult +{ + UInt64 UnpackSize; + UInt64 PackSize; + UInt32 CRC; + UInt16 Method; + Byte ExtractVersion; +}; + +class CAddCommon +{ + CCompressionMethodMode _options; + NCompress::CCopyCoder *_copyCoderSpec; + CMyComPtr<ICompressCoder> _copyCoder; + + CMyComPtr<ICompressCoder> _compressEncoder; + Byte _compressExtractVersion; + + CFilterCoder *_cryptoStreamSpec; + CMyComPtr<ISequentialOutStream> _cryptoStream; + + NCrypto::NZip::CEncoder *_filterSpec; + NCrypto::NWzAes::CEncoder *_filterAesSpec; + +public: + CAddCommon(const CCompressionMethodMode &options); + HRESULT Compress( + DECL_EXTERNAL_CODECS_LOC_VARS + ISequentialInStream *inStream, IOutStream *outStream, + ICompressProgressInfo *progress, CCompressingResult &operationResult); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipCompressionMode.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipCompressionMode.h new file mode 100644 index 000000000..7ef7cfb28 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipCompressionMode.h @@ -0,0 +1,42 @@ +// CompressionMode.h + +#ifndef __ZIP_COMPRESSION_MODE_H +#define __ZIP_COMPRESSION_MODE_H + +#include "Common/MyString.h" + +namespace NArchive { +namespace NZip { + +struct CCompressionMethodMode +{ + CRecordVector<Byte> MethodSequence; + UString MatchFinder; + UInt32 Algo; + UInt32 NumPasses; + UInt32 NumFastBytes; + bool NumMatchFinderCyclesDefined; + UInt32 NumMatchFinderCycles; + UInt32 DicSize; + UInt32 MemSize; + UInt32 Order; + + #ifndef _7ZIP_ST + UInt32 NumThreads; + #endif + bool PasswordIsDefined; + AString Password; + bool IsAesMode; + Byte AesKeyMode; + + CCompressionMethodMode(): + NumMatchFinderCyclesDefined(false), + PasswordIsDefined(false), + IsAesMode(false), + AesKeyMode(3) + {} +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.cpp new file mode 100644 index 000000000..bd1563226 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.cpp @@ -0,0 +1,822 @@ +// ZipHandler.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/IntToString.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../IPassword.h" + +#include "../../Common/FilterCoder.h" +#include "../../Common/ProgressUtils.h" +#include "../../Common/StreamObjects.h" +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" +#include "../../Compress/LzmaDecoder.h" +#include "../../Compress/ImplodeDecoder.h" +#include "../../Compress/PpmdZip.h" +#include "../../Compress/ShrinkDecoder.h" + +#include "../../Crypto/WzAes.h" +#include "../../Crypto/ZipCrypto.h" +#include "../../Crypto/ZipStrong.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/OutStreamWithCRC.h" + +#include "ZipHandler.h" + +using namespace NWindows; + +namespace NArchive { +namespace NZip { + +static const CMethodId kMethodId_ZipBase = 0x040100; +static const CMethodId kMethodId_BZip2 = 0x040202; + +static const char *kHostOS[] = +{ + "FAT", + "AMIGA", + "VMS", + "Unix", + "VM/CMS", + "Atari", + "HPFS", + "Macintosh", + "Z-System", + "CP/M", + "TOPS-20", + "NTFS", + "SMS/QDOS", + "Acorn", + "VFAT", + "MVS", + "BeOS", + "Tandem", + "OS/400", + "OS/X" +}; + +static const char *kUnknownOS = "Unknown"; + +static const char *kMethods[] = +{ + "Store", + "Shrink", + "Reduced1", + "Reduced2", + "Reduced3", + "Reduced4", + "Implode", + "Tokenizing", + "Deflate", + "Deflate64", + "PKImploding" +}; + +static const char *kBZip2Method = "BZip2"; +static const char *kLZMAMethod = "LZMA"; +static const char *kJpegMethod = "Jpeg"; +static const char *kWavPackMethod = "WavPack"; +static const char *kPPMdMethod = "PPMd"; +static const char *kAESMethod = "AES"; +static const char *kZipCryptoMethod = "ZipCrypto"; +static const char *kStrongCryptoMethod = "StrongCrypto"; + +static struct CStrongCryptoPair +{ + UInt16 Id; + const char *Name; +} g_StrongCryptoPairs[] = +{ + { NStrongCryptoFlags::kDES, "DES" }, + { NStrongCryptoFlags::kRC2old, "RC2a" }, + { NStrongCryptoFlags::k3DES168, "3DES-168" }, + { NStrongCryptoFlags::k3DES112, "3DES-112" }, + { NStrongCryptoFlags::kAES128, "pkAES-128" }, + { NStrongCryptoFlags::kAES192, "pkAES-192" }, + { NStrongCryptoFlags::kAES256, "pkAES-256" }, + { NStrongCryptoFlags::kRC2, "RC2" }, + { NStrongCryptoFlags::kBlowfish, "Blowfish" }, + { NStrongCryptoFlags::kTwofish, "Twofish" }, + { NStrongCryptoFlags::kRC4, "RC4" } +}; + +static STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidEncrypted, VT_BOOL}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidCRC, VT_UI4}, + { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidHostOS, VT_BSTR}, + { NULL, kpidUnpackVer, VT_UI4} +}; + +static STATPROPSTG kArcProps[] = +{ + { NULL, kpidBit64, VT_BOOL}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidOffset, VT_UI8} +}; + +CHandler::CHandler() +{ + InitMethodProperties(); +} + +static AString BytesToString(const CByteBuffer &data) +{ + AString s; + int size = (int)data.GetCapacity(); + if (size > 0) + { + char *p = s.GetBuffer(size + 1); + memcpy(p, (const Byte *)data, size); + p[size] = '\0'; + s.ReleaseBuffer(); + } + return s; +} + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + switch(propID) + { + case kpidBit64: if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break; + case kpidComment: prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break; + case kpidPhySize: prop = m_Archive.ArcInfo.GetPhySize(); break; + case kpidOffset: if (m_Archive.ArcInfo.StartPosition != 0) prop = m_Archive.ArcInfo.StartPosition; break; + } + prop.Detach(value); + COM_TRY_END + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = m_Items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItemEx &item = m_Items[index]; + switch(propID) + { + case kpidPath: prop = NItemName::GetOSName2(item.GetUnicodeString(item.Name)); break; + case kpidIsDir: prop = item.IsDir(); break; + case kpidSize: prop = item.UnPackSize; break; + case kpidPackSize: prop = item.PackSize; break; + case kpidTimeType: + { + FILETIME ft; + UInt32 unixTime; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, ft)) + prop = (UInt32)NFileTimeType::kWindows; + else if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime)) + prop = (UInt32)NFileTimeType::kUnix; + else + prop = (UInt32)NFileTimeType::kDOS; + break; + } + case kpidCTime: + { + FILETIME ft; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kCTime, ft)) + prop = ft; + break; + } + case kpidATime: + { + FILETIME ft; + if (item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kATime, ft)) + prop = ft; + break; + } + case kpidMTime: + { + FILETIME utc; + if (!item.CentralExtra.GetNtfsTime(NFileHeader::NNtfsExtra::kMTime, utc)) + { + UInt32 unixTime; + if (item.CentralExtra.GetUnixTime(NFileHeader::NUnixTime::kMTime, unixTime)) + NTime::UnixTimeToFileTime(unixTime, utc); + else + { + FILETIME localFileTime; + if (!NTime::DosTimeToFileTime(item.Time, localFileTime) || + !LocalFileTimeToFileTime(&localFileTime, &utc)) + utc.dwHighDateTime = utc.dwLowDateTime = 0; + } + } + prop = utc; + break; + } + case kpidAttrib: prop = item.GetWinAttributes(); break; + case kpidEncrypted: prop = item.IsEncrypted(); break; + case kpidComment: prop = item.GetUnicodeString(BytesToString(item.Comment)); break; + case kpidCRC: if (item.IsThereCrc()) prop = item.FileCRC; break; + case kpidMethod: + { + UInt16 methodId = item.CompressionMethod; + AString method; + if (item.IsEncrypted()) + { + if (methodId == NFileHeader::NCompressionMethod::kWzAES) + { + method = kAESMethod; + CWzAesExtraField aesField; + if (item.CentralExtra.GetWzAesField(aesField)) + { + method += '-'; + char s[32]; + ConvertUInt64ToString((aesField.Strength + 1) * 64 , s); + method += s; + method += ' '; + methodId = aesField.Method; + } + } + else + { + if (item.IsStrongEncrypted()) + { + CStrongCryptoField f; + bool finded = false; + if (item.CentralExtra.GetStrongCryptoField(f)) + { + for (int i = 0; i < sizeof(g_StrongCryptoPairs) / sizeof(g_StrongCryptoPairs[0]); i++) + { + const CStrongCryptoPair &pair = g_StrongCryptoPairs[i]; + if (f.AlgId == pair.Id) + { + method += pair.Name; + finded = true; + break; + } + } + } + if (!finded) + method += kStrongCryptoMethod; + } + else + method += kZipCryptoMethod; + method += ' '; + } + } + if (methodId < sizeof(kMethods) / sizeof(kMethods[0])) + method += kMethods[methodId]; + else switch (methodId) + { + case NFileHeader::NCompressionMethod::kLZMA: + method += kLZMAMethod; + if (item.IsLzmaEOS()) + method += ":EOS"; + break; + case NFileHeader::NCompressionMethod::kBZip2: method += kBZip2Method; break; + case NFileHeader::NCompressionMethod::kJpeg: method += kJpegMethod; break; + case NFileHeader::NCompressionMethod::kWavPack: method += kWavPackMethod; break; + case NFileHeader::NCompressionMethod::kPPMd: method += kPPMdMethod; break; + default: + { + char s[32]; + ConvertUInt64ToString(methodId, s); + method += s; + } + } + prop = method; + break; + } + case kpidHostOS: + prop = (item.MadeByVersion.HostOS < sizeof(kHostOS) / sizeof(kHostOS[0])) ? + (kHostOS[item.MadeByVersion.HostOS]) : kUnknownOS; + break; + case kpidUnpackVer: + prop = (UInt32)item.ExtractVersion.Version; + break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +class CProgressImp: public CProgressVirt +{ + CMyComPtr<IArchiveOpenCallback> _callback; +public: + STDMETHOD(SetTotal)(UInt64 numFiles); + STDMETHOD(SetCompleted)(UInt64 numFiles); + CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {} +}; + +STDMETHODIMP CProgressImp::SetTotal(UInt64 numFiles) +{ + if (_callback) + return _callback->SetTotal(&numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CProgressImp::SetCompleted(UInt64 numFiles) +{ + if (_callback) + return _callback->SetCompleted(&numFiles, NULL); + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *inStream, + const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + try + { + Close(); + RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(m_Archive.Open(inStream, maxCheckStartPosition)); + CProgressImp progressImp(callback); + return m_Archive.ReadHeaders(m_Items, &progressImp); + } + catch(const CInArchiveException &) { Close(); return S_FALSE; } + catch(...) { Close(); throw; } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + m_Items.Clear(); + m_Archive.Close(); + return S_OK; +} + +////////////////////////////////////// +// CHandler::DecompressItems + +class CLzmaDecoder: + public ICompressCoder, + public CMyUnknownImp +{ + NCompress::NLzma::CDecoder *DecoderSpec; + CMyComPtr<ICompressCoder> Decoder; +public: + CLzmaDecoder(); + STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress); + + MY_UNKNOWN_IMP +}; + +CLzmaDecoder::CLzmaDecoder() +{ + DecoderSpec = new NCompress::NLzma::CDecoder; + Decoder = DecoderSpec; +} + +HRESULT CLzmaDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, + const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress) +{ + Byte buf[9]; + RINOK(ReadStream_FALSE(inStream, buf, 9)); + if (buf[2] != 5 || buf[3] != 0) + return E_NOTIMPL; + RINOK(DecoderSpec->SetDecoderProperties2(buf + 4, 5)); + return Decoder->Code(inStream, outStream, NULL, outSize, progress); +} + +struct CMethodItem +{ + UInt16 ZipMethod; + CMyComPtr<ICompressCoder> Coder; +}; + +class CZipDecoder +{ + NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec; + NCrypto::NZipStrong::CDecoder *_pkAesDecoderSpec; + NCrypto::NWzAes::CDecoder *_wzAesDecoderSpec; + + CMyComPtr<ICompressFilter> _zipCryptoDecoder; + CMyComPtr<ICompressFilter> _pkAesDecoder; + CMyComPtr<ICompressFilter> _wzAesDecoder; + + CFilterCoder *filterStreamSpec; + CMyComPtr<ISequentialInStream> filterStream; + CMyComPtr<ICryptoGetTextPassword> getTextPassword; + CObjectVector<CMethodItem> methodItems; + +public: + CZipDecoder(): + _zipCryptoDecoderSpec(0), + _pkAesDecoderSpec(0), + _wzAesDecoderSpec(0), + filterStreamSpec(0) {} + + HRESULT Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + CInArchive &archive, const CItemEx &item, + ISequentialOutStream *realOutStream, + IArchiveExtractCallback *extractCallback, + ICompressProgressInfo *compressProgress, + UInt32 numThreads, Int32 &res); +}; + +HRESULT CZipDecoder::Decode( + DECL_EXTERNAL_CODECS_LOC_VARS + CInArchive &archive, const CItemEx &item, + ISequentialOutStream *realOutStream, + IArchiveExtractCallback *extractCallback, + ICompressProgressInfo *compressProgress, + UInt32 numThreads, Int32 &res) +{ + res = NExtract::NOperationResult::kDataError; + CInStreamReleaser inStreamReleaser; + + bool needCRC = true; + bool wzAesMode = false; + bool pkAesMode = false; + UInt16 methodId = item.CompressionMethod; + if (item.IsEncrypted()) + { + if (item.IsStrongEncrypted()) + { + CStrongCryptoField f; + if (item.CentralExtra.GetStrongCryptoField(f)) + { + pkAesMode = true; + } + if (!pkAesMode) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + } + if (methodId == NFileHeader::NCompressionMethod::kWzAES) + { + CWzAesExtraField aesField; + if (item.CentralExtra.GetWzAesField(aesField)) + { + wzAesMode = true; + needCRC = aesField.NeedCrc(); + } + } + } + + COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC; + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->SetStream(realOutStream); + outStreamSpec->Init(needCRC); + + UInt64 authenticationPos; + + CMyComPtr<ISequentialInStream> inStream; + { + UInt64 packSize = item.PackSize; + if (wzAesMode) + { + if (packSize < NCrypto::NWzAes::kMacSize) + return S_OK; + packSize -= NCrypto::NWzAes::kMacSize; + } + UInt64 dataPos = item.GetDataPosition(); + inStream.Attach(archive.CreateLimitedStream(dataPos, packSize)); + authenticationPos = dataPos + packSize; + } + + CMyComPtr<ICompressFilter> cryptoFilter; + if (item.IsEncrypted()) + { + if (wzAesMode) + { + CWzAesExtraField aesField; + if (!item.CentralExtra.GetWzAesField(aesField)) + return S_OK; + methodId = aesField.Method; + if (!_wzAesDecoder) + { + _wzAesDecoderSpec = new NCrypto::NWzAes::CDecoder; + _wzAesDecoder = _wzAesDecoderSpec; + } + cryptoFilter = _wzAesDecoder; + Byte properties = aesField.Strength; + RINOK(_wzAesDecoderSpec->SetDecoderProperties2(&properties, 1)); + } + else if (pkAesMode) + { + if (!_pkAesDecoder) + { + _pkAesDecoderSpec = new NCrypto::NZipStrong::CDecoder; + _pkAesDecoder = _pkAesDecoderSpec; + } + cryptoFilter = _pkAesDecoder; + } + else + { + if (!_zipCryptoDecoder) + { + _zipCryptoDecoderSpec = new NCrypto::NZip::CDecoder; + _zipCryptoDecoder = _zipCryptoDecoderSpec; + } + cryptoFilter = _zipCryptoDecoder; + } + CMyComPtr<ICryptoSetPassword> cryptoSetPassword; + RINOK(cryptoFilter.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword)); + + if (!getTextPassword) + extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getTextPassword); + + if (getTextPassword) + { + CMyComBSTR password; + RINOK(getTextPassword->CryptoGetTextPassword(&password)); + AString charPassword; + if (wzAesMode || pkAesMode) + { + charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_ACP); + /* + for (int i = 0;; i++) + { + wchar_t c = password[i]; + if (c == 0) + break; + if (c >= 0x80) + { + res = NExtract::NOperationResult::kDataError; + return S_OK; + } + charPassword += (char)c; + } + */ + } + else + { + // we use OEM. WinZip/Windows probably use ANSI for some files + charPassword = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP); + } + HRESULT result = cryptoSetPassword->CryptoSetPassword( + (const Byte *)(const char *)charPassword, charPassword.Length()); + if (result != S_OK) + return S_OK; + } + else + { + RINOK(cryptoSetPassword->CryptoSetPassword(0, 0)); + } + } + + int m; + for (m = 0; m < methodItems.Size(); m++) + if (methodItems[m].ZipMethod == methodId) + break; + + if (m == methodItems.Size()) + { + CMethodItem mi; + mi.ZipMethod = methodId; + if (methodId == NFileHeader::NCompressionMethod::kStored) + mi.Coder = new NCompress::CCopyCoder; + else if (methodId == NFileHeader::NCompressionMethod::kShrunk) + mi.Coder = new NCompress::NShrink::CDecoder; + else if (methodId == NFileHeader::NCompressionMethod::kImploded) + mi.Coder = new NCompress::NImplode::NDecoder::CCoder; + else if (methodId == NFileHeader::NCompressionMethod::kLZMA) + mi.Coder = new CLzmaDecoder; + else if (methodId == NFileHeader::NCompressionMethod::kPPMd) + mi.Coder = new NCompress::NPpmdZip::CDecoder(true); + else + { + CMethodId szMethodID; + if (methodId == NFileHeader::NCompressionMethod::kBZip2) + szMethodID = kMethodId_BZip2; + else + { + if (methodId > 0xFF) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + szMethodID = kMethodId_ZipBase + (Byte)methodId; + } + + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS szMethodID, mi.Coder, false)); + + if (mi.Coder == 0) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + } + m = methodItems.Add(mi); + } + ICompressCoder *coder = methodItems[m].Coder; + + { + CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties; + coder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties); + if (setDecoderProperties) + { + Byte properties = (Byte)item.Flags; + RINOK(setDecoderProperties->SetDecoderProperties2(&properties, 1)); + } + } + + #ifndef _7ZIP_ST + { + CMyComPtr<ICompressSetCoderMt> setCoderMt; + coder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt); + if (setCoderMt) + { + RINOK(setCoderMt->SetNumberOfThreads(numThreads)); + } + } + #endif + + { + HRESULT result = S_OK; + CMyComPtr<ISequentialInStream> inStreamNew; + if (item.IsEncrypted()) + { + if (!filterStream) + { + filterStreamSpec = new CFilterCoder; + filterStream = filterStreamSpec; + } + filterStreamSpec->Filter = cryptoFilter; + if (wzAesMode) + { + result = _wzAesDecoderSpec->ReadHeader(inStream); + } + else if (pkAesMode) + { + result =_pkAesDecoderSpec->ReadHeader(inStream, item.FileCRC, item.UnPackSize); + if (result == S_OK) + { + bool passwOK; + result = _pkAesDecoderSpec->CheckPassword(passwOK); + if (result == S_OK && !passwOK) + result = S_FALSE; + } + } + else + { + result = _zipCryptoDecoderSpec->ReadHeader(inStream); + } + + if (result == S_OK) + { + RINOK(filterStreamSpec->SetInStream(inStream)); + inStreamReleaser.FilterCoder = filterStreamSpec; + inStreamNew = filterStream; + if (wzAesMode) + { + if (!_wzAesDecoderSpec->CheckPasswordVerifyCode()) + result = S_FALSE; + } + } + } + else + inStreamNew = inStream; + if (result == S_OK) + result = coder->Code(inStreamNew, outStream, NULL, &item.UnPackSize, compressProgress); + if (result == S_FALSE) + return S_OK; + if (result == E_NOTIMPL) + { + res = NExtract::NOperationResult::kUnSupportedMethod; + return S_OK; + } + + RINOK(result); + } + bool crcOK = true; + bool authOk = true; + if (needCRC) + crcOK = (outStreamSpec->GetCRC() == item.FileCRC); + if (wzAesMode) + { + inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize)); + if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK) + authOk = false; + } + + res = ((crcOK && authOk) ? + NExtract::NOperationResult::kOK : + NExtract::NOperationResult::kCRCError); + return S_OK; +} + + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + CZipDecoder myDecoder; + UInt64 totalUnPacked = 0, totalPacked = 0; + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = m_Items.Size(); + if(numItems == 0) + return S_OK; + UInt32 i; + for (i = 0; i < numItems; i++) + { + const CItemEx &item = m_Items[allFilesMode ? i : indices[i]]; + totalUnPacked += item.UnPackSize; + totalPacked += item.PackSize; + } + RINOK(extractCallback->SetTotal(totalUnPacked)); + + UInt64 currentTotalUnPacked = 0, currentTotalPacked = 0; + UInt64 currentItemUnPacked, currentItemPacked; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + for (i = 0; i < numItems; i++, currentTotalUnPacked += currentItemUnPacked, + currentTotalPacked += currentItemPacked) + { + currentItemUnPacked = 0; + currentItemPacked = 0; + + lps->InSize = currentTotalPacked; + lps->OutSize = currentTotalUnPacked; + RINOK(lps->SetCur()); + + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + CItemEx item = m_Items[index]; + if (!item.FromLocal) + { + HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item); + if (res == S_FALSE) + { + if (item.IsDir() || realOutStream || testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnSupportedMethod)); + } + continue; + } + RINOK(res); + } + + if (item.IsDir() || item.IgnoreItem()) + { + // if (!testMode) + { + RINOK(extractCallback->PrepareOperation(askMode)); + realOutStream.Release(); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + } + continue; + } + + currentItemUnPacked = item.UnPackSize; + currentItemPacked = item.PackSize; + + if (!testMode && !realOutStream) + continue; + + RINOK(extractCallback->PrepareOperation(askMode)); + + Int32 res; + RINOK(myDecoder.Decode( + EXTERNAL_CODECS_VARS + m_Archive, item, realOutStream, extractCallback, + progress, _numThreads, res)); + realOutStream.Release(); + + RINOK(extractCallback->SetOperationResult(res)) + } + return S_OK; + COM_TRY_END +} + +IMPL_ISetCompressCodecsInfo + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.h new file mode 100644 index 000000000..04cede84e --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandler.h @@ -0,0 +1,101 @@ +// Zip/Handler.h + +#ifndef __ZIP_HANDLER_H +#define __ZIP_HANDLER_H + +#include "Common/DynamicBuffer.h" +#include "../../ICoder.h" +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#include "ZipIn.h" +#include "ZipCompressionMode.h" + +#ifndef _7ZIP_ST +#include "../../../Windows/System.h" +#endif + +namespace NArchive { +namespace NZip { + +class CHandler: + public IInArchive, + public IOutArchive, + public ISetProperties, + PUBLIC_ISetCompressCodecsInfo + public CMyUnknownImp +{ +public: + MY_QUERYINTERFACE_BEGIN2(IInArchive) + MY_QUERYINTERFACE_ENTRY(IOutArchive) + MY_QUERYINTERFACE_ENTRY(ISetProperties) + QUERY_ENTRY_ISetCompressCodecsInfo + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IInArchive(;) + INTERFACE_IOutArchive(;) + + STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties); + + DECL_ISetCompressCodecsInfo + + CHandler(); +private: + CObjectVector<CItemEx> m_Items; + CInArchive m_Archive; + + int m_Level; + int m_MainMethod; + UInt32 m_DicSize; + UInt32 m_Algo; + UInt32 m_NumPasses; + UInt32 m_NumFastBytes; + UInt32 m_NumMatchFinderCycles; + UInt32 m_MemSize; + UInt32 m_Order; + + bool m_NumMatchFinderCyclesDefined; + + bool m_ForceAesMode; + bool m_IsAesMode; + Byte m_AesKeyMode; + + bool m_WriteNtfsTimeExtra; + bool m_ForceLocal; + bool m_ForceUtf8; + + #ifndef _7ZIP_ST + UInt32 _numThreads; + #endif + + DECL_EXTERNAL_CODECS_VARS + + void InitMethodProperties() + { + m_Level = -1; + m_MainMethod = -1; + m_Algo = + m_DicSize = + m_NumPasses = + m_NumFastBytes = + m_Order = + m_MemSize = + m_NumMatchFinderCycles = 0xFFFFFFFF; + m_NumMatchFinderCyclesDefined = false; + m_ForceAesMode = false; + m_IsAesMode = false; + m_AesKeyMode = 3; // aes-256 + m_WriteNtfsTimeExtra = false; + m_ForceLocal = false; + m_ForceUtf8 = false; + #ifndef _7ZIP_ST + _numThreads = NWindows::NSystem::GetNumberOfProcessors();; + #endif + } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp new file mode 100644 index 000000000..ee1c07fae --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHandlerOut.cpp @@ -0,0 +1,537 @@ +// ZipHandlerOut.cpp + +#include "StdAfx.h" + +#include "Common/ComTry.h" +#include "Common/StringConvert.h" +#include "Common/StringToInt.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../../IPassword.h" + +#include "../../Common/OutBuffer.h" + +#include "../../Crypto/WzAes.h" + +#include "../Common/ItemNameUtils.h" +#include "../Common/ParseProperties.h" + +#include "ZipHandler.h" +#include "ZipUpdate.h" + +using namespace NWindows; +using namespace NCOM; +using namespace NTime; + +namespace NArchive { +namespace NZip { + +static const UInt32 kLzAlgoX1 = 0; +static const UInt32 kLzAlgoX5 = 1; + +static const UInt32 kDeflateNumPassesX1 = 1; +static const UInt32 kDeflateNumPassesX7 = 3; +static const UInt32 kDeflateNumPassesX9 = 10; + +static const UInt32 kDeflateNumFastBytesX1 = 32; +static const UInt32 kDeflateNumFastBytesX7 = 64; +static const UInt32 kDeflateNumFastBytesX9 = 128; + +static const wchar_t *kLzmaMatchFinderX1 = L"HC4"; +static const wchar_t *kLzmaMatchFinderX5 = L"BT4"; + +static const UInt32 kLzmaNumFastBytesX1 = 32; +static const UInt32 kLzmaNumFastBytesX7 = 64; + +static const UInt32 kLzmaDicSizeX1 = 1 << 16; +static const UInt32 kLzmaDicSizeX3 = 1 << 20; +static const UInt32 kLzmaDicSizeX5 = 1 << 24; +static const UInt32 kLzmaDicSizeX7 = 1 << 25; +static const UInt32 kLzmaDicSizeX9 = 1 << 26; + +static const UInt32 kBZip2NumPassesX1 = 1; +static const UInt32 kBZip2NumPassesX7 = 2; +static const UInt32 kBZip2NumPassesX9 = 7; + +static const UInt32 kBZip2DicSizeX1 = 100000; +static const UInt32 kBZip2DicSizeX3 = 500000; +static const UInt32 kBZip2DicSizeX5 = 900000; + +STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType) +{ + *timeType = NFileTimeType::kDOS; + return S_OK; +} + +static bool IsAsciiString(const UString &s) +{ + for (int i = 0; i < s.Length(); i++) + { + wchar_t c = s[i]; + if (c < 0x20 || c > 0x7F) + return false; + } + return true; +} + +#define COM_TRY_BEGIN2 try { +#define COM_TRY_END2 } \ +catch(const CSystemException &e) { return e.ErrorCode; } \ +catch(...) { return E_OUTOFMEMORY; } + +static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &filetime) +{ + filetime.dwHighDateTime = filetime.dwLowDateTime = 0; + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(index, propID, &prop)); + if (prop.vt == VT_FILETIME) + filetime = prop.filetime; + else if (prop.vt != VT_EMPTY) + return E_INVALIDARG; + return S_OK; +} + +STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, + IArchiveUpdateCallback *callback) +{ + COM_TRY_BEGIN2 + CObjectVector<CUpdateItem> updateItems; + bool thereAreAesUpdates = false; + for (UInt32 i = 0; i < numItems; i++) + { + CUpdateItem ui; + Int32 newData; + Int32 newProperties; + UInt32 indexInArchive; + if (!callback) + return E_FAIL; + RINOK(callback->GetUpdateItemInfo(i, &newData, &newProperties, &indexInArchive)); + ui.NewProperties = IntToBool(newProperties); + ui.NewData = IntToBool(newData); + ui.IndexInArchive = indexInArchive; + ui.IndexInClient = i; + bool existInArchive = (indexInArchive != (UInt32)-1); + if (existInArchive && newData) + if (m_Items[indexInArchive].IsAesEncrypted()) + thereAreAesUpdates = true; + + if (IntToBool(newProperties)) + { + UString name; + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidAttrib, &prop)); + if (prop.vt == VT_EMPTY) + ui.Attributes = 0; + else if (prop.vt != VT_UI4) + return E_INVALIDARG; + else + ui.Attributes = prop.ulVal; + } + + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidPath, &prop)); + if (prop.vt == VT_EMPTY) + name.Empty(); + else if (prop.vt != VT_BSTR) + return E_INVALIDARG; + else + name = prop.bstrVal; + } + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidIsDir, &prop)); + if (prop.vt == VT_EMPTY) + ui.IsDir = false; + else if (prop.vt != VT_BOOL) + return E_INVALIDARG; + else + ui.IsDir = (prop.boolVal != VARIANT_FALSE); + } + + { + CPropVariant prop; + RINOK(callback->GetProperty(i, kpidTimeType, &prop)); + if (prop.vt == VT_UI4) + ui.NtfsTimeIsDefined = (prop.ulVal == NFileTimeType::kWindows); + else + ui.NtfsTimeIsDefined = m_WriteNtfsTimeExtra; + } + RINOK(GetTime(callback, i, kpidMTime, ui.NtfsMTime)); + RINOK(GetTime(callback, i, kpidATime, ui.NtfsATime)); + RINOK(GetTime(callback, i, kpidCTime, ui.NtfsCTime)); + + { + FILETIME localFileTime = { 0, 0 }; + if (ui.NtfsMTime.dwHighDateTime != 0 || + ui.NtfsMTime.dwLowDateTime != 0) + if (!FileTimeToLocalFileTime(&ui.NtfsMTime, &localFileTime)) + return E_INVALIDARG; + FileTimeToDosTime(localFileTime, ui.Time); + } + + name = NItemName::MakeLegalName(name); + bool needSlash = ui.IsDir; + const wchar_t kSlash = L'/'; + if (!name.IsEmpty()) + { + if (name[name.Length() - 1] == kSlash) + { + if (!ui.IsDir) + return E_INVALIDARG; + needSlash = false; + } + } + if (needSlash) + name += kSlash; + + bool tryUtf8 = true; + if (m_ForceLocal || !m_ForceUtf8) + { +#ifdef _WIN32 + bool defaultCharWasUsed; + ui.Name = UnicodeStringToMultiByte(name, CP_OEMCP, '_', defaultCharWasUsed); + tryUtf8 = (!m_ForceLocal && (defaultCharWasUsed || + MultiByteToUnicodeString(ui.Name, CP_OEMCP) != name)); +#else + // FIXME + ui.Name = UnicodeStringToMultiByte(name, CP_OEMCP); + tryUtf8 = (!m_ForceLocal); +#endif + } + + if (tryUtf8) + { + int i; + for (i = 0; i < name.Length() && (unsigned)name[i] < 0x80; i++); + ui.IsUtf8 = (i != name.Length()); + if (!ConvertUnicodeToUTF8(name, ui.Name)) + return E_INVALIDARG; + } + + if (ui.Name.Length() >= (1 << 16)) + return E_INVALIDARG; + + ui.IndexInClient = i; + /* + if (existInArchive) + { + const CItemEx &itemInfo = m_Items[indexInArchive]; + // ui.Commented = itemInfo.IsCommented(); + ui.Commented = false; + if (ui.Commented) + { + ui.CommentRange.Position = itemInfo.GetCommentPosition(); + ui.CommentRange.Size = itemInfo.CommentSize; + } + } + else + ui.Commented = false; + */ + } + if (IntToBool(newData)) + { + UInt64 size; + { + NCOM::CPropVariant prop; + RINOK(callback->GetProperty(i, kpidSize, &prop)); + if (prop.vt != VT_UI8) + return E_INVALIDARG; + size = prop.uhVal.QuadPart; + } + ui.Size = size; + } + updateItems.Add(ui); + } + + CMyComPtr<ICryptoGetTextPassword2> getTextPassword; + { + CMyComPtr<IArchiveUpdateCallback> udateCallBack2(callback); + udateCallBack2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword); + } + CCompressionMethodMode options; + + if (getTextPassword) + { + CMyComBSTR password; + Int32 passwordIsDefined; + RINOK(getTextPassword->CryptoGetTextPassword2(&passwordIsDefined, &password)); + options.PasswordIsDefined = IntToBool(passwordIsDefined); + if (options.PasswordIsDefined) + { + options.IsAesMode = (m_ForceAesMode ? m_IsAesMode : thereAreAesUpdates); + options.AesKeyMode = m_AesKeyMode; + + if (!IsAsciiString((const wchar_t *)password)) + return E_INVALIDARG; + if (options.IsAesMode) + { + if (options.Password.Length() > NCrypto::NWzAes::kPasswordSizeMax) + return E_INVALIDARG; + } + options.Password = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP); + } + } + else + options.PasswordIsDefined = false; + + int level = m_Level; + if (level < 0) + level = 5; + + Byte mainMethod; + if (m_MainMethod < 0) + mainMethod = (Byte)(((level == 0) ? + NFileHeader::NCompressionMethod::kStored : + NFileHeader::NCompressionMethod::kDeflated)); + else + mainMethod = (Byte)m_MainMethod; + options.MethodSequence.Add(mainMethod); + if (mainMethod != NFileHeader::NCompressionMethod::kStored) + options.MethodSequence.Add(NFileHeader::NCompressionMethod::kStored); + bool isDeflate = (mainMethod == NFileHeader::NCompressionMethod::kDeflated) || + (mainMethod == NFileHeader::NCompressionMethod::kDeflated64); + bool isLZMA = (mainMethod == NFileHeader::NCompressionMethod::kLZMA); + bool isLz = (isLZMA || isDeflate); + options.NumPasses = m_NumPasses; + options.DicSize = m_DicSize; + options.NumFastBytes = m_NumFastBytes; + options.NumMatchFinderCycles = m_NumMatchFinderCycles; + options.NumMatchFinderCyclesDefined = m_NumMatchFinderCyclesDefined; + options.Algo = m_Algo; + options.MemSize = m_MemSize; + options.Order = m_Order; + #ifndef _7ZIP_ST + options.NumThreads = _numThreads; + #endif + if (isLz) + { + if (isDeflate) + { + if (options.NumPasses == 0xFFFFFFFF) + options.NumPasses = (level >= 9 ? kDeflateNumPassesX9 : + (level >= 7 ? kDeflateNumPassesX7 : + kDeflateNumPassesX1)); + if (options.NumFastBytes == 0xFFFFFFFF) + options.NumFastBytes = (level >= 9 ? kDeflateNumFastBytesX9 : + (level >= 7 ? kDeflateNumFastBytesX7 : + kDeflateNumFastBytesX1)); + } + else if (isLZMA) + { + if (options.DicSize == 0xFFFFFFFF) + options.DicSize = + (level >= 9 ? kLzmaDicSizeX9 : + (level >= 7 ? kLzmaDicSizeX7 : + (level >= 5 ? kLzmaDicSizeX5 : + (level >= 3 ? kLzmaDicSizeX3 : + kLzmaDicSizeX1)))); + + if (options.NumFastBytes == 0xFFFFFFFF) + options.NumFastBytes = (level >= 7 ? kLzmaNumFastBytesX7 : + kLzmaNumFastBytesX1); + + options.MatchFinder = + (level >= 5 ? kLzmaMatchFinderX5 : + kLzmaMatchFinderX1); + } + + if (options.Algo == 0xFFFFFFFF) + options.Algo = (level >= 5 ? kLzAlgoX5 : + kLzAlgoX1); + } + if (mainMethod == NFileHeader::NCompressionMethod::kBZip2) + { + if (options.NumPasses == 0xFFFFFFFF) + options.NumPasses = (level >= 9 ? kBZip2NumPassesX9 : + (level >= 7 ? kBZip2NumPassesX7 : + kBZip2NumPassesX1)); + if (options.DicSize == 0xFFFFFFFF) + options.DicSize = (level >= 5 ? kBZip2DicSizeX5 : + (level >= 3 ? kBZip2DicSizeX3 : + kBZip2DicSizeX1)); + } + if (mainMethod == NFileHeader::NCompressionMethod::kPPMd) + { + int level2 = level; + if (level2 < 1) level2 = 1; + if (level2 > 9) level2 = 9; + + if (options.MemSize == 0xFFFFFFFF) + options.MemSize = (1 << (19 + (level2 > 8 ? 8 : level2))); + + if (options.Order == 0xFFFFFFFF) + options.Order = 3 + level2; + + if (options.Algo == 0xFFFFFFFF) + options.Algo = (level2 >= 7 ? 1 : 0); + } + + return Update( + EXTERNAL_CODECS_VARS + m_Items, updateItems, outStream, + m_Archive.IsOpen() ? &m_Archive : NULL, &options, callback); + COM_TRY_END2 +} + +STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) +{ + #ifndef _7ZIP_ST + const UInt32 numProcessors = NSystem::GetNumberOfProcessors(); + _numThreads = numProcessors; + #endif + InitMethodProperties(); + for (int i = 0; i < numProperties; i++) + { + UString name = UString(names[i]); + name.MakeUpper(); + if (name.IsEmpty()) + return E_INVALIDARG; + + const PROPVARIANT &prop = values[i]; + + if (name[0] == L'X') + { + UInt32 level = 9; + RINOK(ParsePropValue(name.Mid(1), prop, level)); + m_Level = level; + continue; + } + else if (name == L"M") + { + if (prop.vt == VT_BSTR) + { + UString m = prop.bstrVal; + m.MakeUpper(); + if (m == L"COPY") m_MainMethod = NFileHeader::NCompressionMethod::kStored; + else if (m == L"DEFLATE") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated; + else if (m == L"DEFLATE64") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated64; + else if (m == L"BZIP2") m_MainMethod = NFileHeader::NCompressionMethod::kBZip2; + else if (m == L"LZMA") m_MainMethod = NFileHeader::NCompressionMethod::kLZMA; + else if (m == L"PPMD") m_MainMethod = NFileHeader::NCompressionMethod::kPPMd; + else return E_INVALIDARG; + } + else if (prop.vt == VT_UI4) + { + switch(prop.ulVal) + { + case NFileHeader::NCompressionMethod::kStored: + case NFileHeader::NCompressionMethod::kDeflated: + case NFileHeader::NCompressionMethod::kDeflated64: + case NFileHeader::NCompressionMethod::kBZip2: + case NFileHeader::NCompressionMethod::kLZMA: + m_MainMethod = (Byte)prop.ulVal; + break; + default: + return E_INVALIDARG; + } + } + else + return E_INVALIDARG; + } + else if (name.Left(2) == L"EM") + { + if (prop.vt == VT_BSTR) + { + UString valueString = prop.bstrVal; + valueString.MakeUpper(); + if (valueString.Left(3) == L"AES") + { + valueString = valueString.Mid(3); + if (valueString == L"128") + m_AesKeyMode = 1; + else if (valueString == L"192") + m_AesKeyMode = 2; + else if (valueString == L"256" || valueString.IsEmpty()) + m_AesKeyMode = 3; + else + return E_INVALIDARG; + m_IsAesMode = true; + m_ForceAesMode = true; + } + else if (valueString == L"ZIPCRYPTO") + { + m_IsAesMode = false; + m_ForceAesMode = true; + } + else + return E_INVALIDARG; + } + else + return E_INVALIDARG; + } + else if (name[0] == L'D') + { + UInt32 dicSize = kBZip2DicSizeX5; + RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize)); + m_DicSize = dicSize; + } + else if (name.Left(3) == L"MEM") + { + UInt32 memSize = 1 << 24; + RINOK(ParsePropDictionaryValue(name.Mid(3), prop, memSize)); + m_MemSize = memSize; + } + else if (name[0] == L'O') + { + UInt32 order = 8; + RINOK(ParsePropValue(name.Mid(1), prop, order)); + m_Order = order; + } + else if (name.Left(4) == L"PASS") + { + UInt32 num = kDeflateNumPassesX9; + RINOK(ParsePropValue(name.Mid(4), prop, num)); + m_NumPasses = num; + } + else if (name.Left(2) == L"FB") + { + UInt32 num = kDeflateNumFastBytesX9; + RINOK(ParsePropValue(name.Mid(2), prop, num)); + m_NumFastBytes = num; + } + else if (name.Left(2) == L"MC") + { + UInt32 num = 0xFFFFFFFF; + RINOK(ParsePropValue(name.Mid(2), prop, num)); + m_NumMatchFinderCycles = num; + m_NumMatchFinderCyclesDefined = true; + } + else if (name.Left(2) == L"MT") + { + #ifndef _7ZIP_ST + RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads)); + #endif + } + else if (name.Left(1) == L"A") + { + UInt32 num = kLzAlgoX5; + RINOK(ParsePropValue(name.Mid(1), prop, num)); + m_Algo = num; + } + else if (name.CompareNoCase(L"TC") == 0) + { + RINOK(SetBoolProperty(m_WriteNtfsTimeExtra, prop)); + } + else if (name.CompareNoCase(L"CL") == 0) + { + RINOK(SetBoolProperty(m_ForceLocal, prop)); + if (m_ForceLocal) + m_ForceUtf8 = false; + } + else if (name.CompareNoCase(L"CU") == 0) + { + RINOK(SetBoolProperty(m_ForceUtf8, prop)); + if (m_ForceUtf8) + m_ForceLocal = false; + } + else + return E_INVALIDARG; + } + return S_OK; +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.cpp new file mode 100644 index 000000000..582187b51 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.cpp @@ -0,0 +1,36 @@ +// Archive/Zip/Header.h + +#include "StdAfx.h" + +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +namespace NSignature +{ + UInt32 kLocalFileHeader = 0x04034B50 + 1; + UInt32 kDataDescriptor = 0x08074B50 + 1; + UInt32 kCentralFileHeader = 0x02014B50 + 1; + UInt32 kEndOfCentralDir = 0x06054B50 + 1; + UInt32 kZip64EndOfCentralDir = 0x06064B50 + 1; + UInt32 kZip64EndOfCentralDirLocator = 0x07064B50 + 1; + + class CMarkersInitializer + { + public: + CMarkersInitializer() + { + kLocalFileHeader--; + kDataDescriptor--; + kCentralFileHeader--; + kEndOfCentralDir--; + kZip64EndOfCentralDir--; + kZip64EndOfCentralDirLocator--; + } + }; + static CMarkersInitializer g_MarkerInitializer; +} + +}} + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.h new file mode 100644 index 000000000..ce8c1e4f7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipHeader.h @@ -0,0 +1,284 @@ +// Archive/Zip/Header.h + +#ifndef __ARCHIVE_ZIP_HEADER_H +#define __ARCHIVE_ZIP_HEADER_H + +#include "Common/Types.h" + +namespace NArchive { +namespace NZip { + +namespace NSignature +{ + extern UInt32 kLocalFileHeader; + extern UInt32 kDataDescriptor; + extern UInt32 kCentralFileHeader; + extern UInt32 kEndOfCentralDir; + extern UInt32 kZip64EndOfCentralDir; + extern UInt32 kZip64EndOfCentralDirLocator; + + static const UInt32 kMarkerSize = 4; +} + +const UInt32 kEcdSize = 22; +const UInt32 kZip64EcdSize = 44; +const UInt32 kZip64EcdLocatorSize = 20; +/* +struct CEndOfCentralDirectoryRecord +{ + UInt16 ThisDiskNumber; + UInt16 StartCentralDirectoryDiskNumber; + UInt16 NumEntriesInCentaralDirectoryOnThisDisk; + UInt16 NumEntriesInCentaralDirectory; + UInt32 CentralDirectorySize; + UInt32 CentralDirectoryStartOffset; + UInt16 CommentSize; +}; + +struct CEndOfCentralDirectoryRecordFull +{ + UInt32 Signature; + CEndOfCentralDirectoryRecord Header; +}; +*/ + +namespace NFileHeader +{ + /* + struct CVersion + { + Byte Version; + Byte HostOS; + }; + */ + + namespace NCompressionMethod + { + enum EType + { + kStored = 0, + kShrunk = 1, + kReduced1 = 2, + kReduced2 = 3, + kReduced3 = 4, + kReduced4 = 5, + kImploded = 6, + kReservedTokenizing = 7, // reserved for tokenizing + kDeflated = 8, + kDeflated64 = 9, + kPKImploding = 10, + + kBZip2 = 12, + kLZMA = 14, + kTerse = 18, + kLz77 = 19, + kJpeg = 0x60, + kWavPack = 0x61, + kPPMd = 0x62, + kWzAES = 0x63 + }; + const int kNumCompressionMethods = 11; + const Byte kMadeByProgramVersion = 63; + + const Byte kExtractVersion_Default = 10; + const Byte kExtractVersion_Dir = 20; + const Byte kExtractVersion_ZipCrypto = 20; + const Byte kExtractVersion_Deflate = 20; + const Byte kExtractVersion_Deflate64 = 21; + const Byte kExtractVersion_Zip64 = 45; + const Byte kExtractVersion_BZip2 = 46; + const Byte kExtractVersion_Aes = 51; + const Byte kExtractVersion_LZMA = 63; + const Byte kExtractVersion_PPMd = 63; + + // const Byte kSupportedVersion = 20; + } + + namespace NExtraID + { + enum + { + kZip64 = 0x01, + kNTFS = 0x0A, + kStrongEncrypt = 0x17, + kUnixTime = 0x5455, + kWzAES = 0x9901 + }; + } + + namespace NNtfsExtra + { + const UInt16 kTagTime = 1; + enum + { + kMTime = 0, + kATime, + kCTime + }; + } + + namespace NUnixTime + { + enum + { + kMTime = 0, + kATime, + kCTime + }; + } + + const UInt32 kLocalBlockSize = 26; + /* + struct CLocalBlock + { + CVersion ExtractVersion; + + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + UInt16 NameSize; + UInt16 ExtraSize; + }; + */ + + const UInt32 kDataDescriptorSize = 16; + // const UInt32 kDataDescriptor64Size = 16 + 8; + /* + struct CDataDescriptor + { + UInt32 Signature; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + }; + + struct CLocalBlockFull + { + UInt32 Signature; + CLocalBlock Header; + }; + */ + + const UInt32 kCentralBlockSize = 42; + /* + struct CBlock + { + CVersion MadeByVersion; + CVersion ExtractVersion; + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt32 PackSize; + UInt32 UnPackSize; + UInt16 NameSize; + UInt16 ExtraSize; + UInt16 CommentSize; + UInt16 DiskNumberStart; + UInt16 InternalAttributes; + UInt32 ExternalAttributes; + UInt32 LocalHeaderOffset; + }; + + struct CBlockFull + { + UInt32 Signature; + CBlock Header; + }; + */ + + namespace NFlags + { + const int kEncrypted = 1 << 0; + const int kLzmaEOS = 1 << 1; + const int kDescriptorUsedMask = 1 << 3; + const int kStrongEncrypted = 1 << 6; + const int kUtf8 = 1 << 11; + + const int kImplodeDictionarySizeMask = 1 << 1; + const int kImplodeLiteralsOnMask = 1 << 2; + + const int kDeflateTypeBitStart = 1; + const int kNumDeflateTypeBits = 2; + const int kNumDeflateTypes = (1 << kNumDeflateTypeBits); + const int kDeflateTypeMask = (1 << kNumDeflateTypeBits) - 1; + } + + namespace NHostOS + { + enum EEnum + { + kFAT = 0, + kAMIGA = 1, + kVMS = 2, // VAX/VMS + kUnix = 3, + kVM_CMS = 4, + kAtari = 5, // what if it's a minix filesystem? [cjh] + kHPFS = 6, // filesystem used by OS/2 (and NT 3.x) + kMac = 7, + kZ_System = 8, + kCPM = 9, + kTOPS20 = 10, // pkzip 2.50 NTFS + kNTFS = 11, // filesystem used by Windows NT + kQDOS = 12, // SMS/QDOS + kAcorn = 13, // Archimedes Acorn RISC OS + kVFAT = 14, // filesystem used by Windows 95, NT + kMVS = 15, + kBeOS = 16, // hybrid POSIX/database filesystem + kTandem = 17, + kOS400 = 18, + kOSX = 19 + }; + } + namespace NUnixAttribute + { + const UInt32 kIFMT = 0170000; /* Unix file type mask */ + + const UInt32 kIFDIR = 0040000; /* Unix directory */ + const UInt32 kIFREG = 0100000; /* Unix regular file */ + const UInt32 kIFSOCK = 0140000; /* Unix socket (BSD, not SysV or Amiga) */ + const UInt32 kIFLNK = 0120000; /* Unix symbolic link (not SysV, Amiga) */ + const UInt32 kIFBLK = 0060000; /* Unix block special (not Amiga) */ + const UInt32 kIFCHR = 0020000; /* Unix character special (not Amiga) */ + const UInt32 kIFIFO = 0010000; /* Unix fifo (BCC, not MSC or Amiga) */ + + const UInt32 kISUID = 04000; /* Unix set user id on execution */ + const UInt32 kISGID = 02000; /* Unix set group id on execution */ + const UInt32 kISVTX = 01000; /* Unix directory permissions control */ + const UInt32 kENFMT = kISGID; /* Unix record locking enforcement flag */ + const UInt32 kIRWXU = 00700; /* Unix read, write, execute: owner */ + const UInt32 kIRUSR = 00400; /* Unix read permission: owner */ + const UInt32 kIWUSR = 00200; /* Unix write permission: owner */ + const UInt32 kIXUSR = 00100; /* Unix execute permission: owner */ + const UInt32 kIRWXG = 00070; /* Unix read, write, execute: group */ + const UInt32 kIRGRP = 00040; /* Unix read permission: group */ + const UInt32 kIWGRP = 00020; /* Unix write permission: group */ + const UInt32 kIXGRP = 00010; /* Unix execute permission: group */ + const UInt32 kIRWXO = 00007; /* Unix read, write, execute: other */ + const UInt32 kIROTH = 00004; /* Unix read permission: other */ + const UInt32 kIWOTH = 00002; /* Unix write permission: other */ + const UInt32 kIXOTH = 00001; /* Unix execute permission: other */ + } + + namespace NAmigaAttribute + { + const UInt32 kIFMT = 06000; /* Amiga file type mask */ + const UInt32 kIFDIR = 04000; /* Amiga directory */ + const UInt32 kIFREG = 02000; /* Amiga regular file */ + const UInt32 kIHIDDEN = 00200; /* to be supported in AmigaDOS 3.x */ + const UInt32 kISCRIPT = 00100; /* executable script (text command file) */ + const UInt32 kIPURE = 00040; /* allow loading into resident memory */ + const UInt32 kIARCHIVE = 00020; /* not modified since bit was last set */ + const UInt32 kIREAD = 00010; /* can be opened for reading */ + const UInt32 kIWRITE = 00004; /* can be opened for writing */ + const UInt32 kIEXECUTE = 00002; /* executable image, a loadable runfile */ + const UInt32 kIDELETE = 00001; /* can be deleted */ + } +} + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.cpp new file mode 100644 index 000000000..b36b61be7 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -0,0 +1,893 @@ +// Archive/ZipIn.cpp + +#include "StdAfx.h" + +#include "../../../../C/CpuArch.h" + +#include "Common/DynamicBuffer.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/StreamUtils.h" + +#include "ZipIn.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +namespace NArchive { +namespace NZip { + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + _inBufMode = false; + Close(); + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition)); + m_Position = m_StreamStartPosition; + RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); + RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + m_Stream = stream; + return S_OK; +} + +void CInArchive::Close() +{ + _inBuffer.ReleaseStream(); + m_Stream.Release(); +} + +HRESULT CInArchive::Seek(UInt64 offset) +{ + return m_Stream->Seek(offset, STREAM_SEEK_SET, NULL); +} + +////////////////////////////////////// +// Markers + +static inline bool TestMarkerCandidate(const Byte *p, UInt32 &value) +{ + value = Get32(p); + return + (value == NSignature::kLocalFileHeader) || + (value == NSignature::kEndOfCentralDir); +} + +static const UInt32 kNumMarkerAddtionalBytes = 2; +static inline bool TestMarkerCandidate2(const Byte *p, UInt32 &value) +{ + value = Get32(p); + if (value == NSignature::kEndOfCentralDir) + return (Get16(p + 4) == 0); + return (value == NSignature::kLocalFileHeader && p[4] < 128); +} + +HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit) +{ + ArcInfo.Clear(); + m_Position = m_StreamStartPosition; + + Byte marker[NSignature::kMarkerSize]; + RINOK(ReadStream_FALSE(stream, marker, NSignature::kMarkerSize)); + m_Position += NSignature::kMarkerSize; + if (TestMarkerCandidate(marker, m_Signature)) + return S_OK; + + CByteDynamicBuffer dynamicBuffer; + const UInt32 kSearchMarkerBufferSize = 0x10000; + dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize); + Byte *buffer = dynamicBuffer; + UInt32 numBytesPrev = NSignature::kMarkerSize - 1; + memcpy(buffer, marker + 1, numBytesPrev); + UInt64 curTestPos = m_StreamStartPosition + 1; + for (;;) + { + if (searchHeaderSizeLimit != NULL) + if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit) + break; + size_t numReadBytes = kSearchMarkerBufferSize - numBytesPrev; + RINOK(ReadStream(stream, buffer + numBytesPrev, &numReadBytes)); + m_Position += numReadBytes; + UInt32 numBytesInBuffer = numBytesPrev + (UInt32)numReadBytes; + const UInt32 kMarker2Size = NSignature::kMarkerSize + kNumMarkerAddtionalBytes; + if (numBytesInBuffer < kMarker2Size) + break; + UInt32 numTests = numBytesInBuffer - kMarker2Size + 1; + for (UInt32 pos = 0; pos < numTests; pos++) + { + if (buffer[pos] != 0x50) + continue; + if (TestMarkerCandidate2(buffer + pos, m_Signature)) + { + curTestPos += pos; + ArcInfo.StartPosition = curTestPos; + m_Position = curTestPos + NSignature::kMarkerSize; + return S_OK; + } + } + curTestPos += numTests; + numBytesPrev = numBytesInBuffer - numTests; + memmove(buffer, buffer + numTests, numBytesPrev); + } + return S_FALSE; +} + +HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) +{ + size_t realProcessedSize = size; + HRESULT result = S_OK; + if (_inBufMode) + { + try { realProcessedSize = _inBuffer.ReadBytes((Byte *)data, size); } + catch (const CInBufferException &e) { return e.ErrorCode; } + } + else + result = ReadStream(m_Stream, data, &realProcessedSize); + if (processedSize != NULL) + *processedSize = (UInt32)realProcessedSize; + m_Position += realProcessedSize; + return result; +} + +void CInArchive::Skip(UInt64 num) +{ + for (UInt64 i = 0; i < num; i++) + ReadByte(); +} + +void CInArchive::IncreaseRealPosition(UInt64 addValue) +{ + if (m_Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position) != S_OK) + throw CInArchiveException(CInArchiveException::kSeekStreamError); +} + +bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size) +{ + UInt32 realProcessedSize; + if (ReadBytes(data, size, &realProcessedSize) != S_OK) + throw CInArchiveException(CInArchiveException::kReadStreamError); + return (realProcessedSize == size); +} + +void CInArchive::SafeReadBytes(void *data, UInt32 size) +{ + if (!ReadBytesAndTestSize(data, size)) + throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); +} + +void CInArchive::ReadBuffer(CByteBuffer &buffer, UInt32 size) +{ + buffer.SetCapacity(size); + if (size > 0) + SafeReadBytes(buffer, size); +} + +Byte CInArchive::ReadByte() +{ + Byte b; + SafeReadBytes(&b, 1); + return b; +} + +UInt16 CInArchive::ReadUInt16() +{ + Byte buf[2]; + SafeReadBytes(buf, 2); + return Get16(buf); +} + +UInt32 CInArchive::ReadUInt32() +{ + Byte buf[4]; + SafeReadBytes(buf, 4); + return Get32(buf); +} + +UInt64 CInArchive::ReadUInt64() +{ + Byte buf[8]; + SafeReadBytes(buf, 8); + return Get64(buf); +} + +bool CInArchive::ReadUInt32(UInt32 &value) +{ + Byte buf[4]; + if (!ReadBytesAndTestSize(buf, 4)) + return false; + value = Get32(buf); + return true; +} + +void CInArchive::ReadFileName(UInt32 nameSize, AString &dest) +{ + if (nameSize == 0) + dest.Empty(); + char *p = dest.GetBuffer((int)nameSize); + SafeReadBytes(p, nameSize); + p[nameSize] = 0; + dest.ReleaseBuffer(); +} + +void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock, + UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber) +{ + extraBlock.Clear(); + UInt32 remain = extraSize; + while(remain >= 4) + { + CExtraSubBlock subBlock; + subBlock.ID = ReadUInt16(); + UInt32 dataSize = ReadUInt16(); + remain -= 4; + if (dataSize > remain) // it's bug + dataSize = remain; + if (subBlock.ID == NFileHeader::NExtraID::kZip64) + { + if (unpackSize == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + unpackSize = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (packSize == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + packSize = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (localHeaderOffset == 0xFFFFFFFF) + { + if (dataSize < 8) + break; + localHeaderOffset = ReadUInt64(); + remain -= 8; + dataSize -= 8; + } + if (diskStartNumber == 0xFFFF) + { + if (dataSize < 4) + break; + diskStartNumber = ReadUInt32(); + remain -= 4; + dataSize -= 4; + } + for (UInt32 i = 0; i < dataSize; i++) + ReadByte(); + } + else + { + ReadBuffer(subBlock.Data, dataSize); + extraBlock.SubBlocks.Add(subBlock); + } + remain -= dataSize; + } + Skip(remain); +} + +HRESULT CInArchive::ReadLocalItem(CItemEx &item) +{ + const int kBufSize = 26; + Byte p[kBufSize]; + SafeReadBytes(p, kBufSize); + + item.ExtractVersion.Version = p[0]; + item.ExtractVersion.HostOS = p[1]; + item.Flags = Get16(p + 2); + item.CompressionMethod = Get16(p + 4); + item.Time = Get32(p + 6); + item.FileCRC = Get32(p + 10); + item.PackSize = Get32(p + 14); + item.UnPackSize = Get32(p + 18); + UInt32 fileNameSize = Get16(p + 22); + item.LocalExtraSize = Get16(p + 24); + ReadFileName(fileNameSize, item.Name); + item.FileHeaderWithNameSize = 4 + NFileHeader::kLocalBlockSize + fileNameSize; + if (item.LocalExtraSize > 0) + { + UInt64 localHeaderOffset = 0; + UInt32 diskStartNumber = 0; + ReadExtra(item.LocalExtraSize, item.LocalExtra, item.UnPackSize, item.PackSize, + localHeaderOffset, diskStartNumber); + } + /* + if (item.IsDir()) + item.UnPackSize = 0; // check It + */ + return S_OK; +} + +static bool FlagsAreSame(CItem &i1, CItem &i2) +{ + if (i1.CompressionMethod != i2.CompressionMethod) + return false; + // i1.Time + + if (i1.Flags == i2.Flags) + return true; + UInt32 mask = 0xFFFF; + switch(i1.CompressionMethod) + { + case NFileHeader::NCompressionMethod::kDeflated: + mask = 0x7FF9; + break; + default: + if (i1.CompressionMethod <= NFileHeader::NCompressionMethod::kImploded) + mask = 0x7FFF; + } + return ((i1.Flags & mask) == (i2.Flags & mask)); +} + +HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) +{ + if (item.FromLocal) + return S_OK; + try + { + RINOK(Seek(ArcInfo.Base + item.LocalHeaderPosition)); + CItemEx localItem; + if (ReadUInt32() != NSignature::kLocalFileHeader) + return S_FALSE; + RINOK(ReadLocalItem(localItem)); + if (!FlagsAreSame(item, localItem)) + return S_FALSE; + + if ((!localItem.HasDescriptor() && + ( + item.FileCRC != localItem.FileCRC || + item.PackSize != localItem.PackSize || + item.UnPackSize != localItem.UnPackSize + ) + ) || + item.Name.Length() != localItem.Name.Length() + ) + return S_FALSE; + item.FileHeaderWithNameSize = localItem.FileHeaderWithNameSize; + item.LocalExtraSize = localItem.LocalExtraSize; + item.LocalExtra = localItem.LocalExtra; + item.FromLocal = true; + } + catch(...) { return S_FALSE; } + return S_OK; +} + +HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) +{ + if (item.HasDescriptor()) + { + const int kBufferSize = (1 << 12); + Byte buffer[kBufferSize]; + + UInt32 numBytesInBuffer = 0; + UInt32 packedSize = 0; + + bool descriptorWasFound = false; + for (;;) + { + UInt32 processedSize; + RINOK(ReadBytes(buffer + numBytesInBuffer, kBufferSize - numBytesInBuffer, &processedSize)); + numBytesInBuffer += processedSize; + if (numBytesInBuffer < NFileHeader::kDataDescriptorSize) + return S_FALSE; + UInt32 i; + for (i = 0; i <= numBytesInBuffer - NFileHeader::kDataDescriptorSize; i++) + { + // descriptorSignature field is Info-ZIP's extension + // to Zip specification. + UInt32 descriptorSignature = Get32(buffer + i); + + // !!!! It must be fixed for Zip64 archives + UInt32 descriptorPackSize = Get32(buffer + i + 8); + if (descriptorSignature== NSignature::kDataDescriptor && descriptorPackSize == packedSize + i) + { + descriptorWasFound = true; + item.FileCRC = Get32(buffer + i + 4); + item.PackSize = descriptorPackSize; + item.UnPackSize = Get32(buffer + i + 12); + IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - NFileHeader::kDataDescriptorSize)))); + break; + } + } + if (descriptorWasFound) + break; + packedSize += i; + int j; + for (j = 0; i < numBytesInBuffer; i++, j++) + buffer[j] = buffer[i]; + numBytesInBuffer = j; + } + } + else + IncreaseRealPosition(item.PackSize); + return S_OK; +} + +HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) +{ + if (item.FromLocal) + return S_OK; + try + { + RINOK(ReadLocalItemAfterCdItem(item)); + if (item.HasDescriptor()) + { + RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); + if (ReadUInt32() != NSignature::kDataDescriptor) + return S_FALSE; + UInt32 crc = ReadUInt32(); + UInt64 packSize, unpackSize; + + /* + if (IsZip64) + { + packSize = ReadUInt64(); + unpackSize = ReadUInt64(); + } + else + */ + { + packSize = ReadUInt32(); + unpackSize = ReadUInt32(); + } + + if (crc != item.FileCRC || item.PackSize != packSize || item.UnPackSize != unpackSize) + return S_FALSE; + } + } + catch(...) { return S_FALSE; } + return S_OK; +} + +HRESULT CInArchive::ReadCdItem(CItemEx &item) +{ + item.FromCentral = true; + const int kBufSize = 42; + Byte p[kBufSize]; + SafeReadBytes(p, kBufSize); + item.MadeByVersion.Version = p[0]; + item.MadeByVersion.HostOS = p[1]; + item.ExtractVersion.Version = p[2]; + item.ExtractVersion.HostOS = p[3]; + item.Flags = Get16(p + 4); + item.CompressionMethod = Get16(p + 6); + item.Time = Get32(p + 8); + item.FileCRC = Get32(p + 12); + item.PackSize = Get32(p + 16); + item.UnPackSize = Get32(p + 20); + UInt16 headerNameSize = Get16(p + 24); + UInt16 headerExtraSize = Get16(p + 26); + UInt16 headerCommentSize = Get16(p + 28); + UInt32 headerDiskNumberStart = Get16(p + 30); + item.InternalAttributes = Get16(p + 32); + item.ExternalAttributes = Get32(p + 34); + item.LocalHeaderPosition = Get32(p + 38); + ReadFileName(headerNameSize, item.Name); + + if (headerExtraSize > 0) + { + ReadExtra(headerExtraSize, item.CentralExtra, item.UnPackSize, item.PackSize, + item.LocalHeaderPosition, headerDiskNumberStart); + } + + if (headerDiskNumberStart != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + + // May be these strings must be deleted + /* + if (item.IsDir()) + item.UnPackSize = 0; + */ + + ReadBuffer(item.Comment, headerCommentSize); + return S_OK; +} + +HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) +{ + RINOK(Seek(offset)); + const UInt32 kEcd64Size = 56; + Byte buf[kEcd64Size]; + if (!ReadBytesAndTestSize(buf, kEcd64Size)) + return S_FALSE; + if (Get32(buf) != NSignature::kZip64EndOfCentralDir) + return S_FALSE; + // cdInfo.NumEntries = Get64(buf + 24); + cdInfo.Size = Get64(buf + 40); + cdInfo.Offset = Get64(buf + 48); + return S_OK; +} + +HRESULT CInArchive::FindCd(CCdInfo &cdInfo) +{ + UInt64 endPosition; + RINOK(m_Stream->Seek(0, STREAM_SEEK_END, &endPosition)); + const UInt32 kBufSizeMax = (1 << 16) + kEcdSize + kZip64EcdLocatorSize; + CByteBuffer byteBuffer; + byteBuffer.SetCapacity(kBufSizeMax); + Byte *buf = byteBuffer; + UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax; + if (bufSize < kEcdSize) + return S_FALSE; + UInt64 startPosition = endPosition - bufSize; + RINOK(m_Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); + if (m_Position != startPosition) + return S_FALSE; + if (!ReadBytesAndTestSize(buf, bufSize)) + return S_FALSE; + for (int i = (int)(bufSize - kEcdSize); i >= 0; i--) + { + if (Get32(buf + i) == NSignature::kEndOfCentralDir) + { + if (i >= kZip64EcdLocatorSize) + { + const Byte *locator = buf + i - kZip64EcdLocatorSize; + if (Get32(locator) == NSignature::kZip64EndOfCentralDirLocator) + { + UInt64 ecd64Offset = Get64(locator + 8); + if (TryEcd64(ecd64Offset, cdInfo) == S_OK) + return S_OK; + if (TryEcd64(ArcInfo.StartPosition + ecd64Offset, cdInfo) == S_OK) + { + ArcInfo.Base = ArcInfo.StartPosition; + return S_OK; + } + } + } + if (Get32(buf + i + 4) == 0) + { + // cdInfo.NumEntries = GetUInt16(buf + i + 10); + cdInfo.Size = Get32(buf + i + 12); + cdInfo.Offset = Get32(buf + i + 16); + UInt64 curPos = endPosition - bufSize + i; + UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; + if (curPos != cdEnd) + { + /* + if (cdInfo.Offset <= 16 && cdInfo.Size != 0) + { + // here we support some rare ZIP files with Central directory at the start + ArcInfo.Base = 0; + } + else + */ + ArcInfo.Base = curPos - cdEnd; + } + return S_OK; + } + } + } + return S_FALSE; +} + +HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress) +{ + items.Clear(); + RINOK(m_Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); + if (m_Position != cdOffset) + return S_FALSE; + + if (!_inBuffer.Create(1 << 15)) + return E_OUTOFMEMORY; + _inBuffer.SetStream(m_Stream); + _inBuffer.Init(); + _inBufMode = true; + + while(m_Position - cdOffset < cdSize) + { + if (ReadUInt32() != NSignature::kCentralFileHeader) + return S_FALSE; + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + items.Add(cdItem); + if (progress && items.Size() % 1000 == 0) + RINOK(progress->SetCompleted(items.Size())); + } + return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE; +} + +HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress) +{ + ArcInfo.Base = 0; + CCdInfo cdInfo; + RINOK(FindCd(cdInfo)); + HRESULT res = S_FALSE; + cdSize = cdInfo.Size; + cdOffset = cdInfo.Offset; + res = TryReadCd(items, ArcInfo.Base + cdOffset, cdSize, progress); + if (res == S_FALSE && ArcInfo.Base == 0) + { + res = TryReadCd(items, cdInfo.Offset + ArcInfo.StartPosition, cdSize, progress); + if (res == S_OK) + ArcInfo.Base = ArcInfo.StartPosition; + } + if (!ReadUInt32(m_Signature)) + return S_FALSE; + return res; +} + +HRESULT CInArchive::ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset, int &numCdItems) +{ + items.Clear(); + numCdItems = 0; + while (m_Signature == NSignature::kLocalFileHeader) + { + // FSeek points to next byte after signature + // NFileHeader::CLocalBlock localHeader; + CItemEx item; + item.LocalHeaderPosition = m_Position - m_StreamStartPosition - 4; // points to signature; + RINOK(ReadLocalItem(item)); + item.FromLocal = true; + ReadLocalItemDescriptor(item); + items.Add(item); + if (progress && items.Size() % 100 == 0) + RINOK(progress->SetCompleted(items.Size())); + if (!ReadUInt32(m_Signature)) + break; + } + cdOffset = m_Position - 4; + int i; + for (i = 0; i < items.Size(); i++, numCdItems++) + { + if (progress && i % 1000 == 0) + RINOK(progress->SetCompleted(items.Size())); + if (m_Signature == NSignature::kEndOfCentralDir) + break; + + if (m_Signature != NSignature::kCentralFileHeader) + return S_FALSE; + + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + + if (i == 0) + { + int j; + for (j = 0; j < items.Size(); j++) + { + CItemEx &item = items[j]; + if (item.Name == cdItem.Name) + { + ArcInfo.Base = item.LocalHeaderPosition - cdItem.LocalHeaderPosition; + break; + } + } + if (j == items.Size()) + return S_FALSE; + } + + int index; + int left = 0, right = items.Size(); + for (;;) + { + if (left >= right) + return S_FALSE; + index = (left + right) / 2; + UInt64 position = items[index].LocalHeaderPosition - ArcInfo.Base; + if (cdItem.LocalHeaderPosition == position) + break; + if (cdItem.LocalHeaderPosition < position) + right = index; + else + left = index + 1; + } + CItemEx &item = items[index]; + // item.LocalHeaderPosition = cdItem.LocalHeaderPosition; + item.MadeByVersion = cdItem.MadeByVersion; + item.CentralExtra = cdItem.CentralExtra; + + if ( + // item.ExtractVersion != cdItem.ExtractVersion || + !FlagsAreSame(item, cdItem) || + item.FileCRC != cdItem.FileCRC) + return S_FALSE; + + if (item.Name.Length() != cdItem.Name.Length() || + item.PackSize != cdItem.PackSize || + item.UnPackSize != cdItem.UnPackSize + ) + return S_FALSE; + item.Name = cdItem.Name; + item.InternalAttributes = cdItem.InternalAttributes; + item.ExternalAttributes = cdItem.ExternalAttributes; + item.Comment = cdItem.Comment; + item.FromCentral = cdItem.FromCentral; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + } + for (i = 0; i < items.Size(); i++) + items[i].LocalHeaderPosition -= ArcInfo.Base; + return S_OK; +} + +struct CEcd +{ + UInt16 thisDiskNumber; + UInt16 startCDDiskNumber; + UInt16 numEntriesInCDOnThisDisk; + UInt16 numEntriesInCD; + UInt32 cdSize; + UInt32 cdStartOffset; + UInt16 commentSize; + void Parse(const Byte *p); +}; + +void CEcd::Parse(const Byte *p) +{ + thisDiskNumber = Get16(p); + startCDDiskNumber = Get16(p + 2); + numEntriesInCDOnThisDisk = Get16(p + 4); + numEntriesInCD = Get16(p + 6); + cdSize = Get32(p + 8); + cdStartOffset = Get32(p + 12); + commentSize = Get16(p + 16); +} + +struct CEcd64 +{ + UInt16 versionMade; + UInt16 versionNeedExtract; + UInt32 thisDiskNumber; + UInt32 startCDDiskNumber; + UInt64 numEntriesInCDOnThisDisk; + UInt64 numEntriesInCD; + UInt64 cdSize; + UInt64 cdStartOffset; + void Parse(const Byte *p); + CEcd64() { memset(this, 0, sizeof(*this)); } +}; + +void CEcd64::Parse(const Byte *p) +{ + versionMade = Get16(p); + versionNeedExtract = Get16(p + 2); + thisDiskNumber = Get32(p + 4); + startCDDiskNumber = Get32(p + 8); + numEntriesInCDOnThisDisk = Get64(p + 12); + numEntriesInCD = Get64(p + 20); + cdSize = Get64(p + 28); + cdStartOffset = Get64(p + 36); +} + +#define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd. n; +#define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n; + +HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress) +{ + // m_Signature must be kLocalFileHeaderSignature or + // kEndOfCentralDirSignature + // m_Position points to next byte after signature + + IsZip64 = false; + items.Clear(); + + UInt64 cdSize, cdStartOffset; + HRESULT res; + try + { + res = ReadCd(items, cdStartOffset, cdSize, progress); + } + catch(CInArchiveException &) + { + res = S_FALSE; + } + if (res != S_FALSE && res != S_OK) + return res; + + /* + if (res != S_OK) + return res; + res = S_FALSE; + */ + + int numCdItems = items.Size(); + if (res == S_FALSE) + { + _inBufMode = false; + ArcInfo.Base = 0; + RINOK(m_Stream->Seek(ArcInfo.StartPosition, STREAM_SEEK_SET, &m_Position)); + if (m_Position != ArcInfo.StartPosition) + return S_FALSE; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + RINOK(ReadLocalsAndCd(items, progress, cdStartOffset, numCdItems)); + cdSize = (m_Position - 4) - cdStartOffset; + cdStartOffset -= ArcInfo.Base; + } + + CEcd64 ecd64; + bool isZip64 = false; + UInt64 zip64EcdStartOffset = m_Position - 4 - ArcInfo.Base; + if (m_Signature == NSignature::kZip64EndOfCentralDir) + { + IsZip64 = isZip64 = true; + UInt64 recordSize = ReadUInt64(); + + const int kBufSize = kZip64EcdSize; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd64.Parse(buf); + + Skip(recordSize - kZip64EcdSize); + if (!ReadUInt32(m_Signature)) + return S_FALSE; + if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + if (ecd64.numEntriesInCDOnThisDisk != numCdItems || + ecd64.numEntriesInCD != numCdItems || + ecd64.cdSize != cdSize || + (ecd64.cdStartOffset != cdStartOffset && + (!items.IsEmpty()))) + return S_FALSE; + } + if (m_Signature == NSignature::kZip64EndOfCentralDirLocator) + { + /* UInt32 startEndCDDiskNumber = */ ReadUInt32(); + UInt64 endCDStartOffset = ReadUInt64(); + /* UInt32 numberOfDisks = */ ReadUInt32(); + if (zip64EcdStartOffset != endCDStartOffset) + return S_FALSE; + if (!ReadUInt32(m_Signature)) + return S_FALSE; + } + if (m_Signature != NSignature::kEndOfCentralDir) + return S_FALSE; + + const int kBufSize = kEcdSize - 4; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + CEcd ecd; + ecd.Parse(buf); + + COPY_ECD_ITEM_16(thisDiskNumber); + COPY_ECD_ITEM_16(startCDDiskNumber); + COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk); + COPY_ECD_ITEM_16(numEntriesInCD); + COPY_ECD_ITEM_32(cdSize); + COPY_ECD_ITEM_32(cdStartOffset); + + ReadBuffer(ArcInfo.Comment, ecd.commentSize); + + if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) + throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported); + if ((UInt16)ecd64.numEntriesInCDOnThisDisk != ((UInt16)numCdItems) || + (UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) || + (UInt32)ecd64.cdSize != (UInt32)cdSize || + ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdStartOffset && + (!items.IsEmpty()))) + return S_FALSE; + + _inBufMode = false; + _inBuffer.Free(); + IsOkHeaders = (numCdItems == items.Size()); + ArcInfo.FinishPosition = m_Position; + return S_OK; +} + +ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) +{ + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> stream(streamSpec); + SeekInArchive(ArcInfo.Base + position); + streamSpec->SetStream(m_Stream); + streamSpec->Init(size); + return stream.Detach(); +} + +IInStream* CInArchive::CreateStream() +{ + CMyComPtr<IInStream> stream = m_Stream; + return stream.Detach(); +} + +bool CInArchive::SeekInArchive(UInt64 position) +{ + UInt64 newPosition; + if (m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition) != S_OK) + return false; + return (newPosition == position); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.h new file mode 100644 index 000000000..0565339a0 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipIn.h @@ -0,0 +1,125 @@ +// Archive/ZipIn.h + +#ifndef __ZIP_IN_H +#define __ZIP_IN_H + +#include "Common/MyCom.h" + +#include "../../IStream.h" + +#include "../../Common/InBuffer.h" + +#include "ZipHeader.h" +#include "ZipItemEx.h" + +namespace NArchive { +namespace NZip { + +class CInArchiveException +{ +public: + enum ECauseType + { + kUnexpectedEndOfArchive = 0, + kArchiceHeaderCRCError, + kFileHeaderCRCError, + kIncorrectArchive, + kDataDescroptorsAreNotSupported, + kMultiVolumeArchiveAreNotSupported, + kReadStreamError, + kSeekStreamError + } + Cause; + CInArchiveException(ECauseType cause): Cause(cause) {} +}; + +class CInArchiveInfo +{ +public: + UInt64 Base; + UInt64 StartPosition; + UInt64 FinishPosition; + CByteBuffer Comment; + + CInArchiveInfo(): Base(0), StartPosition(0) {} + UInt64 GetPhySize() const { return FinishPosition - StartPosition; } + void Clear() + { + Base = 0; + StartPosition = 0; + Comment.SetCapacity(0); + } +}; + +class CProgressVirt +{ +public: + STDMETHOD(SetTotal)(UInt64 numFiles) PURE; + STDMETHOD(SetCompleted)(UInt64 numFiles) PURE; +}; + +struct CCdInfo +{ + // UInt64 NumEntries; + UInt64 Size; + UInt64 Offset; +}; + +class CInArchive +{ + CMyComPtr<IInStream> m_Stream; + UInt32 m_Signature; + UInt64 m_StreamStartPosition; + UInt64 m_Position; + + bool _inBufMode; + CInBuffer _inBuffer; + + HRESULT Seek(UInt64 offset); + + HRESULT FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + void ReadFileName(UInt32 nameSize, AString &dest); + + HRESULT ReadBytes(void *data, UInt32 size, UInt32 *processedSize); + bool ReadBytesAndTestSize(void *data, UInt32 size); + void SafeReadBytes(void *data, UInt32 size); + void ReadBuffer(CByteBuffer &buffer, UInt32 size); + Byte ReadByte(); + UInt16 ReadUInt16(); + UInt32 ReadUInt32(); + UInt64 ReadUInt64(); + bool ReadUInt32(UInt32 &signature); + + void Skip(UInt64 num); + void IncreaseRealPosition(UInt64 addValue); + + void ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock, + UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber); + HRESULT ReadLocalItem(CItemEx &item); + HRESULT ReadLocalItemDescriptor(CItemEx &item); + HRESULT ReadCdItem(CItemEx &item); + HRESULT TryEcd64(UInt64 offset, CCdInfo &cdInfo); + HRESULT FindCd(CCdInfo &cdInfo); + HRESULT TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress); + HRESULT ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress); + HRESULT ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset, int &numCdItems); +public: + CInArchiveInfo ArcInfo; + bool IsZip64; + bool IsOkHeaders; + + HRESULT ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress); + HRESULT ReadLocalItemAfterCdItem(CItemEx &item); + HRESULT ReadLocalItemAfterCdItemFull(CItemEx &item); + HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); + void Close(); + bool SeekInArchive(UInt64 position); + ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size); + IInStream* CreateStream(); + + bool IsOpen() const { return m_Stream != NULL; } +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.cpp new file mode 100644 index 000000000..4dd58f7d6 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.cpp @@ -0,0 +1,181 @@ +// Archive/ZipItem.cpp + +#include "StdAfx.h" + +#include "ZipHeader.h" +#include "ZipItem.h" +#include "../Common/ItemNameUtils.h" +#include "../../../../C/CpuArch.h" + +namespace NArchive { +namespace NZip { + +bool operator==(const CVersion &v1, const CVersion &v2) +{ + return (v1.Version == v2.Version) && (v1.HostOS == v2.HostOS); +} + +bool operator!=(const CVersion &v1, const CVersion &v2) +{ + return !(v1 == v2); +} + +bool CExtraSubBlock::ExtractNtfsTime(int index, FILETIME &ft) const +{ + ft.dwHighDateTime = ft.dwLowDateTime = 0; + UInt32 size = (UInt32)Data.GetCapacity(); + if (ID != NFileHeader::NExtraID::kNTFS || size < 32) + return false; + const Byte *p = (const Byte *)Data; + p += 4; // for reserved + size -= 4; + while (size > 4) + { + UInt16 tag = GetUi16(p); + UInt32 attrSize = GetUi16(p + 2); + p += 4; + size -= 4; + if (attrSize > size) + attrSize = size; + + if (tag == NFileHeader::NNtfsExtra::kTagTime && attrSize >= 24) + { + p += 8 * index; + ft.dwLowDateTime = GetUi32(p); + ft.dwHighDateTime = GetUi32(p + 4); + return true; + } + p += attrSize; + size -= attrSize; + } + return false; +} + +bool CExtraSubBlock::ExtractUnixTime(int index, UInt32 &res) const +{ + res = 0; + UInt32 size = (UInt32)Data.GetCapacity(); + if (ID != NFileHeader::NExtraID::kUnixTime || size < 5) + return false; + const Byte *p = (const Byte *)Data; + Byte flags = *p++; + size--; + for (int i = 0; i < 3; i++) + if ((flags & (1 << i)) != 0) + { + if (size < 4) + return false; + if (index == i) + { + res = GetUi32(p); + return true; + } + p += 4; + size -= 4; + } + return false; +} + +bool CLocalItem::IsDir() const +{ + return NItemName::HasTailSlash(Name, GetCodePage()); +} + +bool CItem::IsDir() const +{ + if (NItemName::HasTailSlash(Name, GetCodePage())) + return true; + if (!FromCentral) + return false; + WORD highAttributes = WORD((ExternalAttributes >> 16 ) & 0xFFFF); + switch(MadeByVersion.HostOS) + { + case NFileHeader::NHostOS::kAMIGA: + switch (highAttributes & NFileHeader::NAmigaAttribute::kIFMT) + { + case NFileHeader::NAmigaAttribute::kIFDIR: return true; + case NFileHeader::NAmigaAttribute::kIFREG: return false; + default: return false; // change it throw kUnknownAttributes; + } + case NFileHeader::NHostOS::kFAT: + case NFileHeader::NHostOS::kNTFS: + case NFileHeader::NHostOS::kHPFS: + case NFileHeader::NHostOS::kVFAT: + return ((ExternalAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + case NFileHeader::NHostOS::kAtari: + case NFileHeader::NHostOS::kMac: + case NFileHeader::NHostOS::kVMS: + case NFileHeader::NHostOS::kVM_CMS: + case NFileHeader::NHostOS::kAcorn: + case NFileHeader::NHostOS::kMVS: + return false; // change it throw kUnknownAttributes; + default: + /* + switch (highAttributes & NFileHeader::NUnixAttribute::kIFMT) + { + case NFileHeader::NUnixAttribute::kIFDIR: + return true; + default: + return false; + } + */ + return false; + } +} + +#ifndef FILE_ATTRIBUTE_UNIX_EXTENSION +UInt32 CLocalItem::GetWinAttributes() const +{ + DWORD winAttributes = 0; + if (IsDir()) + winAttributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAttributes; +} +#endif + +UInt32 CItem::GetWinAttributes() const +{ + DWORD winAttributes = 0; + switch(MadeByVersion.HostOS) + { + case NFileHeader::NHostOS::kFAT: + case NFileHeader::NHostOS::kNTFS: + if (FromCentral) + winAttributes = ExternalAttributes; + break; +#ifdef FILE_ATTRIBUTE_UNIX_EXTENSION + case NFileHeader::NHostOS::kUnix: + winAttributes = (ExternalAttributes & 0xFFFF0000) | FILE_ATTRIBUTE_UNIX_EXTENSION; + if (winAttributes & (NFileHeader::NUnixAttribute::kIFDIR << 16)) + winAttributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAttributes; +#endif + default: + winAttributes = 0; // must be converted from unix value; + } + if (IsDir()) // test it; + winAttributes |= FILE_ATTRIBUTE_DIRECTORY; + return winAttributes; +} + +void CLocalItem::SetFlagBits(int startBitNumber, int numBits, int value) +{ + UInt16 mask = (UInt16)(((1 << numBits) - 1) << startBitNumber); + Flags &= ~mask; + Flags |= value << startBitNumber; +} + +void CLocalItem::SetBitMask(int bitMask, bool enable) +{ + if(enable) + Flags |= bitMask; + else + Flags &= ~bitMask; +} + +void CLocalItem::SetEncrypted(bool encrypted) + { SetBitMask(NFileHeader::NFlags::kEncrypted, encrypted); } +void CLocalItem::SetUtf8(bool isUtf8) + { SetBitMask(NFileHeader::NFlags::kUtf8, isUtf8); } + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.h new file mode 100644 index 000000000..31f2de732 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItem.h @@ -0,0 +1,281 @@ +// Archive/ZipItem.h + +#ifndef __ARCHIVE_ZIP_ITEM_H +#define __ARCHIVE_ZIP_ITEM_H + +#include "Common/Types.h" +#include "Common/MyString.h" +#include "Common/Buffer.h" +#include "Common/UTFConvert.h" +#include "Common/StringConvert.h" + +#include "ZipHeader.h" + +namespace NArchive { +namespace NZip { + +struct CVersion +{ + Byte Version; + Byte HostOS; +}; + +bool operator==(const CVersion &v1, const CVersion &v2); +bool operator!=(const CVersion &v1, const CVersion &v2); + +struct CExtraSubBlock +{ + UInt16 ID; + CByteBuffer Data; + bool ExtractNtfsTime(int index, FILETIME &ft) const; + bool ExtractUnixTime(int index, UInt32 &res) const; +}; + +struct CWzAesExtraField +{ + UInt16 VendorVersion; // 0x0001 - AE-1, 0x0002 - AE-2, + // UInt16 VendorId; // "AE" + Byte Strength; // 1 - 128-bit , 2 - 192-bit , 3 - 256-bit + UInt16 Method; + + CWzAesExtraField(): VendorVersion(2), Strength(3), Method(0) {} + + bool NeedCrc() const { return (VendorVersion == 1); } + + bool ParseFromSubBlock(const CExtraSubBlock &sb) + { + if (sb.ID != NFileHeader::NExtraID::kWzAES) + return false; + if (sb.Data.GetCapacity() < 7) + return false; + const Byte *p = (const Byte *)sb.Data; + VendorVersion = (((UInt16)p[1]) << 8) | p[0]; + if (p[2] != 'A' || p[3] != 'E') + return false; + Strength = p[4]; + Method = (((UInt16)p[6]) << 16) | p[5]; + return true; + } + void SetSubBlock(CExtraSubBlock &sb) const + { + sb.Data.SetCapacity(7); + sb.ID = NFileHeader::NExtraID::kWzAES; + Byte *p = (Byte *)sb.Data; + p[0] = (Byte)VendorVersion; + p[1] = (Byte)(VendorVersion >> 8); + p[2] = 'A'; + p[3] = 'E'; + p[4] = Strength; + p[5] = (Byte)Method; + p[6] = (Byte)(Method >> 8); + } +}; + +namespace NStrongCryptoFlags +{ + const UInt16 kDES = 0x6601; + const UInt16 kRC2old = 0x6602; + const UInt16 k3DES168 = 0x6603; + const UInt16 k3DES112 = 0x6609; + const UInt16 kAES128 = 0x660E; + const UInt16 kAES192 = 0x660F; + const UInt16 kAES256 = 0x6610; + const UInt16 kRC2 = 0x6702; + const UInt16 kBlowfish = 0x6720; + const UInt16 kTwofish = 0x6721; + const UInt16 kRC4 = 0x6801; +} + +struct CStrongCryptoField +{ + UInt16 Format; + UInt16 AlgId; + UInt16 BitLen; + UInt16 Flags; + + bool ParseFromSubBlock(const CExtraSubBlock &sb) + { + if (sb.ID != NFileHeader::NExtraID::kStrongEncrypt) + return false; + const Byte *p = (const Byte *)sb.Data; + if (sb.Data.GetCapacity() < 8) + return false; + Format = (((UInt16)p[1]) << 8) | p[0]; + AlgId = (((UInt16)p[3]) << 8) | p[2]; + BitLen = (((UInt16)p[5]) << 8) | p[4]; + Flags = (((UInt16)p[7]) << 8) | p[6]; + return (Format == 2); + } +}; + +struct CExtraBlock +{ + CObjectVector<CExtraSubBlock> SubBlocks; + void Clear() { SubBlocks.Clear(); } + size_t GetSize() const + { + size_t res = 0; + for (int i = 0; i < SubBlocks.Size(); i++) + res += SubBlocks[i].Data.GetCapacity() + 2 + 2; + return res; + } + bool GetWzAesField(CWzAesExtraField &aesField) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + if (aesField.ParseFromSubBlock(SubBlocks[i])) + return true; + return false; + } + + bool GetStrongCryptoField(CStrongCryptoField &f) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + if (f.ParseFromSubBlock(SubBlocks[i])) + return true; + return false; + } + + bool HasWzAesField() const + { + CWzAesExtraField aesField; + return GetWzAesField(aesField); + } + + bool GetNtfsTime(int index, FILETIME &ft) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + { + const CExtraSubBlock &sb = SubBlocks[i]; + if (sb.ID == NFileHeader::NExtraID::kNTFS) + return sb.ExtractNtfsTime(index, ft); + } + return false; + } + + bool GetUnixTime(int index, UInt32 &res) const + { + for (int i = 0; i < SubBlocks.Size(); i++) + { + const CExtraSubBlock &sb = SubBlocks[i]; + if (sb.ID == NFileHeader::NExtraID::kUnixTime) + return sb.ExtractUnixTime(index, res); + } + return false; + } + + /* + bool HasStrongCryptoField() const + { + CStrongCryptoField f; + return GetStrongCryptoField(f); + } + */ + + void RemoveUnknownSubBlocks() + { + for (int i = SubBlocks.Size() - 1; i >= 0; i--) + if (SubBlocks[i].ID != NFileHeader::NExtraID::kWzAES) + SubBlocks.Delete(i); + } +}; + + +class CLocalItem +{ +public: + CVersion ExtractVersion; + UInt16 Flags; + UInt16 CompressionMethod; + UInt32 Time; + UInt32 FileCRC; + UInt64 PackSize; + UInt64 UnPackSize; + + AString Name; + + CExtraBlock LocalExtra; + + bool IsUtf8() const { return (Flags & NFileHeader::NFlags::kUtf8) != 0; } + + bool IsEncrypted() const { return (Flags & NFileHeader::NFlags::kEncrypted) != 0; } + bool IsStrongEncrypted() const { return IsEncrypted() && (Flags & NFileHeader::NFlags::kStrongEncrypted) != 0; }; + bool IsAesEncrypted() const { return IsEncrypted() && (IsStrongEncrypted() || CompressionMethod == NFileHeader::NCompressionMethod::kWzAES); }; + + bool IsLzmaEOS() const { return (Flags & NFileHeader::NFlags::kLzmaEOS) != 0; } + + bool IsDir() const; + bool IgnoreItem() const { return false; } + UInt32 GetWinAttributes() const; + + bool HasDescriptor() const { return (Flags & NFileHeader::NFlags::kDescriptorUsedMask) != 0; } + + UString GetUnicodeString(const AString &s) const + { + UString res; + if (IsUtf8()) + if (!ConvertUTF8ToUnicode(s, res)) + res.Empty(); + if (res.IsEmpty()) + res = MultiByteToUnicodeString(s, GetCodePage()); + return res; + } + +private: + void SetFlagBits(int startBitNumber, int numBits, int value); + void SetBitMask(int bitMask, bool enable); +public: + void ClearFlags() { Flags = 0; } + void SetEncrypted(bool encrypted); + void SetUtf8(bool isUtf8); + + WORD GetCodePage() const { return CP_OEMCP; } +}; + +class CItem: public CLocalItem +{ +public: + CVersion MadeByVersion; + UInt16 InternalAttributes; + UInt32 ExternalAttributes; + + UInt64 LocalHeaderPosition; + + FILETIME NtfsMTime; + FILETIME NtfsATime; + FILETIME NtfsCTime; + + CExtraBlock CentralExtra; + CByteBuffer Comment; + + bool FromLocal; + bool FromCentral; + bool NtfsTimeIsDefined; + + bool IsDir() const; + UInt32 GetWinAttributes() const; + + bool IsThereCrc() const + { + if (CompressionMethod == NFileHeader::NCompressionMethod::kWzAES) + { + CWzAesExtraField aesField; + if (CentralExtra.GetWzAesField(aesField)) + return aesField.NeedCrc(); + } + return (FileCRC != 0 || !IsDir()); + } + + WORD GetCodePage() const + { + return (WORD)((MadeByVersion.HostOS == NFileHeader::NHostOS::kFAT + || MadeByVersion.HostOS == NFileHeader::NHostOS::kNTFS + ) ? CP_OEMCP : CP_ACP); + } + CItem() : FromLocal(false), FromCentral(false), NtfsTimeIsDefined(false) {} +}; + +}} + +#endif + + diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItemEx.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItemEx.h new file mode 100644 index 000000000..ab62cdbb4 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipItemEx.h @@ -0,0 +1,34 @@ +// Archive/ZipItemEx.h + +#ifndef __ARCHIVE_ZIP_ITEMEX_H +#define __ARCHIVE_ZIP_ITEMEX_H + +#include "ZipHeader.h" +#include "ZipItem.h" + +namespace NArchive { +namespace NZip { + +class CItemEx: public CItem +{ +public: + UInt32 FileHeaderWithNameSize; + UInt16 LocalExtraSize; + + UInt64 GetLocalFullSize() const + { return FileHeaderWithNameSize + LocalExtraSize + PackSize + + (HasDescriptor() ? NFileHeader::kDataDescriptorSize : 0); }; + /* + UInt64 GetLocalFullSize(bool isZip64) const + { return FileHeaderWithNameSize + LocalExtraSize + PackSize + + (HasDescriptor() ? (isZip64 ? NFileHeader::kDataDescriptor64Size : NFileHeader::kDataDescriptorSize) : 0); }; + */ + UInt64 GetLocalExtraPosition() const + { return LocalHeaderPosition + FileHeaderWithNameSize; }; + UInt64 GetDataPosition() const + { return GetLocalExtraPosition() + LocalExtraSize; }; +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.cpp new file mode 100644 index 000000000..aa82143e3 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.cpp @@ -0,0 +1,289 @@ +// ZipOut.cpp + +#include "StdAfx.h" + +#include "../../Common/OffsetStream.h" + +#include "ZipOut.h" + +namespace NArchive { +namespace NZip { + +void COutArchive::Create(IOutStream *outStream) +{ + if (!m_OutBuffer.Create(1 << 16)) + throw CSystemException(E_OUTOFMEMORY); + m_Stream = outStream; + m_OutBuffer.SetStream(outStream); + m_OutBuffer.Init(); + m_BasePosition = 0; +} + +void COutArchive::MoveBasePosition(UInt64 distanceToMove) +{ + m_BasePosition += distanceToMove; // test overflow +} + +void COutArchive::PrepareWriteCompressedDataZip64(UInt16 fileNameLength, bool isZip64, bool aesEncryption) +{ + m_IsZip64 = isZip64; + m_ExtraSize = isZip64 ? (4 + 8 + 8) : 0; + if (aesEncryption) + m_ExtraSize += 4 + 7; + m_LocalFileHeaderSize = 4 + NFileHeader::kLocalBlockSize + fileNameLength + m_ExtraSize; +} + +void COutArchive::PrepareWriteCompressedData(UInt16 fileNameLength, UInt64 unPackSize, bool aesEncryption) +{ + // We test it to 0xF8000000 to support case when compressed size + // can be larger than uncompressed size. + PrepareWriteCompressedDataZip64(fileNameLength, unPackSize >= 0xF8000000, aesEncryption); +} + +void COutArchive::PrepareWriteCompressedData2(UInt16 fileNameLength, UInt64 unPackSize, UInt64 packSize, bool aesEncryption) +{ + bool isUnPack64 = unPackSize >= 0xFFFFFFFF; + bool isPack64 = packSize >= 0xFFFFFFFF; + bool isZip64 = isPack64 || isUnPack64; + PrepareWriteCompressedDataZip64(fileNameLength, isZip64, aesEncryption); +} + +void COutArchive::WriteBytes(const void *buffer, UInt32 size) +{ + m_OutBuffer.WriteBytes(buffer, size); + m_BasePosition += size; +} + +void COutArchive::WriteByte(Byte b) +{ + WriteBytes(&b, 1); +} + +void COutArchive::WriteUInt16(UInt16 value) +{ + for (int i = 0; i < 2; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteUInt32(UInt32 value) +{ + for (int i = 0; i < 4; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteUInt64(UInt64 value) +{ + for (int i = 0; i < 8; i++) + { + WriteByte((Byte)value); + value >>= 8; + } +} + +void COutArchive::WriteExtra(const CExtraBlock &extra) +{ + if (extra.SubBlocks.Size() != 0) + { + for (int i = 0; i < extra.SubBlocks.Size(); i++) + { + const CExtraSubBlock &subBlock = extra.SubBlocks[i]; + WriteUInt16(subBlock.ID); + WriteUInt16((UInt16)subBlock.Data.GetCapacity()); + WriteBytes(subBlock.Data, (UInt32)subBlock.Data.GetCapacity()); + } + } +} + +void COutArchive::SeekTo(UInt64 offset) +{ + HRESULT res = m_Stream->Seek(offset, STREAM_SEEK_SET, NULL); + if (res != S_OK) + throw CSystemException(res); +} + +void COutArchive::WriteLocalHeader(const CLocalItem &item) +{ + SeekTo(m_BasePosition); + + bool isZip64 = m_IsZip64 || item.PackSize >= 0xFFFFFFFF || item.UnPackSize >= 0xFFFFFFFF; + + WriteUInt32(NSignature::kLocalFileHeader); + { + Byte ver = item.ExtractVersion.Version; + if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64) + ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64; + WriteByte(ver); + } + WriteByte(item.ExtractVersion.HostOS); + WriteUInt16(item.Flags); + WriteUInt16(item.CompressionMethod); + WriteUInt32(item.Time); + WriteUInt32(item.FileCRC); + WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.PackSize); + WriteUInt32(isZip64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize); + WriteUInt16((UInt16)item.Name.Length()); + { + UInt16 localExtraSize = (UInt16)((isZip64 ? (4 + 16): 0) + item.LocalExtra.GetSize()); + if (localExtraSize > m_ExtraSize) + throw CSystemException(E_FAIL); + } + WriteUInt16((UInt16)m_ExtraSize); // test it; + WriteBytes((const char *)item.Name, item.Name.Length()); + + UInt32 extraPos = 0; + if (isZip64) + { + extraPos += 4 + 16; + WriteUInt16(NFileHeader::NExtraID::kZip64); + WriteUInt16(16); + WriteUInt64(item.UnPackSize); + WriteUInt64(item.PackSize); + } + + WriteExtra(item.LocalExtra); + extraPos += (UInt32)item.LocalExtra.GetSize(); + for (; extraPos < m_ExtraSize; extraPos++) + WriteByte(0); + + m_OutBuffer.FlushWithCheck(); + MoveBasePosition(item.PackSize); + SeekTo(m_BasePosition); +} + +void COutArchive::WriteCentralHeader(const CItem &item) +{ + bool isUnPack64 = item.UnPackSize >= 0xFFFFFFFF; + bool isPack64 = item.PackSize >= 0xFFFFFFFF; + bool isPosition64 = item.LocalHeaderPosition >= 0xFFFFFFFF; + bool isZip64 = isPack64 || isUnPack64 || isPosition64; + + WriteUInt32(NSignature::kCentralFileHeader); + WriteByte(item.MadeByVersion.Version); + WriteByte(item.MadeByVersion.HostOS); + { + Byte ver = item.ExtractVersion.Version; + if (isZip64 && ver < NFileHeader::NCompressionMethod::kExtractVersion_Zip64) + ver = NFileHeader::NCompressionMethod::kExtractVersion_Zip64; + WriteByte(ver); + } + WriteByte(item.ExtractVersion.HostOS); + WriteUInt16(item.Flags); + WriteUInt16(item.CompressionMethod); + WriteUInt32(item.Time); + WriteUInt32(item.FileCRC); + WriteUInt32(isPack64 ? 0xFFFFFFFF: (UInt32)item.PackSize); + WriteUInt32(isUnPack64 ? 0xFFFFFFFF: (UInt32)item.UnPackSize); + WriteUInt16((UInt16)item.Name.Length()); + UInt16 zip64ExtraSize = (UInt16)((isUnPack64 ? 8: 0) + (isPack64 ? 8: 0) + (isPosition64 ? 8: 0)); + const UInt16 kNtfsExtraSize = 4 + 2 + 2 + (3 * 8); + UInt16 centralExtraSize = (UInt16)(isZip64 ? (4 + zip64ExtraSize) : 0) + (item.NtfsTimeIsDefined ? (4 + kNtfsExtraSize) : 0); + centralExtraSize = (UInt16)(centralExtraSize + item.CentralExtra.GetSize()); + WriteUInt16(centralExtraSize); // test it; + WriteUInt16((UInt16)item.Comment.GetCapacity()); + WriteUInt16(0); // DiskNumberStart; + WriteUInt16(item.InternalAttributes); + WriteUInt32(item.ExternalAttributes); + WriteUInt32(isPosition64 ? 0xFFFFFFFF: (UInt32)item.LocalHeaderPosition); + WriteBytes((const char *)item.Name, item.Name.Length()); + if (isZip64) + { + WriteUInt16(NFileHeader::NExtraID::kZip64); + WriteUInt16(zip64ExtraSize); + if(isUnPack64) + WriteUInt64(item.UnPackSize); + if(isPack64) + WriteUInt64(item.PackSize); + if(isPosition64) + WriteUInt64(item.LocalHeaderPosition); + } + if (item.NtfsTimeIsDefined) + { + WriteUInt16(NFileHeader::NExtraID::kNTFS); + WriteUInt16(kNtfsExtraSize); + WriteUInt32(0); // reserved + WriteUInt16(NFileHeader::NNtfsExtra::kTagTime); + WriteUInt16(8 * 3); + WriteUInt32(item.NtfsMTime.dwLowDateTime); + WriteUInt32(item.NtfsMTime.dwHighDateTime); + WriteUInt32(item.NtfsATime.dwLowDateTime); + WriteUInt32(item.NtfsATime.dwHighDateTime); + WriteUInt32(item.NtfsCTime.dwLowDateTime); + WriteUInt32(item.NtfsCTime.dwHighDateTime); + } + WriteExtra(item.CentralExtra); + if (item.Comment.GetCapacity() > 0) + WriteBytes(item.Comment, (UInt32)item.Comment.GetCapacity()); +} + +void COutArchive::WriteCentralDir(const CObjectVector<CItem> &items, const CByteBuffer *comment) +{ + SeekTo(m_BasePosition); + + UInt64 cdOffset = GetCurrentPosition(); + for(int i = 0; i < items.Size(); i++) + WriteCentralHeader(items[i]); + UInt64 cd64EndOffset = GetCurrentPosition(); + UInt64 cdSize = cd64EndOffset - cdOffset; + bool cdOffset64 = cdOffset >= 0xFFFFFFFF; + bool cdSize64 = cdSize >= 0xFFFFFFFF; + bool items64 = items.Size() >= 0xFFFF; + bool isZip64 = (cdOffset64 || cdSize64 || items64); + + if (isZip64) + { + WriteUInt32(NSignature::kZip64EndOfCentralDir); + WriteUInt64(kZip64EcdSize); // ThisDiskNumber = 0; + WriteUInt16(45); // version + WriteUInt16(45); // version + WriteUInt32(0); // ThisDiskNumber = 0; + WriteUInt32(0); // StartCentralDirectoryDiskNumber;; + WriteUInt64((UInt64)items.Size()); + WriteUInt64((UInt64)items.Size()); + WriteUInt64((UInt64)cdSize); + WriteUInt64((UInt64)cdOffset); + + WriteUInt32(NSignature::kZip64EndOfCentralDirLocator); + WriteUInt32(0); // number of the disk with the start of the zip64 end of central directory + WriteUInt64(cd64EndOffset); + WriteUInt32(1); // total number of disks + } + WriteUInt32(NSignature::kEndOfCentralDir); + WriteUInt16(0); // ThisDiskNumber = 0; + WriteUInt16(0); // StartCentralDirectoryDiskNumber; + WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size())); + WriteUInt16((UInt16)(items64 ? 0xFFFF: items.Size())); + WriteUInt32(cdSize64 ? 0xFFFFFFFF: (UInt32)cdSize); + WriteUInt32(cdOffset64 ? 0xFFFFFFFF: (UInt32)cdOffset); + UInt32 commentSize = (UInt32)(comment ? comment->GetCapacity() : 0); + WriteUInt16((UInt16)commentSize); + if (commentSize > 0) + WriteBytes((const Byte *)*comment, commentSize); + m_OutBuffer.FlushWithCheck(); +} + +void COutArchive::CreateStreamForCompressing(IOutStream **outStream) +{ + COffsetOutStream *streamSpec = new COffsetOutStream; + CMyComPtr<IOutStream> tempStream(streamSpec); + streamSpec->Init(m_Stream, m_BasePosition + m_LocalFileHeaderSize); + *outStream = tempStream.Detach(); +} + +void COutArchive::SeekToPackedDataPosition() +{ + SeekTo(m_BasePosition + m_LocalFileHeaderSize); +} + +void COutArchive::CreateStreamForCopying(ISequentialOutStream **outStream) +{ + CMyComPtr<ISequentialOutStream> tempStream(m_Stream); + *outStream = tempStream.Detach(); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.h new file mode 100644 index 000000000..2f6349e5c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipOut.h @@ -0,0 +1,56 @@ +// ZipOut.h + +#ifndef __ZIP_OUT_H +#define __ZIP_OUT_H + +#include "Common/MyCom.h" + +#include "../../IStream.h" +#include "../../Common/OutBuffer.h" + +#include "ZipItem.h" + +namespace NArchive { +namespace NZip { + +// can throw CSystemException and COutBufferException + +class COutArchive +{ + CMyComPtr<IOutStream> m_Stream; + COutBuffer m_OutBuffer; + + UInt64 m_BasePosition; + UInt32 m_LocalFileHeaderSize; + UInt32 m_ExtraSize; + bool m_IsZip64; + + void WriteBytes(const void *buffer, UInt32 size); + void WriteByte(Byte b); + void WriteUInt16(UInt16 value); + void WriteUInt32(UInt32 value); + void WriteUInt64(UInt64 value); + + void WriteExtraHeader(const CItem &item); + void WriteCentralHeader(const CItem &item); + void WriteExtra(const CExtraBlock &extra); + void SeekTo(UInt64 offset); +public: + void Create(IOutStream *outStream); + void MoveBasePosition(UInt64 distanceToMove); + UInt64 GetCurrentPosition() const { return m_BasePosition; }; + void PrepareWriteCompressedDataZip64(UInt16 fileNameLength, bool isZip64, bool aesEncryption); + void PrepareWriteCompressedData(UInt16 fileNameLength, UInt64 unPackSize, bool aesEncryption); + void PrepareWriteCompressedData2(UInt16 fileNameLength, UInt64 unPackSize, UInt64 packSize, bool aesEncryption); + void WriteLocalHeader(const CLocalItem &item); + + void WriteCentralDir(const CObjectVector<CItem> &items, const CByteBuffer *comment); + + void CreateStreamForCompressing(IOutStream **outStream); + void CreateStreamForCopying(ISequentialOutStream **outStream); + void SeekToPackedDataPosition(); +}; + +}} + +#endif diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipRegister.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipRegister.cpp new file mode 100644 index 000000000..3e7aade85 --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipRegister.cpp @@ -0,0 +1,18 @@ +// ZipRegister.cpp + +#include "StdAfx.h" + +#include "../../Common/RegisterArc.h" + +#include "ZipHandler.h" +static IInArchive *CreateArc() { return new NArchive::NZip::CHandler; } +#ifndef EXTRACT_ONLY +static IOutArchive *CreateArcOut() { return new NArchive::NZip::CHandler; } +#else +#define CreateArcOut 0 +#endif + +static CArcInfo g_ArcInfo = + { L"zip", L"zip jar xpi odt ods docx xlsx", 0, 1, { 0x50, 0x4B, 0x03, 0x04 }, 4, false, CreateArc, CreateArcOut }; + +REGISTER_ARC(Zip) diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.cpp new file mode 100644 index 000000000..bd0c8221d --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.cpp @@ -0,0 +1,1076 @@ +// ZipUpdate.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" + +#include "Common/AutoPtr.h" +#include "Common/Defs.h" +#include "Common/StringConvert.h" + +#include "Windows/Defs.h" +#include "Windows/Thread.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/OutMemStream.h" +#include "../../Common/ProgressUtils.h" +#ifndef _7ZIP_ST +#include "../../Common/ProgressMt.h" +#endif +#include "../../Common/StreamUtils.h" + +#include "../../Compress/CopyCoder.h" + +#include "ZipAddCommon.h" +#include "ZipOut.h" +#include "ZipUpdate.h" + +using namespace NWindows; +using namespace NSynchronization; + +namespace NArchive { +namespace NZip { + +static const Byte kHostOS = + #ifdef _WIN32 + NFileHeader::NHostOS::kFAT; + #else + NFileHeader::NHostOS::kUnix; + #endif + +static const Byte kMadeByHostOS = kHostOS; +static const Byte kExtractHostOS = kHostOS; + +static const Byte kMethodForDirectory = NFileHeader::NCompressionMethod::kStored; + +static HRESULT CopyBlockToArchive(ISequentialInStream *inStream, + COutArchive &outArchive, ICompressProgressInfo *progress) +{ + CMyComPtr<ISequentialOutStream> outStream; + outArchive.CreateStreamForCopying(&outStream); + return NCompress::CopyStream(inStream, outStream, progress); +} + +static HRESULT WriteRange(IInStream *inStream, COutArchive &outArchive, + const CUpdateRange &range, ICompressProgressInfo *progress) +{ + UInt64 position; + RINOK(inStream->Seek(range.Position, STREAM_SEEK_SET, &position)); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec); + streamSpec->SetStream(inStream); + streamSpec->Init(range.Size); + + RINOK(CopyBlockToArchive(inStreamLimited, outArchive, progress)); + return progress->SetRatioInfo(&range.Size, &range.Size); +} + +static void SetFileHeader( + COutArchive &archive, + const CCompressionMethodMode &options, + const CUpdateItem &ui, + CItem &item) +{ + item.UnPackSize = ui.Size; + bool isDir; + + item.ClearFlags(); + + if (ui.NewProperties) + { + isDir = ui.IsDir; + item.Name = ui.Name; + item.SetUtf8(ui.IsUtf8); + item.ExternalAttributes = ui.Attributes; + item.Time = ui.Time; + item.NtfsMTime = ui.NtfsMTime; + item.NtfsATime = ui.NtfsATime; + item.NtfsCTime = ui.NtfsCTime; + item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined; + } + else + isDir = item.IsDir(); + + item.LocalHeaderPosition = archive.GetCurrentPosition(); + item.MadeByVersion.HostOS = kMadeByHostOS; + item.MadeByVersion.Version = NFileHeader::NCompressionMethod::kMadeByProgramVersion; + + item.ExtractVersion.HostOS = kExtractHostOS; + + item.InternalAttributes = 0; // test it + item.SetEncrypted(!isDir && options.PasswordIsDefined); + if (isDir) + { + item.ExtractVersion.Version = NFileHeader::NCompressionMethod::kExtractVersion_Dir; + item.CompressionMethod = kMethodForDirectory; + item.PackSize = 0; + item.FileCRC = 0; // test it + } +} + +static void SetItemInfoFromCompressingResult(const CCompressingResult &compressingResult, + bool isAesMode, Byte aesKeyMode, CItem &item) +{ + item.ExtractVersion.Version = compressingResult.ExtractVersion; + item.CompressionMethod = compressingResult.Method; + item.FileCRC = compressingResult.CRC; + item.UnPackSize = compressingResult.UnpackSize; + item.PackSize = compressingResult.PackSize; + + item.LocalExtra.Clear(); + item.CentralExtra.Clear(); + + if (isAesMode) + { + CWzAesExtraField wzAesField; + wzAesField.Strength = aesKeyMode; + wzAesField.Method = compressingResult.Method; + item.CompressionMethod = NFileHeader::NCompressionMethod::kWzAES; + item.FileCRC = 0; + CExtraSubBlock sb; + wzAesField.SetSubBlock(sb); + item.LocalExtra.SubBlocks.Add(sb); + item.CentralExtra.SubBlocks.Add(sb); + } +} + +#ifndef _7ZIP_ST + +static THREAD_FUNC_DECL CoderThread(void *threadCoderInfo); + +struct CThreadInfo +{ + #ifdef EXTERNAL_CODECS + CMyComPtr<ICompressCodecsInfo> _codecsInfo; + const CObjectVector<CCodecInfoEx> *_externalCodecs; + #endif + + NWindows::CThread Thread; + NWindows::NSynchronization::CAutoResetEvent CompressEvent; + NWindows::NSynchronization::CAutoResetEventWFMO CompressionCompletedEvent; + bool ExitThread; + + CMtCompressProgress *ProgressSpec; + CMyComPtr<ICompressProgressInfo> Progress; + + COutMemStream *OutStreamSpec; + CMyComPtr<IOutStream> OutStream; + CMyComPtr<ISequentialInStream> InStream; + + CAddCommon Coder; + HRESULT Result; + CCompressingResult CompressingResult; + + bool IsFree; + UInt32 UpdateIndex; + + CThreadInfo(const CCompressionMethodMode &options): + ExitThread(false), + ProgressSpec(0), + OutStreamSpec(0), + Coder(options) + {} + + HRESULT CreateEvents(CSynchro *sync) + { + RINOK(CompressEvent.CreateIfNotCreated()); + return CompressionCompletedEvent.CreateIfNotCreated(sync); + } + HRes CreateThread() { return Thread.Create(CoderThread, this); } + + void WaitAndCode(); + void StopWaitClose() + { + ExitThread = true; + if (OutStreamSpec != 0) + OutStreamSpec->StopWriting(E_ABORT); + if (CompressEvent.IsCreated()) + CompressEvent.Set(); + Thread.Wait(); + Thread.Close(); + } + +}; + +void CThreadInfo::WaitAndCode() +{ + for (;;) + { + CompressEvent.Lock(); + if (ExitThread) + return; + Result = Coder.Compress( + #ifdef EXTERNAL_CODECS + _codecsInfo, _externalCodecs, + #endif + InStream, OutStream, Progress, CompressingResult); + if (Result == S_OK && Progress) + Result = Progress->SetRatioInfo(&CompressingResult.UnpackSize, &CompressingResult.PackSize); + CompressionCompletedEvent.Set(); + } +} + +static THREAD_FUNC_DECL CoderThread(void *threadCoderInfo) +{ + ((CThreadInfo *)threadCoderInfo)->WaitAndCode(); + return 0; +} + +class CThreads +{ +public: + CObjectVector<CThreadInfo> Threads; + ~CThreads() + { + for (int i = 0; i < Threads.Size(); i++) + Threads[i].StopWaitClose(); + } +}; + +struct CMemBlocks2: public CMemLockBlocks +{ + CCompressingResult CompressingResult; + bool Defined; + bool Skip; + CMemBlocks2(): Defined(false), Skip(false) {} +}; + +class CMemRefs +{ +public: + CMemBlockManagerMt *Manager; + CObjectVector<CMemBlocks2> Refs; + CMemRefs(CMemBlockManagerMt *manager): Manager(manager) {} ; + ~CMemRefs() + { + for (int i = 0; i < Refs.Size(); i++) + Refs[i].FreeOpt(Manager); + } +}; + +class CMtProgressMixer2: + public ICompressProgressInfo, + public CMyUnknownImp +{ + UInt64 ProgressOffset; + UInt64 InSizes[2]; + UInt64 OutSizes[2]; + CMyComPtr<IProgress> Progress; + CMyComPtr<ICompressProgressInfo> RatioProgress; + bool _inSizeIsMain; +public: + NWindows::NSynchronization::CCriticalSection CriticalSection; + MY_UNKNOWN_IMP + void Create(IProgress *progress, bool inSizeIsMain); + void SetProgressOffset(UInt64 progressOffset); + HRESULT SetRatioInfo(int index, const UInt64 *inSize, const UInt64 *outSize); + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +void CMtProgressMixer2::Create(IProgress *progress, bool inSizeIsMain) +{ + Progress = progress; + Progress.QueryInterface(IID_ICompressProgressInfo, &RatioProgress); + _inSizeIsMain = inSizeIsMain; + ProgressOffset = InSizes[0] = InSizes[1] = OutSizes[0] = OutSizes[1] = 0; +} + +void CMtProgressMixer2::SetProgressOffset(UInt64 progressOffset) +{ + CriticalSection.Enter(); + InSizes[1] = OutSizes[1] = 0; + ProgressOffset = progressOffset; + CriticalSection.Leave(); +} + +HRESULT CMtProgressMixer2::SetRatioInfo(int index, const UInt64 *inSize, const UInt64 *outSize) +{ + NWindows::NSynchronization::CCriticalSectionLock lock(CriticalSection); + if (index == 0 && RatioProgress) + { + RINOK(RatioProgress->SetRatioInfo(inSize, outSize)); + } + if (inSize != 0) + InSizes[index] = *inSize; + if (outSize != 0) + OutSizes[index] = *outSize; + UInt64 v = ProgressOffset + (_inSizeIsMain ? + (InSizes[0] + InSizes[1]) : + (OutSizes[0] + OutSizes[1])); + return Progress->SetCompleted(&v); +} + +STDMETHODIMP CMtProgressMixer2::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + return SetRatioInfo(0, inSize, outSize); +} + +class CMtProgressMixer: + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + CMtProgressMixer2 *Mixer2; + CMyComPtr<ICompressProgressInfo> RatioProgress; + void Create(IProgress *progress, bool inSizeIsMain); + MY_UNKNOWN_IMP + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + +void CMtProgressMixer::Create(IProgress *progress, bool inSizeIsMain) +{ + Mixer2 = new CMtProgressMixer2; + RatioProgress = Mixer2; + Mixer2->Create(progress, inSizeIsMain); +} + +STDMETHODIMP CMtProgressMixer::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) +{ + return Mixer2->SetRatioInfo(1, inSize, outSize); +} + + +#endif + + +static HRESULT UpdateItemOldData(COutArchive &archive, + IInStream *inStream, + const CUpdateItem &ui, CItemEx &item, + /* bool izZip64, */ + ICompressProgressInfo *progress, + UInt64 &complexity) +{ + if (ui.NewProperties) + { + if (item.HasDescriptor()) + return E_NOTIMPL; + + // use old name size. + // CUpdateRange range(item.GetLocalExtraPosition(), item.LocalExtraSize + item.PackSize); + CUpdateRange range(item.GetDataPosition(), item.PackSize); + + // item.ExternalAttributes = ui.Attributes; + // Test it + item.Name = ui.Name; + item.SetUtf8(ui.IsUtf8); + item.Time = ui.Time; + item.NtfsMTime = ui.NtfsMTime; + item.NtfsATime = ui.NtfsATime; + item.NtfsCTime = ui.NtfsCTime; + item.NtfsTimeIsDefined = ui.NtfsTimeIsDefined; + + item.CentralExtra.RemoveUnknownSubBlocks(); + item.LocalExtra.RemoveUnknownSubBlocks(); + + archive.PrepareWriteCompressedData2((UInt16)item.Name.Length(), item.UnPackSize, item.PackSize, item.LocalExtra.HasWzAesField()); + item.LocalHeaderPosition = archive.GetCurrentPosition(); + archive.SeekToPackedDataPosition(); + RINOK(WriteRange(inStream, archive, range, progress)); + complexity += range.Size; + archive.WriteLocalHeader(item); + } + else + { + CUpdateRange range(item.LocalHeaderPosition, item.GetLocalFullSize()); + + // set new header position + item.LocalHeaderPosition = archive.GetCurrentPosition(); + + RINOK(WriteRange(inStream, archive, range, progress)); + complexity += range.Size; + archive.MoveBasePosition(range.Size); + } + return S_OK; +} + +static void WriteDirHeader(COutArchive &archive, const CCompressionMethodMode *options, + const CUpdateItem &ui, CItemEx &item) +{ + SetFileHeader(archive, *options, ui, item); + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + archive.WriteLocalHeader(item); +} + +static HRESULT Update2St( + DECL_EXTERNAL_CODECS_LOC_VARS + COutArchive &archive, + CInArchive *inArchive, + IInStream *inStream, + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + const CCompressionMethodMode *options, + const CByteBuffer *comment, + IArchiveUpdateCallback *updateCallback) +{ + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(updateCallback, true); + + CAddCommon compressor(*options); + + CObjectVector<CItem> items; + UInt64 unpackSizeTotal = 0, packSizeTotal = 0; + + for (int itemIndex = 0; itemIndex < updateItems.Size(); itemIndex++) + { + lps->InSize = unpackSizeTotal; + lps->OutSize = packSizeTotal; + RINOK(lps->SetCur()); + const CUpdateItem &ui = updateItems[itemIndex]; + CItemEx item; + if (!ui.NewProperties || !ui.NewData) + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + } + + if (ui.NewData) + { + bool isDir = ((ui.NewProperties) ? ui.IsDir : item.IsDir()); + if (isDir) + { + WriteDirHeader(archive, options, ui, item); + } + else + { + CMyComPtr<ISequentialInStream> fileInStream; + HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); + if (res == S_FALSE) + { + lps->ProgressOffset += ui.Size; + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + continue; + } + RINOK(res); + + // file Size can be 64-bit !!! + SetFileHeader(archive, *options, ui, item); + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + CCompressingResult compressingResult; + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + RINOK(compressor.Compress( + EXTERNAL_CODECS_LOC_VARS + fileInStream, outStream, progress, compressingResult)); + SetItemInfoFromCompressingResult(compressingResult, options->IsAesMode, options->AesKeyMode, item); + archive.WriteLocalHeader(item); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + unpackSizeTotal += item.UnPackSize; + packSizeTotal += item.PackSize; + } + } + else + { + UInt64 complexity = 0; + lps->SendRatio = false; + RINOK(UpdateItemOldData(archive, inStream, ui, item, progress, complexity)); + lps->SendRatio = true; + lps->ProgressOffset += complexity; + } + items.Add(item); + lps->ProgressOffset += NFileHeader::kLocalBlockSize; + } + archive.WriteCentralDir(items, comment); + return S_OK; +} + +static HRESULT Update2( + DECL_EXTERNAL_CODECS_LOC_VARS + COutArchive &archive, + CInArchive *inArchive, + IInStream *inStream, + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + const CCompressionMethodMode *options, + const CByteBuffer *comment, + IArchiveUpdateCallback *updateCallback) +{ + UInt64 complexity = 0; + UInt64 numFilesToCompress = 0; + UInt64 numBytesToCompress = 0; + + int i; + for(i = 0; i < updateItems.Size(); i++) + { + const CUpdateItem &ui = updateItems[i]; + if (ui.NewData) + { + complexity += ui.Size; + numBytesToCompress += ui.Size; + numFilesToCompress++; + /* + if (ui.Commented) + complexity += ui.CommentRange.Size; + */ + } + else + { + CItemEx inputItem = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(inputItem) != S_OK) + return E_NOTIMPL; + complexity += inputItem.GetLocalFullSize(); + // complexity += inputItem.GetCentralExtraPlusCommentSize(); + } + complexity += NFileHeader::kLocalBlockSize; + complexity += NFileHeader::kCentralBlockSize; + } + + if (comment) + complexity += comment->GetCapacity(); + complexity++; // end of central + updateCallback->SetTotal(complexity); + + CAddCommon compressor(*options); + + complexity = 0; + + #ifndef _7ZIP_ST + + const size_t kNumMaxThreads = (1 << 10); + UInt32 numThreads = options->NumThreads; + if (numThreads > kNumMaxThreads) + numThreads = kNumMaxThreads; + + const size_t kMemPerThread = (1 << 25); + const size_t kBlockSize = 1 << 16; + + CCompressionMethodMode options2; + if (options != 0) + options2 = *options; + + bool mtMode = ((options != 0) && (numThreads > 1)); + + if (numFilesToCompress <= 1) + mtMode = false; + + if (mtMode) + { + Byte method = options->MethodSequence.Front(); + if (method == NFileHeader::NCompressionMethod::kStored && !options->PasswordIsDefined) + mtMode = false; + if (method == NFileHeader::NCompressionMethod::kBZip2) + { + UInt64 averageSize = numBytesToCompress / numFilesToCompress; + UInt32 blockSize = options->DicSize; + if (blockSize == 0) + blockSize = 1; + UInt64 averageNumberOfBlocks = averageSize / blockSize; + UInt32 numBZip2Threads = 32; + if (averageNumberOfBlocks < numBZip2Threads) + numBZip2Threads = (UInt32)averageNumberOfBlocks; + if (numBZip2Threads < 1) + numBZip2Threads = 1; + numThreads = numThreads / numBZip2Threads; + options2.NumThreads = numBZip2Threads; + if (numThreads <= 1) + mtMode = false; + } + if (method == NFileHeader::NCompressionMethod::kLZMA) + { + UInt32 numLZMAThreads = (options->Algo > 0 ? 2 : 1); + numThreads /= numLZMAThreads; + options2.NumThreads = numLZMAThreads; + if (numThreads <= 1) + mtMode = false; + } + } + + if (!mtMode) + #endif + return Update2St( + EXTERNAL_CODECS_LOC_VARS + archive, inArchive,inStream, + inputItems, updateItems, options, comment, updateCallback); + + + #ifndef _7ZIP_ST + + // Warning : before memManager, threads and compressingCompletedEvents + // in order to have a "good" order for the destructor + NWindows::NSynchronization::CSynchro synchroForCompressingCompletedEvents; + synchroForCompressingCompletedEvents.Create(); + NWindows::NSynchronization::CSynchro synchroForOutStreamSpec; + synchroForOutStreamSpec.Create(); + + + CObjectVector<CItem> items; + + CMtProgressMixer *mtProgressMixerSpec = new CMtProgressMixer; + CMyComPtr<ICompressProgressInfo> progress = mtProgressMixerSpec; + mtProgressMixerSpec->Create(updateCallback, true); + + CMtCompressProgressMixer mtCompressProgressMixer; + mtCompressProgressMixer.Init(numThreads, mtProgressMixerSpec->RatioProgress); + + CMemBlockManagerMt memManager(kBlockSize); + CMemRefs refs(&memManager); + + CThreads threads; + CRecordVector<HANDLE> compressingCompletedEvents; + CRecordVector<int> threadIndices; // list threads in order of updateItems + + { + RINOK(memManager.AllocateSpaceAlways(&synchroForOutStreamSpec,(size_t)numThreads * (kMemPerThread / kBlockSize))); + for(i = 0; i < updateItems.Size(); i++) + refs.Refs.Add(CMemBlocks2()); + + UInt32 i; + for (i = 0; i < numThreads; i++) + threads.Threads.Add(CThreadInfo(options2)); + + for (i = 0; i < numThreads; i++) + { + CThreadInfo &threadInfo = threads.Threads[i]; + #ifdef EXTERNAL_CODECS + threadInfo._codecsInfo = codecsInfo; + threadInfo._externalCodecs = externalCodecs; + #endif + RINOK(threadInfo.CreateEvents(&synchroForCompressingCompletedEvents)); + threadInfo.OutStreamSpec = new COutMemStream(&memManager); + RINOK(threadInfo.OutStreamSpec->CreateEvents(&synchroForOutStreamSpec)); + threadInfo.OutStream = threadInfo.OutStreamSpec; + threadInfo.IsFree = true; + threadInfo.ProgressSpec = new CMtCompressProgress(); + threadInfo.Progress = threadInfo.ProgressSpec; + threadInfo.ProgressSpec->Init(&mtCompressProgressMixer, (int)i); + RINOK(threadInfo.CreateThread()); + } + } + int mtItemIndex = 0; + + int itemIndex = 0; + int lastRealStreamItemIndex = -1; + + while (itemIndex < updateItems.Size()) + { + if ((UInt32)threadIndices.Size() < numThreads && mtItemIndex < updateItems.Size()) + { + const CUpdateItem &ui = updateItems[mtItemIndex++]; + if (!ui.NewData) + continue; + CItemEx item; + if (ui.NewProperties) + { + if (ui.IsDir) + continue; + } + else + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + if (item.IsDir()) + continue; + } + CMyComPtr<ISequentialInStream> fileInStream; + { + NWindows::NSynchronization::CCriticalSectionLock lock(mtProgressMixerSpec->Mixer2->CriticalSection); + HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); + if (res == S_FALSE) + { + complexity += ui.Size; + complexity += NFileHeader::kLocalBlockSize; + mtProgressMixerSpec->Mixer2->SetProgressOffset(complexity); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + refs.Refs[mtItemIndex - 1].Skip = true; + continue; + } + RINOK(res); + RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + } + + for (UInt32 i = 0; i < numThreads; i++) + { + CThreadInfo &threadInfo = threads.Threads[i]; + if (threadInfo.IsFree) + { + threadInfo.IsFree = false; + threadInfo.InStream = fileInStream; + + // !!!!! we must release ref before sending event + // BUG was here in v4.43 and v4.44. It could change ref counter in two threads in same time + fileInStream.Release(); + + threadInfo.OutStreamSpec->Init(); + threadInfo.ProgressSpec->Reinit(); + threadInfo.CompressEvent.Set(); + threadInfo.UpdateIndex = mtItemIndex - 1; + + compressingCompletedEvents.Add(threadInfo.CompressionCompletedEvent); + threadIndices.Add(i); + break; + } + } + continue; + } + + if (refs.Refs[itemIndex].Skip) + { + itemIndex++; + continue; + } + + const CUpdateItem &ui = updateItems[itemIndex]; + + CItemEx item; + if (!ui.NewProperties || !ui.NewData) + { + item = inputItems[ui.IndexInArchive]; + if (inArchive->ReadLocalItemAfterCdItemFull(item) != S_OK) + return E_NOTIMPL; + } + + if (ui.NewData) + { + bool isDir = ((ui.NewProperties) ? ui.IsDir : item.IsDir()); + if (isDir) + { + WriteDirHeader(archive, options, ui, item); + } + else + { + if (lastRealStreamItemIndex < itemIndex) + { + lastRealStreamItemIndex = itemIndex; + SetFileHeader(archive, *options, ui, item); + // file Size can be 64-bit !!! + archive.PrepareWriteCompressedData((UInt16)item.Name.Length(), ui.Size, options->IsAesMode); + } + + CMemBlocks2 &memRef = refs.Refs[itemIndex]; + if (memRef.Defined) + { + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + memRef.WriteToStream(memManager.GetBlockSize(), outStream); + SetItemInfoFromCompressingResult(memRef.CompressingResult, + options->IsAesMode, options->AesKeyMode, item); + SetFileHeader(archive, *options, ui, item); + archive.WriteLocalHeader(item); + // RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); + memRef.FreeOpt(&memManager); + } + else + { + { + CThreadInfo &thread = threads.Threads[threadIndices.Front()]; + if (!thread.OutStreamSpec->WasUnlockEventSent()) + { + CMyComPtr<IOutStream> outStream; + archive.CreateStreamForCompressing(&outStream); + thread.OutStreamSpec->SetOutStream(outStream); + thread.OutStreamSpec->SetRealStreamMode(); + } + } + + DWORD result = ::WaitForMultipleObjects(compressingCompletedEvents.Size(), + &compressingCompletedEvents.Front(), FALSE, INFINITE); + int t = (int)(result - WAIT_OBJECT_0); + CThreadInfo &threadInfo = threads.Threads[threadIndices[t]]; + threadInfo.InStream.Release(); + threadInfo.IsFree = true; + RINOK(threadInfo.Result); + threadIndices.Delete(t); + compressingCompletedEvents.Delete(t); + if (t == 0) + { + RINOK(threadInfo.OutStreamSpec->WriteToRealStream()); + threadInfo.OutStreamSpec->ReleaseOutStream(); + SetItemInfoFromCompressingResult(threadInfo.CompressingResult, + options->IsAesMode, options->AesKeyMode, item); + SetFileHeader(archive, *options, ui, item); + archive.WriteLocalHeader(item); + } + else + { + CMemBlocks2 &memRef = refs.Refs[threadInfo.UpdateIndex]; + threadInfo.OutStreamSpec->DetachData(memRef); + memRef.CompressingResult = threadInfo.CompressingResult; + memRef.Defined = true; + continue; + } + } + } + } + else + { + RINOK(UpdateItemOldData(archive, inStream, ui, item, progress, complexity)); + } + items.Add(item); + complexity += NFileHeader::kLocalBlockSize; + mtProgressMixerSpec->Mixer2->SetProgressOffset(complexity); + itemIndex++; + } + archive.WriteCentralDir(items, comment); + return S_OK; + #endif +} + +static const size_t kCacheBlockSize = (1 << 20); +static const size_t kCacheSize = (kCacheBlockSize << 2); +static const size_t kCacheMask = (kCacheSize - 1); + +class CCacheOutStream: + public IOutStream, + public CMyUnknownImp +{ + CMyComPtr<IOutStream> _stream; + Byte *_cache; + UInt64 _virtPos; + UInt64 _virtSize; + UInt64 _phyPos; + UInt64 _phySize; // <= _virtSize + UInt64 _cachedPos; // (_cachedPos + _cachedSize) <= _virtSize + size_t _cachedSize; + + HRESULT MyWrite(size_t size); + HRESULT MyWriteBlock() + { + return MyWrite(kCacheBlockSize - ((size_t)_cachedPos & (kCacheBlockSize - 1))); + } + HRESULT FlushCache(); +public: + CCacheOutStream(): _cache(0) {} + ~CCacheOutStream(); + bool Allocate(); + HRESULT Init(IOutStream *stream); + + MY_UNKNOWN_IMP + + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); + STDMETHOD(SetSize)(UInt64 newSize); +}; + +bool CCacheOutStream::Allocate() +{ + if (!_cache) + _cache = (Byte *)::MidAlloc(kCacheSize); + return (_cache != NULL); +} + +HRESULT CCacheOutStream::Init(IOutStream *stream) +{ + _virtPos = _phyPos = 0; + _stream = stream; + RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &_virtPos)); + RINOK(_stream->Seek(0, STREAM_SEEK_END, &_virtSize)); + RINOK(_stream->Seek(_virtPos, STREAM_SEEK_SET, &_virtPos)); + _phyPos = _virtPos; + _phySize = _virtSize; + _cachedPos = 0; + _cachedSize = 0; + return S_OK; +} + +HRESULT CCacheOutStream::MyWrite(size_t size) +{ + while (size != 0 && _cachedSize != 0) + { + if (_phyPos != _cachedPos) + { + RINOK(_stream->Seek(_cachedPos, STREAM_SEEK_SET, &_phyPos)); + } + size_t pos = (size_t)_cachedPos & kCacheMask; + size_t curSize = MyMin(kCacheSize - pos, _cachedSize); + curSize = MyMin(curSize, size); + RINOK(WriteStream(_stream, _cache + pos, curSize)); + _phyPos += curSize; + if (_phySize < _phyPos) + _phySize = _phyPos; + _cachedPos += curSize; + _cachedSize -= curSize; + size -= curSize; + } + return S_OK; +} + +HRESULT CCacheOutStream::FlushCache() +{ + return MyWrite(_cachedSize); +} + +CCacheOutStream::~CCacheOutStream() +{ + FlushCache(); + if (_virtSize != _phySize) + _stream->SetSize(_virtSize); + if (_virtPos != _phyPos) + _stream->Seek(_virtPos, STREAM_SEEK_SET, NULL); + ::MidFree(_cache); +} + +STDMETHODIMP CCacheOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + + UInt64 zerosStart = _virtPos; + if (_cachedSize != 0) + { + if (_virtPos < _cachedPos) + { + RINOK(FlushCache()); + } + else + { + UInt64 cachedEnd = _cachedPos + _cachedSize; + if (cachedEnd < _virtPos) + { + if (cachedEnd < _phySize) + { + RINOK(FlushCache()); + } + else + zerosStart = cachedEnd; + } + } + } + + if (_cachedSize == 0 && _phySize < _virtPos) + _cachedPos = zerosStart = _phySize; + + if (zerosStart != _virtPos) + { + // write zeros to [cachedEnd ... _virtPos) + + for (;;) + { + UInt64 cachedEnd = _cachedPos + _cachedSize; + size_t endPos = (size_t)cachedEnd & kCacheMask; + size_t curSize = kCacheSize - endPos; + if (curSize > _virtPos - cachedEnd) + curSize = (size_t)(_virtPos - cachedEnd); + if (curSize == 0) + break; + while (curSize > (kCacheSize - _cachedSize)) + { + RINOK(MyWriteBlock()); + } + memset(_cache + endPos, 0, curSize); + _cachedSize += curSize; + } + } + + if (_cachedSize == 0) + _cachedPos = _virtPos; + + size_t pos = (size_t)_virtPos & kCacheMask; + size = (UInt32)MyMin((size_t)size, kCacheSize - pos); + UInt64 cachedEnd = _cachedPos + _cachedSize; + if (_virtPos != cachedEnd) // _virtPos < cachedEnd + size = (UInt32)MyMin((size_t)size, (size_t)(cachedEnd - _virtPos)); + else + { + // _virtPos == cachedEnd + if (_cachedSize == kCacheSize) + { + RINOK(MyWriteBlock()); + } + size_t startPos = (size_t)_cachedPos & kCacheMask; + if (startPos > pos) + size = (UInt32)MyMin((size_t)size, (size_t)(startPos - pos)); + _cachedSize += size; + } + memcpy(_cache + pos, data, size); + if (processedSize) + *processedSize = size; + _virtPos += size; + if (_virtSize < _virtPos) + _virtSize = _virtPos; + return S_OK; +} + +STDMETHODIMP CCacheOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = _virtSize + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +STDMETHODIMP CCacheOutStream::SetSize(UInt64 newSize) +{ + _virtSize = newSize; + if (newSize < _phySize) + { + RINOK(_stream->SetSize(newSize)); + _phySize = newSize; + } + if (newSize <= _cachedPos) + { + _cachedSize = 0; + _cachedPos = newSize; + } + if (newSize < _cachedPos + _cachedSize) + _cachedSize = (size_t)(newSize - _cachedPos); + return S_OK; +} + + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + ISequentialOutStream *seqOutStream, + CInArchive *inArchive, + CCompressionMethodMode *compressionMethodMode, + IArchiveUpdateCallback *updateCallback) +{ + CMyComPtr<IOutStream> outStream; + { + CMyComPtr<IOutStream> outStreamReal; + seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStreamReal); + if (!outStreamReal) + return E_NOTIMPL; + CCacheOutStream *cacheStream = new CCacheOutStream(); + outStream = cacheStream; + if (!cacheStream->Allocate()) + return E_OUTOFMEMORY; + RINOK(cacheStream->Init(outStreamReal)); + } + + if (inArchive) + { + if (inArchive->ArcInfo.Base != 0 || + inArchive->ArcInfo.StartPosition != 0 || + !inArchive->IsOkHeaders) + return E_NOTIMPL; + } + + COutArchive outArchive; + outArchive.Create(outStream); + /* + if (inArchive && inArchive->ArcInfo.StartPosition > 0) + { + CMyComPtr<ISequentialInStream> inStream; + inStream.Attach(inArchive->CreateLimitedStream(0, inArchive->ArcInfo.StartPosition)); + RINOK(CopyBlockToArchive(inStream, outArchive, NULL)); + outArchive.MoveBasePosition(inArchive->ArcInfo.StartPosition); + } + */ + CMyComPtr<IInStream> inStream; + if (inArchive) + inStream.Attach(inArchive->CreateStream()); + + return Update2( + EXTERNAL_CODECS_LOC_VARS + outArchive, inArchive, inStream, + inputItems, updateItems, + compressionMethodMode, + inArchive ? &inArchive->ArcInfo.Comment : NULL, + updateCallback); +} + +}} diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.h b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.h new file mode 100644 index 000000000..eee16738c --- /dev/null +++ b/src/libs/7zip/unix/CPP/7zip/Archive/Zip/ZipUpdate.h @@ -0,0 +1,58 @@ +// Zip/Update.h + +#ifndef __ZIP_UPDATE_H +#define __ZIP_UPDATE_H + +#include "../../ICoder.h" +#include "../IArchive.h" + +#include "../../Common/CreateCoder.h" + +#include "ZipCompressionMode.h" +#include "ZipIn.h" + +namespace NArchive { +namespace NZip { + +struct CUpdateRange +{ + UInt64 Position; + UInt64 Size; + CUpdateRange() {}; + CUpdateRange(UInt64 position, UInt64 size): Position(position), Size(size) {}; +}; + +struct CUpdateItem +{ + bool NewData; + bool NewProperties; + bool IsDir; + bool NtfsTimeIsDefined; + bool IsUtf8; + int IndexInArchive; + int IndexInClient; + UInt32 Attributes; + UInt32 Time; + UInt64 Size; + AString Name; + // bool Commented; + // CUpdateRange CommentRange; + FILETIME NtfsMTime; + FILETIME NtfsATime; + FILETIME NtfsCTime; + + CUpdateItem(): NtfsTimeIsDefined(false), IsUtf8(false), Size(0) {} +}; + +HRESULT Update( + DECL_EXTERNAL_CODECS_LOC_VARS + const CObjectVector<CItemEx> &inputItems, + const CObjectVector<CUpdateItem> &updateItems, + ISequentialOutStream *seqOutStream, + CInArchive *inArchive, + CCompressionMethodMode *compressionMethodMode, + IArchiveUpdateCallback *updateCallback); + +}} + +#endif |