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