1// Extract.cpp 2 3#include "StdAfx.h" 4 5#include "../../../../C/Sort.h" 6 7#include "../../../Common/StringConvert.h" 8 9#include "../../../Windows/FileDir.h" 10#include "../../../Windows/PropVariant.h" 11#include "../../../Windows/PropVariantConv.h" 12 13#include "../Common/ExtractingFilePath.h" 14 15#include "Extract.h" 16#include "SetProperties.h" 17 18using namespace NWindows; 19using namespace NFile; 20using namespace NDir; 21 22static HRESULT DecompressArchive( 23 CCodecs *codecs, 24 const CArchiveLink &arcLink, 25 UInt64 packSize, 26 const NWildcard::CCensorNode &wildcardCensor, 27 const CExtractOptions &options, 28 bool calcCrc, 29 IExtractCallbackUI *callback, 30 CArchiveExtractCallback *ecs, 31 UString &errorMessage, 32 UInt64 &stdInProcessed) 33{ 34 const CArc &arc = arcLink.Arcs.Back(); 35 stdInProcessed = 0; 36 IInArchive *archive = arc.Archive; 37 CRecordVector<UInt32> realIndices; 38 39 UStringVector removePathParts; 40 41 FString outDir = options.OutputDir; 42 UString replaceName = arc.DefaultName; 43 44 if (arcLink.Arcs.Size() > 1) 45 { 46 // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1". 47 // So it extracts different archives to one folder. 48 // We will use top level archive name 49 const CArc &arc0 = arcLink.Arcs[0]; 50 if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe")) 51 replaceName = arc0.DefaultName; 52 } 53 54 outDir.Replace(FSTRING_ANY_MASK, us2fs(GetCorrectFsPath(replaceName))); 55 56 bool elimIsPossible = false; 57 UString elimPrefix; // only pure name without dir delimiter 58 FString outDirReduced = outDir; 59 60 if (options.ElimDup.Val) 61 { 62 UString dirPrefix; 63 SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix); 64 if (!elimPrefix.IsEmpty()) 65 { 66 if (IsCharDirLimiter(elimPrefix.Back())) 67 elimPrefix.DeleteBack(); 68 if (!elimPrefix.IsEmpty()) 69 { 70 outDirReduced = us2fs(dirPrefix); 71 elimIsPossible = true; 72 } 73 } 74 } 75 76 if (!options.StdInMode) 77 { 78 UInt32 numItems; 79 RINOK(archive->GetNumberOfItems(&numItems)); 80 81 UString filePath; 82 83 for (UInt32 i = 0; i < numItems; i++) 84 { 85 RINOK(arc.GetItemPath(i, filePath)); 86 87 if (elimIsPossible && options.ElimDup.Val) 88 { 89 if (!IsPath1PrefixedByPath2(filePath, elimPrefix)) 90 elimIsPossible = false; 91 else 92 { 93 wchar_t c = filePath[elimPrefix.Len()]; 94 if (c != 0 && !IsCharDirLimiter(c)) 95 elimIsPossible = false; 96 } 97 } 98 99 bool isFolder; 100 RINOK(Archive_IsItem_Folder(archive, i, isFolder)); 101 bool isAltStream; 102 RINOK(Archive_IsItem_AltStream(archive, i, isAltStream)); 103 if (!options.NtOptions.AltStreams.Val && isAltStream) 104 continue; 105 if (!wildcardCensor.CheckPath(isAltStream, filePath, !isFolder)) 106 continue; 107 realIndices.Add(i); 108 } 109 110 if (realIndices.Size() == 0) 111 { 112 callback->ThereAreNoFiles(); 113 return callback->ExtractResult(S_OK); 114 } 115 } 116 117 if (elimIsPossible) 118 outDir = outDirReduced; 119 120 #ifdef _WIN32 121 // GetCorrectFullFsPath doesn't like "..". 122 // outDir.TrimRight(); 123 // outDir = GetCorrectFullFsPath(outDir); 124 #endif 125 126 if (outDir.IsEmpty()) 127 outDir = FString(FTEXT(".")) + FString(FSTRING_PATH_SEPARATOR); 128 else 129 if (!CreateComplexDir(outDir)) 130 { 131 HRESULT res = ::GetLastError(); 132 if (res == S_OK) 133 res = E_FAIL; 134 errorMessage = ((UString)L"Can not create output directory ") + fs2us(outDir); 135 return res; 136 } 137 138 ecs->Init( 139 options.NtOptions, 140 options.StdInMode ? &wildcardCensor : NULL, 141 &arc, 142 callback, 143 options.StdOutMode, options.TestMode, 144 outDir, 145 removePathParts, 146 packSize); 147 148 149 #ifdef SUPPORT_LINKS 150 151 if (!options.StdInMode && 152 !options.TestMode && 153 options.NtOptions.HardLinks.Val) 154 { 155 RINOK(ecs->PrepareHardLinks(&realIndices)); 156 } 157 158 #endif 159 160 161 HRESULT result; 162 Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0; 163 if (options.StdInMode) 164 { 165 result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs); 166 NCOM::CPropVariant prop; 167 if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) 168 ConvertPropVariantToUInt64(prop, stdInProcessed); 169 } 170 else 171 result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); 172 if (result == S_OK && !options.StdInMode) 173 result = ecs->SetDirsTimes(); 174 return callback->ExtractResult(result); 175} 176 177/* v9.31: BUG was fixed: 178 Sorted list for file paths was sorted with case insensitive compare function. 179 But FindInSorted function did binary search via case sensitive compare function */ 180 181int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name) 182{ 183 unsigned left = 0, right = fileName.Size(); 184 while (left != right) 185 { 186 unsigned mid = (left + right) / 2; 187 const UString &midValue = fileName[mid]; 188 int compare = CompareFileNames(name, midValue); 189 if (compare == 0) 190 return mid; 191 if (compare < 0) 192 right = mid; 193 else 194 left = mid + 1; 195 } 196 return -1; 197} 198 199HRESULT Extract( 200 CCodecs *codecs, 201 const CObjectVector<COpenType> &types, 202 const CIntVector &excludedFormats, 203 UStringVector &arcPaths, UStringVector &arcPathsFull, 204 const NWildcard::CCensorNode &wildcardCensor, 205 const CExtractOptions &options, 206 IOpenCallbackUI *openCallback, 207 IExtractCallbackUI *extractCallback, 208 #ifndef _SFX 209 IHashCalc *hash, 210 #endif 211 UString &errorMessage, 212 CDecompressStat &stat) 213{ 214 stat.Clear(); 215 UInt64 totalPackSize = 0; 216 CRecordVector<UInt64> arcSizes; 217 218 unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size(); 219 220 unsigned i; 221 for (i = 0; i < numArcs; i++) 222 { 223 NFind::CFileInfo fi; 224 fi.Size = 0; 225 if (!options.StdInMode) 226 { 227 const FString &arcPath = us2fs(arcPaths[i]); 228 if (!fi.Find(arcPath)) 229 throw "there is no such archive"; 230 if (fi.IsDir()) 231 throw "can't decompress folder"; 232 } 233 arcSizes.Add(fi.Size); 234 totalPackSize += fi.Size; 235 } 236 237 CBoolArr skipArcs(numArcs); 238 for (i = 0; i < numArcs; i++) 239 skipArcs[i] = false; 240 241 CArchiveExtractCallback *ecs = new CArchiveExtractCallback; 242 CMyComPtr<IArchiveExtractCallback> ec(ecs); 243 bool multi = (numArcs > 1); 244 ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode); 245 #ifndef _SFX 246 ecs->SetHashMethods(hash); 247 #endif 248 249 if (multi) 250 { 251 RINOK(extractCallback->SetTotal(totalPackSize)); 252 } 253 254 UInt64 totalPackProcessed = 0; 255 bool thereAreNotOpenArcs = false; 256 257 for (i = 0; i < numArcs; i++) 258 { 259 if (skipArcs[i]) 260 continue; 261 262 const UString &arcPath = arcPaths[i]; 263 NFind::CFileInfo fi; 264 if (options.StdInMode) 265 { 266 fi.Size = 0; 267 fi.Attrib = 0; 268 } 269 else 270 { 271 if (!fi.Find(us2fs(arcPath)) || fi.IsDir()) 272 throw "there is no such archive"; 273 } 274 275 #ifndef _NO_CRYPTO 276 openCallback->Open_ClearPasswordWasAskedFlag(); 277 #endif 278 279 RINOK(extractCallback->BeforeOpen(arcPath)); 280 CArchiveLink arcLink; 281 282 CObjectVector<COpenType> types2 = types; 283 /* 284 #ifndef _SFX 285 if (types.IsEmpty()) 286 { 287 int pos = arcPath.ReverseFind(L'.'); 288 if (pos >= 0) 289 { 290 UString s = arcPath.Ptr(pos + 1); 291 int index = codecs->FindFormatForExtension(s); 292 if (index >= 0 && s == L"001") 293 { 294 s = arcPath.Left(pos); 295 pos = s.ReverseFind(L'.'); 296 if (pos >= 0) 297 { 298 int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1)); 299 if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0 300 { 301 types2.Add(index2); 302 types2.Add(index); 303 } 304 } 305 } 306 } 307 } 308 #endif 309 */ 310 311 COpenOptions op; 312 #ifndef _SFX 313 op.props = &options.Properties; 314 #endif 315 op.codecs = codecs; 316 op.types = &types2; 317 op.excludedFormats = &excludedFormats; 318 op.stdInMode = options.StdInMode; 319 op.stream = NULL; 320 op.filePath = arcPath; 321 HRESULT result = arcLink.Open2(op, openCallback); 322 323 if (result == E_ABORT) 324 return result; 325 326 bool crypted = false; 327 #ifndef _NO_CRYPTO 328 crypted = openCallback->Open_WasPasswordAsked(); 329 #endif 330 331 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) 332 result = S_FALSE; 333 334 // arcLink.Set_ErrorsText(); 335 RINOK(extractCallback->OpenResult(arcPath, result, crypted)); 336 337 338 { 339 FOR_VECTOR (r, arcLink.Arcs) 340 { 341 const CArc &arc = arcLink.Arcs[r]; 342 const CArcErrorInfo &er = arc.ErrorInfo; 343 if (er.IsThereErrorOrWarning()) 344 { 345 RINOK(extractCallback->SetError(r, arc.Path, 346 er.GetErrorFlags(), er.ErrorMessage, 347 er.GetWarningFlags(), er.WarningMessage)); 348 } 349 } 350 } 351 352 if (result != S_OK) 353 { 354 thereAreNotOpenArcs = true; 355 if (!options.StdInMode) 356 { 357 NFind::CFileInfo fi; 358 if (fi.Find(us2fs(arcPath))) 359 if (!fi.IsDir()) 360 totalPackProcessed += fi.Size; 361 } 362 continue; 363 } 364 365 if (!options.StdInMode) 366 { 367 // numVolumes += arcLink.VolumePaths.Size(); 368 // arcLink.VolumesSize; 369 370 // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes); 371 // numArcs = arcPaths.Size(); 372 if (arcLink.VolumePaths.Size() != 0) 373 { 374 Int64 correctionSize = arcLink.VolumesSize; 375 FOR_VECTOR (v, arcLink.VolumePaths) 376 { 377 int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]); 378 if (index >= 0) 379 { 380 if ((unsigned)index > i) 381 { 382 skipArcs[index] = true; 383 correctionSize -= arcSizes[index]; 384 } 385 } 386 } 387 if (correctionSize != 0) 388 { 389 Int64 newPackSize = (Int64)totalPackSize + correctionSize; 390 if (newPackSize < 0) 391 newPackSize = 0; 392 totalPackSize = newPackSize; 393 RINOK(extractCallback->SetTotal(totalPackSize)); 394 } 395 } 396 } 397 398 #ifndef _NO_CRYPTO 399 bool passwordIsDefined; 400 UString password; 401 RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password)); 402 if (passwordIsDefined) 403 { 404 RINOK(extractCallback->SetPassword(password)); 405 } 406 #endif 407 408 FOR_VECTOR (k, arcLink.Arcs) 409 { 410 const CArc &arc = arcLink.Arcs[k]; 411 const CArcErrorInfo &er = arc.ErrorInfo; 412 413 if (er.ErrorFormatIndex >= 0) 414 { 415 RINOK(extractCallback->OpenTypeWarning(arc.Path, 416 codecs->GetFormatNamePtr(arc.FormatIndex), 417 codecs->GetFormatNamePtr(er.ErrorFormatIndex))) 418 /* 419 UString s = L"Can not open the file as [" + codecs->Formats[arc.ErrorFormatIndex].Name + L"] archive\n"; 420 s += L"The file is open as [" + codecs->Formats[arc.FormatIndex].Name + L"] archive"; 421 RINOK(extractCallback->MessageError(s)); 422 */ 423 } 424 { 425 const UString &s = er.ErrorMessage; 426 if (!s.IsEmpty()) 427 { 428 RINOK(extractCallback->MessageError(s)); 429 } 430 } 431 } 432 433 CArc &arc = arcLink.Arcs.Back(); 434 arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice); 435 arc.MTime = fi.MTime; 436 437 UInt64 packProcessed; 438 bool calcCrc = 439 #ifndef _SFX 440 (hash != NULL); 441 #else 442 false; 443 #endif 444 445 RINOK(DecompressArchive( 446 codecs, 447 arcLink, 448 fi.Size + arcLink.VolumesSize, 449 wildcardCensor, 450 options, 451 calcCrc, 452 extractCallback, ecs, errorMessage, packProcessed)); 453 if (!options.StdInMode) 454 packProcessed = fi.Size + arcLink.VolumesSize; 455 totalPackProcessed += packProcessed; 456 ecs->LocalProgressSpec->InSize += packProcessed; 457 ecs->LocalProgressSpec->OutSize = ecs->UnpackSize; 458 if (!errorMessage.IsEmpty()) 459 return E_FAIL; 460 } 461 462 if (multi || thereAreNotOpenArcs) 463 { 464 RINOK(extractCallback->SetTotal(totalPackSize)); 465 RINOK(extractCallback->SetCompleted(&totalPackProcessed)); 466 } 467 stat.NumFolders = ecs->NumFolders; 468 stat.NumFiles = ecs->NumFiles; 469 stat.NumAltStreams = ecs->NumAltStreams; 470 stat.UnpackSize = ecs->UnpackSize; 471 stat.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize; 472 stat.NumArchives = arcPaths.Size(); 473 stat.PackSize = ecs->LocalProgressSpec->InSize; 474 return S_OK; 475} 476