1// 7zDecode.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/LimitedStreams.h"
6#include "../../Common/ProgressUtils.h"
7#include "../../Common/StreamObjects.h"
8#include "../../Common/StreamUtils.h"
9
10#include "7zDecode.h"
11
12namespace NArchive {
13namespace N7z {
14
15Z7_CLASS_IMP_COM_1(
16  CDecProgress
17  , ICompressProgressInfo
18)
19  CMyComPtr<ICompressProgressInfo> _progress;
20public:
21  CDecProgress(ICompressProgressInfo *progress): _progress(progress) {}
22};
23
24Z7_COM7F_IMF(CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize))
25{
26  return _progress->SetRatioInfo(NULL, outSize);
27}
28
29static void Convert_FolderInfo_to_BindInfo(const CFolderEx &folder, CBindInfoEx &bi)
30{
31  bi.Clear();
32
33  bi.Bonds.ClearAndSetSize(folder.Bonds.Size());
34  unsigned i;
35  for (i = 0; i < folder.Bonds.Size(); i++)
36  {
37    NCoderMixer2::CBond &bond = bi.Bonds[i];
38    const N7z::CBond &folderBond = folder.Bonds[i];
39    bond.PackIndex = folderBond.PackIndex;
40    bond.UnpackIndex = folderBond.UnpackIndex;
41  }
42
43  bi.Coders.ClearAndSetSize(folder.Coders.Size());
44  bi.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size());
45  for (i = 0; i < folder.Coders.Size(); i++)
46  {
47    const CCoderInfo &coderInfo = folder.Coders[i];
48    bi.Coders[i].NumStreams = coderInfo.NumStreams;
49    bi.CoderMethodIDs[i] = coderInfo.MethodID;
50  }
51
52  /*
53  if (!bi.SetUnpackCoder())
54    throw 1112;
55  */
56  bi.UnpackCoder = folder.UnpackCoder;
57  bi.PackStreams.ClearAndSetSize(folder.PackStreams.Size());
58  for (i = 0; i < folder.PackStreams.Size(); i++)
59    bi.PackStreams[i] = folder.PackStreams[i];
60}
61
62static inline bool AreCodersEqual(
63    const NCoderMixer2::CCoderStreamsInfo &a1,
64    const NCoderMixer2::CCoderStreamsInfo &a2)
65{
66  return (a1.NumStreams == a2.NumStreams);
67}
68
69static inline bool AreBondsEqual(
70    const NCoderMixer2::CBond &a1,
71    const NCoderMixer2::CBond &a2)
72{
73  return
74    (a1.PackIndex == a2.PackIndex) &&
75    (a1.UnpackIndex == a2.UnpackIndex);
76}
77
78static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
79{
80  if (a1.Coders.Size() != a2.Coders.Size())
81    return false;
82  unsigned i;
83  for (i = 0; i < a1.Coders.Size(); i++)
84    if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
85      return false;
86
87  if (a1.Bonds.Size() != a2.Bonds.Size())
88    return false;
89  for (i = 0; i < a1.Bonds.Size(); i++)
90    if (!AreBondsEqual(a1.Bonds[i], a2.Bonds[i]))
91      return false;
92
93  for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
94    if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
95      return false;
96
97  if (a1.PackStreams.Size() != a2.PackStreams.Size())
98    return false;
99  for (i = 0; i < a1.PackStreams.Size(); i++)
100    if (a1.PackStreams[i] != a2.PackStreams[i])
101      return false;
102
103  /*
104  if (a1.UnpackCoder != a2.UnpackCoder)
105    return false;
106  */
107  return true;
108}
109
110CDecoder::CDecoder(bool useMixerMT):
111    _bindInfoPrev_Defined(false)
112{
113  #if defined(USE_MIXER_ST) && defined(USE_MIXER_MT)
114  _useMixerMT = useMixerMT;
115  #else
116  UNUSED_VAR(useMixerMT)
117  #endif
118}
119
120
121Z7_CLASS_IMP_COM_0(
122  CLockedInStream
123)
124public:
125  CMyComPtr<IInStream> Stream;
126  UInt64 Pos;
127
128  #ifdef USE_MIXER_MT
129  NWindows::NSynchronization::CCriticalSection CriticalSection;
130  #endif
131};
132
133
134#ifdef USE_MIXER_MT
135
136Z7_CLASS_IMP_COM_1(
137  CLockedSequentialInStreamMT
138  , ISequentialInStream
139)
140  CLockedInStream *_glob;
141  UInt64 _pos;
142  CMyComPtr<IUnknown> _globRef;
143public:
144  void Init(CLockedInStream *lockedInStream, UInt64 startPos)
145  {
146    _globRef = lockedInStream;
147    _glob = lockedInStream;
148    _pos = startPos;
149  }
150};
151
152Z7_COM7F_IMF(CLockedSequentialInStreamMT::Read(void *data, UInt32 size, UInt32 *processedSize))
153{
154  NWindows::NSynchronization::CCriticalSectionLock lock(_glob->CriticalSection);
155
156  if (_pos != _glob->Pos)
157  {
158    RINOK(InStream_SeekSet(_glob->Stream, _pos))
159    _glob->Pos = _pos;
160  }
161
162  UInt32 realProcessedSize = 0;
163  const HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
164  _pos += realProcessedSize;
165  _glob->Pos = _pos;
166  if (processedSize)
167    *processedSize = realProcessedSize;
168  return res;
169}
170
171#endif
172
173
174#ifdef USE_MIXER_ST
175
176Z7_CLASS_IMP_COM_1(
177  CLockedSequentialInStreamST
178  , ISequentialInStream
179)
180  CLockedInStream *_glob;
181  UInt64 _pos;
182  CMyComPtr<IUnknown> _globRef;
183public:
184  void Init(CLockedInStream *lockedInStream, UInt64 startPos)
185  {
186    _globRef = lockedInStream;
187    _glob = lockedInStream;
188    _pos = startPos;
189  }
190};
191
192Z7_COM7F_IMF(CLockedSequentialInStreamST::Read(void *data, UInt32 size, UInt32 *processedSize))
193{
194  if (_pos != _glob->Pos)
195  {
196    RINOK(InStream_SeekSet(_glob->Stream, _pos))
197    _glob->Pos = _pos;
198  }
199
200  UInt32 realProcessedSize = 0;
201  const HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
202  _pos += realProcessedSize;
203  _glob->Pos = _pos;
204  if (processedSize)
205    *processedSize = realProcessedSize;
206  return res;
207}
208
209#endif
210
211
212
213HRESULT CDecoder::Decode(
214    DECL_EXTERNAL_CODECS_LOC_VARS
215    IInStream *inStream,
216    UInt64 startPos,
217    const CFolders &folders, unsigned folderIndex,
218    const UInt64 *unpackSize
219
220    , ISequentialOutStream *outStream
221    , ICompressProgressInfo *compressProgress
222
223    , ISequentialInStream **
224        #ifdef USE_MIXER_ST
225        inStreamMainRes
226        #endif
227
228    , bool &dataAfterEnd_Error
229
230    Z7_7Z_DECODER_CRYPRO_VARS_DECL
231
232    #if !defined(Z7_ST)
233    , bool mtMode, UInt32 numThreads, UInt64 memUsage
234    #endif
235    )
236{
237  dataAfterEnd_Error = false;
238
239  const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]];
240  CFolderEx folderInfo;
241  folders.ParseFolderEx(folderIndex, folderInfo);
242
243  if (!folderInfo.IsDecodingSupported())
244    return E_NOTIMPL;
245
246  CBindInfoEx bindInfo;
247  Convert_FolderInfo_to_BindInfo(folderInfo, bindInfo);
248  if (!bindInfo.CalcMapsAndCheck())
249    return E_NOTIMPL;
250
251  UInt64 folderUnpackSize = folders.GetFolderUnpackSize(folderIndex);
252  bool fullUnpack = true;
253  if (unpackSize)
254  {
255    if (*unpackSize > folderUnpackSize)
256      return E_FAIL;
257    fullUnpack = (*unpackSize == folderUnpackSize);
258  }
259
260  /*
261  We don't need to init isEncrypted and passwordIsDefined
262  We must upgrade them only
263
264  #ifndef Z7_NO_CRYPTO
265  isEncrypted = false;
266  passwordIsDefined = false;
267  #endif
268  */
269
270  if (!_bindInfoPrev_Defined || !AreBindInfoExEqual(bindInfo, _bindInfoPrev))
271  {
272    _bindInfoPrev_Defined = false;
273    _mixerRef.Release();
274
275    #ifdef USE_MIXER_MT
276    #ifdef USE_MIXER_ST
277    if (_useMixerMT)
278    #endif
279    {
280      _mixerMT = new NCoderMixer2::CMixerMT(false);
281      _mixerRef = _mixerMT;
282      _mixer = _mixerMT;
283    }
284    #ifdef USE_MIXER_ST
285    else
286    #endif
287    #endif
288    {
289      #ifdef USE_MIXER_ST
290      _mixerST = new NCoderMixer2::CMixerST(false);
291      _mixerRef = _mixerST;
292      _mixer = _mixerST;
293      #endif
294    }
295
296    RINOK(_mixer->SetBindInfo(bindInfo))
297
298    FOR_VECTOR(i, folderInfo.Coders)
299    {
300      const CCoderInfo &coderInfo = folderInfo.Coders[i];
301
302      #ifndef Z7_SFX
303      // we don't support RAR codecs here
304      if ((coderInfo.MethodID >> 8) == 0x403)
305        return E_NOTIMPL;
306      #endif
307
308      CCreatedCoder cod;
309      RINOK(CreateCoder_Id(
310          EXTERNAL_CODECS_LOC_VARS
311          coderInfo.MethodID, false, cod))
312
313      if (coderInfo.IsSimpleCoder())
314      {
315        if (!cod.Coder)
316          return E_NOTIMPL;
317        // CMethodId m = coderInfo.MethodID;
318        // isFilter = (IsFilterMethod(m) || m == k_AES);
319      }
320      else
321      {
322        if (!cod.Coder2 || cod.NumStreams != coderInfo.NumStreams)
323          return E_NOTIMPL;
324      }
325      _mixer->AddCoder(cod);
326
327      // now there is no codec that uses another external codec
328      /*
329      #ifdef Z7_EXTERNAL_CODECS
330      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
331      decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
332      if (setCompressCodecsInfo)
333      {
334        // we must use g_ExternalCodecs also
335        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(_externalCodecs->GetCodecs));
336      }
337      #endif
338      */
339    }
340
341    _bindInfoPrev = bindInfo;
342    _bindInfoPrev_Defined = true;
343  }
344
345  RINOK(_mixer->ReInit2())
346
347  UInt32 packStreamIndex = 0;
348  UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex];
349
350  unsigned i;
351
352  #if !defined(Z7_ST)
353  bool mt_wasUsed = false;
354  #endif
355
356  for (i = 0; i < folderInfo.Coders.Size(); i++)
357  {
358    const CCoderInfo &coderInfo = folderInfo.Coders[i];
359    IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();
360
361    // now there is no codec that uses another external codec
362    /*
363    #ifdef Z7_EXTERNAL_CODECS
364    {
365      Z7_DECL_CMyComPtr_QI_FROM(ISetCompressCodecsInfo,
366          setCompressCodecsInfo, decoder)
367      if (setCompressCodecsInfo)
368      {
369        // we must use g_ExternalCodecs also
370        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(_externalCodecs->GetCodecs))
371      }
372    }
373    #endif
374    */
375
376    #if !defined(Z7_ST)
377    if (!mt_wasUsed)
378    {
379      if (mtMode)
380      {
381        Z7_DECL_CMyComPtr_QI_FROM(ICompressSetCoderMt,
382            setCoderMt, decoder)
383        if (setCoderMt)
384        {
385          mt_wasUsed = true;
386          RINOK(setCoderMt->SetNumberOfThreads(numThreads))
387        }
388      }
389      // if (memUsage != 0)
390      {
391        Z7_DECL_CMyComPtr_QI_FROM(ICompressSetMemLimit,
392            setMemLimit, decoder)
393        if (setMemLimit)
394        {
395          mt_wasUsed = true;
396          RINOK(setMemLimit->SetMemLimit(memUsage))
397        }
398      }
399    }
400    #endif
401
402    {
403      Z7_DECL_CMyComPtr_QI_FROM(
404          ICompressSetDecoderProperties2,
405          setDecoderProperties, decoder)
406      const CByteBuffer &props = coderInfo.Props;
407      const UInt32 size32 = (UInt32)props.Size();
408      if (props.Size() != size32)
409        return E_NOTIMPL;
410      if (setDecoderProperties)
411      {
412        HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, size32);
413        if (res == E_INVALIDARG)
414          res = E_NOTIMPL;
415        RINOK(res)
416      }
417      else if (size32 != 0)
418      {
419        // v23: we fail, if decoder doesn't support properties
420        return E_NOTIMPL;
421      }
422    }
423
424    #ifndef Z7_NO_CRYPTO
425    {
426      Z7_DECL_CMyComPtr_QI_FROM(
427          ICryptoSetPassword,
428          cryptoSetPassword, decoder)
429      if (cryptoSetPassword)
430      {
431        isEncrypted = true;
432        if (!getTextPassword)
433          return E_NOTIMPL;
434        CMyComBSTR_Wipe passwordBSTR;
435        RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR))
436        passwordIsDefined = true;
437        password.Wipe_and_Empty();
438        size_t len = 0;
439        if (passwordBSTR)
440        {
441          password = passwordBSTR;
442          len = password.Len();
443        }
444        CByteBuffer_Wipe buffer(len * 2);
445        for (size_t k = 0; k < len; k++)
446        {
447          const wchar_t c = passwordBSTR[k];
448          ((Byte *)buffer)[k * 2] = (Byte)c;
449          ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
450        }
451        RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()))
452      }
453    }
454    #endif
455
456    bool finishMode = false;
457    {
458      Z7_DECL_CMyComPtr_QI_FROM(
459          ICompressSetFinishMode,
460          setFinishMode, decoder)
461      if (setFinishMode)
462      {
463        finishMode = fullUnpack;
464        RINOK(setFinishMode->SetFinishMode(BoolToUInt(finishMode)))
465      }
466    }
467
468    UInt32 numStreams = (UInt32)coderInfo.NumStreams;
469
470    CObjArray<UInt64> packSizes(numStreams);
471    CObjArray<const UInt64 *> packSizesPointers(numStreams);
472
473    for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
474    {
475      int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
476
477      if (bond >= 0)
478        packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
479      else
480      {
481        int index = folderInfo.Find_in_PackStreams(packStreamIndex);
482        if (index < 0)
483          return E_NOTIMPL;
484        packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
485        packSizesPointers[j] = &packSizes[j];
486      }
487    }
488
489    const UInt64 *unpackSizesPointer =
490        (unpackSize && i == bindInfo.UnpackCoder) ?
491            unpackSize :
492            &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
493
494    _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers, finishMode);
495  }
496
497  if (outStream)
498  {
499    _mixer->SelectMainCoder(!fullUnpack);
500  }
501
502  CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
503
504  CLockedInStream *lockedInStreamSpec = new CLockedInStream;
505  CMyComPtr<IUnknown> lockedInStream = lockedInStreamSpec;
506
507  #ifdef USE_MIXER_MT
508  #ifdef USE_MIXER_ST
509  bool needMtLock = _useMixerMT;
510  #endif
511  #endif
512
513  if (folderInfo.PackStreams.Size() > 1)
514  {
515    // lockedInStream.Pos = (UInt64)(Int64)-1;
516    // RINOK(InStream_GetPos(inStream, lockedInStream.Pos))
517    RINOK(inStream->Seek((Int64)(startPos + packPositions[0]), STREAM_SEEK_SET, &lockedInStreamSpec->Pos))
518    lockedInStreamSpec->Stream = inStream;
519
520    #ifdef USE_MIXER_MT
521    #ifdef USE_MIXER_ST
522    /*
523      For ST-mixer mode:
524      If parallel input stream reading from pack streams is possible,
525      we must use MT-lock for packed streams.
526      Internal decoders in 7-Zip will not read pack streams in parallel in ST-mixer mode.
527      So we force to needMtLock mode only if there is unknown (external) decoder.
528    */
529    if (!needMtLock && _mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex))
530      needMtLock = true;
531    #endif
532    #endif
533  }
534
535  for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++)
536  {
537    CMyComPtr<ISequentialInStream> packStream;
538    const UInt64 packPos = startPos + packPositions[j];
539
540    if (folderInfo.PackStreams.Size() == 1)
541    {
542      RINOK(InStream_SeekSet(inStream, packPos))
543      packStream = inStream;
544    }
545    else
546    {
547      #ifdef USE_MIXER_MT
548      #ifdef USE_MIXER_ST
549      if (needMtLock)
550      #endif
551      {
552        CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT;
553        packStream = lockedStreamImpSpec;
554        lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
555      }
556      #ifdef USE_MIXER_ST
557      else
558      #endif
559      #endif
560      {
561        #ifdef USE_MIXER_ST
562        CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST;
563        packStream = lockedStreamImpSpec;
564        lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
565        #endif
566      }
567    }
568
569    CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
570    inStreams.AddNew() = streamSpec;
571    streamSpec->SetStream(packStream);
572    streamSpec->Init(packPositions[j + 1] - packPositions[j]);
573  }
574
575  const unsigned num = inStreams.Size();
576  CObjArray<ISequentialInStream *> inStreamPointers(num);
577  for (i = 0; i < num; i++)
578    inStreamPointers[i] = inStreams[i];
579
580  if (outStream)
581  {
582    CMyComPtr<ICompressProgressInfo> progress2;
583    if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex))
584      progress2 = new CDecProgress(compressProgress);
585
586    ISequentialOutStream *outStreamPointer = outStream;
587    return _mixer->Code(inStreamPointers, &outStreamPointer,
588        progress2 ? (ICompressProgressInfo *)progress2 : compressProgress,
589        dataAfterEnd_Error);
590  }
591
592  #ifdef USE_MIXER_ST
593    return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);
594  #else
595    return E_FAIL;
596  #endif
597}
598
599}}
600