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 , 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