blob: 4632be863adb40005707cc449f23c16ff443e357 [file] [log] [blame]
// 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"");