7zDecode.cpp (15490B)
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: 20 CDecProgress(ICompressProgressInfo *progress): _progress(progress) {} 21 22 MY_UNKNOWN_IMP1(ICompressProgressInfo) 23 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); 24 }; 25 26 STDMETHODIMP CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize) 27 { 28 return _progress->SetRatioInfo(NULL, outSize); 29 } 30 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 64 static inline bool AreCodersEqual( 65 const NCoderMixer2::CCoderStreamsInfo &a1, 66 const NCoderMixer2::CCoderStreamsInfo &a2) 67 { 68 return (a1.NumStreams == a2.NumStreams); 69 } 70 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 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 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: 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 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: 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 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 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 }}