Files
easy7zip/7zip/UI/Common/OpenArchive.cpp
Igor Pavlov 3c510ba80b 4.20
2016-05-28 00:15:41 +01:00

528 lines
13 KiB
C++
Executable File

// OpenArchive.cpp
#include "StdAfx.h"
#include "OpenArchive.h"
#include "Common/Wildcard.h"
#include "Windows/FileName.h"
#include "Windows/FileDir.h"
#include "Windows/Defs.h"
#include "Windows/PropVariant.h"
#include "../../Common/FileStreams.h"
#include "Common/StringConvert.h"
#ifdef FORMAT_7Z
#include "../../Archive/7z/7zHandler.h"
#endif
#ifdef FORMAT_BZIP2
#include "../../Archive/BZip2/BZip2Handler.h"
#endif
#ifdef FORMAT_GZIP
#include "../../Archive/GZip/GZipHandler.h"
#endif
#ifdef FORMAT_SPLIT
#include "../../Archive/Split/SplitHandler.h"
#endif
#ifdef FORMAT_TAR
#include "../../Archive/Tar/TarHandler.h"
#endif
#ifdef FORMAT_ZIP
#include "../../Archive/Zip/ZipHandler.h"
#endif
#ifdef FORMAT_Z
#include "../../Archive/Z/ZHandler.h"
#endif
#ifndef EXCLUDE_COM
#include "HandlerLoader.h"
#endif
#include "DefaultName.h"
using namespace NWindows;
HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result)
{
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;
return S_OK;
}
HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result)
{
RINOK(GetArchiveItemPath(archive, index, result));
if (result.IsEmpty())
result = defaultName;
return S_OK;
}
HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index,
const FILETIME &defaultFileTime, FILETIME &fileTime)
{
NCOM::CPropVariant prop;
RINOK(archive->GetProperty(index, kpidLastWriteTime, &prop));
if (prop.vt == VT_FILETIME)
fileTime = prop.filetime;
else if (prop.vt == VT_EMPTY)
fileTime = defaultFileTime;
else
return E_FAIL;
return S_OK;
}
static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
{
NCOM::CPropVariant prop;
RINOK(archive->GetProperty(index, propID, &prop));
if(prop.vt == VT_BOOL)
result = VARIANT_BOOLToBool(prop.boolVal);
else if (prop.vt == VT_EMPTY)
result = false;
else
return E_FAIL;
return S_OK;
}
HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
{
return IsArchiveItemProp(archive, index, kpidIsFolder, result);
}
HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result)
{
return IsArchiveItemProp(archive, index, kpidIsAnti, result);
}
// Static-SFX (for Linux) can be big
const UInt64 kMaxCheckStartPosition =
#ifdef _WIN32
1 << 20;
#else
1 << 22;
#endif
HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName)
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<IInStream> inStream(inStreamSpec);
inStreamSpec->Open(fileName);
return archive->Open(inStream, &kMaxCheckStartPosition, NULL);
}
#ifndef _SFX
static inline bool TestSignature(const Byte *p1, const Byte *p2, UInt32 size)
{
for (UInt32 i = 0; i < size; i++)
if (p1[i] != p2[i])
return false;
return true;
}
#endif
HRESULT OpenArchive(
IInStream *inStream,
const UString &fileName,
#ifndef EXCLUDE_COM
HMODULE *module,
#endif
IInArchive **archiveResult,
CArchiverInfo &archiverInfoResult,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback)
{
*archiveResult = NULL;
CObjectVector<CArchiverInfo> archiverInfoList;
ReadArchiverInfoList(archiverInfoList);
UString extension;
{
int dotPos = fileName.ReverseFind(L'.');
if (dotPos >= 0)
extension = fileName.Mid(dotPos + 1);
}
CIntVector orderIndices;
int i;
bool finded = false;
for(i = 0; i < archiverInfoList.Size(); i++)
{
if (archiverInfoList[i].FindExtension(extension) >= 0)
{
orderIndices.Insert(0, i);
finded = true;
}
else
orderIndices.Add(i);
}
#ifndef _SFX
if (!finded)
{
CByteBuffer byteBuffer;
const UInt32 kBufferSize = (200 << 10);
byteBuffer.SetCapacity(kBufferSize);
Byte *buffer = byteBuffer;
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
UInt32 processedSize;
RINOK(inStream->Read(buffer, kBufferSize, &processedSize));
int numFinded = 0;
for (int pos = (int)processedSize; pos >= 0 ; pos--)
{
for(int i = numFinded; i < orderIndices.Size(); i++)
{
int index = orderIndices[i];
const CArchiverInfo &ai = archiverInfoList[index];
const CByteBuffer &sig = ai.StartSignature;
if (sig.GetCapacity() == 0)
continue;
if (pos + sig.GetCapacity() > processedSize)
continue;
if (TestSignature(buffer + pos, sig, sig.GetCapacity()))
{
orderIndices.Delete(i);
orderIndices.Insert(0, index);
numFinded++;
}
}
}
}
#endif
HRESULT badResult = S_OK;
for(i = 0; i < orderIndices.Size(); i++)
{
inStream->Seek(0, STREAM_SEEK_SET, NULL);
const CArchiverInfo &archiverInfo = archiverInfoList[orderIndices[i]];
#ifndef EXCLUDE_COM
CHandlerLoader loader;
#endif
CMyComPtr<IInArchive> archive;
#ifdef FORMAT_7Z
if (archiverInfo.Name.CompareNoCase(L"7z") == 0)
archive = new NArchive::N7z::CHandler;
#endif
#ifdef FORMAT_BZIP2
if (archiverInfo.Name.CompareNoCase(L"BZip2") == 0)
archive = new NArchive::NBZip2::CHandler;
#endif
#ifdef FORMAT_GZIP
if (archiverInfo.Name.CompareNoCase(L"GZip") == 0)
archive = new NArchive::NGZip::CHandler;
#endif
#ifdef FORMAT_SPLIT
if (archiverInfo.Name.CompareNoCase(L"Split") == 0)
archive = new NArchive::NSplit::CHandler;
#endif
#ifdef FORMAT_TAR
if (archiverInfo.Name.CompareNoCase(L"Tar") == 0)
archive = new NArchive::NTar::CHandler;
#endif
#ifdef FORMAT_ZIP
if (archiverInfo.Name.CompareNoCase(L"Zip") == 0)
archive = new NArchive::NZip::CHandler;
#endif
#ifdef FORMAT_Z
if (archiverInfo.Name.CompareNoCase(L"Z") == 0)
archive = new NArchive::NZ::CHandler;
#endif
#ifndef EXCLUDE_COM
if (!archive)
{
HRESULT result = loader.CreateHandler(archiverInfo.FilePath,
archiverInfo.ClassID, (void **)&archive, false);
if (result != S_OK)
continue;
}
#endif EXCLUDE_COM
if (!archive)
return E_FAIL;
HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback);
if(result == S_FALSE)
continue;
if(result != S_OK)
{
badResult = result;
if(result == E_ABORT)
break;
continue;
}
*archiveResult = archive.Detach();
#ifndef EXCLUDE_COM
*module = loader.Detach();
#endif
archiverInfoResult = archiverInfo;
int subExtIndex = archiverInfo.FindExtension(extension);
if (subExtIndex < 0)
subExtIndex = 0;
defaultItemName = GetDefaultName2(fileName,
archiverInfo.Extensions[subExtIndex].Extension,
archiverInfo.Extensions[subExtIndex].AddExtension);
return S_OK;
}
if (badResult != S_OK)
return badResult;
return S_FALSE;
}
HRESULT OpenArchive(const UString &filePath,
#ifndef EXCLUDE_COM
HMODULE *module,
#endif
IInArchive **archiveResult,
CArchiverInfo &archiverInfo,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback)
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<IInStream> inStream(inStreamSpec);
if (!inStreamSpec->Open(filePath))
return GetLastError();
return OpenArchive(inStream, ExtractFileNameFromPath(filePath),
#ifndef EXCLUDE_COM
module,
#endif
archiveResult, archiverInfo,
defaultItemName, openArchiveCallback);
}
static void MakeDefaultName(UString &name)
{
int dotPos = name.ReverseFind(L'.');
if (dotPos < 0)
return;
UString ext = name.Mid(dotPos + 1);
if (ext.IsEmpty())
return;
for (int pos = 0; pos < ext.Length(); pos++)
if (ext[pos] < L'0' || ext[pos] > L'9')
return;
name = name.Left(dotPos);
}
HRESULT OpenArchive(const UString &fileName,
#ifndef EXCLUDE_COM
HMODULE *module0,
HMODULE *module1,
#endif
IInArchive **archive0,
IInArchive **archive1,
CArchiverInfo &archiverInfo0,
CArchiverInfo &archiverInfo1,
UString &defaultItemName0,
UString &defaultItemName1,
IArchiveOpenCallback *openArchiveCallback)
{
HRESULT result = OpenArchive(fileName,
#ifndef EXCLUDE_COM
module0,
#endif
archive0, archiverInfo0, defaultItemName0, openArchiveCallback);
RINOK(result);
CMyComPtr<IInArchiveGetStream> getStream;
result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream);
if (result != S_OK || getStream == 0)
return S_OK;
CMyComPtr<ISequentialInStream> subSeqStream;
result = getStream->GetStream(0, &subSeqStream);
if (result != S_OK)
return S_OK;
CMyComPtr<IInStream> subStream;
if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK)
return S_OK;
if (!subStream)
return S_OK;
UInt32 numItems;
RINOK((*archive0)->GetNumberOfItems(&numItems));
if (numItems < 1)
return S_OK;
UString subPath;
RINOK(GetArchiveItemPath(*archive0, 0, subPath))
if (subPath.IsEmpty())
{
MakeDefaultName(defaultItemName0);
subPath = defaultItemName0;
if (archiverInfo0.Name.CompareNoCase(L"7z") == 0)
{
if (subPath.Right(3).CompareNoCase(L".7z") != 0)
subPath += L".7z";
}
}
else
subPath = ExtractFileNameFromPath(subPath);
CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
if (setSubArchiveName)
setSubArchiveName->SetSubArchiveName(subPath);
result = OpenArchive(subStream, subPath,
#ifndef EXCLUDE_COM
module1,
#endif
archive1, archiverInfo1, defaultItemName1, openArchiveCallback);
return S_OK;
}
HRESULT MyOpenArchive(const UString &archiveName,
#ifndef EXCLUDE_COM
HMODULE *module,
#endif
IInArchive **archive,
UString &defaultItemName,
IOpenCallbackUI *openCallbackUI)
{
COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec;
openCallbackSpec->Callback = openCallbackUI;
UString fullName;
int fileNamePartStartIndex;
NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
openCallbackSpec->Init(
fullName.Left(fileNamePartStartIndex),
fullName.Mid(fileNamePartStartIndex));
CArchiverInfo archiverInfo;
return OpenArchive(archiveName,
#ifndef EXCLUDE_COM
module,
#endif
archive,
archiverInfo,
defaultItemName,
openCallback);
}
HRESULT MyOpenArchive(const UString &archiveName,
#ifndef EXCLUDE_COM
HMODULE *module0,
HMODULE *module1,
#endif
IInArchive **archive0,
IInArchive **archive1,
UString &defaultItemName0,
UString &defaultItemName1,
UStringVector &volumePaths,
IOpenCallbackUI *openCallbackUI)
{
COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec;
openCallbackSpec->Callback = openCallbackUI;
UString fullName;
int fileNamePartStartIndex;
NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
UString prefix = fullName.Left(fileNamePartStartIndex);
UString name = fullName.Mid(fileNamePartStartIndex);
openCallbackSpec->Init(prefix, name);
CArchiverInfo archiverInfo0, archiverInfo1;
HRESULT result = OpenArchive(archiveName,
#ifndef EXCLUDE_COM
module0,
module1,
#endif
archive0,
archive1,
archiverInfo0,
archiverInfo1,
defaultItemName0,
defaultItemName1,
openCallback);
RINOK(result);
volumePaths.Add(prefix + name);
for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++)
volumePaths.Add(prefix + openCallbackSpec->FileNames[i]);
return S_OK;
}
HRESULT CArchiveLink::Close()
{
if (Archive1 != 0)
RINOK(Archive1->Close());
if (Archive0 != 0)
RINOK(Archive0->Close());
return S_OK;
}
void CArchiveLink::Release()
{
if (Archive1 != 0)
Archive1.Release();
if (Archive0 != 0)
Archive0.Release();
#ifndef EXCLUDE_COM
Library1.Free();
Library0.Free();
#endif
}
HRESULT OpenArchive(const UString &archiveName,
CArchiveLink &archiveLink,
IArchiveOpenCallback *openCallback)
{
return OpenArchive(archiveName,
#ifndef EXCLUDE_COM
&archiveLink.Library0, &archiveLink.Library1,
#endif
&archiveLink.Archive0, &archiveLink.Archive1,
archiveLink.ArchiverInfo0, archiveLink.ArchiverInfo1,
archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
openCallback);
}
HRESULT MyOpenArchive(const UString &archiveName,
CArchiveLink &archiveLink,
IOpenCallbackUI *openCallbackUI)
{
return MyOpenArchive(archiveName,
#ifndef EXCLUDE_COM
&archiveLink.Library0, &archiveLink.Library1,
#endif
&archiveLink.Archive0, &archiveLink.Archive1,
archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
archiveLink.VolumePaths,
openCallbackUI);
}
HRESULT ReOpenArchive(CArchiveLink &archiveLink,
const UString &fileName)
{
if (archiveLink.GetNumLevels() > 1)
return E_NOTIMPL;
if (archiveLink.GetNumLevels() == 0)
return MyOpenArchive(fileName, archiveLink, 0);
return ReOpenArchive(archiveLink.GetArchive(), fileName);
}