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