xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/7z/7zDecode.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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 
12 namespace NArchive {
13 namespace N7z {
14 
15 Z7_CLASS_IMP_COM_1(
16   CDecProgress
17   , ICompressProgressInfo
18 )
19   CMyComPtr<ICompressProgressInfo> _progress;
20 public:
21   CDecProgress(ICompressProgressInfo *progress): _progress(progress) {}
22 };
23 
24 Z7_COM7F_IMF(CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize))
25 {
26   return _progress->SetRatioInfo(NULL, outSize);
27 }
28 
29 static 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 
62 static inline bool AreCodersEqual(
63     const NCoderMixer2::CCoderStreamsInfo &a1,
64     const NCoderMixer2::CCoderStreamsInfo &a2)
65 {
66   return (a1.NumStreams == a2.NumStreams);
67 }
68 
69 static 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 
78 static 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 
110 CDecoder::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 
121 Z7_CLASS_IMP_COM_0(
122   CLockedInStream
123 )
124 public:
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 
136 Z7_CLASS_IMP_COM_1(
137   CLockedSequentialInStreamMT
138   , ISequentialInStream
139 )
140   CLockedInStream *_glob;
141   UInt64 _pos;
142   CMyComPtr<IUnknown> _globRef;
143 public:
144   void Init(CLockedInStream *lockedInStream, UInt64 startPos)
145   {
146     _globRef = lockedInStream;
147     _glob = lockedInStream;
148     _pos = startPos;
149   }
150 };
151 
152 Z7_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 
176 Z7_CLASS_IMP_COM_1(
177   CLockedSequentialInStreamST
178   , ISequentialInStream
179 )
180   CLockedInStream *_glob;
181   UInt64 _pos;
182   CMyComPtr<IUnknown> _globRef;
183 public:
184   void Init(CLockedInStream *lockedInStream, UInt64 startPos)
185   {
186     _globRef = lockedInStream;
187     _glob = lockedInStream;
188     _pos = startPos;
189   }
190 };
191 
192 Z7_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 
213 HRESULT 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         const LPCOLESTR psw = passwordBSTR;
446         for (size_t k = 0; k < len; k++)
447         {
448           const wchar_t c = psw[k];
449           ((Byte *)buffer)[k * 2] = (Byte)c;
450           ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
451         }
452         RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()))
453       }
454     }
455     #endif
456 
457     bool finishMode = false;
458     {
459       Z7_DECL_CMyComPtr_QI_FROM(
460           ICompressSetFinishMode,
461           setFinishMode, decoder)
462       if (setFinishMode)
463       {
464         finishMode = fullUnpack;
465         RINOK(setFinishMode->SetFinishMode(BoolToUInt(finishMode)))
466       }
467     }
468 
469     UInt32 numStreams = (UInt32)coderInfo.NumStreams;
470 
471     CObjArray<UInt64> packSizes(numStreams);
472     CObjArray<const UInt64 *> packSizesPointers(numStreams);
473 
474     for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
475     {
476       int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
477 
478       if (bond >= 0)
479         packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
480       else
481       {
482         int index = folderInfo.Find_in_PackStreams(packStreamIndex);
483         if (index < 0)
484           return E_NOTIMPL;
485         packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
486         packSizesPointers[j] = &packSizes[j];
487       }
488     }
489 
490     const UInt64 *unpackSizesPointer =
491         (unpackSize && i == bindInfo.UnpackCoder) ?
492             unpackSize :
493             &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
494 
495     _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers, finishMode);
496   }
497 
498   if (outStream)
499   {
500     _mixer->SelectMainCoder(!fullUnpack);
501   }
502 
503   CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
504 
505   CMyComPtr2_Create<IUnknown, CLockedInStream> lockedInStream;
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, &lockedInStream->Pos))
518     lockedInStream->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(lockedInStream.ClsPtr(), 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(lockedInStream.ClsPtr(), 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