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