3545 lines
88 KiB
C++
3545 lines
88 KiB
C++
// 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 << 23;
|
|
|
|
/*
|
|
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) const;
|
|
void AddItem(const CParseItem &item);
|
|
|
|
CHandler(): _maxEndOffset(0) {}
|
|
};
|
|
|
|
int CHandler::FindInsertPos(const CParseItem &item) const
|
|
{
|
|
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 CStatProp 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_Dir(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) throw()
|
|
{
|
|
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 = (Int64)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;
|
|
}
|
|
|
|
#ifndef _SFX
|
|
|
|
HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const
|
|
{
|
|
if (!GetRawProps)
|
|
return E_FAIL;
|
|
if (index == parent)
|
|
return S_OK;
|
|
UInt32 curIndex = index;
|
|
|
|
UString s;
|
|
|
|
bool prevWasAltStream = false;
|
|
|
|
for (;;)
|
|
{
|
|
#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 && prop.bstrVal)
|
|
s.SetFromBstr(prop.bstrVal);
|
|
else if (prop.vt == VT_EMPTY)
|
|
s.Empty();
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
UInt32 curParent = (UInt32)(Int32)-1;
|
|
UInt32 parentType = 0;
|
|
RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType));
|
|
|
|
if (parentType != NParentType::kAltStream)
|
|
{
|
|
for (;;)
|
|
{
|
|
int pos = s.ReverseFind_PathSepar();
|
|
if (pos < 0)
|
|
{
|
|
break;
|
|
}
|
|
parts.Insert(0, s.Ptr(pos + 1));
|
|
s.DeleteFrom(pos);
|
|
}
|
|
}
|
|
|
|
parts.Insert(0, s);
|
|
|
|
if (prevWasAltStream)
|
|
{
|
|
{
|
|
UString &s2 = parts[parts.Size() - 2];
|
|
s2 += L':';
|
|
s2 += parts.Back();
|
|
}
|
|
parts.DeleteBack();
|
|
}
|
|
|
|
if (parent == curParent)
|
|
return S_OK;
|
|
|
|
prevWasAltStream = false;
|
|
if (parentType == NParentType::kAltStream)
|
|
prevWasAltStream = true;
|
|
|
|
if (curParent == (UInt32)(Int32)-1)
|
|
return E_FAIL;
|
|
curIndex = curParent;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
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.GetBuf(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;
|
|
}
|
|
*s = 0;
|
|
result.ReleaseBuf_SetLen(len);
|
|
if (len != 0)
|
|
return S_OK;
|
|
}
|
|
}
|
|
/*
|
|
else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK &&
|
|
p && propType == NPropDataType::kUtf16z)
|
|
{
|
|
size -= 2;
|
|
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)
|
|
{
|
|
if (parentType != 0)
|
|
totalSize += 2;
|
|
isOK = true;
|
|
break;
|
|
}
|
|
index2 = parent;
|
|
UInt32 size2;
|
|
const void *p2;
|
|
if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK &&
|
|
p2 && propType == NPropDataType::kUtf16z)
|
|
break;
|
|
totalSize += size2;
|
|
}
|
|
}
|
|
|
|
if (isOK)
|
|
{
|
|
wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2);
|
|
UInt32 pos = totalSize - size;
|
|
memcpy((Byte *)sz + pos, p, size);
|
|
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)
|
|
{
|
|
if (parentType != 0)
|
|
sz[pos / 2 - 1] = L':';
|
|
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':';
|
|
}
|
|
#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 && prop.bstrVal)
|
|
result.SetFromBstr(prop.bstrVal);
|
|
else if (prop.vt == VT_EMPTY)
|
|
result.Empty();
|
|
else
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (result.IsEmpty())
|
|
return GetDefaultItemPath(index, result);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CArc::GetDefaultItemPath(UInt32 index, UString &result) const
|
|
{
|
|
result.Empty();
|
|
bool isDir;
|
|
RINOK(Archive_IsItem_Dir(Archive, index, isDir));
|
|
if (!isDir)
|
|
{
|
|
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;
|
|
}
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
|
|
int FindAltStreamColon_in_Path(const wchar_t *path)
|
|
{
|
|
unsigned i = 0;
|
|
int colonPos = -1;
|
|
for (;; i++)
|
|
{
|
|
wchar_t c = path[i];
|
|
if (c == 0)
|
|
return colonPos;
|
|
if (c == ':')
|
|
{
|
|
if (colonPos < 0)
|
|
colonPos = i;
|
|
continue;
|
|
}
|
|
if (c == WCHAR_PATH_SEPARATOR)
|
|
colonPos = -1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const
|
|
{
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
item.IsAltStream = false;
|
|
item.AltStreamName.Empty();
|
|
item.MainPath.Empty();
|
|
#endif
|
|
|
|
item.IsDir = false;
|
|
item.Path.Empty();
|
|
item.ParentIndex = (UInt32)(Int32)-1;
|
|
|
|
item.PathParts.Clear();
|
|
|
|
RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir));
|
|
item.MainIsDir = item.IsDir;
|
|
|
|
RINOK(GetItemPath2(index, item.Path));
|
|
|
|
#ifndef _SFX
|
|
UInt32 mainIndex = index;
|
|
#endif
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
|
|
item.MainPath = item.Path;
|
|
if (Ask_AltStream)
|
|
{
|
|
RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream));
|
|
}
|
|
|
|
bool needFindAltStream = false;
|
|
|
|
if (item.IsAltStream)
|
|
{
|
|
needFindAltStream = true;
|
|
if (GetRawProps)
|
|
{
|
|
UInt32 parentType = 0;
|
|
UInt32 parentIndex;
|
|
RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType));
|
|
if (parentType == NParentType::kAltStream)
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(Archive->GetProperty(index, kpidName, &prop));
|
|
if (prop.vt == VT_BSTR && prop.bstrVal)
|
|
item.AltStreamName.SetFromBstr(prop.bstrVal);
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_FAIL;
|
|
else
|
|
{
|
|
// item.IsAltStream = false;
|
|
}
|
|
/*
|
|
if (item.AltStreamName.IsEmpty())
|
|
item.IsAltStream = false;
|
|
*/
|
|
|
|
needFindAltStream = false;
|
|
item.ParentIndex = parentIndex;
|
|
mainIndex = parentIndex;
|
|
|
|
if (parentIndex == (UInt32)(Int32)-1)
|
|
{
|
|
item.MainPath.Empty();
|
|
item.MainIsDir = true;
|
|
}
|
|
else
|
|
{
|
|
RINOK(GetItemPath2(parentIndex, item.MainPath));
|
|
RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (item.WriteToAltStreamIfColon || needFindAltStream)
|
|
{
|
|
/* Good handler must support GetRawProps::GetParent for alt streams./
|
|
So the following code currently is not used */
|
|
int colon = FindAltStreamColon_in_Path(item.Path);
|
|
if (colon >= 0)
|
|
{
|
|
item.MainPath.DeleteFrom(colon);
|
|
item.AltStreamName = item.Path.Ptr(colon + 1);
|
|
item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[(unsigned)colon - 1]));
|
|
item.IsAltStream = true;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef _SFX
|
|
if (item._use_baseParentFolder_mode)
|
|
{
|
|
RINOK(GetItemPathToParent(mainIndex, item._baseParentFolder, item.PathParts));
|
|
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty())
|
|
{
|
|
int colon;
|
|
{
|
|
UString &s = item.PathParts.Back();
|
|
colon = FindAltStreamColon_in_Path(s);
|
|
if (colon >= 0)
|
|
{
|
|
item.AltStreamName = s.Ptr(colon + 1);
|
|
item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1]));
|
|
item.IsAltStream = true;
|
|
s.DeleteFrom(colon);
|
|
}
|
|
}
|
|
if (colon == 0)
|
|
item.PathParts.DeleteBack();
|
|
}
|
|
#endif
|
|
|
|
}
|
|
else
|
|
#endif
|
|
SplitPathToParts(
|
|
#ifdef SUPPORT_ALT_STREAMS
|
|
item.MainPath
|
|
#else
|
|
item.Path
|
|
#endif
|
|
, item.PathParts);
|
|
|
|
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.IsEqualTo_Ascii_NoCase("exe");
|
|
}
|
|
|
|
static const char * const k_PreArcFormats[] =
|
|
{
|
|
"pe"
|
|
, "elf"
|
|
, "macho"
|
|
, "mub"
|
|
, "te"
|
|
};
|
|
|
|
static bool IsNameFromList(const UString &s, const char * const 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 * const 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,
|
|
public IArchiveOpenVolumeCallback,
|
|
#ifndef _NO_CRYPTO
|
|
public ICryptoGetTextPassword,
|
|
#endif
|
|
public CMyUnknownImp
|
|
{
|
|
public:
|
|
CMyComPtr<IArchiveOpenCallback> Callback;
|
|
CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback;
|
|
UInt64 Files;
|
|
UInt64 Offset;
|
|
|
|
#ifndef _NO_CRYPTO
|
|
CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
|
|
#endif
|
|
|
|
MY_QUERYINTERFACE_BEGIN2(IArchiveOpenCallback)
|
|
MY_QUERYINTERFACE_ENTRY(IArchiveOpenVolumeCallback)
|
|
#ifndef _NO_CRYPTO
|
|
MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
|
|
#endif
|
|
MY_QUERYINTERFACE_END
|
|
MY_ADDREF_RELEASE
|
|
|
|
INTERFACE_IArchiveOpenCallback(;)
|
|
INTERFACE_IArchiveOpenVolumeCallback(;)
|
|
#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 *, const UInt64 *)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes)
|
|
{
|
|
if (!Callback)
|
|
return S_OK;
|
|
UInt64 value = Offset;
|
|
if (bytes)
|
|
value += *bytes;
|
|
return Callback->SetCompleted(&Files, &value);
|
|
}
|
|
|
|
STDMETHODIMP CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
if (OpenVolumeCallback)
|
|
return OpenVolumeCallback->GetProperty(propID, value);
|
|
NCOM::PropVariant_Clear(value);
|
|
return S_OK;
|
|
// return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream)
|
|
{
|
|
if (OpenVolumeCallback)
|
|
return OpenVolumeCallback->GetStream(name, inStream);
|
|
return S_FALSE;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
// OutputDebugStringA("a1");
|
|
// PrintNumber("formatIndex", formatIndex);
|
|
|
|
RINOK(op.codecs->CreateInArchive(formatIndex, archive));
|
|
// OutputDebugStringA("a2");
|
|
if (!archive)
|
|
return S_OK;
|
|
|
|
#ifdef EXTERNAL_CODECS
|
|
if (op.codecs->NeedSetLibCodecs)
|
|
{
|
|
const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
|
|
if (ai.LibIndex >= 0 ?
|
|
!op.codecs->Libs[ai.LibIndex].SetCodecs :
|
|
!op.codecs->Libs.IsEmpty())
|
|
{
|
|
CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
|
|
archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
|
|
if (setCompressCodecsInfo)
|
|
{
|
|
RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef _SFX
|
|
|
|
const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
|
|
|
|
// OutputDebugStringW(ai.Name);
|
|
// OutputDebugStringA("a3");
|
|
|
|
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.SetFromBstr(prop.bstrVal);
|
|
pi.Extension.Empty();
|
|
}
|
|
else
|
|
{
|
|
RINOK(archive->GetArchiveProperty(kpidExtension, &prop));
|
|
if (prop.vt == VT_BSTR)
|
|
pi.Extension.SetFromBstr(prop.bstrVal);
|
|
}
|
|
}
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(archive->GetArchiveProperty(kpidShortComment, &prop));
|
|
if (prop.vt == VT_BSTR)
|
|
pi.Comment.SetFromBstr(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_Dir(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;
|
|
|
|
// OutputDebugStringA("1");
|
|
// OutputDebugStringW(Path);
|
|
|
|
const UString fileName = ExtractFileNameFromPath(Path);
|
|
UString extension;
|
|
{
|
|
int dotPos = fileName.ReverseFind_Dot();
|
|
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[(unsigned)formatIndex] = true;
|
|
|
|
searchMarkerInHandler = true;
|
|
}
|
|
else
|
|
{
|
|
unsigned numFinded = 0;
|
|
#ifndef _SFX
|
|
bool isPrearcExt = false;
|
|
#endif
|
|
|
|
{
|
|
#ifndef _SFX
|
|
|
|
bool isZip = false;
|
|
bool isRar = false;
|
|
|
|
const wchar_t c = extension[0];
|
|
if (c == 'z' || c == 'Z' || c == 'r' || c == 'R')
|
|
{
|
|
bool isNumber = false;
|
|
for (unsigned k = 1;; k++)
|
|
{
|
|
const wchar_t d = extension[k];
|
|
if (d == 0)
|
|
break;
|
|
if (d < '0' || d > '9')
|
|
{
|
|
isNumber = false;
|
|
break;
|
|
}
|
|
isNumber = true;
|
|
}
|
|
if (isNumber)
|
|
if (c == 'z' || c == 'Z')
|
|
isZip = true;
|
|
else
|
|
isRar = true;
|
|
}
|
|
|
|
#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
|
|
#ifndef _SFX
|
|
|| isZip && StringsAreEqualNoCase_Ascii(ai.Name, "zip")
|
|
|| isRar && StringsAreEqualNoCase_Ascii(ai.Name, "rar")
|
|
#endif
|
|
)
|
|
{
|
|
// 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.IsEqualTo("000") || extension.IsEqualTo("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();
|
|
|
|
// we need correct numMainTypes for mutlivolume SFX (if some volume is missing)
|
|
if (numFinded != 0)
|
|
numMainTypes = numFinded;
|
|
|
|
#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];
|
|
|
|
bool exactOnly = false;
|
|
|
|
#ifndef _SFX
|
|
|
|
const CArcInfoEx &ai = op.codecs->Formats[FormatIndex];
|
|
// OutputDebugStringW(ai.Name);
|
|
if (i >= numMainTypes)
|
|
{
|
|
if (!ai.Flags_BackwardOpen()
|
|
// && !ai.Flags_PureStartOpen()
|
|
)
|
|
continue;
|
|
exactOnly = true;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Some handlers do not set total bytes. So we set it here
|
|
if (op.callback)
|
|
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[(unsigned)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[(unsigned)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)
|
|
{
|
|
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];
|
|
|
|
if (op.callback)
|
|
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[(unsigned)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[(unsigned)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[(unsigned)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);
|
|
}
|
|
}
|
|
|
|
{
|
|
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[(unsigned)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);
|
|
|
|
CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL;
|
|
CMyComPtr<IArchiveOpenCallback> openCallback_Offset;
|
|
if (op.callback)
|
|
{
|
|
openCallback_Offset_Spec = new CArchiveOpenCallback_Offset;
|
|
openCallback_Offset = openCallback_Offset_Spec;
|
|
openCallback_Offset_Spec->Callback = op.callback;
|
|
openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback);
|
|
#ifndef _NO_CRYPTO
|
|
openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword);
|
|
#endif
|
|
}
|
|
|
|
if (op.callback)
|
|
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;
|
|
}
|
|
}
|
|
|
|
bool useOffsetCallback = false;
|
|
if (openCallback_Offset)
|
|
{
|
|
openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
|
|
openCallback_Offset_Spec->Offset = pos;
|
|
|
|
useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1);
|
|
|
|
if (pos >= callbackPrev + (1 << 23))
|
|
{
|
|
RINOK(openCallback_Offset_Spec->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;
|
|
|
|
if (openCallback_Offset)
|
|
{
|
|
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,
|
|
useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback,
|
|
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));
|
|
RINOK(Archive_GetArcBoolProp(Archive, kpidReadOnly, IsReadOnly));
|
|
|
|
const UString fileName = ExtractFileNameFromPath(Path);
|
|
UString extension;
|
|
{
|
|
int dotPos = fileName.ReverseFind_Dot();
|
|
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, UString(), UString());
|
|
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 char *k_ExeExt = ".exe";
|
|
static const unsigned k_ExeExt_Len = 4;
|
|
#else
|
|
static const char *k_ExeExt = "";
|
|
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(true);
|
|
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
|
|
&& StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt))
|
|
{
|
|
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;
|
|
Path.AddAscii(".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 (unsigned i = Arcs.Size(); i != 0;)
|
|
{
|
|
i--;
|
|
CMyComPtr<IArchiveKeepModeForNextOpen> keep;
|
|
Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep);
|
|
if (keep)
|
|
keep->KeepModeForNextOpen();
|
|
}
|
|
}
|
|
|
|
HRESULT CArchiveLink::Close()
|
|
{
|
|
for (unsigned i = Arcs.Size(); 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.Add_LF();
|
|
ErrorsText += GetUnicodeString(arc.ErrorFlagsText);
|
|
}
|
|
if (!arc.ErrorMessage.IsEmpty())
|
|
{
|
|
if (!ErrorsText.IsEmpty())
|
|
ErrorsText.Add_LF();
|
|
ErrorsText += arc.ErrorMessage;
|
|
}
|
|
|
|
if (!arc.WarningMessage.IsEmpty())
|
|
{
|
|
if (!ErrorsText.IsEmpty())
|
|
ErrorsText.Add_LF();
|
|
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));
|
|
|
|
|
|
if (op.callback)
|
|
{
|
|
CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
|
|
op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
|
|
if (setSubArchiveName)
|
|
setSubArchiveName->SetSubArchiveName(arc2.Path);
|
|
}
|
|
|
|
arc2.SubfileIndex = mainSubfile;
|
|
|
|
// CIntVector incl;
|
|
CIntVector excl;
|
|
|
|
COpenOptions op2;
|
|
#ifndef _SFX
|
|
op2.props = op.props;
|
|
#endif
|
|
op2.codecs = op.codecs;
|
|
// op2.types = &incl;
|
|
op2.openType = op.openType;
|
|
op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed;
|
|
op2.excludedFormats = !
|
|
op2.stdInMode = false;
|
|
op2.stream = subStream;
|
|
op2.filePath = arc2.Path;
|
|
op2.callback = op.callback;
|
|
op2.callbackSpec = op.callbackSpec;
|
|
|
|
|
|
HRESULT result = arc2.OpenStream(op2);
|
|
resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE);
|
|
if (result == S_FALSE)
|
|
{
|
|
NonOpen_ErrorInfo = arc2.ErrorInfo;
|
|
NonOpen_ArcPath = arc2.Path;
|
|
break;
|
|
}
|
|
RINOK(result);
|
|
RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined));
|
|
Arcs.Add(arc2);
|
|
}
|
|
IsOpen = !Arcs.IsEmpty();
|
|
return resSpec;
|
|
}
|
|
|
|
HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI)
|
|
{
|
|
VolumesSize = 0;
|
|
COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
|
|
CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec;
|
|
openCallbackSpec->Callback = callbackUI;
|
|
|
|
FString prefix, name;
|
|
|
|
if (!op.stream && !op.stdInMode)
|
|
{
|
|
NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name);
|
|
openCallbackSpec->Init(prefix, name);
|
|
}
|
|
else
|
|
{
|
|
openCallbackSpec->SetSubArchiveName(op.filePath);
|
|
}
|
|
|
|
op.callback = callback;
|
|
op.callbackSpec = openCallbackSpec;
|
|
|
|
HRESULT res = Open(op);
|
|
|
|
PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
|
|
// Password = openCallbackSpec->Password;
|
|
|
|
RINOK(res);
|
|
// VolumePaths.Add(fs2us(prefix + name));
|
|
|
|
FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed)
|
|
{
|
|
if (openCallbackSpec->FileNames_WasUsed[i])
|
|
{
|
|
VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]);
|
|
VolumesSize += openCallbackSpec->FileSizes[i];
|
|
}
|
|
}
|
|
// VolumesSize = openCallbackSpec->TotalSize;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CArc::ReOpen(const COpenOptions &op)
|
|
{
|
|
ErrorInfo.ClearErrors();
|
|
ErrorInfo.ErrorFormatIndex = -1;
|
|
|
|
UInt64 fileSize = 0;
|
|
if (op.stream)
|
|
{
|
|
RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
|
|
RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
|
|
}
|
|
FileSize = fileSize;
|
|
|
|
CMyComPtr<IInStream> stream2;
|
|
Int64 globalOffset = GetGlobalOffset();
|
|
if (globalOffset <= 0)
|
|
stream2 = op.stream;
|
|
else
|
|
{
|
|
CTailInStream *tailStreamSpec = new CTailInStream;
|
|
stream2 = tailStreamSpec;
|
|
tailStreamSpec->Stream = op.stream;
|
|
tailStreamSpec->Offset = globalOffset;
|
|
tailStreamSpec->Init();
|
|
RINOK(tailStreamSpec->SeekToStart());
|
|
}
|
|
|
|
// There are archives with embedded STUBs (like ZIP), so we must support signature scanning
|
|
// But for another archives we can use 0 here. So the code can be fixed !!!
|
|
UInt64 maxStartPosition = kMaxCheckStartPosition;
|
|
HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback);
|
|
|
|
if (res == S_OK)
|
|
{
|
|
RINOK(ReadBasicProps(Archive, globalOffset, res));
|
|
ArcStreamOffset = globalOffset;
|
|
if (ArcStreamOffset != 0)
|
|
InStream = op.stream;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI)
|
|
{
|
|
HRESULT res = Open2(op, callbackUI);
|
|
if (callbackUI)
|
|
{
|
|
RINOK(callbackUI->Open_Finished());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
HRESULT CArchiveLink::ReOpen(COpenOptions &op)
|
|
{
|
|
if (Arcs.Size() > 1)
|
|
return E_NOTIMPL;
|
|
|
|
CObjectVector<COpenType> inc;
|
|
CIntVector excl;
|
|
|
|
op.types = &inc;
|
|
op.excludedFormats = !
|
|
op.stdInMode = false;
|
|
op.stream = NULL;
|
|
if (Arcs.Size() == 0) // ???
|
|
return Open2(op, NULL);
|
|
|
|
COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
|
|
CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec;
|
|
|
|
openCallbackSpec->Callback = NULL;
|
|
openCallbackSpec->ReOpenCallback = op.callback;
|
|
{
|
|
FString dirPrefix, fileName;
|
|
NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName);
|
|
openCallbackSpec->Init(dirPrefix, fileName);
|
|
}
|
|
|
|
|
|
CInFileStream *fileStreamSpec = new CInFileStream(true);
|
|
CMyComPtr<IInStream> stream(fileStreamSpec);
|
|
if (!fileStreamSpec->Open(us2fs(op.filePath)))
|
|
return GetLastError();
|
|
op.stream = stream;
|
|
|
|
CArc &arc = Arcs[0];
|
|
HRESULT res = arc.ReOpen(op);
|
|
|
|
PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
|
|
// Password = openCallbackSpec->Password;
|
|
|
|
IsOpen = (res == S_OK);
|
|
return res;
|
|
}
|
|
|
|
#ifndef _SFX
|
|
|
|
bool ParseComplexSize(const wchar_t *s, UInt64 &result)
|
|
{
|
|
result = 0;
|
|
const wchar_t *end;
|
|
UInt64 number = ConvertStringToUInt64(s, &end);
|
|
if (end == s)
|
|
return false;
|
|
if (*end == 0)
|
|
{
|
|
result = number;
|
|
return true;
|
|
}
|
|
if (end[1] != 0)
|
|
return false;
|
|
unsigned numBits;
|
|
switch (MyCharLower_Ascii(*end))
|
|
{
|
|
case 'b': result = number; return true;
|
|
case 'k': numBits = 10; break;
|
|
case 'm': numBits = 20; break;
|
|
case 'g': numBits = 30; break;
|
|
case 't': numBits = 40; break;
|
|
default: return false;
|
|
}
|
|
if (number >= ((UInt64)1 << (64 - numBits)))
|
|
return false;
|
|
result = number << numBits;
|
|
return true;
|
|
}
|
|
|
|
static bool ParseTypeParams(const UString &s, COpenType &type)
|
|
{
|
|
if (s[0] == 0)
|
|
return true;
|
|
if (s[1] == 0)
|
|
{
|
|
switch ((unsigned)(Byte)s[0])
|
|
{
|
|
case 'e': type.EachPos = true; return true;
|
|
case 'a': type.CanReturnArc = true; return true;
|
|
case 'r': type.Recursive = true; return true;
|
|
}
|
|
return false;
|
|
}
|
|
if (s[0] == 's')
|
|
{
|
|
UInt64 result;
|
|
if (!ParseComplexSize(s.Ptr(1), result))
|
|
return false;
|
|
type.MaxStartOffset = result;
|
|
type.MaxStartOffset_Defined = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ParseType(CCodecs &codecs, const UString &s, COpenType &type)
|
|
{
|
|
int pos2 = s.Find(L':');
|
|
|
|
{
|
|
UString name;
|
|
if (pos2 < 0)
|
|
{
|
|
name = s;
|
|
pos2 = s.Len();
|
|
}
|
|
else
|
|
{
|
|
name = s.Left(pos2);
|
|
pos2++;
|
|
}
|
|
|
|
int index = codecs.FindFormatForArchiveType(name);
|
|
type.Recursive = false;
|
|
|
|
if (index < 0)
|
|
{
|
|
if (name[0] == '*')
|
|
{
|
|
if (name[1] != 0)
|
|
return false;
|
|
}
|
|
else if (name[0] == '#')
|
|
{
|
|
if (name[1] != 0)
|
|
return false;
|
|
type.CanReturnArc = false;
|
|
type.CanReturnParser = true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
type.FormatIndex = index;
|
|
|
|
}
|
|
|
|
for (unsigned i = pos2; i < s.Len();)
|
|
{
|
|
int next = s.Find(L':', i);
|
|
if (next < 0)
|
|
next = s.Len();
|
|
const UString name = s.Mid(i, next - i);
|
|
if (name.IsEmpty())
|
|
return false;
|
|
if (!ParseTypeParams(name, type))
|
|
return false;
|
|
i = next + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types)
|
|
{
|
|
types.Clear();
|
|
for (unsigned pos = 0; pos < s.Len();)
|
|
{
|
|
int pos2 = s.Find(L'.', pos);
|
|
if (pos2 < 0)
|
|
pos2 = s.Len();
|
|
UString name = s.Mid(pos, pos2 - pos);
|
|
if (name.IsEmpty())
|
|
return false;
|
|
COpenType type;
|
|
if (!ParseType(codecs, name, type))
|
|
return false;
|
|
types.Add(type);
|
|
pos = pos2 + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif
|