1 // OpenArchive.cpp 2 3 #include "StdAfx.h" 4 5 // #define SHOW_DEBUG_INFO 6 7 #ifdef SHOW_DEBUG_INFO 8 #include <stdio.h> 9 #endif 10 11 #include "../../../../C/CpuArch.h" 12 13 #include "../../../Common/ComTry.h" 14 #include "../../../Common/IntToString.h" 15 #include "../../../Common/StringConvert.h" 16 #include "../../../Common/StringToInt.h" 17 #include "../../../Common/Wildcard.h" 18 19 #include "../../../Windows/FileDir.h" 20 21 #include "../../Common/FileStreams.h" 22 #include "../../Common/LimitedStreams.h" 23 #include "../../Common/ProgressUtils.h" 24 #include "../../Common/StreamUtils.h" 25 26 #include "../../Compress/CopyCoder.h" 27 28 #include "DefaultName.h" 29 #include "OpenArchive.h" 30 31 #ifndef _SFX 32 #include "SetProperties.h" 33 #endif 34 35 #ifdef SHOW_DEBUG_INFO 36 #define PRF(x) x 37 #else 38 #define PRF(x) 39 #endif 40 41 // increase it, if you need to support larger SFX stubs 42 static const UInt64 kMaxCheckStartPosition = 1 << 23; 43 44 /* 45 Open: 46 - formatIndex >= 0 (exact Format) 47 1) Open with main type. Archive handler is allowed to use archive start finder. 48 Warning, if there is tail. 49 50 - formatIndex = -1 (Parser:0) (default) 51 - same as #1 but doesn't return Parser 52 53 - formatIndex = -2 (#1) 54 - file has supported extension (like a.7z) 55 Open with that main type (only starting from start of file). 56 - open OK: 57 - if there is no tail - return OK 58 - if there is tail: 59 - archive is not "Self Exe" - return OK with Warning, that there is tail 60 - archive is "Self Exe" 61 ignore "Self Exe" stub, and tries to open tail 62 - tail can be open as archive - shows that archive and stub size property. 63 - tail can't be open as archive - shows Parser ??? 64 - open FAIL: 65 Try to open with all other types from offset 0 only. 66 If some open type is OK and physical archive size is uequal or larger 67 than file size, then return that archive with warning that can not be open as [extension type]. 68 If extension was EXE, it will try to open as unknown_extension case 69 - file has unknown extension (like a.hhh) 70 It tries to open via parser code. 71 - if there is full archive or tail archive and unknown block or "Self Exe" 72 at front, it shows tail archive and stub size property. 73 - in another cases, if there is some archive inside file, it returns parser/ 74 - in another cases, it retuens S_FALSE 75 76 77 - formatIndex = -3 (#2) 78 - same as #1, but 79 - stub (EXE) + archive is open in Parser 80 81 - formatIndex = -4 (#3) 82 - returns only Parser. skip full file archive. And show other sub-archives 83 84 - formatIndex = -5 (#4) 85 - returns only Parser. skip full file archive. And show other sub-archives for each byte pos 86 87 */ 88 89 90 91 92 using namespace NWindows; 93 94 /* 95 #ifdef _SFX 96 #define OPEN_PROPS_PARAM 97 #else 98 #define OPEN_PROPS_PARAM , props 99 #endif 100 */ 101 102 /* 103 CArc::~CArc() 104 { 105 GetRawProps.Release(); 106 Archive.Release(); 107 printf("\nCArc::~CArc()\n"); 108 } 109 */ 110 111 #ifndef _SFX 112 113 namespace NArchive { 114 namespace NParser { 115 116 struct CParseItem 117 { 118 UInt64 Offset; 119 UInt64 Size; 120 // UInt64 OkSize; 121 UString Name; 122 UString Extension; 123 FILETIME FileTime; 124 UString Comment; 125 UString ArcType; 126 127 bool FileTime_Defined; 128 bool UnpackSize_Defined; 129 bool NumSubDirs_Defined; 130 bool NumSubFiles_Defined; 131 132 bool IsSelfExe; 133 bool IsNotArcType; 134 135 UInt64 UnpackSize; 136 UInt64 NumSubDirs; 137 UInt64 NumSubFiles; 138 139 int FormatIndex; 140 141 bool LenIsUnknown; 142 143 CParseItem(): 144 LenIsUnknown(false), 145 FileTime_Defined(false), 146 UnpackSize_Defined(false), 147 NumSubFiles_Defined(false), 148 NumSubDirs_Defined(false), 149 IsSelfExe(false), 150 IsNotArcType(false) 151 // OkSize(0) 152 {} 153 154 /* 155 bool IsEqualTo(const CParseItem &item) const 156 { 157 return Offset == item.Offset && Size == item.Size; 158 } 159 */ 160 161 void NormalizeOffset() 162 { 163 if ((Int64)Offset < 0) 164 { 165 Size += Offset; 166 // OkSize += Offset; 167 Offset = 0; 168 } 169 } 170 }; 171 172 class CHandler: 173 public IInArchive, 174 public IInArchiveGetStream, 175 public CMyUnknownImp 176 { 177 public: 178 CObjectVector<CParseItem> _items; 179 UInt64 _maxEndOffset; 180 CMyComPtr<IInStream> _stream; 181 182 MY_UNKNOWN_IMP2( 183 IInArchive, 184 IInArchiveGetStream) 185 186 INTERFACE_IInArchive(;) 187 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); 188 189 UInt64 GetLastEnd() const 190 { 191 if (_items.IsEmpty()) 192 return 0; 193 const CParseItem &back = _items.Back(); 194 return back.Offset + back.Size; 195 } 196 197 void AddUnknownItem(UInt64 next); 198 int FindInsertPos(const CParseItem &item) const; 199 void AddItem(const CParseItem &item); 200 201 CHandler(): _maxEndOffset(0) {} 202 }; 203 204 int CHandler::FindInsertPos(const CParseItem &item) const 205 { 206 unsigned left = 0, right = _items.Size(); 207 while (left != right) 208 { 209 unsigned mid = (left + right) / 2; 210 const CParseItem & midItem = _items[mid]; 211 if (item.Offset < midItem.Offset) 212 right = mid; 213 else if (item.Offset > midItem.Offset) 214 left = mid + 1; 215 else if (item.Size < midItem.Size) 216 right = mid; 217 else if (item.Size > midItem.Size) 218 left = mid + 1; 219 else 220 { 221 left = mid + 1; 222 // return -1; 223 } 224 } 225 return left; 226 } 227 228 void CHandler::AddUnknownItem(UInt64 next) 229 { 230 /* 231 UInt64 prevEnd = 0; 232 if (!_items.IsEmpty()) 233 { 234 const CParseItem &back = _items.Back(); 235 prevEnd = back.Offset + back.Size; 236 } 237 */ 238 if (_maxEndOffset < next) 239 { 240 CParseItem item2; 241 item2.Offset = _maxEndOffset; 242 item2.Size = next - _maxEndOffset; 243 _maxEndOffset = next; 244 _items.Add(item2); 245 } 246 else if (_maxEndOffset > next && !_items.IsEmpty()) 247 { 248 CParseItem &back = _items.Back(); 249 if (back.LenIsUnknown) 250 { 251 back.Size = next - back.Offset; 252 _maxEndOffset = next; 253 } 254 } 255 } 256 257 void CHandler::AddItem(const CParseItem &item) 258 { 259 AddUnknownItem(item.Offset); 260 int pos = FindInsertPos(item); 261 if (pos >= 0) 262 { 263 _items.Insert(pos, item); 264 UInt64 next = item.Offset + item.Size; 265 if (_maxEndOffset < next) 266 _maxEndOffset = next; 267 } 268 } 269 270 /* 271 static const CStatProp kProps[] = 272 { 273 { NULL, kpidPath, VT_BSTR}, 274 { NULL, kpidSize, VT_UI8}, 275 { NULL, kpidMTime, VT_FILETIME}, 276 { NULL, kpidType, VT_BSTR}, 277 { NULL, kpidComment, VT_BSTR}, 278 { NULL, kpidOffset, VT_UI8}, 279 { NULL, kpidUnpackSize, VT_UI8}, 280 // { NULL, kpidNumSubDirs, VT_UI8}, 281 }; 282 */ 283 284 static const Byte kProps[] = 285 { 286 kpidPath, 287 kpidSize, 288 kpidMTime, 289 kpidType, 290 kpidComment, 291 kpidOffset, 292 kpidUnpackSize 293 }; 294 295 IMP_IInArchive_Props 296 IMP_IInArchive_ArcProps_NO 297 298 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */) 299 { 300 COM_TRY_BEGIN 301 { 302 Close(); 303 _stream = stream; 304 } 305 return S_OK; 306 COM_TRY_END 307 } 308 309 STDMETHODIMP CHandler::Close() 310 { 311 _items.Clear(); 312 _stream.Release(); 313 return S_OK; 314 } 315 316 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) 317 { 318 *numItems = _items.Size(); 319 return S_OK; 320 } 321 322 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) 323 { 324 COM_TRY_BEGIN 325 NCOM::CPropVariant prop; 326 327 const CParseItem &item = _items[index]; 328 329 switch (propID) 330 { 331 case kpidPath: 332 { 333 char sz[32]; 334 ConvertUInt32ToString(index + 1, sz); 335 UString s(sz); 336 if (!item.Name.IsEmpty()) 337 { 338 s += '.'; 339 s += item.Name; 340 } 341 if (!item.Extension.IsEmpty()) 342 { 343 s += '.'; 344 s += item.Extension; 345 } 346 prop = s; break; 347 } 348 case kpidSize: 349 case kpidPackSize: prop = item.Size; break; 350 case kpidOffset: prop = item.Offset; break; 351 case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break; 352 case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break; 353 case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break; 354 case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break; 355 case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break; 356 case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break; 357 } 358 prop.Detach(value); 359 return S_OK; 360 COM_TRY_END 361 } 362 363 HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, 364 Int32 testMode, IArchiveExtractCallback *extractCallback) 365 { 366 COM_TRY_BEGIN 367 368 bool allFilesMode = (numItems == (UInt32)(Int32)-1); 369 if (allFilesMode) 370 numItems = _items.Size(); 371 if (_stream && numItems == 0) 372 return S_OK; 373 UInt64 totalSize = 0; 374 UInt32 i; 375 for (i = 0; i < numItems; i++) 376 totalSize += _items[allFilesMode ? i : indices[i]].Size; 377 extractCallback->SetTotal(totalSize); 378 379 totalSize = 0; 380 381 CLocalProgress *lps = new CLocalProgress; 382 CMyComPtr<ICompressProgressInfo> progress = lps; 383 lps->Init(extractCallback, false); 384 385 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; 386 CMyComPtr<ISequentialInStream> inStream(streamSpec); 387 streamSpec->SetStream(_stream); 388 389 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; 390 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); 391 392 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); 393 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; 394 395 for (i = 0; i < numItems; i++) 396 { 397 lps->InSize = totalSize; 398 lps->OutSize = totalSize; 399 RINOK(lps->SetCur()); 400 CMyComPtr<ISequentialOutStream> realOutStream; 401 Int32 askMode = testMode ? 402 NExtract::NAskMode::kTest : 403 NExtract::NAskMode::kExtract; 404 Int32 index = allFilesMode ? i : indices[i]; 405 const CParseItem &item = _items[index]; 406 407 RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); 408 UInt64 unpackSize = item.Size; 409 totalSize += unpackSize; 410 bool skipMode = false; 411 if (!testMode && !realOutStream) 412 continue; 413 RINOK(extractCallback->PrepareOperation(askMode)); 414 415 outStreamSpec->SetStream(realOutStream); 416 realOutStream.Release(); 417 outStreamSpec->Init(skipMode ? 0 : unpackSize, true); 418 419 Int32 opRes = NExtract::NOperationResult::kOK; 420 RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); 421 streamSpec->Init(unpackSize); 422 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); 423 424 if (outStreamSpec->GetRem() != 0) 425 opRes = NExtract::NOperationResult::kDataError; 426 outStreamSpec->ReleaseStream(); 427 RINOK(extractCallback->SetOperationResult(opRes)); 428 } 429 430 return S_OK; 431 432 COM_TRY_END 433 } 434 435 436 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) 437 { 438 COM_TRY_BEGIN 439 const CParseItem &item = _items[index]; 440 return CreateLimitedInStream(_stream, item.Offset, item.Size, stream); 441 COM_TRY_END 442 } 443 444 }} 445 446 #endif 447 448 HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw() 449 { 450 NCOM::CPropVariant prop; 451 result = false; 452 RINOK(arc->GetProperty(index, propID, &prop)); 453 if (prop.vt == VT_BOOL) 454 result = VARIANT_BOOLToBool(prop.boolVal); 455 else if (prop.vt != VT_EMPTY) 456 return E_FAIL; 457 return S_OK; 458 } 459 460 HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw() 461 { 462 return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); 463 } 464 465 HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() 466 { 467 return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); 468 } 469 470 HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() 471 { 472 return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); 473 } 474 475 HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() 476 { 477 return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); 478 } 479 480 static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) throw() 481 { 482 NCOM::CPropVariant prop; 483 result = false; 484 RINOK(arc->GetArchiveProperty(propid, &prop)); 485 if (prop.vt == VT_BOOL) 486 result = VARIANT_BOOLToBool(prop.boolVal); 487 else if (prop.vt != VT_EMPTY) 488 return E_FAIL; 489 return S_OK; 490 } 491 492 static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined) 493 { 494 defined = false; 495 NCOM::CPropVariant prop; 496 RINOK(arc->GetArchiveProperty(propid, &prop)); 497 switch (prop.vt) 498 { 499 case VT_UI4: result = prop.ulVal; defined = true; break; 500 case VT_I4: result = (Int64)prop.lVal; defined = true; break; 501 case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break; 502 case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break; 503 case VT_EMPTY: break; 504 default: return E_FAIL; 505 } 506 return S_OK; 507 } 508 509 static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined) 510 { 511 defined = false; 512 NCOM::CPropVariant prop; 513 RINOK(arc->GetArchiveProperty(propid, &prop)); 514 switch (prop.vt) 515 { 516 case VT_UI4: result = prop.ulVal; defined = true; break; 517 case VT_I4: result = prop.lVal; defined = true; break; 518 case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break; 519 case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break; 520 case VT_EMPTY: break; 521 default: return E_FAIL; 522 } 523 return S_OK; 524 } 525 526 #ifndef _SFX 527 528 HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const 529 { 530 if (!GetRawProps) 531 return E_FAIL; 532 if (index == parent) 533 return S_OK; 534 UInt32 curIndex = index; 535 536 UString s; 537 538 bool prevWasAltStream = false; 539 540 for (;;) 541 { 542 #ifdef MY_CPU_LE 543 const void *p; 544 UInt32 size; 545 UInt32 propType; 546 RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType)); 547 if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE) 548 s = (const wchar_t *)p; 549 else 550 #endif 551 { 552 NCOM::CPropVariant prop; 553 RINOK(Archive->GetProperty(curIndex, kpidName, &prop)); 554 if (prop.vt == VT_BSTR && prop.bstrVal) 555 s.SetFromBstr(prop.bstrVal); 556 else if (prop.vt == VT_EMPTY) 557 s.Empty(); 558 else 559 return E_FAIL; 560 } 561 562 UInt32 curParent = (UInt32)(Int32)-1; 563 UInt32 parentType = 0; 564 RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType)); 565 566 // 18.06: fixed : we don't want to split name to parts 567 /* 568 if (parentType != NParentType::kAltStream) 569 { 570 for (;;) 571 { 572 int pos = s.ReverseFind_PathSepar(); 573 if (pos < 0) 574 { 575 break; 576 } 577 parts.Insert(0, s.Ptr(pos + 1)); 578 s.DeleteFrom(pos); 579 } 580 } 581 */ 582 583 parts.Insert(0, s); 584 585 if (prevWasAltStream) 586 { 587 { 588 UString &s2 = parts[parts.Size() - 2]; 589 s2 += ':'; 590 s2 += parts.Back(); 591 } 592 parts.DeleteBack(); 593 } 594 595 if (parent == curParent) 596 return S_OK; 597 598 prevWasAltStream = false; 599 if (parentType == NParentType::kAltStream) 600 prevWasAltStream = true; 601 602 if (curParent == (UInt32)(Int32)-1) 603 return E_FAIL; 604 curIndex = curParent; 605 } 606 } 607 608 #endif 609 610 HRESULT CArc::GetItemPath(UInt32 index, UString &result) const 611 { 612 #ifdef MY_CPU_LE 613 if (GetRawProps) 614 { 615 const void *p; 616 UInt32 size; 617 UInt32 propType; 618 if (!IsTree) 619 { 620 if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK && 621 propType == NPropDataType::kUtf16z) 622 { 623 unsigned len = size / 2 - 1; 624 wchar_t *s = result.GetBuf(len); 625 for (unsigned i = 0; i < len; i++) 626 { 627 wchar_t c = GetUi16(p); 628 p = (const void *)((const Byte *)p + 2); 629 #if WCHAR_PATH_SEPARATOR != L'/' 630 if (c == L'/') 631 c = WCHAR_PATH_SEPARATOR; 632 #endif 633 *s++ = c; 634 } 635 *s = 0; 636 result.ReleaseBuf_SetLen(len); 637 if (len != 0) 638 return S_OK; 639 } 640 } 641 /* 642 else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK && 643 p && propType == NPropDataType::kUtf16z) 644 { 645 size -= 2; 646 UInt32 totalSize = size; 647 bool isOK = false; 648 649 { 650 UInt32 index2 = index; 651 for (;;) 652 { 653 UInt32 parent = (UInt32)(Int32)-1; 654 UInt32 parentType = 0; 655 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) 656 break; 657 if (parent == (UInt32)(Int32)-1) 658 { 659 if (parentType != 0) 660 totalSize += 2; 661 isOK = true; 662 break; 663 } 664 index2 = parent; 665 UInt32 size2; 666 const void *p2; 667 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK && 668 p2 && propType == NPropDataType::kUtf16z) 669 break; 670 totalSize += size2; 671 } 672 } 673 674 if (isOK) 675 { 676 wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2); 677 UInt32 pos = totalSize - size; 678 memcpy((Byte *)sz + pos, p, size); 679 UInt32 index2 = index; 680 for (;;) 681 { 682 UInt32 parent = (UInt32)(Int32)-1; 683 UInt32 parentType = 0; 684 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) 685 break; 686 if (parent == (UInt32)(Int32)-1) 687 { 688 if (parentType != 0) 689 sz[pos / 2 - 1] = L':'; 690 break; 691 } 692 index2 = parent; 693 UInt32 size2; 694 const void *p2; 695 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) 696 break; 697 pos -= size2; 698 memcpy((Byte *)sz + pos, p2, size2); 699 sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':'; 700 } 701 #ifdef _WIN32 702 // result.Replace(L'/', WCHAR_PATH_SEPARATOR); 703 #endif 704 return S_OK; 705 } 706 } 707 */ 708 } 709 #endif 710 711 { 712 NCOM::CPropVariant prop; 713 RINOK(Archive->GetProperty(index, kpidPath, &prop)); 714 if (prop.vt == VT_BSTR && prop.bstrVal) 715 result.SetFromBstr(prop.bstrVal); 716 else if (prop.vt == VT_EMPTY) 717 result.Empty(); 718 else 719 return E_FAIL; 720 } 721 722 if (result.IsEmpty()) 723 return GetDefaultItemPath(index, result); 724 return S_OK; 725 } 726 727 HRESULT CArc::GetDefaultItemPath(UInt32 index, UString &result) const 728 { 729 result.Empty(); 730 bool isDir; 731 RINOK(Archive_IsItem_Dir(Archive, index, isDir)); 732 if (!isDir) 733 { 734 result = DefaultName; 735 NCOM::CPropVariant prop; 736 RINOK(Archive->GetProperty(index, kpidExtension, &prop)); 737 if (prop.vt == VT_BSTR) 738 { 739 result += '.'; 740 result += prop.bstrVal; 741 } 742 else if (prop.vt != VT_EMPTY) 743 return E_FAIL; 744 } 745 return S_OK; 746 } 747 748 HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const 749 { 750 RINOK(GetItemPath(index, result)); 751 if (Ask_Deleted) 752 { 753 bool isDeleted = false; 754 RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted)); 755 if (isDeleted) 756 result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); 757 } 758 return S_OK; 759 } 760 761 #ifdef SUPPORT_ALT_STREAMS 762 763 int FindAltStreamColon_in_Path(const wchar_t *path) 764 { 765 unsigned i = 0; 766 int colonPos = -1; 767 for (;; i++) 768 { 769 wchar_t c = path[i]; 770 if (c == 0) 771 return colonPos; 772 if (c == ':') 773 { 774 if (colonPos < 0) 775 colonPos = i; 776 continue; 777 } 778 if (c == WCHAR_PATH_SEPARATOR) 779 colonPos = -1; 780 } 781 } 782 783 #endif 784 785 HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const 786 { 787 #ifdef SUPPORT_ALT_STREAMS 788 item.IsAltStream = false; 789 item.AltStreamName.Empty(); 790 item.MainPath.Empty(); 791 #endif 792 793 item.IsDir = false; 794 item.Path.Empty(); 795 item.ParentIndex = (UInt32)(Int32)-1; 796 797 item.PathParts.Clear(); 798 799 RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir)); 800 item.MainIsDir = item.IsDir; 801 802 RINOK(GetItemPath2(index, item.Path)); 803 804 #ifndef _SFX 805 UInt32 mainIndex = index; 806 #endif 807 808 #ifdef SUPPORT_ALT_STREAMS 809 810 item.MainPath = item.Path; 811 if (Ask_AltStream) 812 { 813 RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream)); 814 } 815 816 bool needFindAltStream = false; 817 818 if (item.IsAltStream) 819 { 820 needFindAltStream = true; 821 if (GetRawProps) 822 { 823 UInt32 parentType = 0; 824 UInt32 parentIndex; 825 RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType)); 826 if (parentType == NParentType::kAltStream) 827 { 828 NCOM::CPropVariant prop; 829 RINOK(Archive->GetProperty(index, kpidName, &prop)); 830 if (prop.vt == VT_BSTR && prop.bstrVal) 831 item.AltStreamName.SetFromBstr(prop.bstrVal); 832 else if (prop.vt != VT_EMPTY) 833 return E_FAIL; 834 else 835 { 836 // item.IsAltStream = false; 837 } 838 /* 839 if (item.AltStreamName.IsEmpty()) 840 item.IsAltStream = false; 841 */ 842 843 needFindAltStream = false; 844 item.ParentIndex = parentIndex; 845 mainIndex = parentIndex; 846 847 if (parentIndex == (UInt32)(Int32)-1) 848 { 849 item.MainPath.Empty(); 850 item.MainIsDir = true; 851 } 852 else 853 { 854 RINOK(GetItemPath2(parentIndex, item.MainPath)); 855 RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir)); 856 } 857 } 858 } 859 } 860 861 if (item.WriteToAltStreamIfColon || needFindAltStream) 862 { 863 /* Good handler must support GetRawProps::GetParent for alt streams. 864 So the following code currently is not used */ 865 int colon = FindAltStreamColon_in_Path(item.Path); 866 if (colon >= 0) 867 { 868 item.MainPath.DeleteFrom(colon); 869 item.AltStreamName = item.Path.Ptr(colon + 1); 870 item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[(unsigned)colon - 1])); 871 item.IsAltStream = true; 872 } 873 } 874 875 #endif 876 877 #ifndef _SFX 878 if (item._use_baseParentFolder_mode) 879 { 880 RINOK(GetItemPathToParent(mainIndex, item._baseParentFolder, item.PathParts)); 881 882 #ifdef SUPPORT_ALT_STREAMS 883 if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty()) 884 { 885 int colon; 886 { 887 UString &s = item.PathParts.Back(); 888 colon = FindAltStreamColon_in_Path(s); 889 if (colon >= 0) 890 { 891 item.AltStreamName = s.Ptr(colon + 1); 892 item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1])); 893 item.IsAltStream = true; 894 s.DeleteFrom(colon); 895 } 896 } 897 if (colon == 0) 898 item.PathParts.DeleteBack(); 899 } 900 #endif 901 902 } 903 else 904 #endif 905 SplitPathToParts( 906 #ifdef SUPPORT_ALT_STREAMS 907 item.MainPath 908 #else 909 item.Path 910 #endif 911 , item.PathParts); 912 913 return S_OK; 914 } 915 916 #ifndef _SFX 917 918 static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined) 919 { 920 NCOM::CPropVariant prop; 921 defined = false; 922 size = 0; 923 RINOK(archive->GetProperty(index, kpidSize, &prop)); 924 switch (prop.vt) 925 { 926 case VT_UI1: size = prop.bVal; break; 927 case VT_UI2: size = prop.uiVal; break; 928 case VT_UI4: size = prop.ulVal; break; 929 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; 930 case VT_EMPTY: return S_OK; 931 default: return E_FAIL; 932 } 933 defined = true; 934 return S_OK; 935 } 936 937 #endif 938 939 HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const 940 { 941 NCOM::CPropVariant prop; 942 defined = false; 943 size = 0; 944 RINOK(Archive->GetProperty(index, kpidSize, &prop)); 945 switch (prop.vt) 946 { 947 case VT_UI1: size = prop.bVal; break; 948 case VT_UI2: size = prop.uiVal; break; 949 case VT_UI4: size = prop.ulVal; break; 950 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; 951 case VT_EMPTY: return S_OK; 952 default: return E_FAIL; 953 } 954 defined = true; 955 return S_OK; 956 } 957 958 HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const 959 { 960 NCOM::CPropVariant prop; 961 defined = false; 962 ft.dwHighDateTime = ft.dwLowDateTime = 0; 963 RINOK(Archive->GetProperty(index, kpidMTime, &prop)); 964 if (prop.vt == VT_FILETIME) 965 { 966 ft = prop.filetime; 967 defined = true; 968 } 969 else if (prop.vt != VT_EMPTY) 970 return E_FAIL; 971 else if (MTimeDefined) 972 { 973 ft = MTime; 974 defined = true; 975 } 976 return S_OK; 977 } 978 979 #ifndef _SFX 980 981 static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) 982 { 983 for (size_t i = 0; i < size; i++) 984 if (p1[i] != p2[i]) 985 return false; 986 return true; 987 } 988 989 static void MakeCheckOrder(CCodecs *codecs, 990 CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2, 991 const Byte *data, size_t dataSize) 992 { 993 for (unsigned i = 0; i < numTypes; i++) 994 { 995 int index = orderIndices[i]; 996 if (index < 0) 997 continue; 998 const CArcInfoEx &ai = codecs->Formats[(unsigned)index]; 999 if (ai.SignatureOffset != 0) 1000 { 1001 orderIndices2.Add(index); 1002 orderIndices[i] = -1; 1003 continue; 1004 } 1005 1006 const CObjectVector<CByteBuffer> &sigs = ai.Signatures; 1007 FOR_VECTOR (k, sigs) 1008 { 1009 const CByteBuffer &sig = sigs[k]; 1010 if (sig.Size() == 0 && dataSize == 0 || 1011 sig.Size() != 0 && sig.Size() <= dataSize && 1012 TestSignature(data, sig, sig.Size())) 1013 { 1014 orderIndices2.Add(index); 1015 orderIndices[i] = -1; 1016 break; 1017 } 1018 } 1019 } 1020 } 1021 1022 #endif 1023 1024 #ifdef UNDER_CE 1025 static const unsigned kNumHashBytes = 1; 1026 #define HASH_VAL(buf) ((buf)[0]) 1027 #else 1028 static const unsigned kNumHashBytes = 2; 1029 // #define HASH_VAL(buf) ((buf)[0] | ((UInt32)(buf)[1] << 8)) 1030 #define HASH_VAL(buf) GetUi16(buf) 1031 #endif 1032 1033 1034 #ifndef _SFX 1035 1036 static bool IsExeExt(const UString &ext) 1037 { 1038 return ext.IsEqualTo_Ascii_NoCase("exe"); 1039 } 1040 1041 static const char * const k_PreArcFormats[] = 1042 { 1043 "pe" 1044 , "elf" 1045 , "macho" 1046 , "mub" 1047 , "te" 1048 }; 1049 1050 static bool IsNameFromList(const UString &s, const char * const names[], size_t num) 1051 { 1052 for (unsigned i = 0; i < num; i++) 1053 if (StringsAreEqualNoCase_Ascii(s, names[i])) 1054 return true; 1055 return false; 1056 } 1057 1058 1059 static bool IsPreArcFormat(const CArcInfoEx &ai) 1060 { 1061 if (ai.Flags_PreArc()) 1062 return true; 1063 return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats)); 1064 } 1065 1066 static const char * const k_Formats_with_simple_signuature[] = 1067 { 1068 "7z" 1069 , "xz" 1070 , "rar" 1071 , "bzip2" 1072 , "gzip" 1073 , "cab" 1074 , "wim" 1075 , "rpm" 1076 , "vhd" 1077 , "xar" 1078 }; 1079 1080 static bool IsNewStyleSignature(const CArcInfoEx &ai) 1081 { 1082 // if (ai.Version >= 0x91F) 1083 if (ai.NewInterface) 1084 return true; 1085 return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature)); 1086 } 1087 1088 class CArchiveOpenCallback_Offset: 1089 public IArchiveOpenCallback, 1090 public IArchiveOpenVolumeCallback, 1091 #ifndef _NO_CRYPTO 1092 public ICryptoGetTextPassword, 1093 #endif 1094 public CMyUnknownImp 1095 { 1096 public: 1097 CMyComPtr<IArchiveOpenCallback> Callback; 1098 CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback; 1099 UInt64 Files; 1100 UInt64 Offset; 1101 1102 #ifndef _NO_CRYPTO 1103 CMyComPtr<ICryptoGetTextPassword> GetTextPassword; 1104 #endif 1105 1106 MY_QUERYINTERFACE_BEGIN2(IArchiveOpenCallback) 1107 MY_QUERYINTERFACE_ENTRY(IArchiveOpenVolumeCallback) 1108 #ifndef _NO_CRYPTO 1109 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword) 1110 #endif 1111 MY_QUERYINTERFACE_END 1112 MY_ADDREF_RELEASE 1113 1114 INTERFACE_IArchiveOpenCallback(;) 1115 INTERFACE_IArchiveOpenVolumeCallback(;) 1116 #ifndef _NO_CRYPTO 1117 STDMETHOD(CryptoGetTextPassword)(BSTR *password); 1118 #endif 1119 }; 1120 1121 #ifndef _NO_CRYPTO 1122 STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password) 1123 { 1124 COM_TRY_BEGIN 1125 if (GetTextPassword) 1126 return GetTextPassword->CryptoGetTextPassword(password); 1127 return E_NOTIMPL; 1128 COM_TRY_END 1129 } 1130 #endif 1131 1132 STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *) 1133 { 1134 return S_OK; 1135 } 1136 1137 STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes) 1138 { 1139 if (!Callback) 1140 return S_OK; 1141 UInt64 value = Offset; 1142 if (bytes) 1143 value += *bytes; 1144 return Callback->SetCompleted(&Files, &value); 1145 } 1146 1147 STDMETHODIMP CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value) 1148 { 1149 if (OpenVolumeCallback) 1150 return OpenVolumeCallback->GetProperty(propID, value); 1151 NCOM::PropVariant_Clear(value); 1152 return S_OK; 1153 // return E_NOTIMPL; 1154 } 1155 1156 STDMETHODIMP CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream) 1157 { 1158 if (OpenVolumeCallback) 1159 return OpenVolumeCallback->GetStream(name, inStream); 1160 return S_FALSE; 1161 } 1162 1163 #endif 1164 1165 1166 UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp) 1167 { 1168 if (isDefinedProp != NULL) 1169 *isDefinedProp = false; 1170 1171 switch (prop.vt) 1172 { 1173 case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart; 1174 case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal; 1175 case VT_EMPTY: return 0; 1176 default: throw 151199; 1177 } 1178 } 1179 1180 void CArcErrorInfo::ClearErrors() 1181 { 1182 // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!! 1183 1184 ThereIsTail = false; 1185 UnexpecedEnd = false; 1186 IgnoreTail = false; 1187 // NonZerosTail = false; 1188 ErrorFlags_Defined = false; 1189 ErrorFlags = 0; 1190 WarningFlags = 0; 1191 TailSize = 0; 1192 1193 ErrorMessage.Empty(); 1194 WarningMessage.Empty(); 1195 } 1196 1197 HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes) 1198 { 1199 // OkPhySize_Defined = false; 1200 PhySizeDefined = false; 1201 PhySize = 0; 1202 Offset = 0; 1203 AvailPhySize = FileSize - startPos; 1204 1205 ErrorInfo.ClearErrors(); 1206 { 1207 NCOM::CPropVariant prop; 1208 RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop)); 1209 ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined); 1210 } 1211 { 1212 NCOM::CPropVariant prop; 1213 RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop)); 1214 ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop); 1215 } 1216 1217 { 1218 NCOM::CPropVariant prop; 1219 RINOK(archive->GetArchiveProperty(kpidError, &prop)); 1220 if (prop.vt != VT_EMPTY) 1221 ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown error"); 1222 } 1223 1224 { 1225 NCOM::CPropVariant prop; 1226 RINOK(archive->GetArchiveProperty(kpidWarning, &prop)); 1227 if (prop.vt != VT_EMPTY) 1228 ErrorInfo.WarningMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown warning"); 1229 } 1230 1231 if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen()) 1232 { 1233 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined)); 1234 /* 1235 RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined)); 1236 if (!OkPhySize_Defined) 1237 { 1238 OkPhySize_Defined = PhySizeDefined; 1239 OkPhySize = PhySize; 1240 } 1241 */ 1242 1243 bool offsetDefined; 1244 RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined)); 1245 1246 Int64 globalOffset = startPos + Offset; 1247 AvailPhySize = FileSize - globalOffset; 1248 if (PhySizeDefined) 1249 { 1250 UInt64 endPos = globalOffset + PhySize; 1251 if (endPos < FileSize) 1252 { 1253 AvailPhySize = PhySize; 1254 ErrorInfo.ThereIsTail = true; 1255 ErrorInfo.TailSize = FileSize - endPos; 1256 } 1257 else if (endPos > FileSize) 1258 ErrorInfo.UnexpecedEnd = true; 1259 } 1260 } 1261 1262 return S_OK; 1263 } 1264 1265 /* 1266 static PrintNumber(const char *s, int n) 1267 { 1268 char temp[100]; 1269 sprintf(temp, "%s %d", s, n); 1270 OutputDebugStringA(temp); 1271 } 1272 */ 1273 1274 HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive) 1275 { 1276 // OutputDebugStringA("a1"); 1277 // PrintNumber("formatIndex", formatIndex); 1278 1279 RINOK(op.codecs->CreateInArchive(formatIndex, archive)); 1280 // OutputDebugStringA("a2"); 1281 if (!archive) 1282 return S_OK; 1283 1284 #ifdef EXTERNAL_CODECS 1285 if (op.codecs->NeedSetLibCodecs) 1286 { 1287 const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; 1288 if (ai.LibIndex >= 0 ? 1289 !op.codecs->Libs[ai.LibIndex].SetCodecs : 1290 !op.codecs->Libs.IsEmpty()) 1291 { 1292 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; 1293 archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); 1294 if (setCompressCodecsInfo) 1295 { 1296 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs)); 1297 } 1298 } 1299 } 1300 #endif 1301 1302 1303 #ifndef _SFX 1304 1305 const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; 1306 1307 // OutputDebugStringW(ai.Name); 1308 // OutputDebugStringA("a3"); 1309 1310 if (ai.Flags_PreArc()) 1311 { 1312 /* we notify parsers that extract executables, that they don't need 1313 to open archive, if there is tail after executable (for SFX cases) */ 1314 CMyComPtr<IArchiveAllowTail> allowTail; 1315 archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail); 1316 if (allowTail) 1317 allowTail->AllowTail(BoolToInt(true)); 1318 } 1319 1320 if (op.props) 1321 { 1322 /* 1323 FOR_VECTOR (y, op.props) 1324 { 1325 const COptionalOpenProperties &optProps = (*op.props)[y]; 1326 if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0) 1327 { 1328 RINOK(SetProperties(archive, optProps.Props)); 1329 break; 1330 } 1331 } 1332 */ 1333 RINOK(SetProperties(archive, *op.props)); 1334 } 1335 1336 #endif 1337 return S_OK; 1338 } 1339 1340 #ifndef _SFX 1341 1342 static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi) 1343 { 1344 pi.Extension = ai.GetMainExt(); 1345 pi.FileTime_Defined = false; 1346 pi.ArcType = ai.Name; 1347 1348 RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType)); 1349 1350 // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe)); 1351 pi.IsSelfExe = ai.Flags_PreArc(); 1352 1353 { 1354 NCOM::CPropVariant prop; 1355 RINOK(archive->GetArchiveProperty(kpidMTime, &prop)); 1356 if (prop.vt == VT_FILETIME) 1357 { 1358 pi.FileTime_Defined = true; 1359 pi.FileTime = prop.filetime; 1360 } 1361 } 1362 1363 if (!pi.FileTime_Defined) 1364 { 1365 NCOM::CPropVariant prop; 1366 RINOK(archive->GetArchiveProperty(kpidCTime, &prop)); 1367 if (prop.vt == VT_FILETIME) 1368 { 1369 pi.FileTime_Defined = true; 1370 pi.FileTime = prop.filetime; 1371 } 1372 } 1373 1374 { 1375 NCOM::CPropVariant prop; 1376 RINOK(archive->GetArchiveProperty(kpidName, &prop)); 1377 if (prop.vt == VT_BSTR) 1378 { 1379 pi.Name.SetFromBstr(prop.bstrVal); 1380 pi.Extension.Empty(); 1381 } 1382 else 1383 { 1384 RINOK(archive->GetArchiveProperty(kpidExtension, &prop)); 1385 if (prop.vt == VT_BSTR) 1386 pi.Extension.SetFromBstr(prop.bstrVal); 1387 } 1388 } 1389 1390 { 1391 NCOM::CPropVariant prop; 1392 RINOK(archive->GetArchiveProperty(kpidShortComment, &prop)); 1393 if (prop.vt == VT_BSTR) 1394 pi.Comment.SetFromBstr(prop.bstrVal); 1395 } 1396 1397 1398 UInt32 numItems; 1399 RINOK(archive->GetNumberOfItems(&numItems)); 1400 1401 // pi.NumSubFiles = numItems; 1402 // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined)); 1403 // if (!pi.UnpackSize_Defined) 1404 { 1405 pi.NumSubFiles = 0; 1406 pi.NumSubDirs = 0; 1407 pi.UnpackSize = 0; 1408 for (UInt32 i = 0; i < numItems; i++) 1409 { 1410 UInt64 size = 0; 1411 bool defined = false; 1412 Archive_GetItem_Size(archive, i, size, defined); 1413 if (defined) 1414 { 1415 pi.UnpackSize_Defined = true; 1416 pi.UnpackSize += size; 1417 } 1418 1419 bool isDir = false; 1420 Archive_IsItem_Dir(archive, i, isDir); 1421 if (isDir) 1422 pi.NumSubDirs++; 1423 else 1424 pi.NumSubFiles++; 1425 } 1426 if (pi.NumSubDirs != 0) 1427 pi.NumSubDirs_Defined = true; 1428 pi.NumSubFiles_Defined = true; 1429 } 1430 1431 return S_OK; 1432 } 1433 1434 #endif 1435 1436 HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset) 1437 { 1438 if (!op.stream) 1439 return S_OK; 1440 RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL)); 1441 const UInt32 kBufSize = 1 << 11; 1442 Byte buf[kBufSize]; 1443 1444 for (;;) 1445 { 1446 UInt32 processed = 0; 1447 RINOK(op.stream->Read(buf, kBufSize, &processed)); 1448 if (processed == 0) 1449 { 1450 // ErrorInfo.NonZerosTail = false; 1451 ErrorInfo.IgnoreTail = true; 1452 return S_OK; 1453 } 1454 for (size_t i = 0; i < processed; i++) 1455 { 1456 if (buf[i] != 0) 1457 { 1458 // ErrorInfo.IgnoreTail = false; 1459 // ErrorInfo.NonZerosTail = true; 1460 return S_OK; 1461 } 1462 } 1463 } 1464 } 1465 1466 #ifndef _SFX 1467 1468 class CExtractCallback_To_OpenCallback: 1469 public IArchiveExtractCallback, 1470 public ICompressProgressInfo, 1471 public CMyUnknownImp 1472 { 1473 public: 1474 CMyComPtr<IArchiveOpenCallback> Callback; 1475 UInt64 Files; 1476 UInt64 Offset; 1477 1478 MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo) 1479 INTERFACE_IArchiveExtractCallback(;) 1480 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); 1481 void Init(IArchiveOpenCallback *callback) 1482 { 1483 Callback = callback; 1484 Files = 0; 1485 Offset = 0; 1486 } 1487 }; 1488 1489 STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) 1490 { 1491 return S_OK; 1492 } 1493 1494 STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) 1495 { 1496 return S_OK; 1497 } 1498 1499 STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) 1500 { 1501 if (Callback) 1502 { 1503 UInt64 value = Offset; 1504 if (inSize) 1505 value += *inSize; 1506 return Callback->SetCompleted(&Files, &value); 1507 } 1508 return S_OK; 1509 } 1510 1511 STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) 1512 { 1513 *outStream = 0; 1514 return S_OK; 1515 } 1516 1517 STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) 1518 { 1519 return S_OK; 1520 } 1521 1522 STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) 1523 { 1524 return S_OK; 1525 } 1526 1527 static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize, 1528 IInStream *stream, const UInt64 *maxCheckStartPosition, 1529 IArchiveOpenCallback *openCallback, 1530 IArchiveExtractCallback *extractCallback) 1531 { 1532 /* 1533 if (needPhySize) 1534 { 1535 CMyComPtr<IArchiveOpen2> open2; 1536 archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2); 1537 if (open2) 1538 return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback); 1539 } 1540 */ 1541 RINOK(archive->Open(stream, maxCheckStartPosition, openCallback)); 1542 if (needPhySize) 1543 { 1544 bool phySize_Defined = false; 1545 UInt64 phySize = 0; 1546 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined)); 1547 if (phySize_Defined) 1548 return S_OK; 1549 1550 bool phySizeCantBeDetected = false;; 1551 RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected)); 1552 1553 if (!phySizeCantBeDetected) 1554 { 1555 RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback)); 1556 } 1557 } 1558 return S_OK; 1559 } 1560 1561 static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name) 1562 { 1563 FOR_VECTOR (i, orderIndices) 1564 if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name)) 1565 return i; 1566 return -1; 1567 } 1568 1569 #endif 1570 1571 HRESULT CArc::OpenStream2(const COpenOptions &op) 1572 { 1573 // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout); 1574 1575 Archive.Release(); 1576 GetRawProps.Release(); 1577 GetRootProps.Release(); 1578 1579 ErrorInfo.ClearErrors(); 1580 ErrorInfo.ErrorFormatIndex = -1; 1581 1582 IsParseArc = false; 1583 ArcStreamOffset = 0; 1584 1585 // OutputDebugStringA("1"); 1586 // OutputDebugStringW(Path); 1587 1588 const UString fileName = ExtractFileNameFromPath(Path); 1589 UString extension; 1590 { 1591 int dotPos = fileName.ReverseFind_Dot(); 1592 if (dotPos >= 0) 1593 extension = fileName.Ptr(dotPos + 1); 1594 } 1595 1596 CIntVector orderIndices; 1597 1598 bool searchMarkerInHandler = false; 1599 #ifdef _SFX 1600 searchMarkerInHandler = true; 1601 #endif 1602 1603 CBoolArr isMainFormatArr(op.codecs->Formats.Size()); 1604 { 1605 FOR_VECTOR(i, op.codecs->Formats) 1606 isMainFormatArr[i] = false; 1607 } 1608 1609 UInt64 maxStartOffset = 1610 op.openType.MaxStartOffset_Defined ? 1611 op.openType.MaxStartOffset : 1612 kMaxCheckStartPosition; 1613 1614 #ifndef _SFX 1615 bool isUnknownExt = false; 1616 #endif 1617 1618 bool isForced = false; 1619 unsigned numMainTypes = 0; 1620 int formatIndex = op.openType.FormatIndex; 1621 1622 if (formatIndex >= 0) 1623 { 1624 isForced = true; 1625 orderIndices.Add(formatIndex); 1626 numMainTypes = 1; 1627 isMainFormatArr[(unsigned)formatIndex] = true; 1628 1629 searchMarkerInHandler = true; 1630 } 1631 else 1632 { 1633 unsigned numFinded = 0; 1634 #ifndef _SFX 1635 bool isPrearcExt = false; 1636 #endif 1637 1638 { 1639 #ifndef _SFX 1640 1641 bool isZip = false; 1642 bool isRar = false; 1643 1644 const wchar_t c = extension[0]; 1645 if (c == 'z' || c == 'Z' || c == 'r' || c == 'R') 1646 { 1647 bool isNumber = false; 1648 for (unsigned k = 1;; k++) 1649 { 1650 const wchar_t d = extension[k]; 1651 if (d == 0) 1652 break; 1653 if (d < '0' || d > '9') 1654 { 1655 isNumber = false; 1656 break; 1657 } 1658 isNumber = true; 1659 } 1660 if (isNumber) 1661 if (c == 'z' || c == 'Z') 1662 isZip = true; 1663 else 1664 isRar = true; 1665 } 1666 1667 #endif 1668 1669 FOR_VECTOR (i, op.codecs->Formats) 1670 { 1671 const CArcInfoEx &ai = op.codecs->Formats[i]; 1672 1673 if (IgnoreSplit || !op.openType.CanReturnArc) 1674 if (ai.IsSplit()) 1675 continue; 1676 if (op.excludedFormats->FindInSorted(i) >= 0) 1677 continue; 1678 1679 #ifndef _SFX 1680 if (IsPreArcFormat(ai)) 1681 isPrearcExt = true; 1682 #endif 1683 1684 if (ai.FindExtension(extension) >= 0 1685 #ifndef _SFX 1686 || isZip && StringsAreEqualNoCase_Ascii(ai.Name, "zip") 1687 || isRar && StringsAreEqualNoCase_Ascii(ai.Name, "rar") 1688 #endif 1689 ) 1690 { 1691 // PrintNumber("orderIndices.Insert", i); 1692 orderIndices.Insert(numFinded++, i); 1693 isMainFormatArr[i] = true; 1694 } 1695 else 1696 orderIndices.Add(i); 1697 } 1698 } 1699 1700 if (!op.stream) 1701 { 1702 if (numFinded != 1) 1703 return E_NOTIMPL; 1704 orderIndices.DeleteFrom(1); 1705 } 1706 // PrintNumber("numFinded", numFinded ); 1707 1708 /* 1709 if (op.openOnlySpecifiedByExtension) 1710 { 1711 if (numFinded != 0 && !IsExeExt(extension)) 1712 orderIndices.DeleteFrom(numFinded); 1713 } 1714 */ 1715 1716 #ifndef _SFX 1717 1718 if (op.stream && orderIndices.Size() >= 2) 1719 { 1720 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 1721 CByteBuffer byteBuffer; 1722 CIntVector orderIndices2; 1723 if (numFinded == 0 || IsExeExt(extension)) 1724 { 1725 // signature search was here 1726 } 1727 else if (extension.IsEqualTo("000") || extension.IsEqualTo("001")) 1728 { 1729 int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar"); 1730 if (i >= 0) 1731 { 1732 const size_t kBufSize = (1 << 10); 1733 byteBuffer.Alloc(kBufSize); 1734 size_t processedSize = kBufSize; 1735 RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); 1736 if (processedSize >= 16) 1737 { 1738 const Byte *buf = byteBuffer; 1739 const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }; 1740 if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0) 1741 { 1742 orderIndices2.Add(orderIndices[i]); 1743 orderIndices[i] = -1; 1744 if (i >= (int)numFinded) 1745 numFinded++; 1746 } 1747 } 1748 } 1749 } 1750 else 1751 { 1752 const size_t kBufSize = (1 << 10); 1753 byteBuffer.Alloc(kBufSize); 1754 size_t processedSize = kBufSize; 1755 RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); 1756 if (processedSize == 0) 1757 return S_FALSE; 1758 1759 /* 1760 check type order: 1761 1) matched extension, no signuature 1762 2) matched extension, matched signuature 1763 // 3) no signuature 1764 // 4) matched signuature 1765 */ 1766 1767 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0); 1768 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize); 1769 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0); 1770 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize); 1771 } 1772 1773 FOR_VECTOR (i, orderIndices) 1774 { 1775 int val = orderIndices[i]; 1776 if (val != -1) 1777 orderIndices2.Add(val); 1778 } 1779 orderIndices = orderIndices2; 1780 } 1781 1782 if (orderIndices.Size() >= 2) 1783 { 1784 int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso"); 1785 int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf"); 1786 if (iUdf > iIso && iIso >= 0) 1787 { 1788 int isoIndex = orderIndices[iIso]; 1789 int udfIndex = orderIndices[iUdf]; 1790 orderIndices[iUdf] = isoIndex; 1791 orderIndices[iIso] = udfIndex; 1792 } 1793 } 1794 1795 numMainTypes = numFinded; 1796 isUnknownExt = (numMainTypes == 0) || isPrearcExt; 1797 1798 #else // _SFX 1799 1800 numMainTypes = orderIndices.Size(); 1801 1802 // we need correct numMainTypes for mutlivolume SFX (if some volume is missing) 1803 if (numFinded != 0) 1804 numMainTypes = numFinded; 1805 1806 #endif 1807 } 1808 1809 UInt64 fileSize = 0; 1810 if (op.stream) 1811 { 1812 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); 1813 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 1814 } 1815 FileSize = fileSize; 1816 1817 1818 #ifndef _SFX 1819 1820 CBoolArr skipFrontalFormat(op.codecs->Formats.Size()); 1821 { 1822 FOR_VECTOR(i, op.codecs->Formats) 1823 skipFrontalFormat[i] = false; 1824 } 1825 1826 #endif 1827 1828 const COpenType &mode = op.openType; 1829 1830 1831 1832 1833 1834 if (mode.CanReturnArc) 1835 { 1836 // ---------- OPEN main type by extenssion ---------- 1837 1838 unsigned numCheckTypes = orderIndices.Size(); 1839 if (formatIndex >= 0) 1840 numCheckTypes = numMainTypes; 1841 1842 for (unsigned i = 0; i < numCheckTypes; i++) 1843 { 1844 FormatIndex = orderIndices[i]; 1845 1846 bool exactOnly = false; 1847 1848 #ifndef _SFX 1849 1850 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; 1851 // OutputDebugStringW(ai.Name); 1852 if (i >= numMainTypes) 1853 { 1854 if (!ai.Flags_BackwardOpen() 1855 // && !ai.Flags_PureStartOpen() 1856 ) 1857 continue; 1858 exactOnly = true; 1859 } 1860 1861 #endif 1862 1863 // Some handlers do not set total bytes. So we set it here 1864 if (op.callback) 1865 RINOK(op.callback->SetTotal(NULL, &fileSize)); 1866 1867 if (op.stream) 1868 { 1869 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 1870 } 1871 1872 CMyComPtr<IInArchive> archive; 1873 1874 RINOK(PrepareToOpen(op, FormatIndex, archive)); 1875 if (!archive) 1876 continue; 1877 1878 HRESULT result; 1879 if (op.stream) 1880 { 1881 UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0; 1882 result = archive->Open(op.stream, &searchLimit, op.callback); 1883 } 1884 else 1885 { 1886 CMyComPtr<IArchiveOpenSeq> openSeq; 1887 archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); 1888 if (!openSeq) 1889 return E_NOTIMPL; 1890 result = openSeq->OpenSeq(op.seqStream); 1891 } 1892 1893 RINOK(ReadBasicProps(archive, 0, result)); 1894 1895 if (result == S_FALSE) 1896 { 1897 bool isArc = ErrorInfo.IsArc_After_NonOpen(); 1898 1899 #ifndef _SFX 1900 // if it's archive, we allow another open attempt for parser 1901 if (!mode.CanReturnParser || !isArc) 1902 skipFrontalFormat[(unsigned)FormatIndex] = true; 1903 #endif 1904 1905 if (exactOnly) 1906 continue; 1907 1908 if (i == 0 && numMainTypes == 1) 1909 { 1910 // we set NonOpenErrorInfo, only if there is only one main format (defined by extension). 1911 ErrorInfo.ErrorFormatIndex = FormatIndex; 1912 NonOpen_ErrorInfo = ErrorInfo; 1913 1914 if (!mode.CanReturnParser && isArc) 1915 { 1916 // if (formatIndex < 0 && !searchMarkerInHandler) 1917 { 1918 // if bad archive was detected, we don't need additional open attempts 1919 #ifndef _SFX 1920 if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */) 1921 #endif 1922 return S_FALSE; 1923 } 1924 } 1925 } 1926 1927 /* 1928 #ifndef _SFX 1929 if (IsExeExt(extension) || ai.Flags_PreArc()) 1930 { 1931 // openOnlyFullArc = false; 1932 // canReturnTailArc = true; 1933 // limitSignatureSearch = true; 1934 } 1935 #endif 1936 */ 1937 1938 continue; 1939 } 1940 1941 RINOK(result); 1942 1943 #ifndef _SFX 1944 1945 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex]; 1946 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); 1947 1948 bool thereIsTail = ErrorInfo.ThereIsTail; 1949 if (thereIsTail && mode.ZerosTailIsAllowed) 1950 { 1951 RINOK(CheckZerosTail(op, Offset + PhySize)); 1952 if (ErrorInfo.IgnoreTail) 1953 thereIsTail = false; 1954 } 1955 1956 if (Offset > 0) 1957 { 1958 if (exactOnly 1959 || !searchMarkerInHandler 1960 || !specFlags.CanReturn_NonStart() 1961 || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset)) 1962 continue; 1963 } 1964 if (thereIsTail) 1965 { 1966 if (Offset > 0) 1967 { 1968 if (!specFlags.CanReturnMid) 1969 continue; 1970 } 1971 else if (!specFlags.CanReturnFrontal) 1972 continue; 1973 } 1974 1975 if (Offset > 0 || thereIsTail) 1976 { 1977 if (formatIndex < 0) 1978 { 1979 if (IsPreArcFormat(ai)) 1980 { 1981 // openOnlyFullArc = false; 1982 // canReturnTailArc = true; 1983 /* 1984 if (mode.SkipSfxStub) 1985 limitSignatureSearch = true; 1986 */ 1987 // if (mode.SkipSfxStub) 1988 { 1989 // skipFrontalFormat[FormatIndex] = true; 1990 continue; 1991 } 1992 } 1993 } 1994 } 1995 1996 #endif 1997 1998 Archive = archive; 1999 return S_OK; 2000 } 2001 } 2002 2003 2004 2005 #ifndef _SFX 2006 2007 if (!op.stream) 2008 return S_FALSE; 2009 2010 if (formatIndex >= 0 && !mode.CanReturnParser) 2011 { 2012 if (mode.MaxStartOffset_Defined) 2013 { 2014 if (mode.MaxStartOffset == 0) 2015 return S_FALSE; 2016 } 2017 else 2018 { 2019 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)formatIndex]; 2020 if (ai.FindExtension(extension) >= 0) 2021 { 2022 if (ai.Flags_FindSignature() && searchMarkerInHandler) 2023 return S_FALSE; 2024 } 2025 } 2026 } 2027 2028 NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler; 2029 CMyComPtr<IInArchive> handler = handlerSpec; 2030 2031 CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback; 2032 CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec; 2033 extractCallback_To_OpenCallback_Spec->Init(op.callback); 2034 2035 { 2036 // ---------- Check all possible START archives ---------- 2037 // this code is better for full file archives than Parser's code. 2038 2039 CByteBuffer byteBuffer; 2040 bool endOfFile = false; 2041 size_t processedSize; 2042 { 2043 size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF) 2044 if (bufSize > fileSize) 2045 { 2046 bufSize = (size_t)fileSize; 2047 endOfFile = true; 2048 } 2049 byteBuffer.Alloc(bufSize); 2050 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 2051 processedSize = bufSize; 2052 RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); 2053 if (processedSize == 0) 2054 return S_FALSE; 2055 if (processedSize < bufSize) 2056 endOfFile = true; 2057 } 2058 CUIntVector sortedFormats; 2059 2060 unsigned i; 2061 2062 int splitIndex = -1; 2063 2064 for (i = 0; i < orderIndices.Size(); i++) 2065 { 2066 unsigned form = orderIndices[i]; 2067 if (skipFrontalFormat[form]) 2068 continue; 2069 const CArcInfoEx &ai = op.codecs->Formats[form]; 2070 if (ai.IsSplit()) 2071 { 2072 splitIndex = form; 2073 continue; 2074 } 2075 2076 if (ai.IsArcFunc) 2077 { 2078 UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize); 2079 if (isArcRes == k_IsArc_Res_NO) 2080 continue; 2081 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) 2082 continue; 2083 // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue; 2084 sortedFormats.Insert(0, form); 2085 continue; 2086 } 2087 2088 bool isNewStyleSignature = IsNewStyleSignature(ai); 2089 bool needCheck = !isNewStyleSignature 2090 || ai.Signatures.IsEmpty() 2091 || ai.Flags_PureStartOpen() 2092 || ai.Flags_StartOpen() 2093 || ai.Flags_BackwardOpen(); 2094 2095 if (isNewStyleSignature && !ai.Signatures.IsEmpty()) 2096 { 2097 unsigned k; 2098 for (k = 0; k < ai.Signatures.Size(); k++) 2099 { 2100 const CByteBuffer &sig = ai.Signatures[k]; 2101 UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); 2102 if (processedSize < signatureEnd) 2103 { 2104 if (!endOfFile) 2105 needCheck = true; 2106 } 2107 else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0) 2108 break; 2109 } 2110 if (k != ai.Signatures.Size()) 2111 { 2112 sortedFormats.Insert(0, form); 2113 continue; 2114 } 2115 } 2116 if (needCheck) 2117 sortedFormats.Add(form); 2118 } 2119 2120 if (splitIndex >= 0) 2121 sortedFormats.Insert(0, splitIndex); 2122 2123 for (i = 0; i < sortedFormats.Size(); i++) 2124 { 2125 FormatIndex = sortedFormats[i]; 2126 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; 2127 2128 if (op.callback) 2129 RINOK(op.callback->SetTotal(NULL, &fileSize)); 2130 2131 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 2132 2133 CMyComPtr<IInArchive> archive; 2134 RINOK(PrepareToOpen(op, FormatIndex, archive)); 2135 if (!archive) 2136 continue; 2137 2138 PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name)); 2139 HRESULT result; 2140 { 2141 UInt64 searchLimit = 0; 2142 /* 2143 if (mode.CanReturnArc) 2144 result = archive->Open(op.stream, &searchLimit, op.callback); 2145 else 2146 */ 2147 result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback); 2148 } 2149 2150 if (result == S_FALSE) 2151 { 2152 skipFrontalFormat[(unsigned)FormatIndex] = true; 2153 // FIXME: maybe we must use LenIsUnknown. 2154 // printf(" OpenForSize Error"); 2155 continue; 2156 } 2157 RINOK(result); 2158 2159 RINOK(ReadBasicProps(archive, 0, result)); 2160 2161 if (Offset > 0) 2162 { 2163 continue; // good handler doesn't return such Offset > 0 2164 // but there are some cases like false prefixed PK00 archive, when 2165 // we can support it? 2166 } 2167 2168 NArchive::NParser::CParseItem pi; 2169 pi.Offset = Offset; 2170 pi.Size = AvailPhySize; 2171 2172 // bool needScan = false; 2173 2174 if (!PhySizeDefined) 2175 { 2176 // it's for Z format 2177 pi.LenIsUnknown = true; 2178 // needScan = true; 2179 // phySize = arcRem; 2180 // nextNeedCheckStartOpen = false; 2181 } 2182 2183 /* 2184 if (OkPhySize_Defined) 2185 pi.OkSize = pi.OkPhySize; 2186 else 2187 pi.OkSize = pi.Size; 2188 */ 2189 2190 pi.NormalizeOffset(); 2191 // printf(" phySize = %8d", (unsigned)phySize); 2192 2193 2194 if (mode.CanReturnArc) 2195 { 2196 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex]; 2197 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); 2198 bool openCur = false; 2199 2200 if (!ErrorInfo.ThereIsTail) 2201 openCur = true; 2202 else 2203 { 2204 if (mode.ZerosTailIsAllowed) 2205 { 2206 RINOK(CheckZerosTail(op, Offset + PhySize)); 2207 if (ErrorInfo.IgnoreTail) 2208 openCur = true; 2209 } 2210 if (!openCur) 2211 { 2212 openCur = specFlags.CanReturnFrontal; 2213 if (formatIndex < 0) // format is not forced 2214 { 2215 if (IsPreArcFormat(ai)) 2216 { 2217 // if (mode.SkipSfxStub) 2218 { 2219 openCur = false; 2220 } 2221 } 2222 } 2223 } 2224 } 2225 2226 if (openCur) 2227 { 2228 InStream = op.stream; 2229 Archive = archive; 2230 return S_OK; 2231 } 2232 } 2233 2234 skipFrontalFormat[(unsigned)FormatIndex] = true; 2235 2236 2237 // if (!mode.CanReturnArc) 2238 /* 2239 if (!ErrorInfo.ThereIsTail) 2240 continue; 2241 */ 2242 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) 2243 continue; 2244 2245 // printf("\nAdd offset = %d", (int)pi.Offset); 2246 RINOK(ReadParseItemProps(archive, ai, pi)); 2247 handlerSpec->AddItem(pi); 2248 } 2249 } 2250 2251 2252 2253 2254 2255 // ---------- PARSER ---------- 2256 2257 CUIntVector arc2sig; // formatIndex to signatureIndex 2258 CUIntVector sig2arc; // signatureIndex to formatIndex; 2259 { 2260 unsigned sum = 0; 2261 FOR_VECTOR (i, op.codecs->Formats) 2262 { 2263 arc2sig.Add(sum); 2264 const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures; 2265 sum += sigs.Size(); 2266 FOR_VECTOR (k, sigs) 2267 sig2arc.Add(i); 2268 } 2269 } 2270 2271 { 2272 const size_t kBeforeSize = 1 << 16; 2273 const size_t kAfterSize = 1 << 20; 2274 const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize 2275 2276 const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8); 2277 CByteArr hashBuffer(kNumVals); 2278 Byte *hash = hashBuffer; 2279 memset(hash, 0xFF, kNumVals); 2280 Byte prevs[256]; 2281 memset(prevs, 0xFF, sizeof(prevs)); 2282 if (sig2arc.Size() >= 0xFF) 2283 return S_FALSE; 2284 2285 CUIntVector difficultFormats; 2286 CBoolArr difficultBools(256); 2287 { 2288 for (unsigned i = 0; i < 256; i++) 2289 difficultBools[i] = false; 2290 } 2291 2292 bool thereAreHandlersForSearch = false; 2293 2294 // UInt32 maxSignatureEnd = 0; 2295 2296 FOR_VECTOR (i, orderIndices) 2297 { 2298 int index = orderIndices[i]; 2299 if (index < 0) 2300 continue; 2301 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)index]; 2302 bool isDifficult = false; 2303 // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31) 2304 if (!ai.NewInterface) 2305 isDifficult = true; 2306 else 2307 { 2308 if (ai.Flags_StartOpen()) 2309 isDifficult = true; 2310 FOR_VECTOR (k, ai.Signatures) 2311 { 2312 const CByteBuffer &sig = ai.Signatures[k]; 2313 /* 2314 UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); 2315 if (maxSignatureEnd < signatureEnd) 2316 maxSignatureEnd = signatureEnd; 2317 */ 2318 if (sig.Size() < kNumHashBytes) 2319 { 2320 isDifficult = true; 2321 continue; 2322 } 2323 thereAreHandlersForSearch = true; 2324 UInt32 v = HASH_VAL(sig); 2325 unsigned sigIndex = arc2sig[(unsigned)index] + k; 2326 prevs[sigIndex] = hash[v]; 2327 hash[v] = (Byte)sigIndex; 2328 } 2329 } 2330 if (isDifficult) 2331 { 2332 difficultFormats.Add(index); 2333 difficultBools[(unsigned)index] = true; 2334 } 2335 } 2336 2337 if (!thereAreHandlersForSearch) 2338 { 2339 // openOnlyFullArc = true; 2340 // canReturnTailArc = true; 2341 } 2342 2343 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 2344 2345 CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream; 2346 CMyComPtr<IInStream> limitedStream = limitedStreamSpec; 2347 limitedStreamSpec->SetStream(op.stream); 2348 2349 CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL; 2350 CMyComPtr<IArchiveOpenCallback> openCallback_Offset; 2351 if (op.callback) 2352 { 2353 openCallback_Offset_Spec = new CArchiveOpenCallback_Offset; 2354 openCallback_Offset = openCallback_Offset_Spec; 2355 openCallback_Offset_Spec->Callback = op.callback; 2356 openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback); 2357 #ifndef _NO_CRYPTO 2358 openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword); 2359 #endif 2360 } 2361 2362 if (op.callback) 2363 RINOK(op.callback->SetTotal(NULL, &fileSize)); 2364 2365 CByteBuffer &byteBuffer = limitedStreamSpec->Buffer; 2366 byteBuffer.Alloc(kBufSize); 2367 2368 UInt64 callbackPrev = 0; 2369 bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos. 2370 2371 bool endOfFile = false; 2372 UInt64 bufPhyPos = 0; 2373 size_t bytesInBuf = 0; 2374 // UInt64 prevPos = 0; 2375 2376 // ---------- Main Scan Loop ---------- 2377 2378 UInt64 pos = 0; 2379 2380 if (!mode.EachPos && handlerSpec->_items.Size() == 1) 2381 { 2382 NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; 2383 if (!pi.LenIsUnknown && pi.Offset == 0) 2384 pos = pi.Size; 2385 } 2386 2387 for (;;) 2388 { 2389 // printf("\nPos = %d", (int)pos); 2390 UInt64 posInBuf = pos - bufPhyPos; 2391 2392 // if (pos > ((UInt64)1 << 35)) break; 2393 2394 if (!endOfFile) 2395 { 2396 if (bytesInBuf < kBufSize) 2397 { 2398 size_t processedSize = kBufSize - bytesInBuf; 2399 // printf("\nRead ask = %d", (unsigned)processedSize); 2400 UInt64 seekPos = bufPhyPos + bytesInBuf; 2401 RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL)); 2402 RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize)); 2403 // printf(" processed = %d", (unsigned)processedSize); 2404 if (processedSize == 0) 2405 { 2406 fileSize = seekPos; 2407 endOfFile = true; 2408 } 2409 else 2410 { 2411 bytesInBuf += processedSize; 2412 limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos); 2413 } 2414 continue; 2415 } 2416 2417 if (bytesInBuf < posInBuf) 2418 { 2419 UInt64 skipSize = posInBuf - bytesInBuf; 2420 if (skipSize <= kBeforeSize) 2421 { 2422 size_t keepSize = (size_t)(kBeforeSize - skipSize); 2423 // printf("\nmemmove skip = %d", (int)keepSize); 2424 memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize); 2425 bytesInBuf = keepSize; 2426 bufPhyPos = pos - keepSize; 2427 continue; 2428 } 2429 // printf("\nSkip %d", (int)(skipSize - kBeforeSize)); 2430 // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL)); 2431 bytesInBuf = 0; 2432 bufPhyPos = pos - kBeforeSize; 2433 continue; 2434 } 2435 2436 if (bytesInBuf - posInBuf < kAfterSize) 2437 { 2438 size_t beg = (size_t)posInBuf - kBeforeSize; 2439 // printf("\nmemmove for after beg = %d", (int)beg); 2440 memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg); 2441 bufPhyPos += beg; 2442 bytesInBuf -= beg; 2443 continue; 2444 } 2445 } 2446 2447 if (bytesInBuf <= (size_t)posInBuf) 2448 break; 2449 2450 bool useOffsetCallback = false; 2451 if (openCallback_Offset) 2452 { 2453 openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); 2454 openCallback_Offset_Spec->Offset = pos; 2455 2456 useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1); 2457 2458 if (pos >= callbackPrev + (1 << 23)) 2459 { 2460 RINOK(openCallback_Offset_Spec->SetCompleted(NULL, NULL)); 2461 callbackPrev = pos; 2462 } 2463 } 2464 2465 { 2466 UInt64 endPos = bufPhyPos + bytesInBuf; 2467 if (fileSize < endPos) 2468 { 2469 FileSize = fileSize; // why ???? 2470 fileSize = endPos; 2471 } 2472 } 2473 2474 size_t availSize = bytesInBuf - (size_t)posInBuf; 2475 if (availSize < kNumHashBytes) 2476 break; 2477 size_t scanSize = availSize - 2478 ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes); 2479 2480 { 2481 /* 2482 UInt64 scanLimit = openOnlyFullArc ? 2483 maxSignatureEnd : 2484 op.openType.ScanSize + maxSignatureEnd; 2485 */ 2486 if (!mode.CanReturnParser) 2487 { 2488 if (pos > maxStartOffset) 2489 break; 2490 UInt64 remScan = maxStartOffset - pos; 2491 if (scanSize > remScan) 2492 scanSize = (size_t)remScan; 2493 } 2494 } 2495 2496 scanSize++; 2497 2498 const Byte *buf = byteBuffer + (size_t)posInBuf; 2499 const Byte *bufLimit = buf + scanSize; 2500 size_t ppp = 0; 2501 2502 if (!needCheckStartOpen) 2503 { 2504 for (; buf < bufLimit && hash[HASH_VAL(buf)] == 0xFF; buf++); 2505 ppp = buf - (byteBuffer + (size_t)posInBuf); 2506 pos += ppp; 2507 if (buf == bufLimit) 2508 continue; 2509 } 2510 2511 UInt32 v = HASH_VAL(buf); 2512 bool nextNeedCheckStartOpen = true; 2513 unsigned i = hash[v]; 2514 unsigned indexOfDifficult = 0; 2515 2516 // ---------- Open Loop for Current Pos ---------- 2517 bool wasOpen = false; 2518 2519 for (;;) 2520 { 2521 unsigned index; 2522 bool isDifficult; 2523 if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size()) 2524 { 2525 index = difficultFormats[indexOfDifficult++]; 2526 isDifficult = true; 2527 } 2528 else 2529 { 2530 if (i == 0xFF) 2531 break; 2532 index = sig2arc[i]; 2533 unsigned sigIndex = i - arc2sig[index]; 2534 i = prevs[i]; 2535 if (needCheckStartOpen && difficultBools[index]) 2536 continue; 2537 const CArcInfoEx &ai = op.codecs->Formats[index]; 2538 2539 if (pos < ai.SignatureOffset) 2540 continue; 2541 2542 /* 2543 if (openOnlyFullArc) 2544 if (pos != ai.SignatureOffset) 2545 continue; 2546 */ 2547 2548 const CByteBuffer &sig = ai.Signatures[sigIndex]; 2549 2550 if (ppp + sig.Size() > availSize 2551 || !TestSignature(buf, sig, sig.Size())) 2552 continue; 2553 // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos)); 2554 // prevPos = pos; 2555 isDifficult = false; 2556 } 2557 2558 const CArcInfoEx &ai = op.codecs->Formats[index]; 2559 2560 2561 if ((isDifficult && pos == 0) || ai.SignatureOffset == pos) 2562 { 2563 // we don't check same archive second time */ 2564 if (skipFrontalFormat[index]) 2565 continue; 2566 } 2567 2568 UInt64 startArcPos = pos; 2569 if (!isDifficult) 2570 { 2571 if (pos < ai.SignatureOffset) 2572 continue; 2573 startArcPos = pos - ai.SignatureOffset; 2574 /* 2575 // we don't need the check for Z files 2576 if (startArcPos < handlerSpec->GetLastEnd()) 2577 continue; 2578 */ 2579 } 2580 2581 if (ai.IsArcFunc && startArcPos >= bufPhyPos) 2582 { 2583 size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos); 2584 if (offsetInBuf < bytesInBuf) 2585 { 2586 UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf); 2587 if (isArcRes == k_IsArc_Res_NO) 2588 continue; 2589 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) 2590 continue; 2591 /* 2592 if (isArcRes == k_IsArc_Res_YES_LOW_PROB) 2593 { 2594 // if (pos != ai.SignatureOffset) 2595 continue; 2596 } 2597 */ 2598 } 2599 // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name); 2600 } 2601 2602 /* 2603 if (pos == 67109888) 2604 pos = pos; 2605 */ 2606 PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name)); 2607 2608 bool isMainFormat = isMainFormatArr[index]; 2609 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); 2610 2611 CMyComPtr<IInArchive> archive; 2612 RINOK(PrepareToOpen(op, index, archive)); 2613 if (!archive) 2614 return E_FAIL; 2615 2616 // OutputDebugStringW(ai.Name); 2617 2618 UInt64 rem = fileSize - startArcPos; 2619 2620 UInt64 arcStreamOffset = 0; 2621 2622 if (ai.Flags_UseGlobalOffset()) 2623 { 2624 limitedStreamSpec->InitAndSeek(0, fileSize); 2625 limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL); 2626 } 2627 else 2628 { 2629 limitedStreamSpec->InitAndSeek(startArcPos, rem); 2630 arcStreamOffset = startArcPos; 2631 } 2632 2633 UInt64 maxCheckStartPosition = 0; 2634 2635 if (openCallback_Offset) 2636 { 2637 openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); 2638 openCallback_Offset_Spec->Offset = startArcPos; 2639 } 2640 2641 // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset); 2642 extractCallback_To_OpenCallback_Spec->Files = 0; 2643 extractCallback_To_OpenCallback_Spec->Offset = startArcPos; 2644 2645 HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition, 2646 useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback, 2647 extractCallback_To_OpenCallback); 2648 2649 RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result)); 2650 2651 bool isOpen = false; 2652 if (result == S_FALSE) 2653 { 2654 if (!mode.CanReturnParser) 2655 { 2656 if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen()) 2657 { 2658 ErrorInfo.ErrorFormatIndex = index; 2659 NonOpen_ErrorInfo = ErrorInfo; 2660 // if archive was detected, we don't need additional open attempts 2661 return S_FALSE; 2662 } 2663 continue; 2664 } 2665 if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0) 2666 continue; 2667 } 2668 else 2669 { 2670 isOpen = true; 2671 RINOK(result); 2672 PRF(printf(" OK ")); 2673 } 2674 2675 // fprintf(stderr, "\n %8X %S", startArcPos, Path); 2676 // printf("\nOpen OK: %S", ai.Name); 2677 2678 2679 NArchive::NParser::CParseItem pi; 2680 pi.Offset = startArcPos; 2681 2682 if (ai.Flags_UseGlobalOffset()) 2683 pi.Offset = Offset; 2684 else if (Offset != 0) 2685 return E_FAIL; 2686 UInt64 arcRem = FileSize - pi.Offset; 2687 UInt64 phySize = arcRem; 2688 bool phySizeDefined = PhySizeDefined; 2689 if (phySizeDefined) 2690 { 2691 if (pi.Offset + PhySize > FileSize) 2692 { 2693 // ErrorInfo.ThereIsTail = true; 2694 PhySize = FileSize - pi.Offset; 2695 } 2696 phySize = PhySize; 2697 } 2698 if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63)) 2699 return E_FAIL; 2700 2701 /* 2702 if (!ai.UseGlobalOffset) 2703 { 2704 if (phySize > arcRem) 2705 { 2706 ThereIsTail = true; 2707 phySize = arcRem; 2708 } 2709 } 2710 */ 2711 2712 bool needScan = false; 2713 2714 2715 if (isOpen && !phySizeDefined) 2716 { 2717 // it's for Z format 2718 pi.LenIsUnknown = true; 2719 needScan = true; 2720 phySize = arcRem; 2721 nextNeedCheckStartOpen = false; 2722 } 2723 2724 pi.Size = phySize; 2725 /* 2726 if (OkPhySize_Defined) 2727 pi.OkSize = OkPhySize; 2728 */ 2729 pi.NormalizeOffset(); 2730 // printf(" phySize = %8d", (unsigned)phySize); 2731 2732 /* 2733 if (needSkipFullArc) 2734 if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize) 2735 continue; 2736 */ 2737 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) 2738 { 2739 // it's possible for dmg archives 2740 if (!mode.CanReturnArc) 2741 continue; 2742 } 2743 2744 if (mode.EachPos) 2745 pos++; 2746 else if (needScan) 2747 { 2748 pos++; 2749 /* 2750 if (!OkPhySize_Defined) 2751 pos++; 2752 else 2753 pos = pi.Offset + pi.OkSize; 2754 */ 2755 } 2756 else 2757 pos = pi.Offset + pi.Size; 2758 2759 2760 RINOK(ReadParseItemProps(archive, ai, pi)); 2761 2762 if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */) 2763 { 2764 /* It's for DMG format. 2765 This code deletes all previous items that are included to current item */ 2766 2767 while (!handlerSpec->_items.IsEmpty()) 2768 { 2769 { 2770 const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back(); 2771 if (back.Offset < pi.Offset) 2772 break; 2773 if (back.Offset + back.Size > pi.Offset + pi.Size) 2774 break; 2775 } 2776 handlerSpec->_items.DeleteBack(); 2777 } 2778 } 2779 2780 2781 if (isOpen && mode.CanReturnArc && phySizeDefined) 2782 { 2783 // if (pi.Offset + pi.Size >= fileSize) 2784 bool openCur = false; 2785 2786 bool thereIsTail = ErrorInfo.ThereIsTail; 2787 if (thereIsTail && mode.ZerosTailIsAllowed) 2788 { 2789 RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize)); 2790 if (ErrorInfo.IgnoreTail) 2791 thereIsTail = false; 2792 } 2793 2794 if (pi.Offset != 0) 2795 { 2796 if (!pi.IsNotArcType) 2797 if (thereIsTail) 2798 openCur = specFlags.CanReturnMid; 2799 else 2800 openCur = specFlags.CanReturnTail; 2801 } 2802 else 2803 { 2804 if (!thereIsTail) 2805 openCur = true; 2806 else 2807 openCur = specFlags.CanReturnFrontal; 2808 2809 2810 if (formatIndex >= -2) 2811 openCur = true; 2812 } 2813 if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */) 2814 openCur = false; 2815 2816 // We open file as SFX, if there is front archive or first archive is "Self Executable" 2817 if (!openCur && !pi.IsSelfExe && !thereIsTail && 2818 (!pi.IsNotArcType || pi.Offset == 0)) 2819 { 2820 if (handlerSpec->_items.IsEmpty()) 2821 { 2822 if (specFlags.CanReturnTail) 2823 openCur = true; 2824 } 2825 else if (handlerSpec->_items.Size() == 1) 2826 { 2827 if (handlerSpec->_items[0].IsSelfExe) 2828 { 2829 if (mode.SpecUnknownExt.CanReturnTail) 2830 openCur = true; 2831 } 2832 } 2833 } 2834 2835 if (openCur) 2836 { 2837 InStream = op.stream; 2838 Archive = archive; 2839 FormatIndex = index; 2840 ArcStreamOffset = arcStreamOffset; 2841 return S_OK; 2842 } 2843 } 2844 2845 /* 2846 if (openOnlyFullArc) 2847 { 2848 ErrorInfo.ClearErrors(); 2849 return S_FALSE; 2850 } 2851 */ 2852 2853 pi.FormatIndex = index; 2854 2855 // printf("\nAdd offset = %d", (int)pi.Offset); 2856 handlerSpec->AddItem(pi); 2857 wasOpen = true; 2858 break; 2859 } 2860 // ---------- End of Open Loop for Current Pos ---------- 2861 2862 if (!wasOpen) 2863 pos++; 2864 needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen); 2865 } 2866 // ---------- End of Main Scan Loop ---------- 2867 2868 /* 2869 if (handlerSpec->_items.Size() == 1) 2870 { 2871 const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; 2872 if (pi.Size == fileSize && pi.Offset == 0) 2873 { 2874 Archive = archive; 2875 FormatIndex2 = pi.FormatIndex; 2876 return S_OK; 2877 } 2878 } 2879 */ 2880 2881 if (mode.CanReturnParser) 2882 { 2883 bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing 2884 handlerSpec->AddUnknownItem(fileSize); 2885 if (handlerSpec->_items.Size() == 0) 2886 return S_FALSE; 2887 if (returnParser || handlerSpec->_items.Size() != 1) 2888 { 2889 // return S_FALSE; 2890 handlerSpec->_stream = op.stream; 2891 Archive = handler; 2892 ErrorInfo.ClearErrors(); 2893 IsParseArc = true; 2894 FormatIndex = -1; // It's parser 2895 Offset = 0; 2896 return S_OK; 2897 } 2898 } 2899 } 2900 2901 #endif 2902 2903 if (!Archive) 2904 return S_FALSE; 2905 return S_OK; 2906 } 2907 2908 HRESULT CArc::OpenStream(const COpenOptions &op) 2909 { 2910 RINOK(OpenStream2(op)); 2911 // PrintNumber("op.formatIndex 3", op.formatIndex); 2912 2913 if (Archive) 2914 { 2915 GetRawProps.Release(); 2916 GetRootProps.Release(); 2917 Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps); 2918 Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps); 2919 2920 RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree)); 2921 RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted)); 2922 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream)); 2923 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux)); 2924 RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode)); 2925 RINOK(Archive_GetArcBoolProp(Archive, kpidReadOnly, IsReadOnly)); 2926 2927 const UString fileName = ExtractFileNameFromPath(Path); 2928 UString extension; 2929 { 2930 int dotPos = fileName.ReverseFind_Dot(); 2931 if (dotPos >= 0) 2932 extension = fileName.Ptr(dotPos + 1); 2933 } 2934 2935 DefaultName.Empty(); 2936 if (FormatIndex >= 0) 2937 { 2938 const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; 2939 if (ai.Exts.Size() == 0) 2940 DefaultName = GetDefaultName2(fileName, UString(), UString()); 2941 else 2942 { 2943 int subExtIndex = ai.FindExtension(extension); 2944 if (subExtIndex < 0) 2945 subExtIndex = 0; 2946 const CArcExtInfo &extInfo = ai.Exts[subExtIndex]; 2947 DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); 2948 } 2949 } 2950 } 2951 2952 return S_OK; 2953 } 2954 2955 #ifdef _SFX 2956 2957 #ifdef _WIN32 2958 #define k_ExeExt ".exe" 2959 static const unsigned k_ExeExt_Len = 4; 2960 #else 2961 #define k_ExeExt "" 2962 static const unsigned k_ExeExt_Len = 0; 2963 #endif 2964 2965 #endif 2966 2967 HRESULT CArc::OpenStreamOrFile(COpenOptions &op) 2968 { 2969 CMyComPtr<IInStream> fileStream; 2970 CMyComPtr<ISequentialInStream> seqStream; 2971 CInFileStream *fileStreamSpec = NULL; 2972 2973 if (op.stdInMode) 2974 { 2975 seqStream = new CStdInFileStream; 2976 op.seqStream = seqStream; 2977 } 2978 else if (!op.stream) 2979 { 2980 fileStreamSpec = new CInFileStream; 2981 fileStream = fileStreamSpec; 2982 Path = filePath; 2983 if (!fileStreamSpec->Open(us2fs(Path))) 2984 { 2985 return GetLastError(); 2986 } 2987 op.stream = fileStream; 2988 #ifdef _SFX 2989 IgnoreSplit = true; 2990 #endif 2991 } 2992 2993 /* 2994 if (callback) 2995 { 2996 UInt64 fileSize; 2997 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); 2998 RINOK(op.callback->SetTotal(NULL, &fileSize)) 2999 } 3000 */ 3001 3002 HRESULT res = OpenStream(op); 3003 IgnoreSplit = false; 3004 3005 #ifdef _SFX 3006 3007 if (res != S_FALSE 3008 || !fileStreamSpec 3009 || !op.callbackSpec 3010 || NonOpen_ErrorInfo.IsArc_After_NonOpen()) 3011 return res; 3012 3013 { 3014 if (filePath.Len() > k_ExeExt_Len 3015 && StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt)) 3016 { 3017 const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len); 3018 FOR_VECTOR (i, op.codecs->Formats) 3019 { 3020 const CArcInfoEx &ai = op.codecs->Formats[i]; 3021 if (ai.IsSplit()) 3022 continue; 3023 UString path3 = path2; 3024 path3 += '.'; 3025 path3 += ai.GetMainExt(); // "7z" for SFX. 3026 Path = path3; 3027 Path += ".001"; 3028 bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); 3029 if (!isOk) 3030 { 3031 Path = path3; 3032 isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); 3033 } 3034 if (isOk) 3035 { 3036 if (fileStreamSpec->Open(us2fs(Path))) 3037 { 3038 op.stream = fileStream; 3039 NonOpen_ErrorInfo.ClearErrors_Full(); 3040 if (OpenStream(op) == S_OK) 3041 return S_OK; 3042 } 3043 } 3044 } 3045 } 3046 } 3047 3048 #endif 3049 3050 return res; 3051 } 3052 3053 void CArchiveLink::KeepModeForNextOpen() 3054 { 3055 for (unsigned i = Arcs.Size(); i != 0;) 3056 { 3057 i--; 3058 CMyComPtr<IArchiveKeepModeForNextOpen> keep; 3059 Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep); 3060 if (keep) 3061 keep->KeepModeForNextOpen(); 3062 } 3063 } 3064 3065 HRESULT CArchiveLink::Close() 3066 { 3067 for (unsigned i = Arcs.Size(); i != 0;) 3068 { 3069 i--; 3070 RINOK(Arcs[i].Close()); 3071 } 3072 IsOpen = false; 3073 // ErrorsText.Empty(); 3074 return S_OK; 3075 } 3076 3077 void CArchiveLink::Release() 3078 { 3079 // NonOpenErrorFormatIndex = -1; 3080 NonOpen_ErrorInfo.ClearErrors(); 3081 NonOpen_ArcPath.Empty(); 3082 while (!Arcs.IsEmpty()) 3083 Arcs.DeleteBack(); 3084 } 3085 3086 /* 3087 void CArchiveLink::Set_ErrorsText() 3088 { 3089 FOR_VECTOR(i, Arcs) 3090 { 3091 const CArc &arc = Arcs[i]; 3092 if (!arc.ErrorFlagsText.IsEmpty()) 3093 { 3094 if (!ErrorsText.IsEmpty()) 3095 ErrorsText.Add_LF(); 3096 ErrorsText += GetUnicodeString(arc.ErrorFlagsText); 3097 } 3098 if (!arc.ErrorMessage.IsEmpty()) 3099 { 3100 if (!ErrorsText.IsEmpty()) 3101 ErrorsText.Add_LF(); 3102 ErrorsText += arc.ErrorMessage; 3103 } 3104 3105 if (!arc.WarningMessage.IsEmpty()) 3106 { 3107 if (!ErrorsText.IsEmpty()) 3108 ErrorsText.Add_LF(); 3109 ErrorsText += arc.WarningMessage; 3110 } 3111 } 3112 } 3113 */ 3114 3115 HRESULT CArchiveLink::Open(COpenOptions &op) 3116 { 3117 Release(); 3118 if (op.types->Size() >= 32) 3119 return E_NOTIMPL; 3120 3121 HRESULT resSpec; 3122 3123 for (;;) 3124 { 3125 resSpec = S_OK; 3126 3127 op.openType = COpenType(); 3128 if (op.types->Size() >= 1) 3129 { 3130 COpenType latest; 3131 if (Arcs.Size() < op.types->Size()) 3132 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; 3133 else 3134 { 3135 latest = (*op.types)[0]; 3136 if (!latest.Recursive) 3137 break; 3138 } 3139 op.openType = latest; 3140 } 3141 else if (Arcs.Size() >= 32) 3142 break; 3143 3144 /* 3145 op.formatIndex = -1; 3146 if (op.types->Size() >= 1) 3147 { 3148 int latest; 3149 if (Arcs.Size() < op.types->Size()) 3150 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; 3151 else 3152 { 3153 latest = (*op.types)[0]; 3154 if (latest != -2 && latest != -3) 3155 break; 3156 } 3157 if (latest >= 0) 3158 op.formatIndex = latest; 3159 else if (latest == -1 || latest == -2) 3160 { 3161 // default 3162 } 3163 else if (latest == -3) 3164 op.formatIndex = -2; 3165 else 3166 op.formatIndex = latest + 2; 3167 } 3168 else if (Arcs.Size() >= 32) 3169 break; 3170 */ 3171 3172 if (Arcs.IsEmpty()) 3173 { 3174 CArc arc; 3175 arc.filePath = op.filePath; 3176 arc.Path = op.filePath; 3177 arc.SubfileIndex = (UInt32)(Int32)-1; 3178 HRESULT result = arc.OpenStreamOrFile(op); 3179 if (result != S_OK) 3180 { 3181 if (result == S_FALSE) 3182 { 3183 NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo; 3184 // NonOpenErrorFormatIndex = arc.ErrorFormatIndex; 3185 NonOpen_ArcPath = arc.Path; 3186 } 3187 return result; 3188 } 3189 Arcs.Add(arc); 3190 continue; 3191 } 3192 3193 // PrintNumber("op.formatIndex 11", op.formatIndex); 3194 3195 const CArc &arc = Arcs.Back(); 3196 3197 if (op.types->Size() > Arcs.Size()) 3198 resSpec = E_NOTIMPL; 3199 3200 UInt32 mainSubfile; 3201 { 3202 NCOM::CPropVariant prop; 3203 RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop)); 3204 if (prop.vt == VT_UI4) 3205 mainSubfile = prop.ulVal; 3206 else 3207 break; 3208 UInt32 numItems; 3209 RINOK(arc.Archive->GetNumberOfItems(&numItems)); 3210 if (mainSubfile >= numItems) 3211 break; 3212 } 3213 3214 3215 CMyComPtr<IInArchiveGetStream> getStream; 3216 if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream) 3217 break; 3218 3219 CMyComPtr<ISequentialInStream> subSeqStream; 3220 if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream) 3221 break; 3222 3223 CMyComPtr<IInStream> subStream; 3224 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream) 3225 break; 3226 3227 CArc arc2; 3228 RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); 3229 3230 bool zerosTailIsAllowed; 3231 RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed)); 3232 3233 3234 if (op.callback) 3235 { 3236 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; 3237 op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); 3238 if (setSubArchiveName) 3239 setSubArchiveName->SetSubArchiveName(arc2.Path); 3240 } 3241 3242 arc2.SubfileIndex = mainSubfile; 3243 3244 // CIntVector incl; 3245 CIntVector excl; 3246 3247 COpenOptions op2; 3248 #ifndef _SFX 3249 op2.props = op.props; 3250 #endif 3251 op2.codecs = op.codecs; 3252 // op2.types = &incl; 3253 op2.openType = op.openType; 3254 op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed; 3255 op2.excludedFormats = ! 3256 op2.stdInMode = false; 3257 op2.stream = subStream; 3258 op2.filePath = arc2.Path; 3259 op2.callback = op.callback; 3260 op2.callbackSpec = op.callbackSpec; 3261 3262 3263 HRESULT result = arc2.OpenStream(op2); 3264 resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE); 3265 if (result == S_FALSE) 3266 { 3267 NonOpen_ErrorInfo = arc2.ErrorInfo; 3268 NonOpen_ArcPath = arc2.Path; 3269 break; 3270 } 3271 RINOK(result); 3272 RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); 3273 Arcs.Add(arc2); 3274 } 3275 IsOpen = !Arcs.IsEmpty(); 3276 return resSpec; 3277 } 3278 3279 HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI) 3280 { 3281 VolumesSize = 0; 3282 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; 3283 CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec; 3284 openCallbackSpec->Callback = callbackUI; 3285 3286 FString prefix, name; 3287 3288 if (!op.stream && !op.stdInMode) 3289 { 3290 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name); 3291 openCallbackSpec->Init(prefix, name); 3292 } 3293 else 3294 { 3295 openCallbackSpec->SetSubArchiveName(op.filePath); 3296 } 3297 3298 op.callback = callback; 3299 op.callbackSpec = openCallbackSpec; 3300 3301 HRESULT res = Open(op); 3302 3303 PasswordWasAsked = openCallbackSpec->PasswordWasAsked; 3304 // Password = openCallbackSpec->Password; 3305 3306 RINOK(res); 3307 // VolumePaths.Add(fs2us(prefix + name)); 3308 3309 FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed) 3310 { 3311 if (openCallbackSpec->FileNames_WasUsed[i]) 3312 { 3313 VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); 3314 VolumesSize += openCallbackSpec->FileSizes[i]; 3315 } 3316 } 3317 // VolumesSize = openCallbackSpec->TotalSize; 3318 return S_OK; 3319 } 3320 3321 HRESULT CArc::ReOpen(const COpenOptions &op) 3322 { 3323 ErrorInfo.ClearErrors(); 3324 ErrorInfo.ErrorFormatIndex = -1; 3325 3326 UInt64 fileSize = 0; 3327 if (op.stream) 3328 { 3329 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); 3330 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); 3331 } 3332 FileSize = fileSize; 3333 3334 CMyComPtr<IInStream> stream2; 3335 Int64 globalOffset = GetGlobalOffset(); 3336 if (globalOffset <= 0) 3337 stream2 = op.stream; 3338 else 3339 { 3340 CTailInStream *tailStreamSpec = new CTailInStream; 3341 stream2 = tailStreamSpec; 3342 tailStreamSpec->Stream = op.stream; 3343 tailStreamSpec->Offset = globalOffset; 3344 tailStreamSpec->Init(); 3345 RINOK(tailStreamSpec->SeekToStart()); 3346 } 3347 3348 // There are archives with embedded STUBs (like ZIP), so we must support signature scanning 3349 // But for another archives we can use 0 here. So the code can be fixed !!! 3350 UInt64 maxStartPosition = kMaxCheckStartPosition; 3351 HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback); 3352 3353 if (res == S_OK) 3354 { 3355 RINOK(ReadBasicProps(Archive, globalOffset, res)); 3356 ArcStreamOffset = globalOffset; 3357 if (ArcStreamOffset != 0) 3358 InStream = op.stream; 3359 } 3360 return res; 3361 } 3362 3363 HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI) 3364 { 3365 HRESULT res = Open2(op, callbackUI); 3366 if (callbackUI) 3367 { 3368 RINOK(callbackUI->Open_Finished()); 3369 } 3370 return res; 3371 } 3372 3373 HRESULT CArchiveLink::ReOpen(COpenOptions &op) 3374 { 3375 if (Arcs.Size() > 1) 3376 return E_NOTIMPL; 3377 3378 CObjectVector<COpenType> inc; 3379 CIntVector excl; 3380 3381 op.types = &inc; 3382 op.excludedFormats = ! 3383 op.stdInMode = false; 3384 op.stream = NULL; 3385 if (Arcs.Size() == 0) // ??? 3386 return Open2(op, NULL); 3387 3388 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; 3389 CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec; 3390 3391 openCallbackSpec->Callback = NULL; 3392 openCallbackSpec->ReOpenCallback = op.callback; 3393 { 3394 FString dirPrefix, fileName; 3395 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName); 3396 openCallbackSpec->Init(dirPrefix, fileName); 3397 } 3398 3399 3400 CInFileStream *fileStreamSpec = new CInFileStream; 3401 CMyComPtr<IInStream> stream(fileStreamSpec); 3402 if (!fileStreamSpec->Open(us2fs(op.filePath))) 3403 return GetLastError(); 3404 op.stream = stream; 3405 3406 CArc &arc = Arcs[0]; 3407 HRESULT res = arc.ReOpen(op); 3408 3409 PasswordWasAsked = openCallbackSpec->PasswordWasAsked; 3410 // Password = openCallbackSpec->Password; 3411 3412 IsOpen = (res == S_OK); 3413 return res; 3414 } 3415 3416 #ifndef _SFX 3417 3418 bool ParseComplexSize(const wchar_t *s, UInt64 &result) 3419 { 3420 result = 0; 3421 const wchar_t *end; 3422 UInt64 number = ConvertStringToUInt64(s, &end); 3423 if (end == s) 3424 return false; 3425 if (*end == 0) 3426 { 3427 result = number; 3428 return true; 3429 } 3430 if (end[1] != 0) 3431 return false; 3432 unsigned numBits; 3433 switch (MyCharLower_Ascii(*end)) 3434 { 3435 case 'b': result = number; return true; 3436 case 'k': numBits = 10; break; 3437 case 'm': numBits = 20; break; 3438 case 'g': numBits = 30; break; 3439 case 't': numBits = 40; break; 3440 default: return false; 3441 } 3442 if (number >= ((UInt64)1 << (64 - numBits))) 3443 return false; 3444 result = number << numBits; 3445 return true; 3446 } 3447 3448 static bool ParseTypeParams(const UString &s, COpenType &type) 3449 { 3450 if (s[0] == 0) 3451 return true; 3452 if (s[1] == 0) 3453 { 3454 switch ((unsigned)(Byte)s[0]) 3455 { 3456 case 'e': type.EachPos = true; return true; 3457 case 'a': type.CanReturnArc = true; return true; 3458 case 'r': type.Recursive = true; return true; 3459 } 3460 return false; 3461 } 3462 if (s[0] == 's') 3463 { 3464 UInt64 result; 3465 if (!ParseComplexSize(s.Ptr(1), result)) 3466 return false; 3467 type.MaxStartOffset = result; 3468 type.MaxStartOffset_Defined = true; 3469 return true; 3470 } 3471 3472 return false; 3473 } 3474 3475 bool ParseType(CCodecs &codecs, const UString &s, COpenType &type) 3476 { 3477 int pos2 = s.Find(L':'); 3478 3479 { 3480 UString name; 3481 if (pos2 < 0) 3482 { 3483 name = s; 3484 pos2 = s.Len(); 3485 } 3486 else 3487 { 3488 name = s.Left(pos2); 3489 pos2++; 3490 } 3491 3492 int index = codecs.FindFormatForArchiveType(name); 3493 type.Recursive = false; 3494 3495 if (index < 0) 3496 { 3497 if (name[0] == '*') 3498 { 3499 if (name[1] != 0) 3500 return false; 3501 } 3502 else if (name[0] == '#') 3503 { 3504 if (name[1] != 0) 3505 return false; 3506 type.CanReturnArc = false; 3507 type.CanReturnParser = true; 3508 } 3509 else 3510 return false; 3511 } 3512 3513 type.FormatIndex = index; 3514 3515 } 3516 3517 for (unsigned i = pos2; i < s.Len();) 3518 { 3519 int next = s.Find(L':', i); 3520 if (next < 0) 3521 next = s.Len(); 3522 const UString name = s.Mid(i, next - i); 3523 if (name.IsEmpty()) 3524 return false; 3525 if (!ParseTypeParams(name, type)) 3526 return false; 3527 i = next + 1; 3528 } 3529 3530 return true; 3531 } 3532 3533 bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types) 3534 { 3535 types.Clear(); 3536 for (unsigned pos = 0; pos < s.Len();) 3537 { 3538 int pos2 = s.Find(L'.', pos); 3539 if (pos2 < 0) 3540 pos2 = s.Len(); 3541 UString name = s.Mid(pos, pos2 - pos); 3542 if (name.IsEmpty()) 3543 return false; 3544 COpenType type; 3545 if (!ParseType(codecs, name, type)) 3546 return false; 3547 types.Add(type); 3548 pos = pos2 + 1; 3549 } 3550 return true; 3551 } 3552 3553 #endif 3554