// 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; | |