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