1 // OpenArchive.cpp 2 3 #include "StdAfx.h" 4 5 #include "Common/Wildcard.h" 6 7 #include "Windows/FileDir.h" 8 #include "Windows/PropVariant.h" 9 10 #include "../../Common/FileStreams.h" 11 #include "../../Common/StreamUtils.h" 12 13 #include "DefaultName.h" 14 #include "OpenArchive.h" 15 16 using namespace NWindows; 17 18 // Static-SFX (for Linux) can be big. 19 const UInt64 kMaxCheckStartPosition = 1 << 22; 20 21 HRESULT GetArchiveItemBoolProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) 22 { 23 NCOM::CPropVariant prop; 24 result = false; 25 RINOK(archive->GetProperty(index, propID, &prop)); 26 if (prop.vt == VT_BOOL) 27 result = VARIANT_BOOLToBool(prop.boolVal); 28 else if (prop.vt != VT_EMPTY) 29 return E_FAIL; 30 return S_OK; 31 } 32 33 HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) 34 { 35 return GetArchiveItemBoolProp(archive, index, kpidIsDir, result); 36 } 37 38 HRESULT CArc::GetItemPath(UInt32 index, UString &result) const 39 { 40 { 41 NCOM::CPropVariant prop; 42 RINOK(Archive->GetProperty(index, kpidPath, &prop)); 43 if (prop.vt == VT_BSTR) 44 result = prop.bstrVal; 45 else if (prop.vt == VT_EMPTY) 46 result.Empty(); 47 else 48 return E_FAIL; 49 } 50 if (result.IsEmpty()) 51 { 52 result = DefaultName; 53 NCOM::CPropVariant prop; 54 RINOK(Archive->GetProperty(index, kpidExtension, &prop)); 55 if (prop.vt == VT_BSTR) 56 { 57 result += L'.'; 58 result += prop.bstrVal; 59 } 60 else if (prop.vt != VT_EMPTY) 61 return E_FAIL; 62 } 63 return S_OK; 64 } 65 66 HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const 67 { 68 NCOM::CPropVariant prop; 69 defined = false; 70 ft.dwHighDateTime = ft.dwLowDateTime = 0; 71 RINOK(Archive->GetProperty(index, kpidMTime, &prop)); 72 if (prop.vt == VT_FILETIME) 73 { 74 ft = prop.filetime; 75 defined = true; 76 } 77 else if (prop.vt != VT_EMPTY) 78 return E_FAIL; 79 else if (MTimeDefined) 80 { 81 ft = MTime; 82 defined = true; 83 } 84 return S_OK; 85 } 86 87 #ifndef _SFX 88 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) 89 { 90 for (size_t i = 0; i < size; i++) 91 if (p1[i] != p2[i]) 92 return false; 93 return true; 94 } 95 #endif 96 97 #ifdef UNDER_CE 98 static const int kNumHashBytes = 1; 99 #define HASH_VAL(buf, pos) ((buf)[pos]) 100 #else 101 static const int kNumHashBytes = 2; 102 #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) 103 #endif 104 105 106 HRESULT CArc::OpenStream( 107 CCodecs *codecs, 108 int formatIndex, 109 IInStream *stream, 110 ISequentialInStream *seqStream, 111 IArchiveOpenCallback *callback) 112 { 113 Archive.Release(); 114 ErrorMessage.Empty(); 115 const UString fileName = ExtractFileNameFromPath(Path); 116 UString extension; 117 { 118 int dotPos = fileName.ReverseFind(L'.'); 119 if (dotPos >= 0) 120 extension = fileName.Mid(dotPos + 1); 121 } 122 CIntVector orderIndices; 123 if (formatIndex >= 0) 124 orderIndices.Add(formatIndex); 125 else 126 { 127 128 int i; 129 int numFinded = 0; 130 for (i = 0; i < codecs->Formats.Size(); i++) 131 if (codecs->Formats[i].FindExtension(extension) >= 0) 132 orderIndices.Insert(numFinded++, i); 133 else 134 orderIndices.Add(i); 135 136 if (!stream) 137 { 138 if (numFinded != 1) 139 return E_NOTIMPL; 140 orderIndices.DeleteFrom(1); 141 } 142 143 #ifndef _SFX 144 if (orderIndices.Size() >= 2 && (numFinded == 0 || extension.CompareNoCase(L"exe") == 0)) 145 { 146 CIntVector orderIndices2; 147 CByteBuffer byteBuffer; 148 const size_t kBufferSize = (1 << 21); 149 byteBuffer.SetCapacity(kBufferSize); 150 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); 151 size_t processedSize = kBufferSize; 152 RINOK(ReadStream(stream, byteBuffer, &processedSize)); 153 if (processedSize == 0) 154 return S_FALSE; 155 156 const Byte *buf = byteBuffer; 157 CByteBuffer hashBuffer; 158 const UInt32 kNumVals = 1 << (kNumHashBytes * 8); 159 hashBuffer.SetCapacity(kNumVals); 160 Byte *hash = hashBuffer; 161 memset(hash, 0xFF, kNumVals); 162 Byte prevs[256]; 163 if (orderIndices.Size() >= 256) 164 return S_FALSE; 165 int i; 166 for (i = 0; i < orderIndices.Size(); i++) 167 { 168 const CArcInfoEx &ai = codecs->Formats[orderIndices[i]]; 169 const CByteBuffer &sig = ai.StartSignature; 170 if (sig.GetCapacity() < kNumHashBytes) 171 continue; 172 UInt32 v = HASH_VAL(sig, 0); 173 prevs[i] = hash[v]; 174 hash[v] = (Byte)i; 175 } 176 177 processedSize -= (kNumHashBytes - 1); 178 for (UInt32 pos = 0; pos < processedSize; pos++) 179 { 180 for (; pos < processedSize && hash[HASH_VAL(buf, pos)] == 0xFF; pos++); 181 if (pos == processedSize) 182 break; 183 UInt32 v = HASH_VAL(buf, pos); 184 Byte *ptr = &hash[v]; 185 int i = *ptr; 186 do 187 { 188 int index = orderIndices[i]; 189 const CArcInfoEx &ai = codecs->Formats[index]; 190 const CByteBuffer &sig = ai.StartSignature; 191 if (sig.GetCapacity() != 0 && pos + sig.GetCapacity() <= processedSize + (kNumHashBytes - 1) && 192 TestSignature(buf + pos, sig, sig.GetCapacity())) 193 { 194 orderIndices2.Add(index); 195 orderIndices[i] = 0xFF; 196 *ptr = prevs[i]; 197 } 198 else 199 ptr = &prevs[i]; 200 i = *ptr; 201 } 202 while (i != 0xFF); 203 } 204 205 for (i = 0; i < orderIndices.Size(); i++) 206 { 207 int val = orderIndices[i]; 208 if (val != 0xFF) 209 orderIndices2.Add(val); 210 } 211 orderIndices = orderIndices2; 212 } 213 else if (extension == L"000" || extension == L"001") 214 { 215 CByteBuffer byteBuffer; 216 const size_t kBufferSize = (1 << 10); 217 byteBuffer.SetCapacity(kBufferSize); 218 Byte *buffer = byteBuffer; 219 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); 220 size_t processedSize = kBufferSize; 221 RINOK(ReadStream(stream, buffer, &processedSize)); 222 if (processedSize >= 16) 223 { 224 Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}; 225 if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] & 1) != 0) 226 { 227 for (int i = 0; i < orderIndices.Size(); i++) 228 { 229 int index = orderIndices[i]; 230 const CArcInfoEx &ai = codecs->Formats[index]; 231 if (ai.Name.CompareNoCase(L"rar") != 0) 232 continue; 233 orderIndices.Delete(i--); 234 orderIndices.Insert(0, index); 235 break; 236 } 237 } 238 } 239 } 240 if (orderIndices.Size() >= 2) 241 { 242 int isoIndex = codecs->FindFormatForArchiveType(L"iso"); 243 int udfIndex = codecs->FindFormatForArchiveType(L"udf"); 244 int iIso = -1; 245 int iUdf = -1; 246 for (int i = 0; i < orderIndices.Size(); i++) 247 { 248 if (orderIndices[i] == isoIndex) iIso = i; 249 if (orderIndices[i] == udfIndex) iUdf = i; 250 } 251 if (iUdf > iIso && iIso >= 0) 252 { 253 orderIndices[iUdf] = isoIndex; 254 orderIndices[iIso] = udfIndex; 255 } 256 } 257 258 #endif 259 } 260 261 for (int i = 0; i < orderIndices.Size(); i++) 262 { 263 if (stream) 264 { 265 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); 266 } 267 CMyComPtr<IInArchive> archive; 268 269 FormatIndex = orderIndices[i]; 270 RINOK(codecs->CreateInArchive(FormatIndex, archive)); 271 if (!archive) 272 continue; 273 274 #ifdef EXTERNAL_CODECS 275 { 276 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; 277 archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); 278 if (setCompressCodecsInfo) 279 { 280 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); 281 } 282 } 283 #endif 284 285 // OutputDebugStringW(codecs->Formats[FormatIndex].Name); 286 287 HRESULT result; 288 if (stream) 289 result = archive->Open(stream, &kMaxCheckStartPosition, callback); 290 else 291 { 292 CMyComPtr<IArchiveOpenSeq> openSeq; 293 archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); 294 if (!openSeq) 295 return E_NOTIMPL; 296 result = openSeq->OpenSeq(seqStream); 297 } 298 299 if (result == S_FALSE) 300 continue; 301 RINOK(result); 302 303 { 304 NCOM::CPropVariant prop; 305 archive->GetArchiveProperty(kpidError, &prop); 306 if (prop.vt != VT_EMPTY) 307 ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error"; 308 } 309 310 Archive = archive; 311 const CArcInfoEx &format = codecs->Formats[FormatIndex]; 312 if (format.Exts.Size() == 0) 313 DefaultName = GetDefaultName2(fileName, L"", L""); 314 else 315 { 316 int subExtIndex = format.FindExtension(extension); 317 if (subExtIndex < 0) 318 subExtIndex = 0; 319 const CArcExtInfo &extInfo = format.Exts[subExtIndex]; 320 DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); 321 } 322 return S_OK; 323 } 324 return S_FALSE; 325 } 326 327 HRESULT CArc::OpenStreamOrFile( 328 CCodecs *codecs, 329 int formatIndex, 330 bool stdInMode, 331 IInStream *stream, 332 IArchiveOpenCallback *callback) 333 { 334 CMyComPtr<IInStream> fileStream; 335 CMyComPtr<ISequentialInStream> seqStream; 336 if (stdInMode) 337 seqStream = new CStdInFileStream; 338 else if (!stream) 339 { 340 CInFileStream *fileStreamSpec = new CInFileStream; 341 fileStream = fileStreamSpec; 342 if (!fileStreamSpec->Open(Path)) 343 return GetLastError(); 344 stream = fileStream; 345 } 346 347 /* 348 if (callback) 349 { 350 UInt64 fileSize; 351 RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); 352 RINOK(callback->SetTotal(NULL, &fileSize)) 353 } 354 */ 355 356 return OpenStream(codecs, formatIndex, stream, seqStream, callback); 357 } 358 359 HRESULT CArchiveLink::Close() 360 { 361 for (int i = Arcs.Size() - 1; i >= 0; i--) 362 { 363 RINOK(Arcs[i].Archive->Close()); 364 } 365 IsOpen = false; 366 return S_OK; 367 } 368 369 void CArchiveLink::Release() 370 { 371 while (!Arcs.IsEmpty()) 372 Arcs.DeleteBack(); 373 } 374 375 HRESULT CArchiveLink::Open( 376 CCodecs *codecs, 377 const CIntVector &formatIndices, 378 bool stdInMode, 379 IInStream *stream, 380 const UString &filePath, 381 IArchiveOpenCallback *callback) 382 { 383 Release(); 384 if (formatIndices.Size() >= 32) 385 return E_NOTIMPL; 386 387 HRESULT resSpec; 388 389 for (;;) 390 { 391 resSpec = S_OK; 392 int formatIndex = -1; 393 if (formatIndices.Size() >= 1) 394 { 395 if (Arcs.Size() >= formatIndices.Size()) 396 break; 397 formatIndex = formatIndices[formatIndices.Size() - Arcs.Size() - 1]; 398 } 399 else if (Arcs.Size() >= 32) 400 break; 401 402 if (Arcs.IsEmpty()) 403 { 404 CArc arc; 405 arc.Path = filePath; 406 arc.SubfileIndex = (UInt32)(Int32)-1; 407 RINOK(arc.OpenStreamOrFile(codecs, formatIndex, stdInMode, stream, callback)); 408 Arcs.Add(arc); 409 continue; 410 } 411 412 const CArc &arc = Arcs.Back(); 413 414 resSpec = (formatIndices.Size() == 0 ? S_OK : E_NOTIMPL); 415 416 UInt32 mainSubfile; 417 { 418 NCOM::CPropVariant prop; 419 RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop)); 420 if (prop.vt == VT_UI4) 421 mainSubfile = prop.ulVal; 422 else 423 break; 424 UInt32 numItems; 425 RINOK(arc.Archive->GetNumberOfItems(&numItems)); 426 if (mainSubfile >= numItems) 427 break; 428 } 429 430 431 CMyComPtr<IInArchiveGetStream> getStream; 432 if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream) 433 break; 434 435 CMyComPtr<ISequentialInStream> subSeqStream; 436 if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream) 437 break; 438 439 CMyComPtr<IInStream> subStream; 440 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream) 441 break; 442 443 CArc arc2; 444 RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); 445 446 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; 447 callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); 448 if (setSubArchiveName) 449 setSubArchiveName->SetSubArchiveName(arc2.Path); 450 451 arc2.SubfileIndex = mainSubfile; 452 HRESULT result = arc2.OpenStream(codecs, formatIndex, subStream, NULL, callback); 453 resSpec = (formatIndices.Size() == 0 ? S_OK : S_FALSE); 454 if (result == S_FALSE) 455 break; 456 RINOK(result); 457 RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); 458 Arcs.Add(arc2); 459 } 460 IsOpen = !Arcs.IsEmpty(); 461 return S_OK; 462 } 463 464 static void SetCallback(const UString &filePath, 465 IOpenCallbackUI *callbackUI, 466 IArchiveOpenCallback *reOpenCallback, 467 CMyComPtr<IArchiveOpenCallback> &callback) 468 { 469 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; 470 callback = openCallbackSpec; 471 openCallbackSpec->Callback = callbackUI; 472 openCallbackSpec->ReOpenCallback = reOpenCallback; 473 474 UString fullName; 475 int fileNamePartStartIndex; 476 NFile::NDirectory::MyGetFullPathName(filePath, fullName, fileNamePartStartIndex); 477 openCallbackSpec->Init( 478 fullName.Left(fileNamePartStartIndex), 479 fullName.Mid(fileNamePartStartIndex)); 480 } 481 482 HRESULT CArchiveLink::Open2(CCodecs *codecs, 483 const CIntVector &formatIndices, 484 bool stdInMode, 485 IInStream *stream, 486 const UString &filePath, 487 IOpenCallbackUI *callbackUI) 488 { 489 VolumesSize = 0; 490 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; 491 CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec; 492 openCallbackSpec->Callback = callbackUI; 493 494 UString fullName, prefix, name; 495 if (!stream && !stdInMode) 496 { 497 int fileNamePartStartIndex; 498 if (!NFile::NDirectory::MyGetFullPathName(filePath, fullName, fileNamePartStartIndex)) 499 return GetLastError(); 500 prefix = fullName.Left(fileNamePartStartIndex); 501 name = fullName.Mid(fileNamePartStartIndex); 502 openCallbackSpec->Init(prefix, name); 503 } 504 else 505 { 506 openCallbackSpec->SetSubArchiveName(filePath); 507 } 508 509 RINOK(Open(codecs, formatIndices, stdInMode, stream, filePath, callback)); 510 VolumePaths.Add(prefix + name); 511 for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) 512 VolumePaths.Add(prefix + openCallbackSpec->FileNames[i]); 513 VolumesSize = openCallbackSpec->TotalSize; 514 return S_OK; 515 } 516 517 HRESULT CArchiveLink::ReOpen(CCodecs *codecs, const UString &filePath, 518 IArchiveOpenCallback *callback) 519 { 520 if (Arcs.Size() > 1) 521 return E_NOTIMPL; 522 523 if (Arcs.Size() == 0) 524 return Open2(codecs, CIntVector(), false, NULL, filePath, 0); 525 526 CMyComPtr<IArchiveOpenCallback> openCallbackNew; 527 SetCallback(filePath, NULL, callback, openCallbackNew); 528 529 CInFileStream *fileStreamSpec = new CInFileStream; 530 CMyComPtr<IInStream> stream(fileStreamSpec); 531 if (!fileStreamSpec->Open(filePath)) 532 return GetLastError(); 533 HRESULT res = GetArchive()->Open(stream, &kMaxCheckStartPosition, callback); 534 IsOpen = (res == S_OK); 535 return res; 536 } 537