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 16 namespace NArchive { 17 namespace N7z { 18 19 Z7_CLASS_IMP_COM_1( 20 CFolderOutStream 21 , ISequentialOutStream 22 /* , ICompressGetSubStreamSize */ 23 ) 24 CMyComPtr<ISequentialOutStream> _stream; 25 public: 26 bool TestMode; 27 bool CheckCrc; 28 private: 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 44 public: 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 62 HRESULT 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 75 HRESULT 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 108 HRESULT 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 125 HRESULT 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 133 HRESULT 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 143 Z7_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 199 HRESULT 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 /* 216 Z7_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 229 Z7_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 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps; 275 lps->Init(extractCallback, false); 276 277 CDecoder decoder( 278 #if !defined(USE_MIXER_MT) 279 false 280 #elif !defined(USE_MIXER_ST) 281 true 282 #elif !defined(Z7_7Z_SET_PROPERTIES) 283 #ifdef Z7_ST 284 false 285 #else 286 true 287 #endif 288 #else 289 _useMultiThreadMixer 290 #endif 291 ); 292 293 UInt64 curPacked, curUnpacked; 294 295 CMyComPtr<IArchiveExtractCallbackMessage2> callbackMessage; 296 extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage2, &callbackMessage); 297 298 CFolderOutStream *folderOutStream = new CFolderOutStream; 299 CMyComPtr<ISequentialOutStream> outStream(folderOutStream); 300 301 folderOutStream->_db = &_db; 302 folderOutStream->ExtractCallback = extractCallback; 303 folderOutStream->TestMode = (testModeSpec != 0); 304 folderOutStream->CheckCrc = (_crcSize != 0); 305 306 for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked) 307 { 308 RINOK(lps->SetCur()) 309 310 if (i >= numItems) 311 break; 312 313 curUnpacked = 0; 314 curPacked = 0; 315 316 UInt32 fileIndex = allFilesMode ? i : indices[i]; 317 const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 318 319 UInt32 numSolidFiles = 1; 320 321 if (folderIndex != kNumNoIndex) 322 { 323 curPacked = _db.GetFolderFullPackSize(folderIndex); 324 UInt32 nextFile = fileIndex + 1; 325 fileIndex = _db.FolderStartFileIndex[folderIndex]; 326 UInt32 k; 327 328 for (k = i + 1; k < numItems; k++) 329 { 330 const UInt32 fileIndex2 = allFilesMode ? k : indices[k]; 331 if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex 332 || fileIndex2 < nextFile) 333 break; 334 nextFile = fileIndex2 + 1; 335 } 336 337 numSolidFiles = k - i; 338 339 for (k = fileIndex; k < nextFile; k++) 340 curUnpacked += _db.Files[k].Size; 341 } 342 343 { 344 const HRESULT result = folderOutStream->Init(fileIndex, 345 allFilesMode ? NULL : indices + i, 346 numSolidFiles); 347 348 i += numSolidFiles; 349 350 RINOK(result) 351 } 352 353 if (folderOutStream->WasWritingFinished()) 354 { 355 // for debug: to test zero size stream unpacking 356 // if (folderIndex == kNumNoIndex) // enable this check for debug 357 continue; 358 } 359 360 if (folderIndex == kNumNoIndex) 361 return E_FAIL; 362 363 #ifndef Z7_NO_CRYPTO 364 CMyComPtr<ICryptoGetTextPassword> getTextPassword; 365 if (extractCallback) 366 extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); 367 #endif 368 369 try 370 { 371 #ifndef Z7_NO_CRYPTO 372 bool isEncrypted = false; 373 bool passwordIsDefined = false; 374 UString_Wipe password; 375 #endif 376 377 bool dataAfterEnd_Error = false; 378 379 const HRESULT result = decoder.Decode( 380 EXTERNAL_CODECS_VARS 381 _inStream, 382 _db.ArcInfo.DataStartPosition, 383 _db, folderIndex, 384 &curUnpacked, 385 386 outStream, 387 lps, 388 NULL // *inStreamMainRes 389 , dataAfterEnd_Error 390 391 Z7_7Z_DECODER_CRYPRO_VARS 392 #if !defined(Z7_ST) 393 , true, _numThreads, _memUsage_Decompress 394 #endif 395 ); 396 397 if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error) 398 { 399 const bool wasFinished = folderOutStream->WasWritingFinished(); 400 401 int resOp = NExtract::NOperationResult::kDataError; 402 403 if (result != S_FALSE) 404 { 405 if (result == E_NOTIMPL) 406 resOp = NExtract::NOperationResult::kUnsupportedMethod; 407 else if (wasFinished && dataAfterEnd_Error) 408 resOp = NExtract::NOperationResult::kDataAfterEnd; 409 } 410 411 RINOK(folderOutStream->FlushCorrupted(resOp)) 412 413 if (wasFinished) 414 { 415 // we don't show error, if it's after required files 416 if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage) 417 { 418 RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp)) 419 } 420 } 421 continue; 422 } 423 424 if (result != S_OK) 425 return result; 426 427 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)) 428 continue; 429 } 430 catch(...) 431 { 432 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)) 433 // continue; 434 // return E_FAIL; 435 throw; 436 } 437 } 438 439 return S_OK; 440 441 COM_TRY_END 442 } 443 444 }} 445