Source code

Revision control

Copy as Markdown

Other Tools

// 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<ICompressProgressInfo> _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<IInStream> 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<IUnknown> _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<IUnknown> _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
, bool &dataAfterEnd_Error↩
_7Z_DECODER_CRYPRO_VARS_DECL↩
#if !defined(_7ZIP_ST)↩
, bool mtMode, UInt32 numThreads, UInt64 memUsage↩
#endif
)↩
{↩
dataAfterEnd_Error = false;↩
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_Id(↩
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<ISetCompressCodecsInfo> 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;↩
bool mt_wasUsed = false;↩
for (i = 0; i < folderInfo.Coders.Size(); i++)↩
{↩
const CCoderInfo &coderInfo = folderInfo.Coders[i];↩
IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();↩
#if !defined(_7ZIP_ST)↩
if (!mt_wasUsed)↩
{↩
if (mtMode)↩
{↩
CMyComPtr<ICompressSetCoderMt> setCoderMt;↩
decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);↩
if (setCoderMt)↩
{↩
mt_wasUsed = true;↩
RINOK(setCoderMt->SetNumberOfThreads(numThreads));↩
}↩
}↩
// if (memUsage != 0)↩
{↩
CMyComPtr<ICompressSetMemLimit> setMemLimit;↩
decoder->QueryInterface(IID_ICompressSetMemLimit, (void **)&setMemLimit);↩
if (setMemLimit)↩
{↩
mt_wasUsed = true;↩
RINOK(setMemLimit->SetMemLimit(memUsage));↩
}↩
}↩
}↩
#endif
{↩
CMyComPtr<ICompressSetDecoderProperties2> 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);↩
}↩
}↩
#ifndef _NO_CRYPTO↩
{↩
CMyComPtr<ICryptoSetPassword> 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
bool finishMode = false;↩
{↩
CMyComPtr<ICompressSetFinishMode> setFinishMode;↩
decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);↩
if (setFinishMode)↩
{↩
finishMode = fullUnpack;↩
RINOK(setFinishMode->SetFinishMode(BoolToInt(finishMode)));↩
}↩
}↩
UInt32 numStreams = (UInt32)coderInfo.NumStreams;↩
CObjArray<UInt64> packSizes(numStreams);↩
CObjArray<const UInt64 *> 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, finishMode);↩
}↩
if (outStream)↩
{↩
_mixer->SelectMainCoder(!fullUnpack);↩
}↩
CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;↩
CLockedInStream *lockedInStreamSpec = new CLockedInStream;↩
CMyComPtr<IUnknown> 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<ISequentialInStream> 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<ISequentialInStream *> inStreamPointers(num);↩
for (i = 0; i < num; i++)↩
inStreamPointers[i] = inStreams[i];↩
if (outStream)↩
{↩
CMyComPtr<ICompressProgressInfo> 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,↩
dataAfterEnd_Error);↩
}↩
#ifdef USE_MIXER_ST↩
return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);↩
#else
return E_FAIL;↩
#endif
}↩
}}↩