// 7zDecode.cpp #include "StdAfx.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "7zDecode.h" namespace NArchive { namespace N7z { class CDecProgress: public ICompressProgressInfo, public CMyUnknownImp { CMyComPtr _progress; public: CDecProgress(ICompressProgressInfo *progress): _progress(progress) {} MY_UNKNOWN_IMP1(ICompressProgressInfo) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); }; STDMETHODIMP CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize) { return _progress->SetRatioInfo(NULL, outSize); } static void Convert_FolderInfo_to_BindInfo(const CFolderEx &folder, CBindInfoEx &bi) { bi.Clear(); bi.Bonds.ClearAndSetSize(folder.Bonds.Size()); unsigned i; for (i = 0; i < folder.Bonds.Size(); i++) { NCoderMixer2::CBond &bond = bi.Bonds[i]; const N7z::CBond &folderBond = folder.Bonds[i]; bond.PackIndex = folderBond.PackIndex; bond.UnpackIndex = folderBond.UnpackIndex; } bi.Coders.ClearAndSetSize(folder.Coders.Size()); bi.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size()); for (i = 0; i < folder.Coders.Size(); i++) { const CCoderInfo &coderInfo = folder.Coders[i]; bi.Coders[i].NumStreams = coderInfo.NumStreams; bi.CoderMethodIDs[i] = coderInfo.MethodID; } /* if (!bi.SetUnpackCoder()) throw 1112; */ bi.UnpackCoder = folder.UnpackCoder; bi.PackStreams.ClearAndSetSize(folder.PackStreams.Size()); for (i = 0; i < folder.PackStreams.Size(); i++) bi.PackStreams[i] = folder.PackStreams[i]; } static inline bool AreCodersEqual( const NCoderMixer2::CCoderStreamsInfo &a1, const NCoderMixer2::CCoderStreamsInfo &a2) { return (a1.NumStreams == a2.NumStreams); } static inline bool AreBondsEqual( const NCoderMixer2::CBond &a1, const NCoderMixer2::CBond &a2) { return (a1.PackIndex == a2.PackIndex) && (a1.UnpackIndex == a2.UnpackIndex); } static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2) { if (a1.Coders.Size() != a2.Coders.Size()) return false; unsigned i; for (i = 0; i < a1.Coders.Size(); i++) if (!AreCodersEqual(a1.Coders[i], a2.Coders[i])) return false; if (a1.Bonds.Size() != a2.Bonds.Size()) return false; for (i = 0; i < a1.Bonds.Size(); i++) if (!AreBondsEqual(a1.Bonds[i], a2.Bonds[i])) return false; for (i = 0; i < a1.CoderMethodIDs.Size(); i++) if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i]) return false; if (a1.PackStreams.Size() != a2.PackStreams.Size()) return false; for (i = 0; i < a1.PackStreams.Size(); i++) if (a1.PackStreams[i] != a2.PackStreams[i]) return false; /* if (a1.UnpackCoder != a2.UnpackCoder) return false; */ return true; } CDecoder::CDecoder(bool useMixerMT): _bindInfoPrev_Defined(false), _useMixerMT(useMixerMT) {} struct CLockedInStream: public IUnknown, public CMyUnknownImp { CMyComPtr Stream; UInt64 Pos; MY_UNKNOWN_IMP #ifdef USE_MIXER_MT NWindows::NSynchronization::CCriticalSection CriticalSection; #endif }; #ifdef USE_MIXER_MT class CLockedSequentialInStreamMT: public ISequentialInStream, public CMyUnknownImp { CLockedInStream *_glob; UInt64 _pos; CMyComPtr _globRef; public: void Init(CLockedInStream *lockedInStream, UInt64 startPos) { _globRef = lockedInStream; _glob = lockedInStream; _pos = startPos; } MY_UNKNOWN_IMP1(ISequentialInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; STDMETHODIMP CLockedSequentialInStreamMT::Read(void *data, UInt32 size, UInt32 *processedSize) { NWindows::NSynchronization::CCriticalSectionLock lock(_glob->CriticalSection); if (_pos != _glob->Pos) { RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL)); _glob->Pos = _pos; } UInt32 realProcessedSize = 0; HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize); _pos += realProcessedSize; _glob->Pos = _pos; if (processedSize) *processedSize = realProcessedSize; return res; } #endif #ifdef USE_MIXER_ST class CLockedSequentialInStreamST: public ISequentialInStream, public CMyUnknownImp { CLockedInStream *_glob; UInt64 _pos; CMyComPtr _globRef; public: void Init(CLockedInStream *lockedInStream, UInt64 startPos) { _globRef = lockedInStream; _glob = lockedInStream; _pos = startPos; } MY_UNKNOWN_IMP1(ISequentialInStream) STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; STDMETHODIMP CLockedSequentialInStreamST::Read(void *data, UInt32 size, UInt32 *processedSize) { if (_pos != _glob->Pos) { RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL)); _glob->Pos = _pos; } UInt32 realProcessedSize = 0; HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize); _pos += realProcessedSize; _glob->Pos = _pos; if (processedSize) *processedSize = realProcessedSize; return res; } #endif HRESULT CDecoder::Decode( DECL_EXTERNAL_CODECS_LOC_VARS IInStream *inStream, UInt64 startPos, const CFolders &folders, unsigned folderIndex, const UInt64 *unpackSize , ISequentialOutStream *outStream , ICompressProgressInfo *compressProgress , ISequentialInStream ** #ifdef USE_MIXER_ST inStreamMainRes #endif _7Z_DECODER_CRYPRO_VARS_DECL #if !defined(_7ZIP_ST) && !defined(_SFX) , bool mtMode, UInt32 numThreads #endif ) { const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]]; CFolderEx folderInfo; folders.ParseFolderEx(folderIndex, folderInfo); if (!folderInfo.IsDecodingSupported()) return E_NOTIMPL; CBindInfoEx bindInfo; Convert_FolderInfo_to_BindInfo(folderInfo, bindInfo); if (!bindInfo.CalcMapsAndCheck()) return E_NOTIMPL; UInt64 folderUnpackSize = folders.GetFolderUnpackSize(folderIndex); bool fullUnpack = true; if (unpackSize) { if (*unpackSize > folderUnpackSize) return E_FAIL; fullUnpack = (*unpackSize == folderUnpackSize); } /* We don't need to init isEncrypted and passwordIsDefined We must upgrade them only #ifndef _NO_CRYPTO isEncrypted = false; passwordIsDefined = false; #endif */ if (!_bindInfoPrev_Defined || !AreBindInfoExEqual(bindInfo, _bindInfoPrev)) { _mixerRef.Release(); #ifdef USE_MIXER_MT #ifdef USE_MIXER_ST if (_useMixerMT) #endif { _mixerMT = new NCoderMixer2::CMixerMT(false); _mixerRef = _mixerMT; _mixer = _mixerMT; } #ifdef USE_MIXER_ST else #endif #endif { #ifdef USE_MIXER_ST _mixerST = new NCoderMixer2::CMixerST(false); _mixerRef = _mixerST; _mixer = _mixerST; #endif } RINOK(_mixer->SetBindInfo(bindInfo)); FOR_VECTOR(i, folderInfo.Coders) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; #ifndef _SFX // we don't support RAR codecs here if ((coderInfo.MethodID >> 8) == 0x403) return E_NOTIMPL; #endif CCreatedCoder cod; RINOK(CreateCoder( EXTERNAL_CODECS_LOC_VARS coderInfo.MethodID, false, cod)); if (coderInfo.IsSimpleCoder()) { if (!cod.Coder) return E_NOTIMPL; // CMethodId m = coderInfo.MethodID; // isFilter = (IsFilterMethod(m) || m == k_AES); } else { if (!cod.Coder2 || cod.NumStreams != coderInfo.NumStreams) return E_NOTIMPL; } _mixer->AddCoder(cod); // now there is no codec that uses another external codec /* #ifdef EXTERNAL_CODECS CMyComPtr setCompressCodecsInfo; decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { // we must use g_ExternalCodecs also RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs)); } #endif */ } _bindInfoPrev = bindInfo; _bindInfoPrev_Defined = true; } _mixer->ReInit(); UInt32 packStreamIndex = 0; UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex]; unsigned i; for (i = 0; i < folderInfo.Coders.Size(); i++) { const CCoderInfo &coderInfo = folderInfo.Coders[i]; IUnknown *decoder = _mixer->GetCoder(i).GetUnknown(); { CMyComPtr setDecoderProperties; decoder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties); if (setDecoderProperties) { const CByteBuffer &props = coderInfo.Props; size_t size = props.Size(); if (size > 0xFFFFFFFF) return E_NOTIMPL; HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size); if (res == E_INVALIDARG) res = E_NOTIMPL; RINOK(res); } } #if !defined(_7ZIP_ST) && !defined(_SFX) if (mtMode) { CMyComPtr setCoderMt; decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(numThreads)); } } #endif #ifndef _NO_CRYPTO { CMyComPtr cryptoSetPassword; decoder->QueryInterface(IID_ICryptoSetPassword, (void **)&cryptoSetPassword); if (cryptoSetPassword) { isEncrypted = true; if (!getTextPassword) return E_NOTIMPL; CMyComBSTR passwordBSTR; RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR)); passwordIsDefined = true; password.Empty(); size_t len = 0; if (passwordBSTR) { password = passwordBSTR; len = password.Len(); } CByteBuffer buffer(len * 2); for (size_t k = 0; k < len; k++) { wchar_t c = passwordBSTR[k]; ((Byte *)buffer)[k * 2] = (Byte)c; ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8); } RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size())); } } #endif { CMyComPtr setFinishMode; decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode); if (setFinishMode) { RINOK(setFinishMode->SetFinishMode(BoolToInt(fullUnpack))); } } UInt32 numStreams = (UInt32)coderInfo.NumStreams; CObjArray packSizes(numStreams); CObjArray packSizesPointers(numStreams); for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++) { int bond = folderInfo.FindBond_for_PackStream(packStreamIndex); if (bond >= 0) packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex]; else { int index = folderInfo.Find_in_PackStreams(packStreamIndex); if (index < 0) return E_NOTIMPL; packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index]; packSizesPointers[j] = &packSizes[j]; } } const UInt64 *unpackSizesPointer = (unpackSize && i == bindInfo.UnpackCoder) ? unpackSize : &folders.CoderUnpackSizes[unpackStreamIndexStart + i]; _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers); } if (outStream) { _mixer->SelectMainCoder(!fullUnpack); } CObjectVector< CMyComPtr > inStreams; CLockedInStream *lockedInStreamSpec = new CLockedInStream; CMyComPtr lockedInStream = lockedInStreamSpec; bool needMtLock = false; if (folderInfo.PackStreams.Size() > 1) { // lockedInStream.Pos = (UInt64)(Int64)-1; // RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &lockedInStream.Pos)); RINOK(inStream->Seek(startPos + packPositions[0], STREAM_SEEK_SET, &lockedInStreamSpec->Pos)); lockedInStreamSpec->Stream = inStream; #ifdef USE_MIXER_ST if (_mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex)) #endif needMtLock = true; } for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++) { CMyComPtr packStream; UInt64 packPos = startPos + packPositions[j]; if (folderInfo.PackStreams.Size() == 1) { RINOK(inStream->Seek(packPos, STREAM_SEEK_SET, NULL)); packStream = inStream; } else { #ifdef USE_MIXER_MT #ifdef USE_MIXER_ST if (_useMixerMT || needMtLock) #endif { CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT; packStream = lockedStreamImpSpec; lockedStreamImpSpec->Init(lockedInStreamSpec, packPos); } #ifdef USE_MIXER_ST else #endif #endif { #ifdef USE_MIXER_ST CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST; packStream = lockedStreamImpSpec; lockedStreamImpSpec->Init(lockedInStreamSpec, packPos); #endif } } CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; inStreams.AddNew() = streamSpec; streamSpec->SetStream(packStream); streamSpec->Init(packPositions[j + 1] - packPositions[j]); } unsigned num = inStreams.Size(); CObjArray inStreamPointers(num); for (i = 0; i < num; i++) inStreamPointers[i] = inStreams[i]; if (outStream) { CMyComPtr progress2; if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex)) progress2 = new CDecProgress(compressProgress); ISequentialOutStream *outStreamPointer = outStream; return _mixer->Code(inStreamPointers, &outStreamPointer, progress2 ? (ICompressProgressInfo *)progress2 : compressProgress); } #ifdef USE_MIXER_ST return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes); #else return E_FAIL; #endif } }}