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   bool mt_wasUsed = false;
359 
360   for (i = 0; i < folderInfo.Coders.Size(); i++)
361   {
362     const CCoderInfo &coderInfo = folderInfo.Coders[i];
363     IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();
364 
365     #if !defined(_7ZIP_ST)
366     if (!mt_wasUsed)
367     {
368       if (mtMode)
369       {
370         CMyComPtr<ICompressSetCoderMt> setCoderMt;
371         decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
372         if (setCoderMt)
373         {
374           mt_wasUsed = true;
375           RINOK(setCoderMt->SetNumberOfThreads(numThreads));
376         }
377       }
378       // if (memUsage != 0)
379       {
380         CMyComPtr<ICompressSetMemLimit> setMemLimit;
381         decoder->QueryInterface(IID_ICompressSetMemLimit, (void **)&setMemLimit);
382         if (setMemLimit)
383         {
384           mt_wasUsed = true;
385           RINOK(setMemLimit->SetMemLimit(memUsage));
386         }
387       }
388     }
389     #endif
390 
391     {
392       CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
393       decoder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
394       if (setDecoderProperties)
395       {
396         const CByteBuffer &props = coderInfo.Props;
397         size_t size = props.Size();
398         if (size > 0xFFFFFFFF)
399           return E_NOTIMPL;
400         HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size);
401         if (res == E_INVALIDARG)
402           res = E_NOTIMPL;
403         RINOK(res);
404       }
405     }
406 
407     #ifndef _NO_CRYPTO
408     {
409       CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
410       decoder->QueryInterface(IID_ICryptoSetPassword, (void **)&cryptoSetPassword);
411       if (cryptoSetPassword)
412       {
413         isEncrypted = true;
414         if (!getTextPassword)
415           return E_NOTIMPL;
416         CMyComBSTR passwordBSTR;
417         RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
418         passwordIsDefined = true;
419         password.Empty();
420         size_t len = 0;
421         if (passwordBSTR)
422         {
423           password = passwordBSTR;
424           len = password.Len();
425         }
426         CByteBuffer buffer(len * 2);
427         for (size_t k = 0; k < len; k++)
428         {
429           wchar_t c = passwordBSTR[k];
430           ((Byte *)buffer)[k * 2] = (Byte)c;
431           ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
432         }
433         RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()));
434       }
435     }
436     #endif
437 
438     bool finishMode = false;
439     {
440       CMyComPtr<ICompressSetFinishMode> setFinishMode;
441       decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
442       if (setFinishMode)
443       {
444         finishMode = fullUnpack;
445         RINOK(setFinishMode->SetFinishMode(BoolToInt(finishMode)));
446       }
447     }
448 
449     UInt32 numStreams = (UInt32)coderInfo.NumStreams;
450 
451     CObjArray<UInt64> packSizes(numStreams);
452     CObjArray<const UInt64 *> packSizesPointers(numStreams);
453 
454     for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
455     {
456       int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
457 
458       if (bond >= 0)
459         packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
460       else
461       {
462         int index = folderInfo.Find_in_PackStreams(packStreamIndex);
463         if (index < 0)
464           return E_NOTIMPL;
465         packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
466         packSizesPointers[j] = &packSizes[j];
467       }
468     }
469 
470     const UInt64 *unpackSizesPointer =
471         (unpackSize && i == bindInfo.UnpackCoder) ?
472             unpackSize :
473             &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
474 
475     _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers, finishMode);
476   }
477 
478   if (outStream)
479   {
480     _mixer->SelectMainCoder(!fullUnpack);
481   }
482 
483   CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
484 
485   CLockedInStream *lockedInStreamSpec = new CLockedInStream;
486   CMyComPtr<IUnknown> lockedInStream = lockedInStreamSpec;
487 
488   bool needMtLock = false;
489 
490   if (folderInfo.PackStreams.Size() > 1)
491   {
492     // lockedInStream.Pos = (UInt64)(Int64)-1;
493     // RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &lockedInStream.Pos));
494     RINOK(inStream->Seek(startPos + packPositions[0], STREAM_SEEK_SET, &lockedInStreamSpec->Pos));
495     lockedInStreamSpec->Stream = inStream;
496 
497     #ifdef USE_MIXER_ST
498     if (_mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex))
499     #endif
500       needMtLock = true;
501   }
502 
503   for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++)
504   {
505     CMyComPtr<ISequentialInStream> packStream;
506     UInt64 packPos = startPos + packPositions[j];
507 
508     if (folderInfo.PackStreams.Size() == 1)
509     {
510       RINOK(inStream->Seek(packPos, STREAM_SEEK_SET, NULL));
511       packStream = inStream;
512     }
513     else
514     {
515       #ifdef USE_MIXER_MT
516       #ifdef USE_MIXER_ST
517       if (_useMixerMT || needMtLock)
518       #endif
519       {
520         CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT;
521         packStream = lockedStreamImpSpec;
522         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
523       }
524       #ifdef USE_MIXER_ST
525       else
526       #endif
527       #endif
528       {
529         #ifdef USE_MIXER_ST
530         CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST;
531         packStream = lockedStreamImpSpec;
532         lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
533         #endif
534       }
535     }
536 
537     CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
538     inStreams.AddNew() = streamSpec;
539     streamSpec->SetStream(packStream);
540     streamSpec->Init(packPositions[j + 1] - packPositions[j]);
541   }
542 
543   unsigned num = inStreams.Size();
544   CObjArray<ISequentialInStream *> inStreamPointers(num);
545   for (i = 0; i < num; i++)
546     inStreamPointers[i] = inStreams[i];
547 
548   if (outStream)
549   {
550     CMyComPtr<ICompressProgressInfo> progress2;
551     if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex))
552       progress2 = new CDecProgress(compressProgress);
553 
554     ISequentialOutStream *outStreamPointer = outStream;
555     return _mixer->Code(inStreamPointers, &outStreamPointer,
556         progress2 ? (ICompressProgressInfo *)progress2 : compressProgress,
557         dataAfterEnd_Error);
558   }
559 
560   #ifdef USE_MIXER_ST
561     return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);
562   #else
563     return E_FAIL;
564   #endif
565 }
566 
567 }}
568