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