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