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