1// 7zExtract.cpp 2 3#include "StdAfx.h" 4 5#include "../../../../C/7zCrc.h" 6 7#include "../../../Common/ComTry.h" 8 9#include "../../Common/ProgressUtils.h" 10 11#include "7zDecode.h" 12#include "7zHandler.h" 13 14// EXTERN_g_ExternalCodecs 15 16namespace NArchive { 17namespace N7z { 18 19Z7_CLASS_IMP_COM_1( 20 CFolderOutStream 21 , ISequentialOutStream 22 /* , ICompressGetSubStreamSize */ 23) 24 CMyComPtr<ISequentialOutStream> _stream; 25public: 26 bool TestMode; 27 bool CheckCrc; 28private: 29 bool _fileIsOpen; 30 bool _calcCrc; 31 UInt32 _crc; 32 UInt64 _rem; 33 34 const UInt32 *_indexes; 35 // unsigned _startIndex; 36 unsigned _numFiles; 37 unsigned _fileIndex; 38 39 HRESULT OpenFile(bool isCorrupted = false); 40 HRESULT CloseFile_and_SetResult(Int32 res); 41 HRESULT CloseFile(); 42 HRESULT ProcessEmptyFiles(); 43 44public: 45 const CDbEx *_db; 46 CMyComPtr<IArchiveExtractCallback> ExtractCallback; 47 48 bool ExtraWriteWasCut; 49 50 CFolderOutStream(): 51 TestMode(false), 52 CheckCrc(true) 53 {} 54 55 HRESULT Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles); 56 HRESULT FlushCorrupted(Int32 callbackOperationResult); 57 58 bool WasWritingFinished() const { return _numFiles == 0; } 59}; 60 61 62HRESULT CFolderOutStream::Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles) 63{ 64 // _startIndex = startIndex; 65 _fileIndex = startIndex; 66 _indexes = indexes; 67 _numFiles = numFiles; 68 69 _fileIsOpen = false; 70 ExtraWriteWasCut = false; 71 72 return ProcessEmptyFiles(); 73} 74 75HRESULT CFolderOutStream::OpenFile(bool isCorrupted) 76{ 77 const CFileItem &fi = _db->Files[_fileIndex]; 78 const UInt32 nextFileIndex = (_indexes ? *_indexes : _fileIndex); 79 Int32 askMode = (_fileIndex == nextFileIndex) ? TestMode ? 80 NExtract::NAskMode::kTest : 81 NExtract::NAskMode::kExtract : 82 NExtract::NAskMode::kSkip; 83 84 if (isCorrupted 85 && askMode == NExtract::NAskMode::kExtract 86 && !_db->IsItemAnti(_fileIndex) 87 && !fi.IsDir) 88 askMode = NExtract::NAskMode::kTest; 89 90 CMyComPtr<ISequentialOutStream> realOutStream; 91 RINOK(ExtractCallback->GetStream(_fileIndex, &realOutStream, askMode)) 92 93 _stream = realOutStream; 94 _crc = CRC_INIT_VAL; 95 _calcCrc = (CheckCrc && fi.CrcDefined && !fi.IsDir); 96 97 _fileIsOpen = true; 98 _rem = fi.Size; 99 100 if (askMode == NExtract::NAskMode::kExtract 101 && !realOutStream 102 && !_db->IsItemAnti(_fileIndex) 103 && !fi.IsDir) 104 askMode = NExtract::NAskMode::kSkip; 105 return ExtractCallback->PrepareOperation(askMode); 106} 107 108HRESULT CFolderOutStream::CloseFile_and_SetResult(Int32 res) 109{ 110 _stream.Release(); 111 _fileIsOpen = false; 112 113 if (!_indexes) 114 _numFiles--; 115 else if (*_indexes == _fileIndex) 116 { 117 _indexes++; 118 _numFiles--; 119 } 120 121 _fileIndex++; 122 return ExtractCallback->SetOperationResult(res); 123} 124 125HRESULT CFolderOutStream::CloseFile() 126{ 127 const CFileItem &fi = _db->Files[_fileIndex]; 128 return CloseFile_and_SetResult((!_calcCrc || fi.Crc == CRC_GET_DIGEST(_crc)) ? 129 NExtract::NOperationResult::kOK : 130 NExtract::NOperationResult::kCRCError); 131} 132 133HRESULT CFolderOutStream::ProcessEmptyFiles() 134{ 135 while (_numFiles != 0 && _db->Files[_fileIndex].Size == 0) 136 { 137 RINOK(OpenFile()) 138 RINOK(CloseFile()) 139 } 140 return S_OK; 141} 142 143Z7_COM7F_IMF(CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)) 144{ 145 if (processedSize) 146 *processedSize = 0; 147 148 while (size != 0) 149 { 150 if (_fileIsOpen) 151 { 152 UInt32 cur = (size < _rem ? size : (UInt32)_rem); 153 if (_calcCrc) 154 { 155 const UInt32 k_Step = (UInt32)1 << 20; 156 if (cur > k_Step) 157 cur = k_Step; 158 } 159 HRESULT result = S_OK; 160 if (_stream) 161 result = _stream->Write(data, cur, &cur); 162 if (_calcCrc) 163 _crc = CrcUpdate(_crc, data, cur); 164 if (processedSize) 165 *processedSize += cur; 166 data = (const Byte *)data + cur; 167 size -= cur; 168 _rem -= cur; 169 if (_rem == 0) 170 { 171 RINOK(CloseFile()) 172 RINOK(ProcessEmptyFiles()) 173 } 174 RINOK(result) 175 if (cur == 0) 176 break; 177 continue; 178 } 179 180 RINOK(ProcessEmptyFiles()) 181 if (_numFiles == 0) 182 { 183 // we support partial extracting 184 /* 185 if (processedSize) 186 *processedSize += size; 187 break; 188 */ 189 ExtraWriteWasCut = true; 190 // return S_FALSE; 191 return k_My_HRESULT_WritingWasCut; 192 } 193 RINOK(OpenFile()) 194 } 195 196 return S_OK; 197} 198 199HRESULT CFolderOutStream::FlushCorrupted(Int32 callbackOperationResult) 200{ 201 while (_numFiles != 0) 202 { 203 if (_fileIsOpen) 204 { 205 RINOK(CloseFile_and_SetResult(callbackOperationResult)) 206 } 207 else 208 { 209 RINOK(OpenFile(true)) 210 } 211 } 212 return S_OK; 213} 214 215/* 216Z7_COM7F_IMF(CFolderOutStream::GetSubStreamSize(UInt64 subStream, UInt64 *value)) 217{ 218 *value = 0; 219 // const unsigned numFiles_Original = _numFiles + _fileIndex - _startIndex; 220 const unsigned numFiles_Original = _numFiles; 221 if (subStream >= numFiles_Original) 222 return S_FALSE; // E_FAIL; 223 *value = _db->Files[_startIndex + (unsigned)subStream].Size; 224 return S_OK; 225} 226*/ 227 228 229Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, 230 Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec)) 231{ 232 // for GCC 233 // CFolderOutStream *folderOutStream = new CFolderOutStream; 234 // CMyComPtr<ISequentialOutStream> outStream(folderOutStream); 235 236 COM_TRY_BEGIN 237 238 CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; 239 240 UInt64 importantTotalUnpacked = 0; 241 242 // numItems = (UInt32)(Int32)-1; 243 244 const bool allFilesMode = (numItems == (UInt32)(Int32)-1); 245 if (allFilesMode) 246 numItems = _db.Files.Size(); 247 248 if (numItems == 0) 249 return S_OK; 250 251 { 252 CNum prevFolder = kNumNoIndex; 253 UInt32 nextFile = 0; 254 255 UInt32 i; 256 257 for (i = 0; i < numItems; i++) 258 { 259 const UInt32 fileIndex = allFilesMode ? i : indices[i]; 260 const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 261 if (folderIndex == kNumNoIndex) 262 continue; 263 if (folderIndex != prevFolder || fileIndex < nextFile) 264 nextFile = _db.FolderStartFileIndex[folderIndex]; 265 for (CNum index = nextFile; index <= fileIndex; index++) 266 importantTotalUnpacked += _db.Files[index].Size; 267 nextFile = fileIndex + 1; 268 prevFolder = folderIndex; 269 } 270 } 271 272 RINOK(extractCallback->SetTotal(importantTotalUnpacked)) 273 274 CLocalProgress *lps = new CLocalProgress; 275 CMyComPtr<ICompressProgressInfo> progress = lps; 276 lps->Init(extractCallback, false); 277 278 CDecoder decoder( 279 #if !defined(USE_MIXER_MT) 280 false 281 #elif !defined(USE_MIXER_ST) 282 true 283 #elif !defined(Z7_7Z_SET_PROPERTIES) 284 #ifdef Z7_ST 285 false 286 #else 287 true 288 #endif 289 #else 290 _useMultiThreadMixer 291 #endif 292 ); 293 294 UInt64 curPacked, curUnpacked; 295 296 CMyComPtr<IArchiveExtractCallbackMessage2> callbackMessage; 297 extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage2, &callbackMessage); 298 299 CFolderOutStream *folderOutStream = new CFolderOutStream; 300 CMyComPtr<ISequentialOutStream> outStream(folderOutStream); 301 302 folderOutStream->_db = &_db; 303 folderOutStream->ExtractCallback = extractCallback; 304 folderOutStream->TestMode = (testModeSpec != 0); 305 folderOutStream->CheckCrc = (_crcSize != 0); 306 307 for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked) 308 { 309 RINOK(lps->SetCur()) 310 311 if (i >= numItems) 312 break; 313 314 curUnpacked = 0; 315 curPacked = 0; 316 317 UInt32 fileIndex = allFilesMode ? i : indices[i]; 318 const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 319 320 UInt32 numSolidFiles = 1; 321 322 if (folderIndex != kNumNoIndex) 323 { 324 curPacked = _db.GetFolderFullPackSize(folderIndex); 325 UInt32 nextFile = fileIndex + 1; 326 fileIndex = _db.FolderStartFileIndex[folderIndex]; 327 UInt32 k; 328 329 for (k = i + 1; k < numItems; k++) 330 { 331 const UInt32 fileIndex2 = allFilesMode ? k : indices[k]; 332 if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex 333 || fileIndex2 < nextFile) 334 break; 335 nextFile = fileIndex2 + 1; 336 } 337 338 numSolidFiles = k - i; 339 340 for (k = fileIndex; k < nextFile; k++) 341 curUnpacked += _db.Files[k].Size; 342 } 343 344 { 345 const HRESULT result = folderOutStream->Init(fileIndex, 346 allFilesMode ? NULL : indices + i, 347 numSolidFiles); 348 349 i += numSolidFiles; 350 351 RINOK(result) 352 } 353 354 if (folderOutStream->WasWritingFinished()) 355 { 356 // for debug: to test zero size stream unpacking 357 // if (folderIndex == kNumNoIndex) // enable this check for debug 358 continue; 359 } 360 361 if (folderIndex == kNumNoIndex) 362 return E_FAIL; 363 364 #ifndef Z7_NO_CRYPTO 365 CMyComPtr<ICryptoGetTextPassword> getTextPassword; 366 if (extractCallback) 367 extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); 368 #endif 369 370 try 371 { 372 #ifndef Z7_NO_CRYPTO 373 bool isEncrypted = false; 374 bool passwordIsDefined = false; 375 UString_Wipe password; 376 #endif 377 378 bool dataAfterEnd_Error = false; 379 380 const HRESULT result = decoder.Decode( 381 EXTERNAL_CODECS_VARS 382 _inStream, 383 _db.ArcInfo.DataStartPosition, 384 _db, folderIndex, 385 &curUnpacked, 386 387 outStream, 388 progress, 389 NULL // *inStreamMainRes 390 , dataAfterEnd_Error 391 392 Z7_7Z_DECODER_CRYPRO_VARS 393 #if !defined(Z7_ST) 394 , true, _numThreads, _memUsage_Decompress 395 #endif 396 ); 397 398 if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error) 399 { 400 const bool wasFinished = folderOutStream->WasWritingFinished(); 401 402 int resOp = NExtract::NOperationResult::kDataError; 403 404 if (result != S_FALSE) 405 { 406 if (result == E_NOTIMPL) 407 resOp = NExtract::NOperationResult::kUnsupportedMethod; 408 else if (wasFinished && dataAfterEnd_Error) 409 resOp = NExtract::NOperationResult::kDataAfterEnd; 410 } 411 412 RINOK(folderOutStream->FlushCorrupted(resOp)) 413 414 if (wasFinished) 415 { 416 // we don't show error, if it's after required files 417 if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage) 418 { 419 RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp)) 420 } 421 } 422 continue; 423 } 424 425 if (result != S_OK) 426 return result; 427 428 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)) 429 continue; 430 } 431 catch(...) 432 { 433 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)) 434 // continue; 435 // return E_FAIL; 436 throw; 437 } 438 } 439 440 return S_OK; 441 442 COM_TRY_END 443} 444 445}} 446