• 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(_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(_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     _mixerRef.Release();
280 
281     #ifdef USE_MIXER_MT
282     #ifdef USE_MIXER_ST
283     if (_useMixerMT)
284     #endif
285     {
286       _mixerMT = new NCoderMixer2::CMixerMT(false);
287       _mixerRef = _mixerMT;
288       _mixer = _mixerMT;
289     }
290     #ifdef USE_MIXER_ST
291     else
292     #endif
293     #endif
294     {
295       #ifdef USE_MIXER_ST
296       _mixerST = new NCoderMixer2::CMixerST(false);
297       _mixerRef = _mixerST;
298       _mixer = _mixerST;
299       #endif
300     }
301 
302     RINOK(_mixer->SetBindInfo(bindInfo));
303 
304     FOR_VECTOR(i, folderInfo.Coders)
305     {
306       const CCoderInfo &coderInfo = folderInfo.Coders[i];
307 
308       #ifndef _SFX
309       // we don't support RAR codecs here
310       if ((coderInfo.MethodID >> 8) == 0x403)
311         return E_NOTIMPL;
312       #endif
313 
314       CCreatedCoder cod;
315       RINOK(CreateCoder_Id(
316           EXTERNAL_CODECS_LOC_VARS
317           coderInfo.MethodID, false, cod));
318 
319       if (coderInfo.IsSimpleCoder())
320       {
321         if (!cod.Coder)
322           return E_NOTIMPL;
323         // CMethodId m = coderInfo.MethodID;
324         // isFilter = (IsFilterMethod(m) || m == k_AES);
325       }
326       else
327       {
328         if (!cod.Coder2 || cod.NumStreams != coderInfo.NumStreams)
329           return E_NOTIMPL;
330       }
331       _mixer->AddCoder(cod);
332 
333       // now there is no codec that uses another external codec
334       /*
335       #ifdef EXTERNAL_CODECS
336       CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
337       decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
338       if (setCompressCodecsInfo)
339       {
340         // we must use g_ExternalCodecs also
341         RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs));
342       }
343       #endif
344       */
345     }
346 
347     _bindInfoPrev = bindInfo;
348     _bindInfoPrev_Defined = true;
349   }
350 
351   _mixer->ReInit();
352 
353   UInt32 packStreamIndex = 0;
354   UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex];
355 
356   unsigned i;
357 
358   #if !defined(_7ZIP_ST)
359   bool mt_wasUsed = false;
360   #endif
361 
362   for (i = 0; i < folderInfo.Coders.Size(); i++)
363   {
364     const CCoderInfo &coderInfo = folderInfo.Coders[i];
365     IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();
366 
367     #if !defined(_7ZIP_ST)
368     if (!mt_wasUsed)
369     {
370       if (mtMode)
371       {
372         CMyComPtr<ICompressSetCoderMt> setCoderMt;
373         decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
374         if (setCoderMt)
375         {
376           mt_wasUsed = true;
377           RINOK(setCoderMt->SetNumberOfThreads(numThreads));
378         }
379       }
380       // if (memUsage != 0)
381       {
382         CMyComPtr<ICompressSetMemLimit> setMemLimit;
383         decoder->QueryInterface(IID_ICompressSetMemLimit, (void **)&setMemLimit);
384         if (setMemLimit)
385         {
386           mt_wasUsed = true;
387           RINOK(setMemLimit->SetMemLimit(memUsage));
388         }
389       }
390     }
391     #endif
392 
393     {
394       CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
395       decoder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
396       if (setDecoderProperties)
397       {
398         const CByteBuffer &props = coderInfo.Props;
399         size_t size = props.Size();
400         if (size > 0xFFFFFFFF)
401           return E_NOTIMPL;
402         HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size);
403         if (res == E_INVALIDARG)
404           res = E_NOTIMPL;
405         RINOK(res);
406       }
407     }
408 
409     #ifndef _NO_CRYPTO
410     {
411       CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
412       decoder->QueryInterface(IID_ICryptoSetPassword, (void **)&cryptoSetPassword);
413       if (cryptoSetPassword)
414       {
415         isEncrypted = true;
416         if (!getTextPassword)
417           return E_NOTIMPL;
418         CMyComBSTR passwordBSTR;
419         RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
420         passwordIsDefined = true;
421         password.Empty();
422         size_t len = 0;
423         if (passwordBSTR)
424         {
425           password = passwordBSTR;
426           len = password.Len();
427         }
428         CByteBuffer buffer(len * 2);
429         for (size_t k = 0; k < len; k++)
430         {
431           wchar_t c = passwordBSTR[k];
432           ((Byte *)buffer)[k * 2] = (Byte)c;
433           ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
434         }
435         RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()));
436       }
437     }
438     #endif
439 
440     bool finishMode = false;
441     {
442       CMyComPtr<ICompressSetFinishMode> setFinishMode;
443       decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
444       if (setFinishMode)
445       {
446         finishMode = fullUnpack;
447         RINOK(setFinishMode->SetFinishMode(BoolToInt(finishMode)));
448       }
449     }
450 
451     UInt32 numStreams = (UInt32)coderInfo.NumStreams;
452 
453     CObjArray<UInt64> packSizes(numStreams);
454     CObjArray<const UInt64 *> packSizesPointers(numStreams);
455 
456     for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
457     {
458       int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
459 
460       if (bond >= 0)
461         packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
462       else
463       {
464         int index = folderInfo.Find_in_PackStreams(packStreamIndex);
465         if (index < 0)
466           return E_NOTIMPL;
467         packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
468         packSizesPointers[j] = &packSizes[j];
469       }
470     }
471 
472     const UInt64 *unpackSizesPointer =
473         (unpackSize && i == bindInfo.UnpackCoder) ?
474             unpackSize :
475             &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
476 
477     _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers, finishMode);
478   }
479 
480   if (outStream)
481   {
482     _mixer->SelectMainCoder(!fullUnpack);
483   }
484 
485   CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
486 
487   CLockedInStream *lockedInStreamSpec = new CLockedInStream;
488   CMyComPtr<IUnknown> lockedInStream = lockedInStreamSpec;
489 
490   bool needMtLock = false;
491 
492   if (folderInfo.PackStreams.Size() > 1)
493   {
494     // lockedInStream.Pos = (UInt64)(Int64)-1;
495     // RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &lockedInStream.Pos));
496     RINOK(inStream->Seek(startPos + packPositions[0], STREAM_SEEK_SET, &lockedInStreamSpec->Pos));
497     lockedInStreamSpec->Stream = inStream;
498 
499     #ifdef USE_MIXER_ST
500     if (_mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex))
501     #endif
502       needMtLock = true;
503   }
504 
505   for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++)
506   {
507     CMyComPtr<ISequentialInStream> packStream;
508     UInt64 packPos = startPos + packPositions[j];
509 
510     if (folderInfo.PackStreams.Size() == 1)
511     {
512       RINOK(inStream->Seek(packPos, STREAM_SEEK_SET, NULL));
513       packStream = inStream;
514     }
515     else
516     {
517       #ifdef USE_MIXER_MT
518       #ifdef USE_MIXER_ST
519       if (_useMixerMT || needMtLock)
520       #endif
521       {
522         CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT;
523         packStream = lockedStreamImpSpec;
524         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
525       }
526       #ifdef USE_MIXER_ST
527       else
528       #endif
529       #endif
530       {
531         #ifdef USE_MIXER_ST
532         CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST;
533         packStream = lockedStreamImpSpec;
534         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
535         #endif
536       }
537     }
538 
539     CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
540     inStreams.AddNew() = streamSpec;
541     streamSpec->SetStream(packStream);
542     streamSpec->Init(packPositions[j + 1] - packPositions[j]);
543   }
544 
545   unsigned num = inStreams.Size();
546   CObjArray<ISequentialInStream *> inStreamPointers(num);
547   for (i = 0; i < num; i++)
548     inStreamPointers[i] = inStreams[i];
549 
550   if (outStream)
551   {
552     CMyComPtr<ICompressProgressInfo> progress2;
553     if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex))
554       progress2 = new CDecProgress(compressProgress);
555 
556     ISequentialOutStream *outStreamPointer = outStream;
557     return _mixer->Code(inStreamPointers, &outStreamPointer,
558         progress2 ? (ICompressProgressInfo *)progress2 : compressProgress,
559         dataAfterEnd_Error);
560   }
561 
562   #ifdef USE_MIXER_ST
563     return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);
564   #else
565     return E_FAIL;
566   #endif
567 }
568 
569 }}
570