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