// OpenArchive.cpp #include "StdAfx.h" // #define SHOW_DEBUG_INFO #ifdef SHOW_DEBUG_INFO #include <stdio.h> #endif #include "../../../../C/CpuArch.h" #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../../Common/Wildcard.h" #include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" #include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamUtils.h" #include "../../Compress/CopyCoder.h" #include "DefaultName.h" #include "OpenArchive.h" #ifndef _SFX #include "SetProperties.h" #endif #ifdef SHOW_DEBUG_INFO #define PRF(x) x #else #define PRF(x) #endif // increase it, if you need to support larger SFX stubs static const UInt64 kMaxCheckStartPosition = 1 << 22; /* Open: - formatIndex >= 0 (exact Format) 1) Open with main type. Archive handler is allowed to use archive start finder. Warning, if there is tail. - formatIndex = -1 (Parser:0) (default) - same as #1 but doesn't return Parser - formatIndex = -2 (#1) - file has supported extension (like a.7z) Open with that main type (only starting from start of file). - open OK: - if there is no tail - return OK - if there is tail: - archive is not "Self Exe" - return OK with Warning, that there is tail - archive is "Self Exe" ignore "Self Exe" stub, and tries to open tail - tail can be open as archive - shows that archive and stub size property. - tail can't be open as archive - shows Parser ??? - open FAIL: Try to open with all other types from offset 0 only. If some open type is OK and physical archive size is uequal or larger than file size, then return that archive with warning that can not be open as [extension type]. If extension was EXE, it will try to open as unknown_extension case - file has unknown extension (like a.hhh) It tries to open via parser code. - if there is full archive or tail archive and unknown block or "Self Exe" at front, it shows tail archive and stub size property. - in another cases, if there is some archive inside file, it returns parser/ - in another cases, it retuens S_FALSE - formatIndex = -3 (#2) - same as #1, but - stub (EXE) + archive is open in Parser - formatIndex = -4 (#3) - returns only Parser. skip full file archive. And show other sub-archives - formatIndex = -5 (#4) - returns only Parser. skip full file archive. And show other sub-archives for each byte pos */ using namespace NWindows; /* #ifdef _SFX #define OPEN_PROPS_PARAM #else #define OPEN_PROPS_PARAM , props #endif */ /* CArc::~CArc() { GetRawProps.Release(); Archive.Release(); printf("\nCArc::~CArc()\n"); } */ #ifndef _SFX namespace NArchive { namespace NParser { struct CParseItem { UInt64 Offset; UInt64 Size; // UInt64 OkSize; UString Name; UString Extension; FILETIME FileTime; UString Comment; UString ArcType; bool FileTime_Defined; bool UnpackSize_Defined; bool NumSubDirs_Defined; bool NumSubFiles_Defined; bool IsSelfExe; bool IsNotArcType; UInt64 UnpackSize; UInt64 NumSubDirs; UInt64 NumSubFiles; int FormatIndex; bool LenIsUnknown; CParseItem(): LenIsUnknown(false), FileTime_Defined(false), UnpackSize_Defined(false), NumSubFiles_Defined(false), NumSubDirs_Defined(false), IsSelfExe(false), IsNotArcType(false) // OkSize(0) {} /* bool IsEqualTo(const CParseItem &item) const { return Offset == item.Offset && Size == item.Size; } */ void NormalizeOffset() { if ((Int64)Offset < 0) { Size += Offset; // OkSize += Offset; Offset = 0; } } }; class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { public: CObjectVector<CParseItem> _items; UInt64 _maxEndOffset; CMyComPtr<IInStream> _stream; MY_UNKNOWN_IMP2( IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); UInt64 GetLastEnd() const { if (_items.IsEmpty()) return 0; const CParseItem &back = _items.Back(); return back.Offset + back.Size; } void AddUnknownItem(UInt64 next); int FindInsertPos(const CParseItem &item); void AddItem(const CParseItem &item); // void Init(); CHandler() { _maxEndOffset = 0; } }; int CHandler::FindInsertPos(const CParseItem &item) { unsigned left = 0, right = _items.Size(); while (left != right) { unsigned mid = (left + right) / 2; const CParseItem & midItem = _items[mid]; if (item.Offset < midItem.Offset) right = mid; else if (item.Offset > midItem.Offset) left = mid + 1; else if (item.Size < midItem.Size) right = mid; else if (item.Size > midItem.Size) left = mid + 1; else { left = mid + 1; // return -1; } } return left; } void CHandler::AddUnknownItem(UInt64 next) { /* UInt64 prevEnd = 0; if (!_items.IsEmpty()) { const CParseItem &back = _items.Back(); prevEnd = back.Offset + back.Size; } */ if (_maxEndOffset < next) { CParseItem item2; item2.Offset = _maxEndOffset; item2.Size = next - _maxEndOffset; _maxEndOffset = next; _items.Add(item2); } else if (_maxEndOffset > next && !_items.IsEmpty()) { CParseItem &back = _items.Back(); if (back.LenIsUnknown) { back.Size = next - back.Offset; _maxEndOffset = next; } } } void CHandler::AddItem(const CParseItem &item) { AddUnknownItem(item.Offset); int pos = FindInsertPos(item); if (pos >= 0) { _items.Insert(pos, item); UInt64 next = item.Offset + item.Size; if (_maxEndOffset < next) _maxEndOffset = next; } } /* static const STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidSize, VT_UI8}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidType, VT_BSTR}, { NULL, kpidComment, VT_BSTR}, { NULL, kpidOffset, VT_UI8}, { NULL, kpidUnpackSize, VT_UI8}, // { NULL, kpidNumSubDirs, VT_UI8}, }; */ static const Byte kProps[] = { kpidPath, kpidSize, kpidMTime, kpidType, kpidComment, kpidOffset, kpidUnpackSize }; IMP_IInArchive_Props IMP_IInArchive_ArcProps_NO STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */) { COM_TRY_BEGIN { Close(); _stream = stream; } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { _items.Clear(); _stream.Release(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _items.Size(); return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; const CParseItem &item = _items[index]; switch (propID) { case kpidPath: { wchar_t sz[32]; ConvertUInt32ToString(index + 1, sz); UString s = sz; if (!item.Name.IsEmpty()) { s += L'.'; s += item.Name; } if (!item.Extension.IsEmpty()) { s += L'.'; s += item.Extension; } prop = s; break; } case kpidSize: case kpidPackSize: prop = item.Size; break; case kpidOffset: prop = item.Offset; break; case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break; case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break; case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break; case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break; case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break; case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break; } prop.Detach(value); return S_OK; COM_TRY_END } HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = _items.Size(); if (_stream && numItems == 0) return S_OK; UInt64 totalSize = 0; UInt32 i; for (i = 0; i < numItems; i++) totalSize += _items[allFilesMode ? i : indices[i]].Size; extractCallback->SetTotal(totalSize); totalSize = 0; CLocalProgress *lps = new CLocalProgress; CMyComPtr<ICompressProgressInfo> progress = lps; lps->Init(extractCallback, false); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr<ISequentialInStream> inStream(streamSpec); streamSpec->SetStream(_stream); CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; for (i = 0; i < numItems; i++) { lps->InSize = totalSize; lps->OutSize = totalSize; RINOK(lps->SetCur()); CMyComPtr<ISequentialOutStream> realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; const CParseItem &item = _items[index]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); UInt64 unpackSize = item.Size; totalSize += unpackSize; bool skipMode = false; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); outStreamSpec->SetStream(realOutStream); realOutStream.Release(); outStreamSpec->Init(skipMode ? 0 : unpackSize, true); Int32 opRes = NExtract::NOperationResult::kOK; RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); streamSpec->Init(unpackSize); RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); if (outStreamSpec->GetRem() != 0) opRes = NExtract::NOperationResult::kDataError; outStreamSpec->ReleaseStream(); RINOK(extractCallback->SetOperationResult(opRes)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN const CParseItem &item = _items[index]; return CreateLimitedInStream(_stream, item.Offset, item.Size, stream); COM_TRY_END } }} #endif HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw() { NCOM::CPropVariant prop; result = false; RINOK(arc->GetProperty(index, propID, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } HRESULT Archive_IsItem_Folder(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); } HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); } HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); } HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() { return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); } static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) { NCOM::CPropVariant prop; result = false; RINOK(arc->GetArchiveProperty(propid, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined) { defined = false; NCOM::CPropVariant prop; RINOK(arc->GetArchiveProperty(propid, &prop)); switch (prop.vt) { case VT_UI4: result = prop.ulVal; defined = true; break; case VT_I4: result = prop.lVal; defined = true; break; case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break; case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break; case VT_EMPTY: break; default: return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const { if (!GetRawProps) return E_FAIL; UInt32 curIndex = index; bool prevWasAltStream = false; for (;;) { UString s; #ifdef MY_CPU_LE const void *p; UInt32 size; UInt32 propType; RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType)); if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE) s = (const wchar_t *)p; else #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(curIndex, kpidName, &prop)); if (prop.vt == VT_BSTR) s = prop.bstrVal; else if (prop.vt == VT_EMPTY) s = L"[Content]"; else return E_FAIL; } if (prevWasAltStream) parts[0] = s + L":" + parts[0]; else parts.Insert(0, s); UInt32 curParent = (UInt32)(Int32)-1; UInt32 parentType = 0; RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType)); if (parent == curParent) return S_OK; if (curParent == (UInt32)(Int32)-1) return E_FAIL; prevWasAltStream = (parentType == NParentType::kAltStream); curIndex = curParent; } } HRESULT CArc::GetItemPath(UInt32 index, UString &result) const { #ifdef MY_CPU_LE if (GetRawProps) { const void *p; UInt32 size; UInt32 propType; if (!IsTree) { if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK && propType == NPropDataType::kUtf16z) { unsigned len = size / 2 - 1; wchar_t *s = result.GetBuffer(len); for (unsigned i = 0; i < len; i++) { wchar_t c = GetUi16(p); p = (const void *)((const Byte *)p + 2); #if WCHAR_PATH_SEPARATOR != L'/' if (c == L'/') c = WCHAR_PATH_SEPARATOR; #endif *s++ = c; } result.ReleaseBuffer(len); if (len != 0) return S_OK; } } /* else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK && p && propType == NPropDataType::kUtf16z) { UInt32 totalSize = size; bool isOK = false; { UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) { isOK = true; break; } index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; totalSize += size2; } } if (isOK) { wchar_t *sz = result.GetBuffer(totalSize / 2); UInt32 pos = totalSize - size; memcpy((Byte *)sz + pos, p, size - 2); UInt32 index2 = index; for (;;) { UInt32 parent = (UInt32)(Int32)-1; UInt32 parentType = 0; if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) break; if (parent == (UInt32)(Int32)-1) break; index2 = parent; UInt32 size2; const void *p2; if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) break; pos -= size2; memcpy((Byte *)sz + pos, p2, size2); sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':'; } result.ReleaseBuffer((totalSize - 2) / 2); #ifdef _WIN32 // result.Replace(L'/', WCHAR_PATH_SEPARATOR); #endif return S_OK; } } */ } #endif { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidPath, &prop)); if (prop.vt == VT_BSTR) result = prop.bstrVal; else if (prop.vt == VT_EMPTY) result.Empty(); else return E_FAIL; } if (result.IsEmpty()) { result = DefaultName; NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidExtension, &prop)); if (prop.vt == VT_BSTR) { result += L'.'; result += prop.bstrVal; } else if (prop.vt != VT_EMPTY) return E_FAIL; } return S_OK; } HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const { RINOK(GetItemPath(index, result)); if (Ask_Deleted) { bool isDeleted = false; RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted)); if (isDeleted) result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); } return S_OK; } #ifndef _SFX static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined) { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } #endif HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const { NCOM::CPropVariant prop; defined = false; size = 0; RINOK(Archive->GetProperty(index, kpidSize, &prop)); switch (prop.vt) { case VT_UI1: size = prop.bVal; break; case VT_UI2: size = prop.uiVal; break; case VT_UI4: size = prop.ulVal; break; case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; case VT_EMPTY: return S_OK; default: return E_FAIL; } defined = true; return S_OK; } HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const { NCOM::CPropVariant prop; defined = false; ft.dwHighDateTime = ft.dwLowDateTime = 0; RINOK(Archive->GetProperty(index, kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { ft = prop.filetime; defined = true; } else if (prop.vt != VT_EMPTY) return E_FAIL; else if (MTimeDefined) { ft = MTime; defined = true; } return S_OK; } #ifndef _SFX static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) { for (size_t i = 0; i < size; i++) if (p1[i] != p2[i]) return false; return true; } static void MakeCheckOrder(CCodecs *codecs, CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2, const Byte *data, size_t dataSize) { for (unsigned i = 0; i < numTypes; i++) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = codecs->Formats[index]; if (ai.SignatureOffset != 0) { orderIndices2.Add(index); orderIndices[i] = -1; continue; } const CObjectVector<CByteBuffer> &sigs = ai.Signatures; FOR_VECTOR (k, sigs) { const CByteBuffer &sig = sigs[k]; if (sig.Size() == 0 && dataSize == 0 || sig.Size() != 0 && sig.Size() <= dataSize && TestSignature(data, sig, sig.Size())) { orderIndices2.Add(index); orderIndices[i] = -1; break; } } } } #endif #ifdef UNDER_CE static const unsigned kNumHashBytes = 1; #define HASH_VAL(buf, pos) ((buf)[pos]) #else static const unsigned kNumHashBytes = 2; #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) #endif #ifndef _SFX static bool IsExeExt(const UString &ext) { return ext.IsEqualToNoCase(L"exe"); } static const char *k_PreArcFormats[] = { "pe" , "elf" , "macho" , "mub" , "te" }; static bool IsNameFromList(const UString &s, const char *names[], size_t num) { for (unsigned i = 0; i < num; i++) if (StringsAreEqualNoCase_Ascii(s, names[i])) return true; return false; } static bool IsPreArcFormat(const CArcInfoEx &ai) { if (ai.Flags_PreArc()) return true; return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats)); } static const char *k_Formats_with_simple_signuature[] = { "7z" , "xz" , "rar" , "bzip2" , "gzip" , "cab" , "wim" , "rpm" , "vhd" , "xar" }; static bool IsNewStyleSignature(const CArcInfoEx &ai) { // if (ai.Version >= 0x91F) if (ai.NewInterface) return true; return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature)); } class CArchiveOpenCallback_Offset: public IArchiveOpenCallback, #ifndef _NO_CRYPTO public ICryptoGetTextPassword, #endif public CMyUnknownImp { public: CMyComPtr<IArchiveOpenCallback> Callback; UInt64 Files; UInt64 Offset; #ifndef _NO_CRYPTO CMyComPtr<ICryptoGetTextPassword> GetTextPassword; MY_UNKNOWN_IMP2( IArchiveOpenCallback, ICryptoGetTextPassword) #else MY_UNKNOWN_IMP1(IArchiveOpenCallback) #endif STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); #ifndef _NO_CRYPTO STDMETHOD(CryptoGetTextPassword)(BSTR *password); #endif }; #ifndef _NO_CRYPTO STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password) { COM_TRY_BEGIN if (GetTextPassword) return GetTextPassword->CryptoGetTextPassword(password); return E_NOTIMPL; COM_TRY_END } #endif STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) { return S_OK; } STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 * /* files */, const UInt64 *bytes) { if (!Callback) return S_OK; UInt64 value = Offset; if (bytes) value += *bytes; return Callback->SetCompleted(&Files, &value); } #endif UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp) { if (isDefinedProp != NULL) *isDefinedProp = false; switch (prop.vt) { case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart; case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal; case VT_EMPTY: return 0; default: throw 151199; } } void CArcErrorInfo::ClearErrors() { // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!! ThereIsTail = false; UnexpecedEnd = false; IgnoreTail = false; // NonZerosTail = false; ErrorFlags_Defined = false; ErrorFlags = 0; WarningFlags = 0; TailSize = 0; ErrorMessage.Empty(); WarningMessage.Empty(); } HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes) { // OkPhySize_Defined = false; PhySizeDefined = false; PhySize = 0; Offset = 0; AvailPhySize = FileSize - startPos; ErrorInfo.ClearErrors(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop)); ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop)); ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop); } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidError, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error"; } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidWarning, &prop)); if (prop.vt != VT_EMPTY) ErrorInfo.WarningMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown warning"; } if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen()) { RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined)); /* RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined)); if (!OkPhySize_Defined) { OkPhySize_Defined = PhySizeDefined; OkPhySize = PhySize; } */ bool offsetDefined; RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined)); Int64 globalOffset = startPos + Offset; AvailPhySize = FileSize - globalOffset; if (PhySizeDefined) { UInt64 endPos = globalOffset + PhySize; if (endPos < FileSize) { AvailPhySize = PhySize; ErrorInfo.ThereIsTail = true; ErrorInfo.TailSize = FileSize - endPos; } else if (endPos > FileSize) ErrorInfo.UnexpecedEnd = true; } } return S_OK; } /* static PrintNumber(const char *s, int n) { char temp[100]; sprintf(temp, "%s %d", s, n); OutputDebugStringA(temp); } */ HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive) { // OutputDebugStringW(L"a1"); // PrintNumber("formatIndex", formatIndex); RINOK(op.codecs->CreateInArchive(formatIndex, archive)); // OutputDebugStringW(L"a2"); if (!archive) return S_OK; #ifdef EXTERNAL_CODECS { CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); if (setCompressCodecsInfo) { RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs)); } } #endif // OutputDebugStringW(ai.Name); // OutputDebugStringW(L"a3"); #ifndef _SFX const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_PreArc()) { /* we notify parsers that extract executables, that they don't need to open archive, if there is tail after executable (for SFX cases) */ CMyComPtr<IArchiveAllowTail> allowTail; archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail); if (allowTail) allowTail->AllowTail(BoolToInt(true)); } if (op.props) { /* FOR_VECTOR (y, op.props) { const COptionalOpenProperties &optProps = (*op.props)[y]; if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0) { RINOK(SetProperties(archive, optProps.Props)); break; } } */ RINOK(SetProperties(archive, *op.props)); } #endif return S_OK; } #ifndef _SFX static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi) { pi.Extension = ai.GetMainExt(); pi.FileTime_Defined = false; pi.ArcType = ai.Name; RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType)); // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe)); pi.IsSelfExe = ai.Flags_PreArc(); { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidMTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } if (!pi.FileTime_Defined) { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidCTime, &prop)); if (prop.vt == VT_FILETIME) { pi.FileTime_Defined = true; pi.FileTime = prop.filetime; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidName, &prop)); if (prop.vt == VT_BSTR) { pi.Name = prop.bstrVal; pi.Extension.Empty(); } else { RINOK(archive->GetArchiveProperty(kpidExtension, &prop)); if (prop.vt == VT_BSTR) pi.Extension = prop.bstrVal; } } { NCOM::CPropVariant prop; RINOK(archive->GetArchiveProperty(kpidShortComment, &prop)); if (prop.vt == VT_BSTR) pi.Comment = prop.bstrVal; } UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); // pi.NumSubFiles = numItems; // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined)); // if (!pi.UnpackSize_Defined) { pi.NumSubFiles = 0; pi.NumSubDirs = 0; pi.UnpackSize = 0; for (UInt32 i = 0; i < numItems; i++) { UInt64 size = 0; bool defined = false; Archive_GetItem_Size(archive, i, size, defined); if (defined) { pi.UnpackSize_Defined = true; pi.UnpackSize += size; } bool isDir = false; Archive_IsItem_Folder(archive, i, isDir); if (isDir) pi.NumSubDirs++; else pi.NumSubFiles++; } if (pi.NumSubDirs != 0) pi.NumSubDirs_Defined = true; pi.NumSubFiles_Defined = true; } return S_OK; } #endif HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset) { if (!op.stream) return S_OK; RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL)); const UInt32 kBufSize = 1 << 11; Byte buf[kBufSize]; for (;;) { UInt32 processed = 0; RINOK(op.stream->Read(buf, kBufSize, &processed)); if (processed == 0) { // ErrorInfo.NonZerosTail = false; ErrorInfo.IgnoreTail = true; return S_OK; } for (size_t i = 0; i < processed; i++) { if (buf[i] != 0) { // ErrorInfo.IgnoreTail = false; // ErrorInfo.NonZerosTail = true; return S_OK; } } } } #ifndef _SFX class CExtractCallback_To_OpenCallback: public IArchiveExtractCallback, public ICompressProgressInfo, public CMyUnknownImp { public: CMyComPtr<IArchiveOpenCallback> Callback; UInt64 Files; UInt64 Offset; MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo) INTERFACE_IArchiveExtractCallback(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); void Init(IArchiveOpenCallback *callback) { Callback = callback; Files = 0; Offset = 0; } }; STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) { if (Callback) { UInt64 value = Offset; if (inSize) value += *inSize; return Callback->SetCompleted(&Files, &value); } return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) { *outStream = 0; return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) { return S_OK; } STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) { return S_OK; } static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize, IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback, IArchiveExtractCallback *extractCallback) { /* if (needPhySize) { CMyComPtr<IArchiveOpen2> open2; archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2); if (open2) return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback); } */ RINOK(archive->Open(stream, maxCheckStartPosition, openCallback)); if (needPhySize) { bool phySize_Defined = false; UInt64 phySize = 0; RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined)); if (phySize_Defined) return S_OK; bool phySizeCantBeDetected = false;; RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected)); if (!phySizeCantBeDetected) { RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback)); } } return S_OK; } static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name) { FOR_VECTOR (i, orderIndices) if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name)) return i; return -1; } #endif HRESULT CArc::OpenStream2(const COpenOptions &op) { // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout); Archive.Release(); GetRawProps.Release(); GetRootProps.Release(); ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; IsParseArc = false; ArcStreamOffset = 0; // OutputDebugStringW(L"1"); // OutputDebugStringW(Path); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } CIntVector orderIndices; bool searchMarkerInHandler = false; #ifdef _SFX searchMarkerInHandler = true; #endif CBoolArr isMainFormatArr(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) isMainFormatArr[i] = false; } UInt64 maxStartOffset = op.openType.MaxStartOffset_Defined ? op.openType.MaxStartOffset : kMaxCheckStartPosition; #ifndef _SFX bool isUnknownExt = false; #endif bool isForced = false; unsigned numMainTypes = 0; int formatIndex = op.openType.FormatIndex; if (formatIndex >= 0) { isForced = true; orderIndices.Add(formatIndex); numMainTypes = 1; isMainFormatArr[formatIndex] = true; searchMarkerInHandler = true; } else { unsigned numFinded = 0; #ifndef _SFX bool isPrearcExt = false; #endif { FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (IgnoreSplit || !op.openType.CanReturnArc) if (ai.IsSplit()) continue; if (op.excludedFormats->FindInSorted(i) >= 0) continue; #ifndef _SFX if (IsPreArcFormat(ai)) isPrearcExt = true; #endif if (ai.FindExtension(extension) >= 0) { // PrintNumber("orderIndices.Insert", i); orderIndices.Insert(numFinded++, i); isMainFormatArr[i] = true; } else orderIndices.Add(i); } } if (!op.stream) { if (numFinded != 1) return E_NOTIMPL; orderIndices.DeleteFrom(1); } // PrintNumber("numFinded", numFinded ); /* if (op.openOnlySpecifiedByExtension) { if (numFinded != 0 && !IsExeExt(extension)) orderIndices.DeleteFrom(numFinded); } */ #ifndef _SFX if (op.stream && orderIndices.Size() >= 2) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CByteBuffer byteBuffer; CIntVector orderIndices2; if (numFinded == 0 || IsExeExt(extension)) { // signature search was here } else if (extension == L"000" || extension == L"001") { int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar"); if (i >= 0) { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize >= 16) { const Byte *buf = byteBuffer; const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }; if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0) { orderIndices2.Add(orderIndices[i]); orderIndices[i] = -1; if (i >= (int)numFinded) numFinded++; } } } } else { const size_t kBufSize = (1 << 10); byteBuffer.Alloc(kBufSize); size_t processedSize = kBufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; /* check type order: 1) matched extension, no signuature 2) matched extension, matched signuature // 3) no signuature // 4) matched signuature */ MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0); MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0); // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize); } FOR_VECTOR (i, orderIndices) { int val = orderIndices[i]; if (val != -1) orderIndices2.Add(val); } orderIndices = orderIndices2; } if (orderIndices.Size() >= 2) { int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso"); int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf"); if (iUdf > iIso && iIso >= 0) { int isoIndex = orderIndices[iIso]; int udfIndex = orderIndices[iUdf]; orderIndices[iUdf] = isoIndex; orderIndices[iIso] = udfIndex; } } numMainTypes = numFinded; isUnknownExt = (numMainTypes == 0) || isPrearcExt; #else // _SFX numMainTypes = orderIndices.Size(); #endif } UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; #ifndef _SFX CBoolArr skipFrontalFormat(op.codecs->Formats.Size()); { FOR_VECTOR(i, op.codecs->Formats) skipFrontalFormat[i] = false; } #endif const COpenType &mode = op.openType; if (mode.CanReturnArc) { // ---------- OPEN main type by extenssion ---------- unsigned numCheckTypes = orderIndices.Size(); if (formatIndex >= 0) numCheckTypes = numMainTypes; for (unsigned i = 0; i < numCheckTypes; i++) { FormatIndex = orderIndices[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; // OutputDebugStringW(ai.Name); bool exactOnly = false; if (i >= numMainTypes) { if (!ai.Flags_BackwardOpen() // && !ai.Flags_PureStartOpen() ) continue; exactOnly = true; } // Some handlers do not set total bytes. So we set it here RINOK(op.callback->SetTotal(NULL, &fileSize)); if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } CMyComPtr<IInArchive> archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; HRESULT result; if (op.stream) { UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0; result = archive->Open(op.stream, &searchLimit, op.callback); } else { CMyComPtr<IArchiveOpenSeq> openSeq; archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); if (!openSeq) return E_NOTIMPL; result = openSeq->OpenSeq(op.seqStream); } RINOK(ReadBasicProps(archive, 0, result)); if (result == S_FALSE) { bool isArc = ErrorInfo.IsArc_After_NonOpen(); #ifndef _SFX // if it's archive, we allow another open attempt for parser if (!mode.CanReturnParser || !isArc) skipFrontalFormat[FormatIndex] = true; #endif if (exactOnly) continue; if (i == 0 && numMainTypes == 1) { // we set NonOpenErrorInfo, only if there is only one main format (defined by extension). ErrorInfo.ErrorFormatIndex = FormatIndex; NonOpen_ErrorInfo = ErrorInfo; if (!mode.CanReturnParser && isArc) { // if (formatIndex < 0 && !searchMarkerInHandler) { // if bad archive was detected, we don't need additional open attempts #ifndef _SFX if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */) #endif return S_FALSE; } } } /* #ifndef _SFX if (IsExeExt(extension) || ai.Flags_PreArc()) { // openOnlyFullArc = false; // canReturnTailArc = true; // limitSignatureSearch = true; } #endif */ continue; } RINOK(result); #ifndef _SFX bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (Offset > 0) { if (exactOnly || !searchMarkerInHandler || !specFlags.CanReturn_NonStart() || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset)) continue; } if (thereIsTail) { if (Offset > 0) { if (!specFlags.CanReturnMid) continue; } else if (!specFlags.CanReturnFrontal) continue; } if (Offset > 0 || thereIsTail) { if (formatIndex < 0) { if (IsPreArcFormat(ai)) { // openOnlyFullArc = false; // canReturnTailArc = true; /* if (mode.SkipSfxStub) limitSignatureSearch = true; */ // if (mode.SkipSfxStub) { // skipFrontalFormat[FormatIndex] = true; continue; } } } } #endif Archive = archive; return S_OK; } } #ifndef _SFX if (!op.stream) return S_FALSE; if (formatIndex >= 0 && !mode.CanReturnParser) { if (mode.MaxStartOffset_Defined) { if (mode.MaxStartOffset == 0) return S_FALSE; } else { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.FindExtension(extension) >= 0) { const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; if (ai.Flags_FindSignature() && searchMarkerInHandler) return S_FALSE; } } } NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler; CMyComPtr<IInArchive> handler = handlerSpec; CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback; CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec; extractCallback_To_OpenCallback_Spec->Init(op.callback); { // ---------- Check all possible START archives ---------- // this code is better for full file archives than Parser's code. CByteBuffer byteBuffer; bool endOfFile = false; size_t processedSize; { size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF) if (bufSize > fileSize) { bufSize = (size_t)fileSize; endOfFile = true; } byteBuffer.Alloc(bufSize); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); processedSize = bufSize; RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); if (processedSize == 0) return S_FALSE; if (processedSize < bufSize) endOfFile = true; } CUIntVector sortedFormats; unsigned i; int splitIndex = -1; for (i = 0; i < orderIndices.Size(); i++) { unsigned form = orderIndices[i]; if (skipFrontalFormat[form]) continue; const CArcInfoEx &ai = op.codecs->Formats[form]; if (ai.IsSplit()) { splitIndex = form; continue; } if (ai.IsArcFunc) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue; sortedFormats.Insert(0, form); continue; } bool isNewStyleSignature = IsNewStyleSignature(ai); bool needCheck = !isNewStyleSignature || ai.Signatures.IsEmpty() || ai.Flags_PureStartOpen() || ai.Flags_StartOpen() || ai.Flags_BackwardOpen(); if (isNewStyleSignature && !ai.Signatures.IsEmpty()) { unsigned k; for (k = 0; k < ai.Signatures.Size(); k++) { const CByteBuffer &sig = ai.Signatures[k]; UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (processedSize < signatureEnd) { if (!endOfFile) needCheck = true; } else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0) break; } if (k != ai.Signatures.Size()) { sortedFormats.Insert(0, form); continue; } } if (needCheck) sortedFormats.Add(form); } if (splitIndex >= 0) sortedFormats.Insert(0, splitIndex); for (i = 0; i < sortedFormats.Size(); i++) { FormatIndex = sortedFormats[i]; const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; RINOK(op.callback->SetTotal(NULL, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CMyComPtr<IInArchive> archive; RINOK(PrepareToOpen(op, FormatIndex, archive)); if (!archive) continue; PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name)); HRESULT result; { UInt64 searchLimit = 0; /* if (mode.CanReturnArc) result = archive->Open(op.stream, &searchLimit, op.callback); else */ result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback); } if (result == S_FALSE) { skipFrontalFormat[FormatIndex] = true; // FIXME: maybe we must use LenIsUnknown. // printf(" OpenForSize Error"); continue; } RINOK(result); RINOK(ReadBasicProps(archive, 0, result)); if (Offset > 0) { continue; // good handler doesn't return such Offset > 0 // but there are some cases like false prefixed PK00 archive, when // we can support it? } NArchive::NParser::CParseItem pi; pi.Offset = Offset; pi.Size = AvailPhySize; // bool needScan = false; if (!PhySizeDefined) { // it's for Z format pi.LenIsUnknown = true; // needScan = true; // phySize = arcRem; // nextNeedCheckStartOpen = false; } /* if (OkPhySize_Defined) pi.OkSize = pi.OkPhySize; else pi.OkSize = pi.Size; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); if (mode.CanReturnArc) { bool isMainFormat = isMainFormatArr[FormatIndex]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); bool openCur = false; if (!ErrorInfo.ThereIsTail) openCur = true; else { if (mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, Offset + PhySize)); if (ErrorInfo.IgnoreTail) openCur = true; } if (!openCur) { openCur = specFlags.CanReturnFrontal; if (formatIndex < 0) // format is not forced { if (IsPreArcFormat(ai)) { // if (mode.SkipSfxStub) { openCur = false; } } } } } if (openCur) { InStream = op.stream; Archive = archive; return S_OK; } } skipFrontalFormat[FormatIndex] = true; // if (!mode.CanReturnArc) /* if (!ErrorInfo.ThereIsTail) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) continue; // printf("\nAdd offset = %d", (int)pi.Offset); RINOK(ReadParseItemProps(archive, ai, pi)); handlerSpec->AddItem(pi); } } // ---------- PARSER ---------- CUIntVector arc2sig; // formatIndex to signatureIndex CUIntVector sig2arc; // signatureIndex to formatIndex; { unsigned sum = 0; FOR_VECTOR (i, op.codecs->Formats) { arc2sig.Add(sum); const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures; sum += sigs.Size(); FOR_VECTOR (k, sigs) sig2arc.Add(i); } } { CArchiveOpenCallback_Offset *openCallback_Offset_Spec = new CArchiveOpenCallback_Offset; CMyComPtr<IArchiveOpenCallback> openCallback_Offset = openCallback_Offset_Spec; const size_t kBeforeSize = 1 << 16; const size_t kAfterSize = 1 << 20; const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8); CByteArr hashBuffer(kNumVals); Byte *hash = hashBuffer; memset(hash, 0xFF, kNumVals); Byte prevs[256]; memset(prevs, 0xFF, sizeof(prevs)); if (sig2arc.Size() >= 0xFF) return S_FALSE; CUIntVector difficultFormats; CBoolArr difficultBools(256); { for (unsigned i = 0; i < 256; i++) difficultBools[i] = false; } bool thereAreHandlersForSearch = false; // UInt32 maxSignatureEnd = 0; FOR_VECTOR (i, orderIndices) { int index = orderIndices[i]; if (index < 0) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; bool isDifficult = false; // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31) if (!ai.NewInterface) isDifficult = true; else { if (ai.Flags_StartOpen()) isDifficult = true; FOR_VECTOR (k, ai.Signatures) { const CByteBuffer &sig = ai.Signatures[k]; /* UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); if (maxSignatureEnd < signatureEnd) maxSignatureEnd = signatureEnd; */ if (sig.Size() < kNumHashBytes) { isDifficult = true; continue; } thereAreHandlersForSearch = true; UInt32 v = HASH_VAL(sig, 0); unsigned sigIndex = arc2sig[index] + k; prevs[sigIndex] = hash[v]; hash[v] = (Byte)sigIndex; } } if (isDifficult) { difficultFormats.Add(index); difficultBools[index] = true; } } if (!thereAreHandlersForSearch) { // openOnlyFullArc = true; // canReturnTailArc = true; } RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream; CMyComPtr<IInStream> limitedStream = limitedStreamSpec; limitedStreamSpec->SetStream(op.stream); openCallback_Offset_Spec->Callback = op.callback; #ifndef _NO_CRYPTO if (op.callback) { openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword); } #endif RINOK(op.callback->SetTotal(NULL, &fileSize)); CByteBuffer &byteBuffer = limitedStreamSpec->Buffer; byteBuffer.Alloc(kBufSize); UInt64 callbackPrev = 0; bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos. bool endOfFile = false; UInt64 bufPhyPos = 0; size_t bytesInBuf = 0; // UInt64 prevPos = 0; // ---------- Main Scan Loop ---------- UInt64 pos = 0; if (!mode.EachPos && handlerSpec->_items.Size() == 1) { NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (!pi.LenIsUnknown && pi.Offset == 0) pos = pi.Size; } for (;;) { // printf("\nPos = %d", (int)pos); UInt64 posInBuf = pos - bufPhyPos; // if (pos > ((UInt64)1 << 35)) break; if (!endOfFile) { if (bytesInBuf < kBufSize) { size_t processedSize = kBufSize - bytesInBuf; // printf("\nRead ask = %d", (unsigned)processedSize); UInt64 seekPos = bufPhyPos + bytesInBuf; RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL)); RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize)); // printf(" processed = %d", (unsigned)processedSize); if (processedSize == 0) { fileSize = seekPos; endOfFile = true; } else { bytesInBuf += processedSize; limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos); } continue; } if (bytesInBuf < posInBuf) { UInt64 skipSize = posInBuf - bytesInBuf; if (skipSize <= kBeforeSize) { size_t keepSize = (size_t)(kBeforeSize - skipSize); // printf("\nmemmove skip = %d", (int)keepSize); memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize); bytesInBuf = keepSize; bufPhyPos = pos - keepSize; continue; } // printf("\nSkip %d", (int)(skipSize - kBeforeSize)); // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL)); bytesInBuf = 0; bufPhyPos = pos - kBeforeSize; continue; } if (bytesInBuf - posInBuf < kAfterSize) { size_t beg = (size_t)posInBuf - kBeforeSize; // printf("\nmemmove for after beg = %d", (int)beg); memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg); bufPhyPos += beg; bytesInBuf -= beg; continue; } } if (pos >= callbackPrev + (1 << 23)) { openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = pos; RINOK(openCallback_Offset->SetCompleted(NULL, NULL)); callbackPrev = pos; } { UInt64 endPos = bufPhyPos + bytesInBuf; if (fileSize < endPos) { FileSize = fileSize; // why ???? fileSize = endPos; } } size_t availSize = bytesInBuf - (size_t)posInBuf; if (availSize < kNumHashBytes) break; size_t scanSize = availSize - ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes); { /* UInt64 scanLimit = openOnlyFullArc ? maxSignatureEnd : op.openType.ScanSize + maxSignatureEnd; */ if (!mode.CanReturnParser) { if (pos > maxStartOffset) break; UInt64 remScan = maxStartOffset - pos; if (scanSize > remScan) scanSize = (size_t)remScan; } } scanSize++; const Byte *buf = byteBuffer + (size_t)posInBuf; size_t ppp = 0; if (!needCheckStartOpen) { for (; ppp < scanSize && hash[HASH_VAL(buf, ppp)] == 0xFF; ppp++); pos += ppp; if (ppp == scanSize) continue; } UInt32 v = HASH_VAL(buf, ppp); bool nextNeedCheckStartOpen = true; unsigned i = hash[v]; unsigned indexOfDifficult = 0; // ---------- Open Loop for Current Pos ---------- bool wasOpen = false; for (;;) { unsigned index; bool isDifficult; if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size()) { index = difficultFormats[indexOfDifficult++]; isDifficult = true; } else { if (i == 0xFF) break; index = sig2arc[i]; unsigned sigIndex = i - arc2sig[index]; i = prevs[i]; if (needCheckStartOpen && difficultBools[index]) continue; const CArcInfoEx &ai = op.codecs->Formats[index]; if (pos < ai.SignatureOffset) continue; /* if (openOnlyFullArc) if (pos != ai.SignatureOffset) continue; */ const CByteBuffer &sig = ai.Signatures[sigIndex]; if (ppp + sig.Size() > availSize || !TestSignature(buf + ppp, sig, sig.Size())) continue; // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos)); // prevPos = pos; isDifficult = false; } const CArcInfoEx &ai = op.codecs->Formats[index]; if ((isDifficult && pos == 0) || ai.SignatureOffset == pos) { // we don't check same archive second time */ if (skipFrontalFormat[index]) continue; } UInt64 startArcPos = pos; if (!isDifficult) { if (pos < ai.SignatureOffset) continue; startArcPos = pos - ai.SignatureOffset; /* // we don't need the check for Z files if (startArcPos < handlerSpec->GetLastEnd()) continue; */ } if (ai.IsArcFunc && startArcPos >= bufPhyPos) { size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos); if (offsetInBuf < bytesInBuf) { UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf); if (isArcRes == k_IsArc_Res_NO) continue; if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) continue; /* if (isArcRes == k_IsArc_Res_YES_LOW_PROB) { // if (pos != ai.SignatureOffset) continue; } */ } // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name); } /* if (pos == 67109888) pos = pos; */ PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name)); bool isMainFormat = isMainFormatArr[index]; const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); CMyComPtr<IInArchive> archive; RINOK(PrepareToOpen(op, index, archive)); if (!archive) return E_FAIL; // OutputDebugStringW(ai.Name); UInt64 rem = fileSize - startArcPos; UInt64 arcStreamOffset = 0; if (ai.Flags_UseGlobalOffset()) { limitedStreamSpec->InitAndSeek(0, fileSize); limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL); } else { limitedStreamSpec->InitAndSeek(startArcPos, rem); arcStreamOffset = startArcPos; } UInt64 maxCheckStartPosition = 0; openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); openCallback_Offset_Spec->Offset = startArcPos; // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset); extractCallback_To_OpenCallback_Spec->Files = 0; extractCallback_To_OpenCallback_Spec->Offset = startArcPos; HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition, openCallback_Offset, extractCallback_To_OpenCallback); RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result)); bool isOpen = false; if (result == S_FALSE) { if (!mode.CanReturnParser) { if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen()) { ErrorInfo.ErrorFormatIndex = index; NonOpen_ErrorInfo = ErrorInfo; // if archive was detected, we don't need additional open attempts return S_FALSE; } continue; } if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0) continue; } else { isOpen = true; RINOK(result); PRF(printf(" OK ")); } // fprintf(stderr, "\n %8X %S", startArcPos, Path); // printf("\nOpen OK: %S", ai.Name); NArchive::NParser::CParseItem pi; pi.Offset = startArcPos; if (ai.Flags_UseGlobalOffset()) pi.Offset = Offset; else if (Offset != 0) return E_FAIL; UInt64 arcRem = FileSize - pi.Offset; UInt64 phySize = arcRem; bool phySizeDefined = PhySizeDefined; if (phySizeDefined) { if (pi.Offset + PhySize > FileSize) { // ErrorInfo.ThereIsTail = true; PhySize = FileSize - pi.Offset; } phySize = PhySize; } if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63)) return E_FAIL; /* if (!ai.UseGlobalOffset) { if (phySize > arcRem) { ThereIsTail = true; phySize = arcRem; } } */ bool needScan = false; if (isOpen && !phySizeDefined) { // it's for Z format pi.LenIsUnknown = true; needScan = true; phySize = arcRem; nextNeedCheckStartOpen = false; } pi.Size = phySize; /* if (OkPhySize_Defined) pi.OkSize = OkPhySize; */ pi.NormalizeOffset(); // printf(" phySize = %8d", (unsigned)phySize); /* if (needSkipFullArc) if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize) continue; */ if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) { // it's possible for dmg archives if (!mode.CanReturnArc) continue; } if (mode.EachPos) pos++; else if (needScan) { pos++; /* if (!OkPhySize_Defined) pos++; else pos = pi.Offset + pi.OkSize; */ } else pos = pi.Offset + pi.Size; RINOK(ReadParseItemProps(archive, ai, pi)); if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */) { /* It's for DMG format. This code deletes all previous items that are included to current item */ while (!handlerSpec->_items.IsEmpty()) { { const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back(); if (back.Offset < pi.Offset) break; if (back.Offset + back.Size > pi.Offset + pi.Size) break; } handlerSpec->_items.DeleteBack(); } } if (isOpen && mode.CanReturnArc && phySizeDefined) { // if (pi.Offset + pi.Size >= fileSize) bool openCur = false; bool thereIsTail = ErrorInfo.ThereIsTail; if (thereIsTail && mode.ZerosTailIsAllowed) { RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize)); if (ErrorInfo.IgnoreTail) thereIsTail = false; } if (pi.Offset != 0) { if (!pi.IsNotArcType) if (thereIsTail) openCur = specFlags.CanReturnMid; else openCur = specFlags.CanReturnTail; } else { if (!thereIsTail) openCur = true; else openCur = specFlags.CanReturnFrontal; if (formatIndex >= -2) openCur = true; } if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */) openCur = false; // We open file as SFX, if there is front archive or first archive is "Self Executable" if (!openCur && !pi.IsSelfExe && !thereIsTail && (!pi.IsNotArcType || pi.Offset == 0)) { if (handlerSpec->_items.IsEmpty()) { if (specFlags.CanReturnTail) openCur = true; } else if (handlerSpec->_items.Size() == 1) { if (handlerSpec->_items[0].IsSelfExe) { if (mode.SpecUnknownExt.CanReturnTail) openCur = true; } } } if (openCur) { InStream = op.stream; Archive = archive; FormatIndex = index; ArcStreamOffset = arcStreamOffset; return S_OK; } } /* if (openOnlyFullArc) { ErrorInfo.ClearErrors(); return S_FALSE; } */ pi.FormatIndex = index; // printf("\nAdd offset = %d", (int)pi.Offset); handlerSpec->AddItem(pi); wasOpen = true; break; } // ---------- End of Open Loop for Current Pos ---------- if (!wasOpen) pos++; needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen); } // ---------- End of Main Scan Loop ---------- /* if (handlerSpec->_items.Size() == 1) { const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; if (pi.Size == fileSize && pi.Offset == 0) { Archive = archive; FormatIndex2 = pi.FormatIndex; return S_OK; } } */ if (mode.CanReturnParser) { bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing handlerSpec->AddUnknownItem(fileSize); if (handlerSpec->_items.Size() == 0) return S_FALSE; if (returnParser || handlerSpec->_items.Size() != 1) { // return S_FALSE; handlerSpec->_stream = op.stream; Archive = handler; ErrorInfo.ClearErrors(); IsParseArc = true; FormatIndex = -1; // It's parser Offset = 0; return S_OK; } } } #endif if (!Archive) return S_FALSE; return S_OK; } HRESULT CArc::OpenStream(const COpenOptions &op) { RINOK(OpenStream2(op)); // PrintNumber("op.formatIndex 3", op.formatIndex); if (Archive) { GetRawProps.Release(); GetRootProps.Release(); Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps); Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps); RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream)); RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux)); RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode)); const UString fileName = ExtractFileNameFromPath(Path); UString extension; { int dotPos = fileName.ReverseFind(L'.'); if (dotPos >= 0) extension = fileName.Ptr(dotPos + 1); } DefaultName.Empty(); if (FormatIndex >= 0) { const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; if (ai.Exts.Size() == 0) DefaultName = GetDefaultName2(fileName, L"", L""); else { int subExtIndex = ai.FindExtension(extension); if (subExtIndex < 0) subExtIndex = 0; const CArcExtInfo &extInfo = ai.Exts[subExtIndex]; DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); } } } return S_OK; } #ifdef _SFX #ifdef _WIN32 static const wchar_t *k_ExeExt = L".exe"; static const unsigned k_ExeExt_Len = 4; #else static const wchar_t *k_ExeExt = L""; static const unsigned k_ExeExt_Len = 0; #endif #endif HRESULT CArc::OpenStreamOrFile(COpenOptions &op) { CMyComPtr<IInStream> fileStream; CMyComPtr<ISequentialInStream> seqStream; CInFileStream *fileStreamSpec = NULL; if (op.stdInMode) { seqStream = new CStdInFileStream; op.seqStream = seqStream; } else if (!op.stream) { fileStreamSpec = new CInFileStream; fileStream = fileStreamSpec; Path = filePath; if (!fileStreamSpec->Open(us2fs(Path))) { return GetLastError(); } op.stream = fileStream; #ifdef _SFX IgnoreSplit = true; #endif } /* if (callback) { UInt64 fileSize; RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.callback->SetTotal(NULL, &fileSize)) } */ HRESULT res = OpenStream(op); IgnoreSplit = false; #ifdef _SFX if (res != S_FALSE || !fileStreamSpec || !op.callbackSpec || NonOpen_ErrorInfo.IsArc_After_NonOpen()) return res; { if (filePath.Len() > k_ExeExt_Len && MyStringCompareNoCase(filePath.RightPtr(k_ExeExt_Len), k_ExeExt) == 0) { const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len); FOR_VECTOR (i, op.codecs->Formats) { const CArcInfoEx &ai = op.codecs->Formats[i]; if (ai.IsSplit()) continue; UString path3 = path2; path3 += L"."; path3 += ai.GetMainExt(); // "7z" for SFX. Path = path3 + L".001"; bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); if (!isOk) { Path = path3; isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); } if (isOk) { if (fileStreamSpec->Open(us2fs(Path))) { op.stream = fileStream; NonOpen_ErrorInfo.ClearErrors_Full(); if (OpenStream(op) == S_OK) return S_OK; } } } } } #endif return res; } void CArchiveLink::KeepModeForNextOpen() { for (int i = Arcs.Size() - 1; i >= 0; i--) { CMyComPtr<IArchiveKeepModeForNextOpen> keep; Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep); if (keep) keep->KeepModeForNextOpen(); } } HRESULT CArchiveLink::Close() { for (int i = Arcs.Size() - 1; i >= 0; i--) { RINOK(Arcs[i].Close()); } IsOpen = false; // ErrorsText.Empty(); return S_OK; } void CArchiveLink::Release() { // NonOpenErrorFormatIndex = -1; NonOpen_ErrorInfo.ClearErrors(); NonOpen_ArcPath.Empty(); while (!Arcs.IsEmpty()) Arcs.DeleteBack(); } /* void CArchiveLink::Set_ErrorsText() { FOR_VECTOR(i, Arcs) { const CArc &arc = Arcs[i]; if (!arc.ErrorFlagsText.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += GetUnicodeString(arc.ErrorFlagsText); } if (!arc.ErrorMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.ErrorMessage; } if (!arc.WarningMessage.IsEmpty()) { if (!ErrorsText.IsEmpty()) ErrorsText += L'\n'; ErrorsText += arc.WarningMessage; } } } */ HRESULT CArchiveLink::Open(COpenOptions &op) { Release(); if (op.types->Size() >= 32) return E_NOTIMPL; HRESULT resSpec; for (;;) { resSpec = S_OK; op.openType = COpenType(); if (op.types->Size() >= 1) { COpenType latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (!latest.Recursive) break; } op.openType = latest; } else if (Arcs.Size() >= 32) break; /* op.formatIndex = -1; if (op.types->Size() >= 1) { int latest; if (Arcs.Size() < op.types->Size()) latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; else { latest = (*op.types)[0]; if (latest != -2 && latest != -3) break; } if (latest >= 0) op.formatIndex = latest; else if (latest == -1 || latest == -2) { // default } else if (latest == -3) op.formatIndex = -2; else op.formatIndex = latest + 2; } else if (Arcs.Size() >= 32) break; */ if (Arcs.IsEmpty()) { CArc arc; arc.filePath = op.filePath; arc.Path = op.filePath; arc.SubfileIndex = (UInt32)(Int32)-1; HRESULT result = arc.OpenStreamOrFile(op); if (result != S_OK) { if (result == S_FALSE) { NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo; // NonOpenErrorFormatIndex = arc.ErrorFormatIndex; NonOpen_ArcPath = arc.Path; } return result; } Arcs.Add(arc); continue; } // PrintNumber("op.formatIndex 11", op.formatIndex); const CArc &arc = Arcs.Back(); if (op.types->Size() > Arcs.Size()) resSpec = E_NOTIMPL; UInt32 mainSubfile; { NCOM::CPropVariant prop; RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop)); if (prop.vt == VT_UI4) mainSubfile = prop.ulVal; else break; UInt32 numItems; RINOK(arc.Archive->GetNumberOfItems(&numItems)); if (mainSubfile >= numItems) break; } CMyComPtr<IInArchiveGetStream> getStream; if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream) break; CMyComPtr<ISequentialInStream> subSeqStream; if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream) break; CMyComPtr<IInStream> subStream; if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream) break; CArc arc2; RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); bool zerosTailIsAllowed; RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed)); CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); if (setSubArchiveName) setSubArchiveName->SetSubArchiveName(arc2.Path); arc2.SubfileIndex = mainSubfile; // CIntVector incl; CIntVector excl; COpenOptions op2; #ifndef _SFX op2.props = op.props; #endif op2.codecs = op.codecs; // op2.types = &incl; op2.openType = op.openType; op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed; op2.excludedFormats = ! op2.stdInMode = false; op2.stream = subStream; op2.filePath = arc2.Path; op2.callback = op.callback; op2.callbackSpec = op.callbackSpec; HRESULT result = arc2.OpenStream(op2); resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE); if (result == S_FALSE) { NonOpen_ErrorInfo = arc2.ErrorInfo; NonOpen_ArcPath = arc2.Path; break; } RINOK(result); RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); Arcs.Add(arc2); } IsOpen = !Arcs.IsEmpty(); return resSpec; } static void SetCallback(const FString &filePath, IOpenCallbackUI *callbackUI, IArchiveOpenCallback *reOpenCallback, CMyComPtr<IArchiveOpenCallback> &callback) { COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; openCallbackSpec->ReOpenCallback = reOpenCallback; FString dirPrefix, fileName; NFile::NDir::GetFullPathAndSplit(filePath, dirPrefix, fileName); openCallbackSpec->Init(dirPrefix, fileName); } HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI) { VolumesSize = 0; COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec; openCallbackSpec->Callback = callbackUI; FString prefix, name; if (!op.stream && !op.stdInMode) { NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name); openCallbackSpec->Init(prefix, name); } else { openCallbackSpec->SetSubArchiveName(op.filePath); } op.callback = callback; op.callbackSpec = openCallbackSpec; RINOK(Open(op)); // VolumePaths.Add(fs2us(prefix + name)); FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed) { if (openCallbackSpec->FileNames_WasUsed[i]) { VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); VolumesSize += openCallbackSpec->FileSizes[i]; } } // VolumesSize = openCallbackSpec->TotalSize; return S_OK; } HRESULT CArc::ReOpen(const COpenOptions &op) { ErrorInfo.ClearErrors(); ErrorInfo.ErrorFormatIndex = -1; UInt64 fileSize = 0; if (op.stream) { RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); } FileSize = fileSize; CMyComPtr<IInStream> stream2; Int64 globalOffset = GetGlobalOffset(); if (globalOffset <= 0) stream2 = op.stream; else { CTailInStream *tailStreamSpec = new CTailInStream; stream2 = tailStreamSpec; tailStreamSpec->Stream = op.stream; tailStreamSpec->Offset = globalOffset; tailStreamSpec->Init(); RINOK(tailStreamSpec->SeekToStart()); } // There are archives with embedded STUBs (like ZIP), so we must support signature scanning // But for another archives we can use 0 here. So the code can be fixed !!! UInt64 maxStartPosition = kMaxCheckStartPosition; HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback); if (res == S_OK) { RINOK(ReadBasicProps(Archive, globalOffset, res)); ArcStreamOffset = globalOffset; if (ArcStreamOffset != 0) InStream = op.stream; } return res; } HRESULT CArchiveLink::ReOpen(COpenOptions &op) { if (Arcs.Size() > 1) return E_NOTIMPL; CObjectVector<COpenType> inc; CIntVector excl; op.types = &inc; op.excludedFormats = ! op.stdInMode = false; op.stream = NULL; if (Arcs.Size() == 0) // ??? return Open2(op, NULL); CMyComPtr<IArchiveOpenCallback> openCallbackNew; SetCallback(us2fs(op.filePath), NULL, op.callback, openCallbackNew); CInFileStream *fileStreamSpec = new CInFileStream; CMyComPtr<IInStream> stream(fileStreamSpec); if (!fileStreamSpec->Open(us2fs(op.filePath))) return GetLastError(); op.stream = stream; CArc &arc = Arcs[0]; HRESULT res = arc.ReOpen(op); IsOpen = (res == S_OK); return res; } #ifndef _SFX bool ParseComplexSize(const wchar_t *s, UInt64 &result) { result = 0; const wchar_t *end; UInt64 number = ConvertStringToUInt64(s, &end); if (end == s) return false; if (*end == 0) { result = number; return true; } if (end[1] != 0) return false; unsigned numBits; switch (MyCharLower_Ascii(*end)) { case 'b': result = number; return true; case 'k': numBits = 10; break; case 'm': numBits = 20; break; case 'g': numBits = 30; break; case 't': numBits = 40; break; default: return false; } if (number >= ((UInt64)1 << (64 - numBits))) return false; result = number << numBits; return true; } static bool ParseTypeParams(const UString &s, COpenType &type) { if (s[0] == 0) return true; if (s[1] == 0) { switch ((unsigned)(Byte)s[0]) { case 'e': type.EachPos = true; return true; case 'a': type.CanReturnArc = true; return true; case 'r': type.Recursive = true; return true; } return false; } if (s[0] == 's') { UInt64 result; if (!ParseComplexSize(s.Ptr(1), result)) return false; type.MaxStartOffset = result; type.MaxStartOffset_Defined = true; return true; } return false; } bool ParseType(CCodecs &codecs, const UString &s, COpenType &type) { int pos2 = s.Find(':'); UString name; if (pos2 < 0) { name = s; pos2 = s.Len(); } else { name = s.Left(pos2); pos2++; } int index = codecs.FindFormatForArchiveType(name); type.Recursive = false; if (index < 0) { if (name[0] == '*') { if (name[1] != 0) return false; } else if (name[0] == '#') { if (name[1] != 0) return false; type.CanReturnArc = false; type.CanReturnParser = true; } else return false; } type.FormatIndex = index; for (unsigned i = pos2; i < s.Len();) { int next = s.Find(':', i); if (next < 0) next = s.Len(); UString name = s.Mid(i, next - i); if (name.IsEmpty()) return false; if (!ParseTypeParams(name, type)) return false; i = next + 1; } return true; } bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types) { types.Clear(); for (unsigned pos = 0; pos < s.Len();) { int pos2 = s.Find('.', pos); if (pos2 < 0) pos2 = s.Len(); UString name = s.Mid(pos, pos2 - pos); if (name.IsEmpty()) return false; COpenType type; if (!ParseType(codecs, name, type)) return false; types.Add(type); pos = pos2 + 1; } return true; } #endif