// 7zExtract.cpp #include "StdAfx.h" #include "../../../Common/ComTry.h" #include "../../Common/ProgressUtils.h" #include "7zDecode.h" // #include "7z1Decode.h" #include "7zFolderOutStream.h" #include "7zHandler.h" namespace NArchive { namespace N7z { struct CExtractFolderInfo { #ifdef _7Z_VOL int VolumeIndex; #endif CNum FileIndex; CNum FolderIndex; CBoolVector ExtractStatuses; UInt64 UnpackSize; CExtractFolderInfo( #ifdef _7Z_VOL int volumeIndex, #endif CNum fileIndex, CNum folderIndex): #ifdef _7Z_VOL VolumeIndex(volumeIndex), #endif FileIndex(fileIndex), FolderIndex(folderIndex), UnpackSize(0) { if (fileIndex != kNumNoIndex) { ExtractStatuses.ClearAndSetSize(1); ExtractStatuses[0] = true; } }; }; STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) { COM_TRY_BEGIN bool testMode = (testModeSpec != 0); CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; UInt64 importantTotalUnpacked = 0; bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = #ifdef _7Z_VOL _refs.Size(); #else _db.Files.Size(); #endif if(numItems == 0) return S_OK; /* if(_volumes.Size() != 1) return E_FAIL; const CVolume &volume = _volumes.Front(); const CDbEx &_db = volume.Database; IInStream *_inStream = volume.Stream; */ CObjectVector<CExtractFolderInfo> extractFolderInfoVector; for (UInt32 ii = 0; ii < numItems; ii++) { // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex]; UInt32 ref2Index = allFilesMode ? ii : indices[ii]; // const CRef2 &ref2 = _refs[ref2Index]; // for (UInt32 ri = 0; ri < ref2.Refs.Size(); ri++) { #ifdef _7Z_VOL // const CRef &ref = ref2.Refs[ri]; const CRef &ref = _refs[ref2Index]; int volumeIndex = ref.VolumeIndex; const CVolume &volume = _volumes[volumeIndex]; const CDbEx &db = volume.Database; UInt32 fileIndex = ref.ItemIndex; #else const CDbEx &db = _db; UInt32 fileIndex = ref2Index; #endif CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex]; if (folderIndex == kNumNoIndex) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif fileIndex, kNumNoIndex)); continue; } if (extractFolderInfoVector.IsEmpty() || folderIndex != extractFolderInfoVector.Back().FolderIndex #ifdef _7Z_VOL || volumeIndex != extractFolderInfoVector.Back().VolumeIndex #endif ) { extractFolderInfoVector.Add(CExtractFolderInfo( #ifdef _7Z_VOL volumeIndex, #endif kNumNoIndex, folderIndex)); UInt64 unpackSize = db.GetFolderUnpackSize(folderIndex); importantTotalUnpacked += unpackSize; extractFolderInfoVector.Back().UnpackSize = unpackSize; } CExtractFolderInfo &efi = extractFolderInfoVector.Back(); // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex]; CNum startIndex = db.FolderStartFileIndex[folderIndex]; for (CNum index = efi.ExtractStatuses.Size(); index <= fileIndex - startIndex; index++) { // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize; // Count partial_folder_size // efi.UnpackSize += unpackSize; // importantTotalUnpacked += unpackSize; efi.ExtractStatuses.Add(index == fileIndex - startIndex); } } } RINOK(extractCallback->SetTotal(importantTotalUnpacked)); CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); // CDecoder1 decoder; UInt64 totalPacked = 0; UInt64 totalUnpacked = 0; UInt64 curPacked, curUnpacked; CLocalProgress *lps = new CLocalProgress; CMyComPtr<ICompressProgressInfo> progress = lps; lps->Init(extractCallback, false); for (unsigned i = 0;; i++, totalUnpacked += curUnpacked, totalPacked += curPacked) { lps->OutSize = totalUnpacked; lps->InSize = totalPacked; RINOK(lps->SetCur()); if (i >= extractFolderInfoVector.Size()) break; const CExtractFolderInfo &efi = extractFolderInfoVector[i]; curUnpacked = efi.UnpackSize; curPacked = 0; CFolderOutStream *folderOutStream = new CFolderOutStream; CMyComPtr<ISequentialOutStream> outStream(folderOutStream); #ifdef _7Z_VOL const CVolume &volume = _volumes[efi.VolumeIndex]; const CDbEx &db = volume.Database; #else const CDbEx &db = _db; #endif CNum startIndex; if (efi.FileIndex != kNumNoIndex) startIndex = efi.FileIndex; else startIndex = db.FolderStartFileIndex[efi.FolderIndex]; HRESULT result = folderOutStream->Init(&db, #ifdef _7Z_VOL volume.StartRef2Index, #else 0, #endif startIndex, &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0); RINOK(result); if (efi.FileIndex != kNumNoIndex) continue; CNum folderIndex = efi.FolderIndex; curPacked = _db.GetFolderFullPackSize(folderIndex); #ifndef _NO_CRYPTO CMyComPtr<ICryptoGetTextPassword> getTextPassword; if (extractCallback) extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); #endif try { #ifndef _NO_CRYPTO bool isEncrypted = false; bool passwordIsDefined = false; #endif HRESULT result = decoder.Decode( EXTERNAL_CODECS_VARS #ifdef _7Z_VOL volume.Stream, #else _inStream, #endif db.ArcInfo.DataStartPosition, db, folderIndex, outStream, progress _7Z_DECODER_CRYPRO_VARS #if !defined(_7ZIP_ST) && !defined(_SFX) , true, _numThreads #endif ); if (result == S_FALSE) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } if (result == E_NOTIMPL) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kUnsupportedMethod)); continue; } if (result != S_OK) return result; if (folderOutStream->WasWritingFinished() != S_OK) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } catch(...) { RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); continue; } } return S_OK; COM_TRY_END } }}