1// 7zDecode.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/LimitedStreams.h"
6#include "../../Common/LockedStream.h"
7#include "../../Common/ProgressUtils.h"
8#include "../../Common/StreamObjects.h"
9
10#include "7zDecode.h"
11
12namespace NArchive {
13namespace N7z {
14
15static void ConvertFolderItemInfoToBindInfo(const CFolder &folder,
16    CBindInfoEx &bindInfo)
17{
18  bindInfo.Clear();
19  int i;
20  for (i = 0; i < folder.BindPairs.Size(); i++)
21  {
22    NCoderMixer::CBindPair bindPair;
23    bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex;
24    bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex;
25    bindInfo.BindPairs.Add(bindPair);
26  }
27  UInt32 outStreamIndex = 0;
28  for (i = 0; i < folder.Coders.Size(); i++)
29  {
30    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
31    const CCoderInfo &coderInfo = folder.Coders[i];
32    coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams;
33    coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams;
34    bindInfo.Coders.Add(coderStreamsInfo);
35    bindInfo.CoderMethodIDs.Add(coderInfo.MethodID);
36    for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++)
37      if (folder.FindBindPairForOutStream(outStreamIndex) < 0)
38        bindInfo.OutStreams.Add(outStreamIndex);
39  }
40  for (i = 0; i < folder.PackStreams.Size(); i++)
41    bindInfo.InStreams.Add((UInt32)folder.PackStreams[i]);
42}
43
44static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1,
45    const NCoderMixer::CCoderStreamsInfo &a2)
46{
47  return (a1.NumInStreams == a2.NumInStreams) &&
48    (a1.NumOutStreams == a2.NumOutStreams);
49}
50
51static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2)
52{
53  return (a1.InIndex == a2.InIndex) &&
54    (a1.OutIndex == a2.OutIndex);
55}
56
57static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
58{
59  if (a1.Coders.Size() != a2.Coders.Size())
60    return false;
61  int i;
62  for (i = 0; i < a1.Coders.Size(); i++)
63    if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
64      return false;
65  if (a1.BindPairs.Size() != a2.BindPairs.Size())
66    return false;
67  for (i = 0; i < a1.BindPairs.Size(); i++)
68    if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i]))
69      return false;
70  for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
71    if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
72      return false;
73  if (a1.InStreams.Size() != a2.InStreams.Size())
74    return false;
75  if (a1.OutStreams.Size() != a2.OutStreams.Size())
76    return false;
77  return true;
78}
79
80CDecoder::CDecoder(bool multiThread)
81{
82  #ifndef _ST_MODE
83  multiThread = true;
84  #endif
85  _multiThread = multiThread;
86  _bindInfoExPrevIsDefined = false;
87}
88
89HRESULT CDecoder::Decode(
90    DECL_EXTERNAL_CODECS_LOC_VARS
91    IInStream *inStream,
92    UInt64 startPos,
93    const UInt64 *packSizes,
94    const CFolder &folderInfo,
95    ISequentialOutStream *outStream,
96    ICompressProgressInfo *compressProgress
97    #ifndef _NO_CRYPTO
98    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
99    #endif
100    #if !defined(_7ZIP_ST) && !defined(_SFX)
101    , bool mtMode, UInt32 numThreads
102    #endif
103    )
104{
105  if (!folderInfo.CheckStructure())
106    return E_NOTIMPL;
107  #ifndef _NO_CRYPTO
108  passwordIsDefined = false;
109  #endif
110  CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
111
112  CLockedInStream lockedInStream;
113  lockedInStream.Init(inStream);
114
115  for (int j = 0; j < folderInfo.PackStreams.Size(); j++)
116  {
117    CLockedSequentialInStreamImp *lockedStreamImpSpec = new
118        CLockedSequentialInStreamImp;
119    CMyComPtr<ISequentialInStream> lockedStreamImp = lockedStreamImpSpec;
120    lockedStreamImpSpec->Init(&lockedInStream, startPos);
121    startPos += packSizes[j];
122
123    CLimitedSequentialInStream *streamSpec = new
124        CLimitedSequentialInStream;
125    CMyComPtr<ISequentialInStream> inStream = streamSpec;
126    streamSpec->SetStream(lockedStreamImp);
127    streamSpec->Init(packSizes[j]);
128    inStreams.Add(inStream);
129  }
130
131  int numCoders = folderInfo.Coders.Size();
132
133  CBindInfoEx bindInfo;
134  ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo);
135  bool createNewCoders;
136  if (!_bindInfoExPrevIsDefined)
137    createNewCoders = true;
138  else
139    createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev);
140  if (createNewCoders)
141  {
142    int i;
143    _decoders.Clear();
144    // _decoders2.Clear();
145
146    _mixerCoder.Release();
147
148    if (_multiThread)
149    {
150      _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT;
151      _mixerCoder = _mixerCoderMTSpec;
152      _mixerCoderCommon = _mixerCoderMTSpec;
153    }
154    else
155    {
156      #ifdef _ST_MODE
157      _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST;
158      _mixerCoder = _mixerCoderSTSpec;
159      _mixerCoderCommon = _mixerCoderSTSpec;
160      #endif
161    }
162    RINOK(_mixerCoderCommon->SetBindInfo(bindInfo));
163
164    for (i = 0; i < numCoders; i++)
165    {
166      const CCoderInfo &coderInfo = folderInfo.Coders[i];
167
168
169      CMyComPtr<ICompressCoder> decoder;
170      CMyComPtr<ICompressCoder2> decoder2;
171      RINOK(CreateCoder(
172          EXTERNAL_CODECS_LOC_VARS
173          coderInfo.MethodID, decoder, decoder2, false));
174      CMyComPtr<IUnknown> decoderUnknown;
175      if (coderInfo.IsSimpleCoder())
176      {
177        if (decoder == 0)
178          return E_NOTIMPL;
179
180        decoderUnknown = (IUnknown *)decoder;
181
182        if (_multiThread)
183          _mixerCoderMTSpec->AddCoder(decoder);
184        #ifdef _ST_MODE
185        else
186          _mixerCoderSTSpec->AddCoder(decoder, false);
187        #endif
188      }
189      else
190      {
191        if (decoder2 == 0)
192          return E_NOTIMPL;
193        decoderUnknown = (IUnknown *)decoder2;
194        if (_multiThread)
195          _mixerCoderMTSpec->AddCoder2(decoder2);
196        #ifdef _ST_MODE
197        else
198          _mixerCoderSTSpec->AddCoder2(decoder2, false);
199        #endif
200      }
201      _decoders.Add(decoderUnknown);
202      #ifdef EXTERNAL_CODECS
203      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
204      decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
205      if (setCompressCodecsInfo)
206      {
207        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo));
208      }
209      #endif
210    }
211    _bindInfoExPrev = bindInfo;
212    _bindInfoExPrevIsDefined = true;
213  }
214  int i;
215  _mixerCoderCommon->ReInit();
216
217  UInt32 packStreamIndex = 0, unpackStreamIndex = 0;
218  UInt32 coderIndex = 0;
219  // UInt32 coder2Index = 0;
220
221  for (i = 0; i < numCoders; i++)
222  {
223    const CCoderInfo &coderInfo = folderInfo.Coders[i];
224    CMyComPtr<IUnknown> &decoder = _decoders[coderIndex];
225
226    {
227      CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
228      decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
229      if (setDecoderProperties)
230      {
231        const CByteBuffer &props = coderInfo.Props;
232        size_t size = props.GetCapacity();
233        if (size > 0xFFFFFFFF)
234          return E_NOTIMPL;
235        // if (size > 0)
236        {
237          RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size));
238        }
239      }
240    }
241
242    #if !defined(_7ZIP_ST) && !defined(_SFX)
243    if (mtMode)
244    {
245      CMyComPtr<ICompressSetCoderMt> setCoderMt;
246      decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
247      if (setCoderMt)
248      {
249        RINOK(setCoderMt->SetNumberOfThreads(numThreads));
250      }
251    }
252    #endif
253
254    #ifndef _NO_CRYPTO
255    {
256      CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
257      decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
258      if (cryptoSetPassword)
259      {
260        if (getTextPassword == 0)
261          return E_FAIL;
262        CMyComBSTR passwordBSTR;
263        RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
264        CByteBuffer buffer;
265        passwordIsDefined = true;
266        const UString password(passwordBSTR);
267        const UInt32 sizeInBytes = password.Length() * 2;
268        buffer.SetCapacity(sizeInBytes);
269        for (int i = 0; i < password.Length(); i++)
270        {
271          wchar_t c = password[i];
272          ((Byte *)buffer)[i * 2] = (Byte)c;
273          ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
274        }
275        RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
276      }
277    }
278    #endif
279
280    coderIndex++;
281
282    UInt32 numInStreams = (UInt32)coderInfo.NumInStreams;
283    UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams;
284    CRecordVector<const UInt64 *> packSizesPointers;
285    CRecordVector<const UInt64 *> unpackSizesPointers;
286    packSizesPointers.Reserve(numInStreams);
287    unpackSizesPointers.Reserve(numOutStreams);
288    UInt32 j;
289    for (j = 0; j < numOutStreams; j++, unpackStreamIndex++)
290      unpackSizesPointers.Add(&folderInfo.UnpackSizes[unpackStreamIndex]);
291
292    for (j = 0; j < numInStreams; j++, packStreamIndex++)
293    {
294      int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex);
295      if (bindPairIndex >= 0)
296        packSizesPointers.Add(
297        &folderInfo.UnpackSizes[(UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]);
298      else
299      {
300        int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex);
301        if (index < 0)
302          return E_FAIL;
303        packSizesPointers.Add(&packSizes[index]);
304      }
305    }
306
307    _mixerCoderCommon->SetCoderInfo(i,
308        &packSizesPointers.Front(),
309        &unpackSizesPointers.Front());
310  }
311  UInt32 mainCoder, temp;
312  bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp);
313
314  if (_multiThread)
315    _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder);
316  /*
317  else
318    _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);;
319  */
320
321  if (numCoders == 0)
322    return 0;
323  CRecordVector<ISequentialInStream *> inStreamPointers;
324  inStreamPointers.Reserve(inStreams.Size());
325  for (i = 0; i < inStreams.Size(); i++)
326    inStreamPointers.Add(inStreams[i]);
327  ISequentialOutStream *outStreamPointer = outStream;
328  return _mixerCoder->Code(&inStreamPointers.Front(), NULL,
329    inStreams.Size(), &outStreamPointer, NULL, 1, compressProgress);
330}
331
332}}
333