Add PhysFS 2.0.3.

This commit is contained in:
rude
2013-09-25 13:01:20 +02:00
parent f2a8dd670a
commit b170f825e8
523 changed files with 98507 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,104 @@
// ArchiveCommandLine.h
#ifndef __ARCHIVECOMMANDLINE_H
#define __ARCHIVECOMMANDLINE_H
#include "Common/Wildcard.h"
#include "Common/CommandLineParser.h"
#include "Extract.h"
#include "Update.h"
struct CArchiveCommandLineException: public AString
{
CArchiveCommandLineException(const char *errorMessage): AString(errorMessage) {}
};
namespace NCommandType { enum EEnum
{
kAdd = 0,
kUpdate,
kDelete,
kTest,
kExtract,
kFullExtract,
kList,
kBenchmark,
kInfo
};}
namespace NRecursedType { enum EEnum
{
kRecursed,
kWildCardOnlyRecursed,
kNonRecursed
};}
struct CArchiveCommand
{
NCommandType::EEnum CommandType;
bool IsFromExtractGroup() const;
bool IsFromUpdateGroup() const;
bool IsTestMode() const { return CommandType == NCommandType::kTest; }
NExtract::NPathMode::EEnum GetPathMode() const;
};
struct CArchiveCommandLineOptions
{
bool HelpMode;
#ifdef _WIN32
bool LargePages;
#endif
bool IsInTerminal;
bool IsStdOutTerminal;
bool IsStdErrTerminal;
bool StdInMode;
bool StdOutMode;
bool EnableHeaders;
bool YesToAll;
bool ShowDialog;
// NWildcard::CCensor ArchiveWildcardCensor;
NWildcard::CCensor WildcardCensor;
CArchiveCommand Command;
UString ArchiveName;
bool PasswordEnabled;
UString Password;
bool TechMode;
// Extract
bool AppendName;
UString OutputDir;
NExtract::NOverwriteMode::EEnum OverwriteMode;
UStringVector ArchivePathsSorted;
UStringVector ArchivePathsFullSorted;
CObjectVector<CProperty> ExtractProperties;
CUpdateOptions UpdateOptions;
UString ArcType;
bool EnablePercents;
// Benchmark
UInt32 NumIterations;
UInt32 NumThreads;
UInt32 DictionarySize;
UString Method;
CArchiveCommandLineOptions(): StdInMode(false), StdOutMode(false) {};
};
class CArchiveCommandLineParser
{
NCommandLineParser::CParser parser;
public:
CArchiveCommandLineParser();
void Parse1(const UStringVector &commandStrings, CArchiveCommandLineOptions &options);
void Parse2(CArchiveCommandLineOptions &options);
};
#endif
@@ -0,0 +1,479 @@
// ArchiveExtractCallback.cpp
#include "StdAfx.h"
#include "ArchiveExtractCallback.h"
#include "Common/Wildcard.h"
#include "Common/StringConvert.h"
#include "Common/ComTry.h"
#include "Windows/FileDir.h"
#include "Windows/FileFind.h"
#include "Windows/Time.h"
#include "Windows/Defs.h"
#include "Windows/PropVariant.h"
#include "Windows/PropVariantConversions.h"
#include "../../Common/FilePathAutoRename.h"
#include "../Common/ExtractingFilePath.h"
#include "OpenArchive.h"
using namespace NWindows;
static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";
static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";
static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
void CArchiveExtractCallback::Init(
IInArchive *archiveHandler,
IFolderArchiveExtractCallback *extractCallback2,
bool stdOutMode,
const UString &directoryPath,
const UStringVector &removePathParts,
const UString &itemDefaultName,
const FILETIME &utcLastWriteTimeDefault,
UInt32 attributesDefault,
UInt64 packSize)
{
_stdOutMode = stdOutMode;
_numErrors = 0;
_unpTotal = 1;
_packTotal = packSize;
_extractCallback2 = extractCallback2;
_compressProgress.Release();
_extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
LocalProgressSpec->Init(extractCallback2, true);
LocalProgressSpec->SendProgress = false;
_itemDefaultName = itemDefaultName;
_utcLastWriteTimeDefault = utcLastWriteTimeDefault;
_attributesDefault = attributesDefault;
_removePathParts = removePathParts;
_archiveHandler = archiveHandler;
_directoryPath = directoryPath;
NFile::NName::NormalizeDirPathPrefix(_directoryPath);
}
STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
{
COM_TRY_BEGIN
_unpTotal = size;
if (!_multiArchives && _extractCallback2)
return _extractCallback2->SetTotal(size);
return S_OK;
COM_TRY_END
}
static void NormalizeVals(UInt64 &v1, UInt64 &v2)
{
const UInt64 kMax = (UInt64)1 << 31;
while (v1 > kMax)
{
v1 >>= 1;
v2 >>= 1;
}
}
static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
{
NormalizeVals(packTotal, unpTotal);
NormalizeVals(unpCur, unpTotal);
if (unpTotal == 0)
unpTotal = 1;
return unpCur * packTotal / unpTotal;
}
STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
{
COM_TRY_BEGIN
if (!_extractCallback2)
return S_OK;
if (_multiArchives)
{
if (completeValue != NULL)
{
UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
return _extractCallback2->SetCompleted(&packCur);
}
}
return _extractCallback2->SetCompleted(completeValue);
COM_TRY_END
}
STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
{
COM_TRY_BEGIN
return _localProgress->SetRatioInfo(inSize, outSize);
COM_TRY_END
}
void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)
{
fullPath = _directoryPath;
for(int i = 0; i < dirPathParts.Size(); i++)
{
if (i > 0)
fullPath += wchar_t(NFile::NName::kDirDelimiter);
fullPath += dirPathParts[i];
NFile::NDirectory::MyCreateDirectory(fullPath);
}
}
static UString MakePathNameFromParts(const UStringVector &parts)
{
UString result;
for(int i = 0; i < parts.Size(); i++)
{
if(i != 0)
result += wchar_t(NFile::NName::kDirDelimiter);
result += parts[i];
}
return result;
}
HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
{
filetimeIsDefined = false;
NCOM::CPropVariant prop;
RINOK(_archiveHandler->GetProperty(index, propID, &prop));
if (prop.vt == VT_FILETIME)
{
filetime = prop.filetime;
filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
}
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
{
COM_TRY_BEGIN
*outStream = 0;
_outFileStream.Release();
_encrypted = false;
_isSplit = false;
_curSize = 0;
UString fullPath;
RINOK(GetArchiveItemPath(_archiveHandler, index, _itemDefaultName, fullPath));
RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory));
_filePath = fullPath;
{
NCOM::CPropVariant prop;
RINOK(_archiveHandler->GetProperty(index, kpidPosition, &prop));
if (prop.vt != VT_EMPTY)
{
if (prop.vt != VT_UI8)
return E_FAIL;
_position = prop.uhVal.QuadPart;
_isSplit = true;
}
}
RINOK(IsArchiveItemProp(_archiveHandler, index, kpidEncrypted, _encrypted));
bool newFileSizeDefined;
UInt64 newFileSize;
{
NCOM::CPropVariant prop;
RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
newFileSizeDefined = (prop.vt != VT_EMPTY);
if (newFileSizeDefined)
{
newFileSize = ConvertPropVariantToUInt64(prop);
_curSize = newFileSize;
}
}
if(askExtractMode == NArchive::NExtract::NAskMode::kExtract)
{
if (_stdOutMode)
{
CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
*outStream = outStreamLoc.Detach();
return S_OK;
}
{
NCOM::CPropVariant prop;
RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &prop));
if (prop.vt == VT_EMPTY)
{
_processedFileInfo.Attributes = _attributesDefault;
_processedFileInfo.AttributesAreDefined = false;
}
else
{
if (prop.vt != VT_UI4)
return E_FAIL;
_processedFileInfo.Attributes = prop.ulVal;
_processedFileInfo.AttributesAreDefined = true;
}
}
RINOK(GetTime(index, kpidCreationTime, _processedFileInfo.CreationTime,
_processedFileInfo.IsCreationTimeDefined));
RINOK(GetTime(index, kpidLastWriteTime, _processedFileInfo.LastWriteTime,
_processedFileInfo.IsLastWriteTimeDefined));
RINOK(GetTime(index, kpidLastAccessTime, _processedFileInfo.LastAccessTime,
_processedFileInfo.IsLastAccessTimeDefined));
bool isAnti = false;
RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti));
UStringVector pathParts;
SplitPathToParts(fullPath, pathParts);
if(pathParts.IsEmpty())
return E_FAIL;
int numRemovePathParts = 0;
switch(_pathMode)
{
case NExtract::NPathMode::kFullPathnames:
break;
case NExtract::NPathMode::kCurrentPathnames:
{
numRemovePathParts = _removePathParts.Size();
if (pathParts.Size() <= numRemovePathParts)
return E_FAIL;
for (int i = 0; i < numRemovePathParts; i++)
if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
return E_FAIL;
break;
}
case NExtract::NPathMode::kNoPathnames:
{
numRemovePathParts = pathParts.Size() - 1;
break;
}
}
pathParts.Delete(0, numRemovePathParts);
MakeCorrectPath(pathParts);
UString processedPath = MakePathNameFromParts(pathParts);
if (!isAnti)
{
if (!_processedFileInfo.IsDirectory)
{
if (!pathParts.IsEmpty())
pathParts.DeleteBack();
}
if (!pathParts.IsEmpty())
{
UString fullPathNew;
CreateComplexDirectory(pathParts, fullPathNew);
if (_processedFileInfo.IsDirectory)
NFile::NDirectory::SetDirTime(fullPathNew,
(WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL,
(WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL,
(WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault);
}
}
UString fullProcessedPath = _directoryPath + processedPath;
if(_processedFileInfo.IsDirectory)
{
_diskFilePath = fullProcessedPath;
if (isAnti)
NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
return S_OK;
}
if (!_isSplit)
{
NFile::NFind::CFileInfoW fileInfo;
if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
{
switch(_overwriteMode)
{
case NExtract::NOverwriteMode::kSkipExisting:
return S_OK;
case NExtract::NOverwriteMode::kAskBefore:
{
Int32 overwiteResult;
RINOK(_extractCallback2->AskOverwrite(
fullProcessedPath, &fileInfo.LastWriteTime, &fileInfo.Size, fullPath,
_processedFileInfo.IsLastWriteTimeDefined ? &_processedFileInfo.LastWriteTime : NULL,
newFileSizeDefined ? &newFileSize : NULL,
&overwiteResult))
switch(overwiteResult)
{
case NOverwriteAnswer::kCancel:
return E_ABORT;
case NOverwriteAnswer::kNo:
return S_OK;
case NOverwriteAnswer::kNoToAll:
_overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
return S_OK;
case NOverwriteAnswer::kYesToAll:
_overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
break;
case NOverwriteAnswer::kYes:
break;
case NOverwriteAnswer::kAutoRename:
_overwriteMode = NExtract::NOverwriteMode::kAutoRename;
break;
default:
return E_FAIL;
}
}
}
if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
{
if (!AutoRenamePath(fullProcessedPath))
{
UString message = UString(kCantAutoRename) + fullProcessedPath;
RINOK(_extractCallback2->MessageError(message));
return E_FAIL;
}
}
else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
{
UString existPath = fullProcessedPath;
if (!AutoRenamePath(existPath))
{
UString message = kCantAutoRename + fullProcessedPath;
RINOK(_extractCallback2->MessageError(message));
return E_FAIL;
}
if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
{
UString message = UString(kCantRenameFile) + fullProcessedPath;
RINOK(_extractCallback2->MessageError(message));
return E_FAIL;
}
}
else
if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
{
UString message = UString(kCantDeleteOutputFile) + fullProcessedPath;
RINOK(_extractCallback2->MessageError(message));
return S_OK;
// return E_FAIL;
}
}
}
if (!isAnti)
{
_outFileStreamSpec = new COutFileStream;
CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
{
// if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
{
UString message = L"can not open output file " + fullProcessedPath;
RINOK(_extractCallback2->MessageError(message));
return S_OK;
}
}
if (_isSplit)
{
RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
}
_outFileStream = outStreamLoc;
*outStream = outStreamLoc.Detach();
}
_diskFilePath = fullProcessedPath;
}
else
{
*outStream = NULL;
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
{
COM_TRY_BEGIN
_extractMode = false;
switch (askExtractMode)
{
case NArchive::NExtract::NAskMode::kExtract:
_extractMode = true;
};
return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDirectory,
askExtractMode, _isSplit ? &_position: 0);
COM_TRY_END
}
STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
{
COM_TRY_BEGIN
switch(operationResult)
{
case NArchive::NExtract::NOperationResult::kOK:
case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
case NArchive::NExtract::NOperationResult::kCRCError:
case NArchive::NExtract::NOperationResult::kDataError:
break;
default:
_outFileStream.Release();
return E_FAIL;
}
if (_outFileStream != NULL)
{
_outFileStreamSpec->SetTime(
(WriteCreated && _processedFileInfo.IsCreationTimeDefined) ? &_processedFileInfo.CreationTime : NULL,
(WriteAccessed && _processedFileInfo.IsLastAccessTimeDefined) ? &_processedFileInfo.LastAccessTime : NULL,
(WriteModified && _processedFileInfo.IsLastWriteTimeDefined) ? &_processedFileInfo.LastWriteTime : &_utcLastWriteTimeDefault);
_curSize = _outFileStreamSpec->ProcessedSize;
RINOK(_outFileStreamSpec->Close());
_outFileStream.Release();
}
UnpackSize += _curSize;
if (_processedFileInfo.IsDirectory)
NumFolders++;
else
NumFiles++;
if (_extractMode && _processedFileInfo.AttributesAreDefined)
NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
return S_OK;
COM_TRY_END
}
/*
STDMETHODIMP CArchiveExtractCallback::GetInStream(
const wchar_t *name, ISequentialInStream **inStream)
{
COM_TRY_BEGIN
CInFileStream *inFile = new CInFileStream;
CMyComPtr<ISequentialInStream> inStreamTemp = inFile;
if (!inFile->Open(_srcDirectoryPrefix + name))
return ::GetLastError();
*inStream = inStreamTemp.Detach();
return S_OK;
COM_TRY_END
}
*/
STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
{
COM_TRY_BEGIN
if (!_cryptoGetTextPassword)
{
RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
&_cryptoGetTextPassword));
}
return _cryptoGetTextPassword->CryptoGetTextPassword(password);
COM_TRY_END
}
@@ -0,0 +1,139 @@
// ArchiveExtractCallback.h
#ifndef __ARCHIVEEXTRACTCALLBACK_H
#define __ARCHIVEEXTRACTCALLBACK_H
#include "../../Archive/IArchive.h"
#include "IFileExtractCallback.h"
#include "Common/MyString.h"
#include "Common/MyCom.h"
#include "../../Common/FileStreams.h"
#include "../../Common/ProgressUtils.h"
#include "../../IPassword.h"
#include "ExtractMode.h"
class CArchiveExtractCallback:
public IArchiveExtractCallback,
// public IArchiveVolumeExtractCallback,
public ICryptoGetTextPassword,
public ICompressProgressInfo,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo)
// COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback)
// IProgress
STDMETHOD(SetTotal)(UInt64 size);
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
// IExtractCallBack
STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
STDMETHOD(PrepareOperation)(Int32 askExtractMode);
STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
// IArchiveVolumeExtractCallback
// STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream);
// ICryptoGetTextPassword
STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
private:
CMyComPtr<IInArchive> _archiveHandler;
CMyComPtr<IFolderArchiveExtractCallback> _extractCallback2;
CMyComPtr<ICompressProgressInfo> _compressProgress;
CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
UString _directoryPath;
NExtract::NPathMode::EEnum _pathMode;
NExtract::NOverwriteMode::EEnum _overwriteMode;
UString _filePath;
UInt64 _position;
bool _isSplit;
UString _diskFilePath;
bool _extractMode;
bool WriteModified;
bool WriteCreated;
bool WriteAccessed;
bool _encrypted;
struct CProcessedFileInfo
{
FILETIME CreationTime;
FILETIME LastWriteTime;
FILETIME LastAccessTime;
UInt32 Attributes;
bool IsCreationTimeDefined;
bool IsLastWriteTimeDefined;
bool IsLastAccessTimeDefined;
bool IsDirectory;
bool AttributesAreDefined;
} _processedFileInfo;
UInt64 _curSize;
COutFileStream *_outFileStreamSpec;
CMyComPtr<ISequentialOutStream> _outFileStream;
UStringVector _removePathParts;
UString _itemDefaultName;
FILETIME _utcLastWriteTimeDefault;
UInt32 _attributesDefault;
bool _stdOutMode;
void CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath);
HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined);
public:
CArchiveExtractCallback():
WriteModified(true),
WriteCreated(false),
WriteAccessed(false),
_multiArchives(false)
{
LocalProgressSpec = new CLocalProgress();
_localProgress = LocalProgressSpec;
}
CLocalProgress *LocalProgressSpec;
CMyComPtr<ICompressProgressInfo> _localProgress;
UInt64 _packTotal;
UInt64 _unpTotal;
bool _multiArchives;
UInt64 NumFolders;
UInt64 NumFiles;
UInt64 UnpackSize;
void InitForMulti(bool multiArchives,
NExtract::NPathMode::EEnum pathMode,
NExtract::NOverwriteMode::EEnum overwriteMode)
{
_multiArchives = multiArchives; NumFolders = NumFiles = UnpackSize = 0;
_pathMode = pathMode;
_overwriteMode = overwriteMode;
}
void Init(
IInArchive *archiveHandler,
IFolderArchiveExtractCallback *extractCallback2,
bool stdOutMode,
const UString &directoryPath,
const UStringVector &removePathParts,
const UString &itemDefaultName,
const FILETIME &utcLastWriteTimeDefault,
UInt32 attributesDefault,
UInt64 packSize);
UInt64 _numErrors;
};
#endif
@@ -0,0 +1,46 @@
// ArchiveName.cpp
#include "StdAfx.h"
#include "Windows/FileFind.h"
#include "Windows/FileDir.h"
using namespace NWindows;
UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName)
{
UString resultName = L"Archive";
if (fromPrev)
{
UString dirPrefix;
if (NFile::NDirectory::GetOnlyDirPrefix(srcName, dirPrefix))
{
if (dirPrefix.Length() > 0)
if (dirPrefix[dirPrefix.Length() - 1] == '\\')
{
dirPrefix.Delete(dirPrefix.Length() - 1);
NFile::NFind::CFileInfoW fileInfo;
if (NFile::NFind::FindFile(dirPrefix, fileInfo))
resultName = fileInfo.Name;
}
}
}
else
{
NFile::NFind::CFileInfoW fileInfo;
if (!NFile::NFind::FindFile(srcName, fileInfo))
return resultName;
resultName = fileInfo.Name;
if (!fileInfo.IsDirectory() && !keepName)
{
int dotPos = resultName.ReverseFind('.');
if (dotPos > 0)
{
UString archiveName2 = resultName.Left(dotPos);
if (archiveName2.ReverseFind('.') < 0)
resultName = archiveName2;
}
}
}
return resultName;
}
@@ -0,0 +1,10 @@
// ArchiveName.h
#ifndef __ARCHIVENAME_H
#define __ARCHIVENAME_H
#include "Common/MyString.h"
UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName);
#endif
@@ -0,0 +1,137 @@
// ArchiveOpenCallback.cpp
#include "StdAfx.h"
#include "ArchiveOpenCallback.h"
#include "Common/StringConvert.h"
#include "Common/ComTry.h"
#include "Windows/PropVariant.h"
#include "../../Common/FileStreams.h"
using namespace NWindows;
STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes)
{
COM_TRY_BEGIN
if (!Callback)
return S_OK;
return Callback->SetTotal(files, bytes);
COM_TRY_END
}
STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes)
{
COM_TRY_BEGIN
if (!Callback)
return S_OK;
return Callback->SetTotal(files, bytes);
COM_TRY_END
}
STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NCOM::CPropVariant propVariant;
if (_subArchiveMode)
{
switch(propID)
{
case kpidName:
propVariant = _subArchiveName;
break;
}
propVariant.Detach(value);
return S_OK;
}
switch(propID)
{
case kpidName:
propVariant = _fileInfo.Name;
break;
case kpidIsFolder:
propVariant = _fileInfo.IsDirectory();
break;
case kpidSize:
propVariant = _fileInfo.Size;
break;
case kpidAttributes:
propVariant = (UInt32)_fileInfo.Attributes;
break;
case kpidLastAccessTime:
propVariant = _fileInfo.LastAccessTime;
break;
case kpidCreationTime:
propVariant = _fileInfo.CreationTime;
break;
case kpidLastWriteTime:
propVariant = _fileInfo.LastWriteTime;
break;
}
propVariant.Detach(value);
return S_OK;
COM_TRY_END
}
int COpenCallbackImp::FindName(const UString &name)
{
for (int i = 0; i < FileNames.Size(); i++)
if (name.CompareNoCase(FileNames[i]) == 0)
return i;
return -1;
}
struct CInFileStreamVol: public CInFileStream
{
UString Name;
COpenCallbackImp *OpenCallbackImp;
CMyComPtr<IArchiveOpenCallback> OpenCallbackRef;
~CInFileStreamVol()
{
int index = OpenCallbackImp->FindName(Name);
if (index >= 0)
OpenCallbackImp->FileNames.Delete(index);
}
};
STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream)
{
COM_TRY_BEGIN
if (_subArchiveMode)
return S_FALSE;
if (Callback)
{
RINOK(Callback->CheckBreak());
}
*inStream = NULL;
UString fullPath = _folderPrefix + name;
if (!NFile::NFind::FindFile(fullPath, _fileInfo))
return S_FALSE;
if (_fileInfo.IsDirectory())
return S_FALSE;
CInFileStreamVol *inFile = new CInFileStreamVol;
CMyComPtr<IInStream> inStreamTemp = inFile;
if (!inFile->Open(fullPath))
return ::GetLastError();
*inStream = inStreamTemp.Detach();
inFile->Name = name;
inFile->OpenCallbackImp = this;
inFile->OpenCallbackRef = this;
FileNames.Add(name);
TotalSize += _fileInfo.Size;
return S_OK;
COM_TRY_END
}
#ifndef _NO_CRYPTO
STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password)
{
COM_TRY_BEGIN
if (!Callback)
return E_NOTIMPL;
return Callback->CryptoGetTextPassword(password);
COM_TRY_END
}
#endif
@@ -0,0 +1,93 @@
// ArchiveOpenCallback.h
#ifndef __ARCHIVE_OPEN_CALLBACK_H
#define __ARCHIVE_OPEN_CALLBACK_H
#include "Common/MyString.h"
#include "Common/MyCom.h"
#include "Windows/FileFind.h"
#ifndef _NO_CRYPTO
#include "../../IPassword.h"
#endif
#include "../../Archive/IArchive.h"
struct IOpenCallbackUI
{
virtual HRESULT CheckBreak() = 0;
virtual HRESULT SetTotal(const UInt64 *files, const UInt64 *bytes) = 0;
virtual HRESULT SetCompleted(const UInt64 *files, const UInt64 *bytes) = 0;
#ifndef _NO_CRYPTO
virtual HRESULT CryptoGetTextPassword(BSTR *password) = 0;
virtual HRESULT GetPasswordIfAny(UString &password) = 0;
virtual bool WasPasswordAsked() = 0;
virtual void ClearPasswordWasAskedFlag() = 0;
#endif
};
class COpenCallbackImp:
public IArchiveOpenCallback,
public IArchiveOpenVolumeCallback,
public IArchiveOpenSetSubArchiveName,
#ifndef _NO_CRYPTO
public ICryptoGetTextPassword,
#endif
public CMyUnknownImp
{
public:
#ifndef _NO_CRYPTO
MY_UNKNOWN_IMP3(
IArchiveOpenVolumeCallback,
ICryptoGetTextPassword,
IArchiveOpenSetSubArchiveName
)
#else
MY_UNKNOWN_IMP2(
IArchiveOpenVolumeCallback,
IArchiveOpenSetSubArchiveName
)
#endif
STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
// IArchiveOpenVolumeCallback
STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value);
STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream);
#ifndef _NO_CRYPTO
// ICryptoGetTextPassword
STDMETHOD(CryptoGetTextPassword)(BSTR *password);
#endif
STDMETHOD(SetSubArchiveName(const wchar_t *name))
{
_subArchiveMode = true;
_subArchiveName = name;
return S_OK;
}
private:
UString _folderPrefix;
NWindows::NFile::NFind::CFileInfoW _fileInfo;
bool _subArchiveMode;
UString _subArchiveName;
public:
UStringVector FileNames;
IOpenCallbackUI *Callback;
UInt64 TotalSize;
COpenCallbackImp(): Callback(NULL) {}
void Init(const UString &folderPrefix, const UString &fileName)
{
_folderPrefix = folderPrefix;
if (!NWindows::NFile::NFind::FindFile(_folderPrefix + fileName, _fileInfo))
throw 1;
FileNames.Clear();
_subArchiveMode = false;
TotalSize = 0;
}
int FindName(const UString &name);
};
#endif
@@ -0,0 +1,26 @@
// DefaultName.cpp
#include "StdAfx.h"
#include "DefaultName.h"
static const wchar_t *kEmptyFileAlias = L"[Content]";
UString GetDefaultName2(const UString &fileName,
const UString &extension, const UString &addSubExtension)
{
int extLength = extension.Length();
int fileNameLength = fileName.Length();
if (fileNameLength > extLength + 1)
{
int dotPos = fileNameLength - (extLength + 1);
if (fileName[dotPos] == '.')
if (extension.CompareNoCase(fileName.Mid(dotPos + 1)) == 0)
return fileName.Left(dotPos) + addSubExtension;
}
int dotPos = fileName.ReverseFind(L'.');
if (dotPos > 0)
return fileName.Left(dotPos) + addSubExtension;
return kEmptyFileAlias;
}
@@ -0,0 +1,11 @@
// DefaultName.h
#ifndef __DEFAULTNAME_H
#define __DEFAULTNAME_H
#include "Common/MyString.h"
UString GetDefaultName2(const UString &fileName,
const UString &extension, const UString &addSubExtension);
#endif
@@ -0,0 +1,34 @@
// DirItem.h
#ifndef __DIR_ITEM_H
#define __DIR_ITEM_H
#include "Common/MyString.h"
#include "Common/Types.h"
struct CDirItem
{
UInt32 Attributes;
FILETIME CreationTime;
FILETIME LastAccessTime;
FILETIME LastWriteTime;
UInt64 Size;
UString Name;
UString FullPath;
bool IsDirectory() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
};
struct CArchiveItem
{
bool IsDirectory;
// DWORD Attributes;
// NWindows::NCOM::CPropVariant LastWriteTime;
FILETIME LastWriteTime;
bool SizeIsDefined;
UInt64 Size;
UString Name;
bool Censored;
int IndexInServer;
};
#endif
@@ -0,0 +1,281 @@
// EnumDirItems.cpp
#include "StdAfx.h"
#include "Common/StringConvert.h"
#include "Common/Wildcard.h"
#include "Common/MyCom.h"
#include "EnumDirItems.h"
using namespace NWindows;
using namespace NFile;
using namespace NName;
void AddDirFileInfo(
const UString &prefix, // prefix for logical path
const UString &fullPathName, // path on disk: can be relative to some basePrefix
const NFind::CFileInfoW &fileInfo,
CObjectVector<CDirItem> &dirItems)
{
CDirItem item;
item.Attributes = fileInfo.Attributes;
item.Size = fileInfo.Size;
item.CreationTime = fileInfo.CreationTime;
item.LastAccessTime = fileInfo.LastAccessTime;
item.LastWriteTime = fileInfo.LastWriteTime;
item.Name = prefix + fileInfo.Name;
item.FullPath = fullPathName;
dirItems.Add(item);
}
static void EnumerateDirectory(
const UString &baseFolderPrefix, // base (disk) prefix for scanning
const UString &directory, // additional disk prefix starting from baseFolderPrefix
const UString &prefix, // logical prefix
CObjectVector<CDirItem> &dirItems,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes)
{
NFind::CEnumeratorW enumerator(baseFolderPrefix + directory + wchar_t(kAnyStringWildcard));
for (;;)
{
NFind::CFileInfoW fileInfo;
bool found;
if (!enumerator.Next(fileInfo, found))
{
errorCodes.Add(::GetLastError());
errorPaths.Add(baseFolderPrefix + directory);
return;
}
if (!found)
break;
AddDirFileInfo(prefix, directory + fileInfo.Name, fileInfo, dirItems);
if (fileInfo.IsDirectory())
{
EnumerateDirectory(baseFolderPrefix, directory + fileInfo.Name + wchar_t(kDirDelimiter),
prefix + fileInfo.Name + wchar_t(kDirDelimiter), dirItems, errorPaths, errorCodes);
}
}
}
void EnumerateDirItems(
const UString &baseFolderPrefix, // base (disk) prefix for scanning
const UStringVector &fileNames, // names relative to baseFolderPrefix
const UString &archiveNamePrefix,
CObjectVector<CDirItem> &dirItems,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes)
{
for(int i = 0; i < fileNames.Size(); i++)
{
const UString &fileName = fileNames[i];
NFind::CFileInfoW fileInfo;
if (!NFind::FindFile(baseFolderPrefix + fileName, fileInfo))
{
errorCodes.Add(::GetLastError());
errorPaths.Add(baseFolderPrefix + fileName);
continue;
}
AddDirFileInfo(archiveNamePrefix, fileName, fileInfo, dirItems);
if (fileInfo.IsDirectory())
{
EnumerateDirectory(baseFolderPrefix, fileName + wchar_t(kDirDelimiter),
archiveNamePrefix + fileInfo.Name + wchar_t(kDirDelimiter),
dirItems, errorPaths, errorCodes);
}
}
}
static HRESULT EnumerateDirItems(
const NWildcard::CCensorNode &curNode,
const UString &diskPrefix, // full disk path prefix
const UString &archivePrefix, // prefix from root
const UStringVector &addArchivePrefix, // prefix from curNode
CObjectVector<CDirItem> &dirItems,
bool enterToSubFolders,
IEnumDirItemCallback *callback,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes)
{
if (!enterToSubFolders)
if (curNode.NeedCheckSubDirs())
enterToSubFolders = true;
if (callback)
RINOK(callback->CheckBreak());
// try direct_names case at first
if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
{
// check that all names are direct
int i;
for (i = 0; i < curNode.IncludeItems.Size(); i++)
{
const NWildcard::CItem &item = curNode.IncludeItems[i];
if (item.Recursive || item.PathParts.Size() != 1)
break;
const UString &name = item.PathParts.Front();
if (name.IsEmpty() || DoesNameContainWildCard(name))
break;
}
if (i == curNode.IncludeItems.Size())
{
// all names are direct (no wildcards)
// so we don't need file_system's dir enumerator
CRecordVector<bool> needEnterVector;
for (i = 0; i < curNode.IncludeItems.Size(); i++)
{
const NWildcard::CItem &item = curNode.IncludeItems[i];
const UString &name = item.PathParts.Front();
const UString fullPath = diskPrefix + name;
NFind::CFileInfoW fileInfo;
if (!NFind::FindFile(fullPath, fileInfo))
{
errorCodes.Add(::GetLastError());
errorPaths.Add(fullPath);
continue;
}
bool isDir = fileInfo.IsDirectory();
if (isDir && !item.ForDir || !isDir && !item.ForFile)
{
errorCodes.Add((DWORD)E_FAIL);
errorPaths.Add(fullPath);
continue;
}
const UString realName = fileInfo.Name;
const UString realDiskPath = diskPrefix + realName;
{
UStringVector pathParts;
pathParts.Add(fileInfo.Name);
if (curNode.CheckPathToRoot(false, pathParts, !isDir))
continue;
}
AddDirFileInfo(archivePrefix, realDiskPath, fileInfo, dirItems);
if (!isDir)
continue;
UStringVector addArchivePrefixNew;
const NWildcard::CCensorNode *nextNode = 0;
int index = curNode.FindSubNode(name);
if (index >= 0)
{
for (int t = needEnterVector.Size(); t <= index; t++)
needEnterVector.Add(true);
needEnterVector[index] = false;
nextNode = &curNode.SubNodes[index];
}
else
{
nextNode = &curNode;
addArchivePrefixNew.Add(name); // don't change it to realName. It's for shortnames support
}
RINOK(EnumerateDirItems(*nextNode,
realDiskPath + wchar_t(kDirDelimiter),
archivePrefix + realName + wchar_t(kDirDelimiter),
addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes));
}
for (i = 0; i < curNode.SubNodes.Size(); i++)
{
if (i < needEnterVector.Size())
if (!needEnterVector[i])
continue;
const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
const UString fullPath = diskPrefix + nextNode.Name;
NFind::CFileInfoW fileInfo;
if (!NFind::FindFile(fullPath, fileInfo))
{
if (!nextNode.AreThereIncludeItems())
continue;
errorCodes.Add(::GetLastError());
errorPaths.Add(fullPath);
continue;
}
if (!fileInfo.IsDirectory())
{
errorCodes.Add((DWORD)E_FAIL);
errorPaths.Add(fullPath);
continue;
}
RINOK(EnumerateDirItems(nextNode,
diskPrefix + fileInfo.Name + wchar_t(kDirDelimiter),
archivePrefix + fileInfo.Name + wchar_t(kDirDelimiter),
UStringVector(), dirItems, false, callback, errorPaths, errorCodes));
}
return S_OK;
}
}
NFind::CEnumeratorW enumerator(diskPrefix + wchar_t(kAnyStringWildcard));
for (;;)
{
NFind::CFileInfoW fileInfo;
bool found;
if (!enumerator.Next(fileInfo, found))
{
errorCodes.Add(::GetLastError());
errorPaths.Add(diskPrefix);
break;
}
if (!found)
break;
if (callback)
RINOK(callback->CheckBreak());
const UString &name = fileInfo.Name;
bool enterToSubFolders2 = enterToSubFolders;
UStringVector addArchivePrefixNew = addArchivePrefix;
addArchivePrefixNew.Add(name);
{
UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fileInfo.IsDirectory()))
continue;
}
if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fileInfo.IsDirectory()))
{
AddDirFileInfo(archivePrefix, diskPrefix + name, fileInfo, dirItems);
if (fileInfo.IsDirectory())
enterToSubFolders2 = true;
}
if (!fileInfo.IsDirectory())
continue;
const NWildcard::CCensorNode *nextNode = 0;
if (addArchivePrefix.IsEmpty())
{
int index = curNode.FindSubNode(name);
if (index >= 0)
nextNode = &curNode.SubNodes[index];
}
if (!enterToSubFolders2 && nextNode == 0)
continue;
addArchivePrefixNew = addArchivePrefix;
if (nextNode == 0)
{
nextNode = &curNode;
addArchivePrefixNew.Add(name);
}
RINOK(EnumerateDirItems(*nextNode,
diskPrefix + name + wchar_t(kDirDelimiter),
archivePrefix + name + wchar_t(kDirDelimiter),
addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes));
}
return S_OK;
}
HRESULT EnumerateItems(
const NWildcard::CCensor &censor,
CObjectVector<CDirItem> &dirItems,
IEnumDirItemCallback *callback,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes)
{
for (int i = 0; i < censor.Pairs.Size(); i++)
{
const NWildcard::CPair &pair = censor.Pairs[i];
RINOK(EnumerateDirItems(pair.Head, pair.Prefix, L"", UStringVector(), dirItems, false,
callback, errorPaths, errorCodes));
}
return S_OK;
}
@@ -0,0 +1,39 @@
// EnumDirItems.h
#ifndef __ENUM_DIR_ITEMS_H
#define __ENUM_DIR_ITEMS_H
#include "Common/Wildcard.h"
#include "DirItem.h"
#include "Windows/FileFind.h"
void AddDirFileInfo(
const UString &prefix,
const UString &fullPathName,
const NWindows::NFile::NFind::CFileInfoW &fileInfo,
CObjectVector<CDirItem> &dirItems);
void EnumerateDirItems(
const UString &baseFolderPrefix,
const UStringVector &fileNames,
const UString &archiveNamePrefix,
CObjectVector<CDirItem> &dirItems,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes);
struct IEnumDirItemCallback
{
virtual HRESULT CheckBreak() { return S_OK; }
};
HRESULT EnumerateItems(
const NWildcard::CCensor &censor,
CObjectVector<CDirItem> &dirItems,
IEnumDirItemCallback *callback,
UStringVector &errorPaths,
CRecordVector<DWORD> &errorCodes);
#endif
@@ -0,0 +1,27 @@
// ExitCode.h
#ifndef __EXIT_CODE_H
#define __EXIT_CODE_H
namespace NExitCode {
enum EEnum {
kSuccess = 0, // Successful operation
kWarning = 1, // Non fatal error(s) occurred
kFatalError = 2, // A fatal error occurred
// kCRCError = 3, // A CRC error occurred when unpacking
// kLockedArchive = 4, // Attempt to modify an archive previously locked
// kWriteError = 5, // Write to disk error
// kOpenError = 6, // Open file error
kUserError = 7, // Command line option error
kMemoryError = 8, // Not enough memory for operation
// kCreateFileError = 9, // Create file error
kUserBreak = 255 // User stopped the process
};
}
#endif
@@ -0,0 +1,187 @@
// Extract.cpp
#include "StdAfx.h"
#include "Extract.h"
#include "Windows/Defs.h"
#include "Windows/FileDir.h"
#include "OpenArchive.h"
#include "SetProperties.h"
using namespace NWindows;
HRESULT DecompressArchive(
IInArchive *archive,
UInt64 packSize,
const UString &defaultName,
const NWildcard::CCensorNode &wildcardCensor,
const CExtractOptions &options,
IExtractCallbackUI *callback,
CArchiveExtractCallback *extractCallbackSpec,
UString &errorMessage)
{
CRecordVector<UInt32> realIndices;
UInt32 numItems;
RINOK(archive->GetNumberOfItems(&numItems));
for(UInt32 i = 0; i < numItems; i++)
{
UString filePath;
RINOK(GetArchiveItemPath(archive, i, options.DefaultItemName, filePath));
bool isFolder;
RINOK(IsArchiveItemFolder(archive, i, isFolder));
if (!wildcardCensor.CheckPath(filePath, !isFolder))
continue;
realIndices.Add(i);
}
if (realIndices.Size() == 0)
{
callback->ThereAreNoFiles();
return S_OK;
}
UStringVector removePathParts;
UString outDir = options.OutputDir;
outDir.Replace(L"*", defaultName);
if(!outDir.IsEmpty())
if(!NFile::NDirectory::CreateComplexDirectory(outDir))
{
HRESULT res = ::GetLastError();
if (res == S_OK)
res = E_FAIL;
errorMessage = ((UString)L"Can not create output directory ") + outDir;
return res;
}
extractCallbackSpec->Init(
archive,
callback,
options.StdOutMode,
outDir,
removePathParts,
options.DefaultItemName,
options.ArchiveFileInfo.LastWriteTime,
options.ArchiveFileInfo.Attributes,
packSize);
#ifdef COMPRESS_MT
RINOK(SetProperties(archive, options.Properties));
#endif
HRESULT result = archive->Extract(&realIndices.Front(),
realIndices.Size(), options.TestMode? 1: 0, extractCallbackSpec);
return callback->ExtractResult(result);
}
HRESULT DecompressArchives(
CCodecs *codecs,
UStringVector &archivePaths, UStringVector &archivePathsFull,
const NWildcard::CCensorNode &wildcardCensor,
const CExtractOptions &optionsSpec,
IOpenCallbackUI *openCallback,
IExtractCallbackUI *extractCallback,
UString &errorMessage,
CDecompressStat &stat)
{
stat.Clear();
CExtractOptions options = optionsSpec;
int i;
UInt64 totalPackSize = 0;
CRecordVector<UInt64> archiveSizes;
for (i = 0; i < archivePaths.Size(); i++)
{
const UString &archivePath = archivePaths[i];
NFile::NFind::CFileInfoW archiveFileInfo;
if (!NFile::NFind::FindFile(archivePath, archiveFileInfo))
throw "there is no such archive";
if (archiveFileInfo.IsDirectory())
throw "can't decompress folder";
archiveSizes.Add(archiveFileInfo.Size);
totalPackSize += archiveFileInfo.Size;
}
CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec);
bool multi = (archivePaths.Size() > 1);
extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode);
if (multi)
{
RINOK(extractCallback->SetTotal(totalPackSize));
}
for (i = 0; i < archivePaths.Size(); i++)
{
const UString &archivePath = archivePaths[i];
NFile::NFind::CFileInfoW archiveFileInfo;
if (!NFile::NFind::FindFile(archivePath, archiveFileInfo))
throw "there is no such archive";
if (archiveFileInfo.IsDirectory())
throw "there is no such archive";
options.ArchiveFileInfo = archiveFileInfo;
#ifndef _NO_CRYPTO
openCallback->ClearPasswordWasAskedFlag();
#endif
RINOK(extractCallback->BeforeOpen(archivePath));
CArchiveLink archiveLink;
HRESULT result = MyOpenArchive(codecs, archivePath, archiveLink, openCallback);
bool crypted = false;
#ifndef _NO_CRYPTO
crypted = openCallback->WasPasswordAsked();
#endif
RINOK(extractCallback->OpenResult(archivePath, result, crypted));
if (result != S_OK)
continue;
for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
{
int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
if (index >= 0 && index > i)
{
archivePaths.Delete(index);
archivePathsFull.Delete(index);
totalPackSize -= archiveSizes[index];
archiveSizes.Delete(index);
}
}
if (archiveLink.VolumePaths.Size() != 0)
{
totalPackSize += archiveLink.VolumesSize;
RINOK(extractCallback->SetTotal(totalPackSize));
}
#ifndef _NO_CRYPTO
UString password;
RINOK(openCallback->GetPasswordIfAny(password));
if (!password.IsEmpty())
{
RINOK(extractCallback->SetPassword(password));
}
#endif
options.DefaultItemName = archiveLink.GetDefaultItemName();
RINOK(DecompressArchive(
archiveLink.GetArchive(),
archiveFileInfo.Size + archiveLink.VolumesSize,
archiveLink.GetDefaultItemName(),
wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage));
extractCallbackSpec->LocalProgressSpec->InSize += archiveFileInfo.Size +
archiveLink.VolumesSize;
extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize;
if (!errorMessage.IsEmpty())
return E_FAIL;
}
stat.NumFolders = extractCallbackSpec->NumFolders;
stat.NumFiles = extractCallbackSpec->NumFiles;
stat.UnpackSize = extractCallbackSpec->UnpackSize;
stat.NumArchives = archivePaths.Size();
stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize;
return S_OK;
}
@@ -0,0 +1,77 @@
// Extract.h
#ifndef __EXTRACT_H
#define __EXTRACT_H
#include "Common/Wildcard.h"
#include "Windows/FileFind.h"
#include "../../Archive/IArchive.h"
#include "ArchiveExtractCallback.h"
#include "ArchiveOpenCallback.h"
#include "ExtractMode.h"
#include "Property.h"
#include "../Common/LoadCodecs.h"
class CExtractOptions
{
public:
bool StdOutMode;
bool TestMode;
NExtract::NPathMode::EEnum PathMode;
UString OutputDir;
bool YesToAll;
UString DefaultItemName;
NWindows::NFile::NFind::CFileInfoW ArchiveFileInfo;
// bool ShowDialog;
// bool PasswordEnabled;
// UString Password;
#ifdef COMPRESS_MT
CObjectVector<CProperty> Properties;
#endif
NExtract::NOverwriteMode::EEnum OverwriteMode;
#ifdef EXTERNAL_CODECS
CCodecs *Codecs;
#endif
CExtractOptions():
StdOutMode(false),
YesToAll(false),
TestMode(false),
PathMode(NExtract::NPathMode::kFullPathnames),
OverwriteMode(NExtract::NOverwriteMode::kAskBefore)
{}
/*
bool FullPathMode() const { return (ExtractMode == NExtractMode::kTest) ||
(ExtractMode == NExtractMode::kFullPath); }
*/
};
struct CDecompressStat
{
UInt64 NumArchives;
UInt64 UnpackSize;
UInt64 PackSize;
UInt64 NumFolders;
UInt64 NumFiles;
void Clear() { NumArchives = PackSize = UnpackSize = NumFolders = NumFiles = 0; }
};
HRESULT DecompressArchives(
CCodecs *codecs,
UStringVector &archivePaths, UStringVector &archivePathsFull,
const NWildcard::CCensorNode &wildcardCensor,
const CExtractOptions &options,
IOpenCallbackUI *openCallback,
IExtractCallbackUI *extractCallback,
UString &errorMessage,
CDecompressStat &stat);
#endif
@@ -0,0 +1,31 @@
// ExtractMode.h
#ifndef __EXTRACT_MODE_H
#define __EXTRACT_MODE_H
namespace NExtract {
namespace NPathMode
{
enum EEnum
{
kFullPathnames,
kCurrentPathnames,
kNoPathnames
};
}
namespace NOverwriteMode
{
enum EEnum
{
kAskBefore,
kWithoutPrompt,
kSkipExisting,
kAutoRename,
kAutoRenameExisting
};
}
}
#endif
@@ -0,0 +1,96 @@
// ExtractingFilePath.cpp
#include "StdAfx.h"
#include "ExtractingFilePath.h"
static UString ReplaceIncorrectChars(const UString &s)
{
#ifdef _WIN32
UString res;
for (int i = 0; i < s.Length(); i++)
{
wchar_t c = s[i];
if (c < 0x20 || c == '*' || c == '?' || c == '<' || c == '>' || c == '|' || c == ':' || c == '"')
c = '_';
res += c;
}
return res;
#else
return s;
#endif
}
#ifdef _WIN32
static const wchar_t *g_ReservedNames[] =
{
L"CON", L"PRN", L"AUX", L"NUL"
};
static bool CheckTail(const UString &name, int len)
{
int dotPos = name.Find(L'.');
if (dotPos < 0)
dotPos = name.Length();
UString s = name.Left(dotPos);
s.TrimRight();
return (s.Length() != len);
}
static bool CheckNameNum(const UString &name, const wchar_t *reservedName)
{
int len = MyStringLen(reservedName);
if (name.Length() <= len)
return true;
if (name.Left(len).CompareNoCase(reservedName) != 0)
return true;
wchar_t c = name[len];
if (c < L'0' || c > L'9')
return true;
return CheckTail(name, len + 1);
}
static bool IsSupportedName(const UString &name)
{
for (int i = 0; i < sizeof(g_ReservedNames) / sizeof(g_ReservedNames[0]); i++)
{
const wchar_t *reservedName = g_ReservedNames[i];
int len = MyStringLen(reservedName);
if (name.Length() < len)
continue;
if (name.Left(len).CompareNoCase(reservedName) != 0)
continue;
if (!CheckTail(name, len))
return false;
}
if (!CheckNameNum(name, L"COM"))
return false;
return CheckNameNum(name, L"LPT");
}
#endif
static UString GetCorrectFileName(const UString &path)
{
if (path == L".." || path == L".")
return UString();
return ReplaceIncorrectChars(path);
}
void MakeCorrectPath(UStringVector &pathParts)
{
for (int i = 0; i < pathParts.Size();)
{
UString &s = pathParts[i];
s = GetCorrectFileName(s);
if (s.IsEmpty())
pathParts.Delete(i);
else
{
#ifdef _WIN32
if (!IsSupportedName(s))
s = (UString)L"_" + s;
#endif
i++;
}
}
}
@@ -0,0 +1,10 @@
// ExtractingFilePath.h
#ifndef __EXTRACTINGFILEPATH_H
#define __EXTRACTINGFILEPATH_H
#include "Common/MyString.h"
void MakeCorrectPath(UStringVector &pathParts);
#endif
@@ -0,0 +1,43 @@
// IFileExtractCallback.h
#ifndef __IFILEEXTRACTCALLBACK_H
#define __IFILEEXTRACTCALLBACK_H
#include "Common/MyString.h"
#include "../../IDecl.h"
namespace NOverwriteAnswer
{
enum EEnum
{
kYes,
kYesToAll,
kNo,
kNoToAll,
kAutoRename,
kCancel
};
}
DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07)
{
public:
STDMETHOD(AskOverwrite)(
const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
Int32 *answer) PURE;
STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE;
STDMETHOD(MessageError)(const wchar_t *message) PURE;
STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE;
};
struct IExtractCallbackUI: IFolderArchiveExtractCallback
{
virtual HRESULT BeforeOpen(const wchar_t *name) = 0;
virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0;
virtual HRESULT ThereAreNoFiles() = 0;
virtual HRESULT ExtractResult(HRESULT result) = 0;
virtual HRESULT SetPassword(const UString &password) = 0;
};
#endif
@@ -0,0 +1,644 @@
// LoadCodecs.cpp
#include "StdAfx.h"
#include "LoadCodecs.h"
#include "../../../Common/MyCom.h"
#ifdef NEW_FOLDER_INTERFACE
#include "../../../Common/StringToInt.h"
#endif
#include "../../../Windows/PropVariant.h"
#include "../../ICoder.h"
#include "../../Common/RegisterArc.h"
#ifdef EXTERNAL_CODECS
#include "../../../Windows/FileFind.h"
#include "../../../Windows/DLL.h"
#ifdef NEW_FOLDER_INTERFACE
#include "../../../Windows/ResourceString.h"
static const UINT kIconTypesResId = 100;
#endif
#ifdef _WIN32
#include "Windows/Registry.h"
#endif
using namespace NWindows;
using namespace NFile;
#ifdef _WIN32
extern HINSTANCE g_hInstance;
#endif
static CSysString GetLibraryFolderPrefix()
{
#ifdef _WIN32
TCHAR fullPath[MAX_PATH + 1];
::GetModuleFileName(g_hInstance, fullPath, MAX_PATH);
CSysString path = fullPath;
int pos = path.ReverseFind(TEXT(CHAR_PATH_SEPARATOR));
return path.Left(pos + 1);
#else
return CSysString(); // FIX IT
#endif
}
#define kCodecsFolderName TEXT("Codecs")
#define kFormatsFolderName TEXT("Formats")
static TCHAR *kMainDll = TEXT("7z.dll");
#ifdef _WIN32
static LPCTSTR kRegistryPath = TEXT("Software\\7-zip");
static LPCTSTR kProgramPathValue = TEXT("Path");
static bool ReadPathFromRegistry(HKEY baseKey, CSysString &path)
{
NRegistry::CKey key;
if(key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
if (key.QueryValue(kProgramPathValue, path) == ERROR_SUCCESS)
{
NName::NormalizeDirPathPrefix(path);
return true;
}
return false;
}
#endif
CSysString GetBaseFolderPrefixFromRegistry()
{
CSysString moduleFolderPrefix = GetLibraryFolderPrefix();
NFind::CFileInfo fileInfo;
if (NFind::FindFile(moduleFolderPrefix + kMainDll, fileInfo))
if (!fileInfo.IsDirectory())
return moduleFolderPrefix;
if (NFind::FindFile(moduleFolderPrefix + kCodecsFolderName, fileInfo))
if (fileInfo.IsDirectory())
return moduleFolderPrefix;
if (NFind::FindFile(moduleFolderPrefix + kFormatsFolderName, fileInfo))
if (fileInfo.IsDirectory())
return moduleFolderPrefix;
#ifdef _WIN32
CSysString path;
if (ReadPathFromRegistry(HKEY_CURRENT_USER, path))
return path;
if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, path))
return path;
#endif
return moduleFolderPrefix;
}
typedef UInt32 (WINAPI *GetNumberOfMethodsFunc)(UInt32 *numMethods);
typedef UInt32 (WINAPI *GetNumberOfFormatsFunc)(UInt32 *numFormats);
typedef UInt32 (WINAPI *GetHandlerPropertyFunc)(PROPID propID, PROPVARIANT *value);
typedef UInt32 (WINAPI *GetHandlerPropertyFunc2)(UInt32 index, PROPID propID, PROPVARIANT *value);
typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *iid, void **outObject);
typedef UInt32 (WINAPI *SetLargePageModeFunc)();
static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 index,
PROPID propId, CLSID &clsId, bool &isAssigned)
{
NWindows::NCOM::CPropVariant prop;
isAssigned = false;
RINOK(getMethodProperty(index, propId, &prop));
if (prop.vt == VT_BSTR)
{
isAssigned = true;
clsId = *(const GUID *)prop.bstrVal;
}
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
HRESULT CCodecs::LoadCodecs()
{
CCodecLib &lib = Libs.Back();
lib.GetMethodProperty = (GetMethodPropertyFunc)lib.Lib.GetProcAddress("GetMethodProperty");
if (lib.GetMethodProperty == NULL)
return S_OK;
UInt32 numMethods = 1;
GetNumberOfMethodsFunc getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)lib.Lib.GetProcAddress("GetNumberOfMethods");
if (getNumberOfMethodsFunc != NULL)
{
RINOK(getNumberOfMethodsFunc(&numMethods));
}
for(UInt32 i = 0; i < numMethods; i++)
{
CDllCodecInfo info;
info.LibIndex = Libs.Size() - 1;
info.CodecIndex = i;
RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned));
RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned));
Codecs.Add(info);
}
return S_OK;
}
static HRESULT ReadProp(
GetHandlerPropertyFunc getProp,
GetHandlerPropertyFunc2 getProp2,
UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
{
if (getProp2)
return getProp2(index, propID, &prop);;
return getProp(propID, &prop);
}
static HRESULT ReadBoolProp(
GetHandlerPropertyFunc getProp,
GetHandlerPropertyFunc2 getProp2,
UInt32 index, PROPID propID, bool &res)
{
NCOM::CPropVariant prop;
RINOK(ReadProp(getProp, getProp2, index, propID, prop));
if (prop.vt == VT_BOOL)
res = VARIANT_BOOLToBool(prop.boolVal);
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
static HRESULT ReadStringProp(
GetHandlerPropertyFunc getProp,
GetHandlerPropertyFunc2 getProp2,
UInt32 index, PROPID propID, UString &res)
{
NCOM::CPropVariant prop;
RINOK(ReadProp(getProp, getProp2, index, propID, prop));
if (prop.vt == VT_BSTR)
res = prop.bstrVal;
else if (prop.vt != VT_EMPTY)
return E_FAIL;
return S_OK;
}
#endif
static const unsigned int kNumArcsMax = 32;
static unsigned int g_NumArcs = 0;
static const CArcInfo *g_Arcs[kNumArcsMax];
void RegisterArc(const CArcInfo *arcInfo)
{
if (g_NumArcs < kNumArcsMax)
g_Arcs[g_NumArcs++] = arcInfo;
}
static void SplitString(const UString &srcString, UStringVector &destStrings)
{
destStrings.Clear();
UString s;
int len = srcString.Length();
if (len == 0)
return;
for (int i = 0; i < len; i++)
{
wchar_t c = srcString[i];
if (c == L' ')
{
if (!s.IsEmpty())
{
destStrings.Add(s);
s.Empty();
}
}
else
s += c;
}
if (!s.IsEmpty())
destStrings.Add(s);
}
void CArcInfoEx::AddExts(const wchar_t* ext, const wchar_t* addExt)
{
UStringVector exts, addExts;
SplitString(ext, exts);
if (addExt != 0)
SplitString(addExt, addExts);
for (int i = 0; i < exts.Size(); i++)
{
CArcExtInfo extInfo;
extInfo.Ext = exts[i];
if (i < addExts.Size())
{
extInfo.AddExt = addExts[i];
if (extInfo.AddExt == L"*")
extInfo.AddExt.Empty();
}
Exts.Add(extInfo);
}
}
#ifdef EXTERNAL_CODECS
HRESULT CCodecs::LoadFormats()
{
const NDLL::CLibrary &lib = Libs.Back().Lib;
GetHandlerPropertyFunc getProp = 0;
GetHandlerPropertyFunc2 getProp2 = (GetHandlerPropertyFunc2)
lib.GetProcAddress("GetHandlerProperty2");
if (getProp2 == NULL)
{
getProp = (GetHandlerPropertyFunc)
lib.GetProcAddress("GetHandlerProperty");
if (getProp == NULL)
return S_OK;
}
UInt32 numFormats = 1;
GetNumberOfFormatsFunc getNumberOfFormats = (GetNumberOfFormatsFunc)
lib.GetProcAddress("GetNumberOfFormats");
if (getNumberOfFormats != NULL)
{
RINOK(getNumberOfFormats(&numFormats));
}
if (getProp2 == NULL)
numFormats = 1;
for(UInt32 i = 0; i < numFormats; i++)
{
CArcInfoEx item;
item.LibIndex = Libs.Size() - 1;
item.FormatIndex = i;
RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kName, item.Name));
NCOM::CPropVariant prop;
if (ReadProp(getProp, getProp2, i, NArchive::kClassID, prop) != S_OK)
continue;
if (prop.vt != VT_BSTR)
continue;
item.ClassID = *(const GUID *)prop.bstrVal;
prop.Clear();
UString ext, addExt;
RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kExtension, ext));
RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kAddExtension, addExt));
item.AddExts(ext, addExt);
ReadBoolProp(getProp, getProp2, i, NArchive::kUpdate, item.UpdateEnabled);
if (item.UpdateEnabled)
ReadBoolProp(getProp, getProp2, i, NArchive::kKeepName, item.KeepName);
if (ReadProp(getProp, getProp2, i, NArchive::kStartSignature, prop) == S_OK)
if (prop.vt == VT_BSTR)
{
UINT len = ::SysStringByteLen(prop.bstrVal);
item.StartSignature.SetCapacity(len);
memmove(item.StartSignature, prop.bstrVal, len);
}
Formats.Add(item);
}
return S_OK;
}
#ifdef NEW_FOLDER_INTERFACE
void CCodecLib::LoadIcons()
{
UString iconTypes = MyLoadStringW((HMODULE)Lib, kIconTypesResId);
UStringVector pairs;
SplitString(iconTypes, pairs);
for (int i = 0; i < pairs.Size(); i++)
{
const UString &s = pairs[i];
int pos = s.Find(L':');
if (pos < 0)
continue;
CIconPair iconPair;
const wchar_t *end;
UString num = s.Mid(pos + 1);
iconPair.IconIndex = (UInt32)ConvertStringToUInt64(num, &end);
if (*end != L'\0')
continue;
iconPair.Ext = s.Left(pos);
IconPairs.Add(iconPair);
}
}
int CCodecLib::FindIconIndex(const UString &ext) const
{
for (int i = 0; i < IconPairs.Size(); i++)
{
const CIconPair &pair = IconPairs[i];
if (ext.CompareNoCase(pair.Ext) == 0)
return pair.IconIndex;
}
return -1;
}
#endif
#ifdef _7ZIP_LARGE_PAGES
extern "C"
{
extern SIZE_T g_LargePageSize;
}
#endif
HRESULT CCodecs::LoadDll(const CSysString &dllPath)
{
{
NDLL::CLibrary library;
if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
return S_OK;
}
Libs.Add(CCodecLib());
CCodecLib &lib = Libs.Back();
#ifdef NEW_FOLDER_INTERFACE
lib.Path = dllPath;
#endif
bool used = false;
HRESULT res = S_OK;
if (lib.Lib.Load(dllPath))
{
#ifdef NEW_FOLDER_INTERFACE
lib.LoadIcons();
#endif
#ifdef _7ZIP_LARGE_PAGES
if (g_LargePageSize != 0)
{
SetLargePageModeFunc setLargePageMode = (SetLargePageModeFunc)lib.Lib.GetProcAddress("SetLargePageMode");
if (setLargePageMode != 0)
setLargePageMode();
}
#endif
lib.CreateObject = (CreateObjectFunc)lib.Lib.GetProcAddress("CreateObject");
if (lib.CreateObject != 0)
{
int startSize = Codecs.Size();
res = LoadCodecs();
used = (Codecs.Size() != startSize);
if (res == S_OK)
{
startSize = Formats.Size();
res = LoadFormats();
used = used || (Formats.Size() != startSize);
}
}
}
if (!used)
Libs.DeleteBack();
return res;
}
HRESULT CCodecs::LoadDllsFromFolder(const CSysString &folderPrefix)
{
NFile::NFind::CEnumerator enumerator(folderPrefix + CSysString(TEXT("*")));
NFile::NFind::CFileInfo fileInfo;
while (enumerator.Next(fileInfo))
{
if (fileInfo.IsDirectory())
continue;
RINOK(LoadDll(folderPrefix + fileInfo.Name));
}
return S_OK;
}
#endif
#ifndef _SFX
static inline void SetBuffer(CByteBuffer &bb, const Byte *data, int size)
{
bb.SetCapacity(size);
memmove((Byte *)bb, data, size);
}
#endif
HRESULT CCodecs::Load()
{
Formats.Clear();
#ifdef EXTERNAL_CODECS
Codecs.Clear();
#endif
for (UInt32 i = 0; i < g_NumArcs; i++)
{
const CArcInfo &arc = *g_Arcs[i];
CArcInfoEx item;
item.Name = arc.Name;
item.CreateInArchive = arc.CreateInArchive;
item.CreateOutArchive = arc.CreateOutArchive;
item.AddExts(arc.Ext, arc.AddExt);
item.UpdateEnabled = (arc.CreateOutArchive != 0);
item.KeepName = arc.KeepName;
#ifndef _SFX
SetBuffer(item.StartSignature, arc.Signature, arc.SignatureSize);
#endif
Formats.Add(item);
}
#ifdef EXTERNAL_CODECS
const CSysString baseFolder = GetBaseFolderPrefixFromRegistry();
RINOK(LoadDll(baseFolder + kMainDll));
RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName TEXT(STRING_PATH_SEPARATOR)));
RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName TEXT(STRING_PATH_SEPARATOR)));
#endif
return S_OK;
}
int CCodecs::FindFormatForArchiveName(const UString &archivePath) const
{
int slashPos1 = archivePath.ReverseFind(L'\\');
int slashPos2 = archivePath.ReverseFind(L'.');
int dotPos = archivePath.ReverseFind(L'.');
if (dotPos < 0 || dotPos < slashPos1 || dotPos < slashPos2)
return -1;
UString ext = archivePath.Mid(dotPos + 1);
for (int i = 0; i < Formats.Size(); i++)
{
const CArcInfoEx &arc = Formats[i];
if (!arc.UpdateEnabled)
continue;
// if (arc.FindExtension(ext) >= 0)
UString mainExt = arc.GetMainExt();
if (!mainExt.IsEmpty() && ext.CompareNoCase(mainExt) == 0)
return i;
}
return -1;
}
int CCodecs::FindFormatForArchiveType(const UString &arcType) const
{
for (int i = 0; i < Formats.Size(); i++)
{
const CArcInfoEx &arc = Formats[i];
if (!arc.UpdateEnabled)
continue;
if (arc.Name.CompareNoCase(arcType) == 0)
return i;
}
return -1;
}
#ifdef EXTERNAL_CODECS
#ifdef EXPORT_CODECS
extern unsigned int g_NumCodecs;
STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject);
STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
// STDAPI GetNumberOfMethods(UINT32 *numCodecs);
#endif
STDMETHODIMP CCodecs::GetNumberOfMethods(UINT32 *numMethods)
{
*numMethods =
#ifdef EXPORT_CODECS
g_NumCodecs +
#endif
Codecs.Size();
return S_OK;
}
STDMETHODIMP CCodecs::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *value)
{
#ifdef EXPORT_CODECS
if (index < g_NumCodecs)
return GetMethodProperty(index, propID, value);
#endif
const CDllCodecInfo &ci = Codecs[index
#ifdef EXPORT_CODECS
- g_NumCodecs
#endif
];
if (propID == NMethodPropID::kDecoderIsAssigned)
{
NWindows::NCOM::CPropVariant propVariant;
propVariant = ci.DecoderIsAssigned;
propVariant.Detach(value);
return S_OK;
}
if (propID == NMethodPropID::kEncoderIsAssigned)
{
NWindows::NCOM::CPropVariant propVariant;
propVariant = ci.EncoderIsAssigned;
propVariant.Detach(value);
return S_OK;
}
return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value);
}
STDMETHODIMP CCodecs::CreateDecoder(UINT32 index, const GUID *iid, void **coder)
{
#ifdef EXPORT_CODECS
if (index < g_NumCodecs)
return CreateCoder2(false, index, iid, coder);
#endif
const CDllCodecInfo &ci = Codecs[index
#ifdef EXPORT_CODECS
- g_NumCodecs
#endif
];
if (ci.DecoderIsAssigned)
return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder);
return S_OK;
}
STDMETHODIMP CCodecs::CreateEncoder(UINT32 index, const GUID *iid, void **coder)
{
#ifdef EXPORT_CODECS
if (index < g_NumCodecs)
return CreateCoder2(true, index, iid, coder);
#endif
const CDllCodecInfo &ci = Codecs[index
#ifdef EXPORT_CODECS
- g_NumCodecs
#endif
];
if (ci.EncoderIsAssigned)
return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder);
return S_OK;
}
HRESULT CCodecs::CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const
{
for (int i = 0; i < Codecs.Size(); i++)
{
const CDllCodecInfo &codec = Codecs[i];
if (encode && !codec.EncoderIsAssigned || !encode && !codec.DecoderIsAssigned)
continue;
const CCodecLib &lib = Libs[codec.LibIndex];
UString res;
NWindows::NCOM::CPropVariant prop;
RINOK(lib.GetMethodProperty(codec.CodecIndex, NMethodPropID::kName, &prop));
if (prop.vt == VT_BSTR)
res = prop.bstrVal;
else if (prop.vt != VT_EMPTY)
continue;
if (name.CompareNoCase(res) == 0)
return lib.CreateObject(encode ? &codec.Encoder : &codec.Decoder, &IID_ICompressCoder, (void **)&coder);
}
return CLASS_E_CLASSNOTAVAILABLE;
}
int CCodecs::GetCodecLibIndex(UInt32 index)
{
#ifdef EXPORT_CODECS
if (index < g_NumCodecs)
return -1;
#endif
#ifdef EXTERNAL_CODECS
const CDllCodecInfo &ci = Codecs[index
#ifdef EXPORT_CODECS
- g_NumCodecs
#endif
];
return ci.LibIndex;
#else
return -1;
#endif
}
bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index)
{
#ifdef EXPORT_CODECS
if (index < g_NumCodecs)
{
NWindows::NCOM::CPropVariant prop;
if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK)
if (prop.vt != VT_EMPTY)
return true;
return false;
}
#endif
#ifdef EXTERNAL_CODECS
const CDllCodecInfo &ci = Codecs[index
#ifdef EXPORT_CODECS
- g_NumCodecs
#endif
];
return ci.EncoderIsAssigned;
#else
return false;
#endif
}
HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id)
{
UString s;
NWindows::NCOM::CPropVariant prop;
RINOK(GetProperty(index, NMethodPropID::kID, &prop));
if (prop.vt != VT_UI8)
return E_INVALIDARG;
id = prop.uhVal.QuadPart;
return S_OK;
}
UString CCodecs::GetCodecName(UInt32 index)
{
UString s;
NWindows::NCOM::CPropVariant prop;
if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
if (prop.vt == VT_BSTR)
s = prop.bstrVal;
return s;
}
#endif
@@ -0,0 +1,215 @@
// LoadCodecs.h
#ifndef __LOADCODECS_H
#define __LOADCODECS_H
#include "../../../Common/Types.h"
#include "../../../Common/MyCom.h"
#include "../../../Common/MyString.h"
#include "../../../Common/Buffer.h"
#include "../../ICoder.h"
#ifdef EXTERNAL_CODECS
#include "../../../Windows/DLL.h"
#endif
struct CDllCodecInfo
{
CLSID Encoder;
CLSID Decoder;
bool EncoderIsAssigned;
bool DecoderIsAssigned;
int LibIndex;
UInt32 CodecIndex;
};
#include "../../Archive/IArchive.h"
typedef IInArchive * (*CreateInArchiveP)();
typedef IOutArchive * (*CreateOutArchiveP)();
struct CArcExtInfo
{
UString Ext;
UString AddExt;
CArcExtInfo() {}
CArcExtInfo(const UString &ext): Ext(ext) {}
CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {}
};
struct CArcInfoEx
{
#ifdef EXTERNAL_CODECS
int LibIndex;
UInt32 FormatIndex;
CLSID ClassID;
#endif
bool UpdateEnabled;
CreateInArchiveP CreateInArchive;
CreateOutArchiveP CreateOutArchive;
UString Name;
CObjectVector<CArcExtInfo> Exts;
#ifndef _SFX
CByteBuffer StartSignature;
// CByteBuffer FinishSignature;
#ifdef NEW_FOLDER_INTERFACE
UStringVector AssociateExts;
#endif
#endif
bool KeepName;
UString GetMainExt() const
{
if (Exts.IsEmpty())
return UString();
return Exts[0].Ext;
}
int FindExtension(const UString &ext) const
{
for (int i = 0; i < Exts.Size(); i++)
if (ext.CompareNoCase(Exts[i].Ext) == 0)
return i;
return -1;
}
UString GetAllExtensions() const
{
UString s;
for (int i = 0; i < Exts.Size(); i++)
{
if (i > 0)
s += ' ';
s += Exts[i].Ext;
}
return s;
}
void AddExts(const wchar_t* ext, const wchar_t* addExt);
CArcInfoEx():
#ifdef EXTERNAL_CODECS
LibIndex(-1),
#endif
UpdateEnabled(false),
CreateInArchive(0), CreateOutArchive(0),
KeepName(false)
#ifndef _SFX
#endif
{}
};
#ifdef EXTERNAL_CODECS
typedef UInt32 (WINAPI *GetMethodPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT *value);
typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *interfaceID, void **outObject);
struct CCodecLib
{
NWindows::NDLL::CLibrary Lib;
GetMethodPropertyFunc GetMethodProperty;
CreateObjectFunc CreateObject;
#ifdef NEW_FOLDER_INTERFACE
struct CIconPair
{
UString Ext;
UInt32 IconIndex;
};
CSysString Path;
CObjectVector<CIconPair> IconPairs;
void LoadIcons();
int FindIconIndex(const UString &ext) const;
#endif
CCodecLib(): GetMethodProperty(0) {}
};
#endif
class CCodecs:
#ifdef EXTERNAL_CODECS
public ICompressCodecsInfo,
#else
public IUnknown,
#endif
public CMyUnknownImp
{
public:
#ifdef EXTERNAL_CODECS
CObjectVector<CCodecLib> Libs;
CObjectVector<CDllCodecInfo> Codecs;
HRESULT LoadCodecs();
HRESULT LoadFormats();
HRESULT LoadDll(const CSysString &path);
HRESULT LoadDllsFromFolder(const CSysString &folderPrefix);
HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const
{
return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive);
}
#endif
public:
CObjectVector<CArcInfoEx> Formats;
HRESULT Load();
int FindFormatForArchiveName(const UString &archivePath) const;
int FindFormatForArchiveType(const UString &arcType) const;
MY_UNKNOWN_IMP
#ifdef EXTERNAL_CODECS
STDMETHOD(GetNumberOfMethods)(UINT32 *numMethods);
STDMETHOD(GetProperty)(UINT32 index, PROPID propID, PROPVARIANT *value);
STDMETHOD(CreateDecoder)(UINT32 index, const GUID *interfaceID, void **coder);
STDMETHOD(CreateEncoder)(UINT32 index, const GUID *interfaceID, void **coder);
#endif
int GetCodecLibIndex(UInt32 index);
bool GetCodecEncoderIsAssigned(UInt32 index);
HRESULT GetCodecId(UInt32 index, UInt64 &id);
UString GetCodecName(UInt32 index);
HRESULT CreateInArchive(int formatIndex, CMyComPtr<IInArchive> &archive) const
{
const CArcInfoEx &ai = Formats[formatIndex];
#ifdef EXTERNAL_CODECS
if (ai.LibIndex < 0)
#endif
{
archive = ai.CreateInArchive();
return S_OK;
}
#ifdef EXTERNAL_CODECS
return CreateArchiveHandler(ai, (void **)&archive, false);
#endif
}
HRESULT CreateOutArchive(int formatIndex, CMyComPtr<IOutArchive> &archive) const
{
const CArcInfoEx &ai = Formats[formatIndex];
#ifdef EXTERNAL_CODECS
if (ai.LibIndex < 0)
#endif
{
archive = ai.CreateOutArchive();
return S_OK;
}
#ifdef EXTERNAL_CODECS
return CreateArchiveHandler(ai, (void **)&archive, true);
#endif
}
int FindOutFormatFromName(const UString &name) const
{
for (int i = 0; i < Formats.Size(); i++)
{
const CArcInfoEx &arc = Formats[i];
if (!arc.UpdateEnabled)
continue;
if (arc.Name.CompareNoCase(name) == 0)
return i;
}
return -1;
}
#ifdef EXTERNAL_CODECS
HRESULT CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const;
#endif
};
#endif
@@ -0,0 +1,461 @@
// 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/StreamUtils.h"
#include "Common/StringConvert.h"
#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;
}
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 = 1 << 22;
HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback)
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<IInStream> inStream(inStreamSpec);
inStreamSpec->Open(fileName);
return archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback);
}
#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;
}
#endif
HRESULT OpenArchive(
CCodecs *codecs,
IInStream *inStream,
const UString &fileName,
IInArchive **archiveResult,
int &formatIndex,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback)
{
*archiveResult = NULL;
UString extension;
{
int dotPos = fileName.ReverseFind(L'.');
if (dotPos >= 0)
extension = fileName.Mid(dotPos + 1);
}
CIntVector orderIndices;
int i;
int numFinded = 0;
for (i = 0; i < codecs->Formats.Size(); i++)
if (codecs->Formats[i].FindExtension(extension) >= 0)
orderIndices.Insert(numFinded++, i);
else
orderIndices.Add(i);
#ifndef _SFX
if (numFinded != 1)
{
CIntVector orderIndices2;
CByteBuffer byteBuffer;
const UInt32 kBufferSize = (200 << 10);
byteBuffer.SetCapacity(kBufferSize);
Byte *buffer = byteBuffer;
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
UInt32 processedSize;
RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize));
for (UInt32 pos = 0; pos < processedSize; pos++)
{
for (int i = 0; i < orderIndices.Size(); i++)
{
int index = orderIndices[i];
const CArcInfoEx &ai = codecs->Formats[index];
const CByteBuffer &sig = ai.StartSignature;
if (sig.GetCapacity() == 0)
continue;
if (pos + sig.GetCapacity() > processedSize)
continue;
if (TestSignature(buffer + pos, sig, sig.GetCapacity()))
{
orderIndices2.Add(index);
orderIndices.Delete(i--);
}
}
}
orderIndices2 += orderIndices;
orderIndices = orderIndices2;
}
else if (extension == L"000" || extension == L"001")
{
CByteBuffer byteBuffer;
const UInt32 kBufferSize = (1 << 10);
byteBuffer.SetCapacity(kBufferSize);
Byte *buffer = byteBuffer;
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
UInt32 processedSize;
RINOK(ReadStream(inStream, buffer, kBufferSize, &processedSize));
if (processedSize >= 16)
{
Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00};
if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] && 1) != 0)
{
for (int i = 0; i < orderIndices.Size(); i++)
{
int index = orderIndices[i];
const CArcInfoEx &ai = codecs->Formats[index];
if (ai.Name.CompareNoCase(L"rar") != 0)
continue;
orderIndices.Delete(i--);
orderIndices.Insert(0, index);
break;
}
}
}
}
#endif
HRESULT badResult = S_OK;
for(i = 0; i < orderIndices.Size(); i++)
{
inStream->Seek(0, STREAM_SEEK_SET, NULL);
CMyComPtr<IInArchive> archive;
formatIndex = orderIndices[i];
RINOK(codecs->CreateInArchive(formatIndex, archive));
if (!archive)
continue;
#ifdef EXTERNAL_CODECS
{
CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
if (setCompressCodecsInfo)
{
RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
}
}
#endif
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();
const CArcInfoEx &format = codecs->Formats[formatIndex];
if (format.Exts.Size() == 0)
{
defaultItemName = GetDefaultName2(fileName, L"", L"");
}
else
{
int subExtIndex = format.FindExtension(extension);
if (subExtIndex < 0)
subExtIndex = 0;
defaultItemName = GetDefaultName2(fileName,
format.Exts[subExtIndex].Ext,
format.Exts[subExtIndex].AddExt);
}
return S_OK;
}
if (badResult != S_OK)
return badResult;
return S_FALSE;
}
HRESULT OpenArchive(
CCodecs *codecs,
const UString &filePath,
IInArchive **archiveResult,
int &formatIndex,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback)
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<IInStream> inStream(inStreamSpec);
if (!inStreamSpec->Open(filePath))
return GetLastError();
return OpenArchive(codecs, inStream, ExtractFileNameFromPath(filePath),
archiveResult, formatIndex,
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(
CCodecs *codecs,
const UString &fileName,
IInArchive **archive0,
IInArchive **archive1,
int &formatIndex0,
int &formatIndex1,
UString &defaultItemName0,
UString &defaultItemName1,
IArchiveOpenCallback *openArchiveCallback)
{
HRESULT result = OpenArchive(codecs, fileName,
archive0, formatIndex0, 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;
const CArcInfoEx &format = codecs->Formats[formatIndex0];
if (format.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(codecs, subStream, subPath,
archive1, formatIndex1, defaultItemName1, openArchiveCallback);
return S_OK;
}
static void SetCallback(const UString &archiveName,
IOpenCallbackUI *openCallbackUI, CMyComPtr<IArchiveOpenCallback> &openCallback)
{
COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
openCallback = openCallbackSpec;
openCallbackSpec->Callback = openCallbackUI;
UString fullName;
int fileNamePartStartIndex;
NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
openCallbackSpec->Init(
fullName.Left(fileNamePartStartIndex),
fullName.Mid(fileNamePartStartIndex));
}
HRESULT MyOpenArchive(
CCodecs *codecs,
const UString &archiveName,
IInArchive **archive, UString &defaultItemName, IOpenCallbackUI *openCallbackUI)
{
CMyComPtr<IArchiveOpenCallback> openCallback;
SetCallback(archiveName, openCallbackUI, openCallback);
int formatInfo;
return OpenArchive(codecs, archiveName, archive, formatInfo, defaultItemName, openCallback);
}
HRESULT MyOpenArchive(
CCodecs *codecs,
const UString &archiveName,
IInArchive **archive0,
IInArchive **archive1,
UString &defaultItemName0,
UString &defaultItemName1,
UStringVector &volumePaths,
UInt64 &volumesSize,
IOpenCallbackUI *openCallbackUI)
{
volumesSize = 0;
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);
int formatIndex0, formatIndex1;
RINOK(OpenArchive(codecs, archiveName,
archive0,
archive1,
formatIndex0,
formatIndex1,
defaultItemName0,
defaultItemName1,
openCallback));
volumePaths.Add(prefix + name);
for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++)
volumePaths.Add(prefix + openCallbackSpec->FileNames[i]);
volumesSize = openCallbackSpec->TotalSize;
return S_OK;
}
HRESULT CArchiveLink::Close()
{
if (Archive1 != 0)
RINOK(Archive1->Close());
if (Archive0 != 0)
RINOK(Archive0->Close());
IsOpen = false;
return S_OK;
}
void CArchiveLink::Release()
{
IsOpen = false;
Archive1.Release();
Archive0.Release();
}
HRESULT OpenArchive(
CCodecs *codecs,
const UString &archiveName,
CArchiveLink &archiveLink,
IArchiveOpenCallback *openCallback)
{
HRESULT res = OpenArchive(codecs, archiveName,
&archiveLink.Archive0, &archiveLink.Archive1,
archiveLink.FormatIndex0, archiveLink.FormatIndex1,
archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
openCallback);
archiveLink.IsOpen = (res == S_OK);
return res;
}
HRESULT MyOpenArchive(CCodecs *codecs,
const UString &archiveName,
CArchiveLink &archiveLink,
IOpenCallbackUI *openCallbackUI)
{
HRESULT res = MyOpenArchive(codecs, archiveName,
&archiveLink.Archive0, &archiveLink.Archive1,
archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
archiveLink.VolumePaths,
archiveLink.VolumesSize,
openCallbackUI);
archiveLink.IsOpen = (res == S_OK);
return res;
}
HRESULT ReOpenArchive(CCodecs *codecs, CArchiveLink &archiveLink, const UString &fileName)
{
if (archiveLink.GetNumLevels() > 1)
return E_NOTIMPL;
if (archiveLink.GetNumLevels() == 0)
return MyOpenArchive(codecs, fileName, archiveLink, 0);
CMyComPtr<IArchiveOpenCallback> openCallback;
SetCallback(fileName, NULL, openCallback);
HRESULT res = ReOpenArchive(archiveLink.GetArchive(), fileName, openCallback);
archiveLink.IsOpen = (res == S_OK);
return res;
}
@@ -0,0 +1,130 @@
// OpenArchive.h
#ifndef __OPENARCHIVE_H
#define __OPENARCHIVE_H
#include "Common/MyString.h"
#include "Windows/FileFind.h"
#include "../../Archive/IArchive.h"
#include "LoadCodecs.h"
#include "ArchiveOpenCallback.h"
HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result);
HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result);
HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index,
const FILETIME &defaultFileTime, FILETIME &fileTime);
HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result);
HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result);
HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result);
struct ISetSubArchiveName
{
virtual void SetSubArchiveName(const wchar_t *name) = 0;
};
HRESULT OpenArchive(
CCodecs *codecs,
IInStream *inStream,
const UString &fileName,
IInArchive **archiveResult,
int &formatIndex,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback);
HRESULT OpenArchive(
CCodecs *codecs,
const UString &filePath,
IInArchive **archive,
int &formatIndex,
UString &defaultItemName,
IArchiveOpenCallback *openArchiveCallback);
HRESULT OpenArchive(
CCodecs *codecs,
const UString &filePath,
IInArchive **archive0,
IInArchive **archive1,
int &formatIndex0,
int &formatIndex1,
UString &defaultItemName0,
UString &defaultItemName1,
IArchiveOpenCallback *openArchiveCallback);
HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback);
HRESULT MyOpenArchive(
CCodecs *codecs,
const UString &archiveName,
IInArchive **archive,
UString &defaultItemName,
IOpenCallbackUI *openCallbackUI);
HRESULT MyOpenArchive(
CCodecs *codecs,
const UString &archiveName,
IInArchive **archive0,
IInArchive **archive1,
UString &defaultItemName0,
UString &defaultItemName1,
UStringVector &volumePaths,
UInt64 &volumesSize,
IOpenCallbackUI *openCallbackUI);
struct CArchiveLink
{
CMyComPtr<IInArchive> Archive0;
CMyComPtr<IInArchive> Archive1;
UString DefaultItemName0;
UString DefaultItemName1;
int FormatIndex0;
int FormatIndex1;
UStringVector VolumePaths;
UInt64 VolumesSize;
int GetNumLevels() const
{
int result = 0;
if (Archive0)
{
result++;
if (Archive1)
result++;
}
return result;
}
bool IsOpen;
CArchiveLink(): IsOpen(false), VolumesSize(0) {};
IInArchive *GetArchive() { return Archive1 != 0 ? Archive1: Archive0; }
UString GetDefaultItemName() { return Archive1 != 0 ? DefaultItemName1: DefaultItemName0; }
int GetArchiverIndex() const { return Archive1 != 0 ? FormatIndex1: FormatIndex0; }
HRESULT Close();
void Release();
};
HRESULT OpenArchive(
CCodecs *codecs,
const UString &archiveName,
CArchiveLink &archiveLink,
IArchiveOpenCallback *openCallback);
HRESULT MyOpenArchive(
CCodecs *codecs,
const UString &archiveName,
CArchiveLink &archiveLink,
IOpenCallbackUI *openCallbackUI);
HRESULT ReOpenArchive(
CCodecs *codecs,
CArchiveLink &archiveLink,
const UString &fileName);
#endif
@@ -0,0 +1,89 @@
// PropIDUtils.cpp
#include "StdAfx.h"
#include "PropIDUtils.h"
#include "Common/IntToString.h"
#include "Common/StringConvert.h"
#include "Windows/FileFind.h"
#include "Windows/PropVariantConversions.h"
#include "../../PropID.h"
using namespace NWindows;
static UString ConvertUInt32ToString(UInt32 value)
{
wchar_t buffer[32];
ConvertUInt64ToString(value, buffer);
return buffer;
}
static void ConvertUInt32ToHex(UInt32 value, wchar_t *s)
{
for (int i = 0; i < 8; i++)
{
int t = value & 0xF;
value >>= 4;
s[7 - i] = (wchar_t)((t < 10) ? (L'0' + t) : (L'A' + (t - 10)));
}
s[8] = L'\0';
}
UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full)
{
switch(propID)
{
case kpidCreationTime:
case kpidLastWriteTime:
case kpidLastAccessTime:
{
if (propVariant.vt != VT_FILETIME)
return UString(); // It is error;
FILETIME localFileTime;
if (propVariant.filetime.dwHighDateTime == 0 &&
propVariant.filetime.dwLowDateTime == 0)
return UString();
if (!::FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime))
return UString(); // It is error;
return ConvertFileTimeToString(localFileTime, true, full);
}
case kpidCRC:
{
if(propVariant.vt != VT_UI4)
break;
wchar_t temp[12];
ConvertUInt32ToHex(propVariant.ulVal, temp);
return temp;
}
case kpidAttributes:
{
if(propVariant.vt != VT_UI4)
break;
UString result;
UInt32 attributes = propVariant.ulVal;
if (NFile::NFind::NAttributes::IsReadOnly(attributes)) result += L'R';
if (NFile::NFind::NAttributes::IsHidden(attributes)) result += L'H';
if (NFile::NFind::NAttributes::IsSystem(attributes)) result += L'S';
if (NFile::NFind::NAttributes::IsDirectory(attributes)) result += L'D';
if (NFile::NFind::NAttributes::IsArchived(attributes)) result += L'A';
if (NFile::NFind::NAttributes::IsCompressed(attributes)) result += L'C';
if (NFile::NFind::NAttributes::IsEncrypted(attributes)) result += L'E';
return result;
}
case kpidDictionarySize:
{
if(propVariant.vt != VT_UI4)
break;
UInt32 size = propVariant.ulVal;
if (size % (1 << 20) == 0)
return ConvertUInt32ToString(size >> 20) + L"MB";
if (size % (1 << 10) == 0)
return ConvertUInt32ToString(size >> 10) + L"KB";
return ConvertUInt32ToString(size);
}
}
return ConvertPropVariantToString(propVariant);
}
@@ -0,0 +1,10 @@
// PropIDUtils.h
#ifndef __PROPIDUTILS_H
#define __PROPIDUTILS_H
#include "Common/MyString.h"
UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full = true);
#endif
@@ -0,0 +1,14 @@
// Property.h
#ifndef __PROPERTY_H
#define __PROPERTY_H
#include "Common/MyString.h"
struct CProperty
{
UString Name;
UString Value;
};
#endif
@@ -0,0 +1,65 @@
// SetProperties.cpp
#include "StdAfx.h"
#include "SetProperties.h"
#include "Windows/PropVariant.h"
#include "Common/MyString.h"
#include "Common/StringToInt.h"
#include "Common/MyCom.h"
#include "../../Archive/IArchive.h"
using namespace NWindows;
using namespace NCOM;
static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop)
{
const wchar_t *endPtr;
UInt64 result = ConvertStringToUInt64(s, &endPtr);
if (endPtr - (const wchar_t *)s != s.Length())
prop = s;
else if (result <= 0xFFFFFFFF)
prop = (UInt32)result;
else
prop = result;
}
HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties)
{
if (properties.IsEmpty())
return S_OK;
CMyComPtr<ISetProperties> setProperties;
unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties);
if (!setProperties)
return S_OK;
UStringVector realNames;
CPropVariant *values = new CPropVariant[properties.Size()];
try
{
int i;
for(i = 0; i < properties.Size(); i++)
{
const CProperty &property = properties[i];
NCOM::CPropVariant propVariant;
if (!property.Value.IsEmpty())
ParseNumberString(property.Value, propVariant);
realNames.Add(property.Name);
values[i] = propVariant;
}
CRecordVector<const wchar_t *> names;
for(i = 0; i < realNames.Size(); i++)
names.Add((const wchar_t *)realNames[i]);
RINOK(setProperties->SetProperties(&names.Front(), values, names.Size()));
}
catch(...)
{
delete []values;
throw;
}
delete []values;
return S_OK;
}
@@ -0,0 +1,10 @@
// SetProperties.h
#ifndef __SETPROPERTIES_H
#define __SETPROPERTIES_H
#include "Property.h"
HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties);
#endif
@@ -0,0 +1,22 @@
// SortUtils.cpp
#include "StdAfx.h"
#include "SortUtils.h"
#include "Common/Wildcard.h"
static int CompareStrings(const int *p1, const int *p2, void *param)
{
const UStringVector &strings = *(const UStringVector *)param;
return CompareFileNames(strings[*p1], strings[*p2]);
}
void SortFileNames(const UStringVector &strings, CIntVector &indices)
{
indices.Clear();
int numItems = strings.Size();
indices.Reserve(numItems);
for(int i = 0; i < numItems; i++)
indices.Add(i);
indices.Sort(CompareStrings, (void *)&strings);
}
@@ -0,0 +1,10 @@
// SortUtils.h
#ifndef __SORTUTLS_H
#define __SORTUTLS_H
#include "Common/MyString.h"
void SortFileNames(const UStringVector &strings, CIntVector &indices);
#endif
@@ -0,0 +1,22 @@
// TempFiles.cpp
#include "StdAfx.h"
#include "TempFiles.h"
#include "Windows/FileDir.h"
#include "Windows/FileIO.h"
using namespace NWindows;
using namespace NFile;
void CTempFiles::Clear()
{
while(!Paths.IsEmpty())
{
NDirectory::DeleteFileAlways((LPCWSTR)Paths.Back());
Paths.DeleteBack();
}
}
@@ -0,0 +1,16 @@
// TempFiles.h
#ifndef __TEMPFILES_H
#define __TEMPFILES_H
#include "Common/MyString.h"
class CTempFiles
{
void Clear();
public:
UStringVector Paths;
~CTempFiles() { Clear(); }
};
#endif
@@ -0,0 +1,852 @@
// Update.cpp
#include "StdAfx.h"
#ifdef _WIN32
#include <mapi.h>
#endif
#include "Update.h"
#include "Common/IntToString.h"
#include "Common/StringConvert.h"
#include "Common/CommandLineParser.h"
#ifdef _WIN32
#include "Windows/DLL.h"
#endif
#include "Windows/Defs.h"
#include "Windows/FileDir.h"
#include "Windows/FileFind.h"
#include "Windows/FileName.h"
#include "Windows/PropVariant.h"
#include "Windows/PropVariantConversions.h"
// #include "Windows/Synchronization.h"
#include "../../Common/FileStreams.h"
#include "../../Compress/Copy/CopyCoder.h"
#include "../Common/DirItem.h"
#include "../Common/EnumDirItems.h"
#include "../Common/UpdateProduce.h"
#include "../Common/OpenArchive.h"
#include "TempFiles.h"
#include "UpdateCallback.h"
#include "EnumDirItems.h"
#include "SetProperties.h"
static const char *kUpdateIsNotSupoorted =
"update operations are not supported for this archive";
using namespace NCommandLineParser;
using namespace NWindows;
using namespace NCOM;
using namespace NFile;
using namespace NName;
static const wchar_t *kTempFolderPrefix = L"7zE";
using namespace NUpdateArchive;
static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream)
{
CMyComPtr<ICompressCoder> copyCoder = new NCompress::CCopyCoder;
return copyCoder->Code(inStream, outStream, NULL, NULL, NULL);
}
class COutMultiVolStream:
public IOutStream,
public CMyUnknownImp
{
int _streamIndex; // required stream
UInt64 _offsetPos; // offset from start of _streamIndex index
UInt64 _absPos;
UInt64 _length;
struct CSubStreamInfo
{
COutFileStream *StreamSpec;
CMyComPtr<IOutStream> Stream;
UString Name;
UInt64 Pos;
UInt64 RealSize;
};
CObjectVector<CSubStreamInfo> Streams;
public:
// CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
CRecordVector<UInt64> Sizes;
UString Prefix;
CTempFiles *TempFiles;
void Init()
{
_streamIndex = 0;
_offsetPos = 0;
_absPos = 0;
_length = 0;
}
HRESULT Close();
MY_UNKNOWN_IMP1(IOutStream)
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
STDMETHOD(SetSize)(Int64 newSize);
};
// static NSynchronization::CCriticalSection g_TempPathsCS;
HRESULT COutMultiVolStream::Close()
{
HRESULT res = S_OK;
for (int i = 0; i < Streams.Size(); i++)
{
CSubStreamInfo &s = Streams[i];
if (s.StreamSpec)
{
HRESULT res2 = s.StreamSpec->Close();
if (res2 != S_OK)
res = res2;
}
}
return res;
}
STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
if(processedSize != NULL)
*processedSize = 0;
while(size > 0)
{
if (_streamIndex >= Streams.Size())
{
CSubStreamInfo subStream;
wchar_t temp[32];
ConvertUInt64ToString(_streamIndex + 1, temp);
UString res = temp;
while (res.Length() < 3)
res = UString(L'0') + res;
UString name = Prefix + res;
subStream.StreamSpec = new COutFileStream;
subStream.Stream = subStream.StreamSpec;
if(!subStream.StreamSpec->Create(name, false))
return ::GetLastError();
{
// NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
TempFiles->Paths.Add(name);
}
subStream.Pos = 0;
subStream.RealSize = 0;
subStream.Name = name;
Streams.Add(subStream);
continue;
}
CSubStreamInfo &subStream = Streams[_streamIndex];
int index = _streamIndex;
if (index >= Sizes.Size())
index = Sizes.Size() - 1;
UInt64 volSize = Sizes[index];
if (_offsetPos >= volSize)
{
_offsetPos -= volSize;
_streamIndex++;
continue;
}
if (_offsetPos != subStream.Pos)
{
// CMyComPtr<IOutStream> outStream;
// RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
subStream.Pos = _offsetPos;
}
UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
UInt32 realProcessed;
RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
data = (void *)((Byte *)data + realProcessed);
size -= realProcessed;
subStream.Pos += realProcessed;
_offsetPos += realProcessed;
_absPos += realProcessed;
if (_absPos > _length)
_length = _absPos;
if (_offsetPos > subStream.RealSize)
subStream.RealSize = _offsetPos;
if(processedSize != NULL)
*processedSize += realProcessed;
if (subStream.Pos == volSize)
{
_streamIndex++;
_offsetPos = 0;
}
if (realProcessed == 0 && curSize != 0)
return E_FAIL;
break;
}
return S_OK;
}
STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
if(seekOrigin >= 3)
return STG_E_INVALIDFUNCTION;
switch(seekOrigin)
{
case STREAM_SEEK_SET:
_absPos = offset;
break;
case STREAM_SEEK_CUR:
_absPos += offset;
break;
case STREAM_SEEK_END:
_absPos = _length + offset;
break;
}
_offsetPos = _absPos;
if (newPosition != NULL)
*newPosition = _absPos;
_streamIndex = 0;
return S_OK;
}
STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize)
{
if (newSize < 0)
return E_INVALIDARG;
int i = 0;
while (i < Streams.Size())
{
CSubStreamInfo &subStream = Streams[i++];
if ((UInt64)newSize < subStream.RealSize)
{
RINOK(subStream.Stream->SetSize(newSize));
subStream.RealSize = newSize;
break;
}
newSize -= subStream.RealSize;
}
while (i < Streams.Size())
{
{
CSubStreamInfo &subStream = Streams.Back();
subStream.Stream.Release();
NDirectory::DeleteFileAlways(subStream.Name);
}
Streams.DeleteBack();
}
_offsetPos = _absPos;
_streamIndex = 0;
_length = newSize;
return S_OK;
}
static const wchar_t *kDefaultArchiveType = L"7z";
static const wchar_t *kSFXExtension =
#ifdef _WIN32
L"exe";
#else
L"";
#endif
bool CUpdateOptions::Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType)
{
if (!arcType.IsEmpty())
MethodMode.FormatIndex = codecs->FindFormatForArchiveType(arcType);
else
{
MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
if (MethodMode.FormatIndex < 0)
MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
}
if (MethodMode.FormatIndex < 0)
return false;
const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
UString typeExt = arcInfo.GetMainExt();
UString ext = typeExt;
if (SfxMode)
ext = kSFXExtension;
ArchivePath.BaseExtension = ext;
ArchivePath.VolExtension = typeExt;
ArchivePath.ParseFromPath(arcPath);
for (int i = 0; i < Commands.Size(); i++)
{
CUpdateArchiveCommand &uc = Commands[i];
uc.ArchivePath.BaseExtension = ext;
uc.ArchivePath.VolExtension = typeExt;
uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
}
return true;
}
static HRESULT Compress(
CCodecs *codecs,
const CActionSet &actionSet,
IInArchive *archive,
const CCompressionMethodMode &compressionMethod,
CArchivePath &archivePath,
const CObjectVector<CArchiveItem> &archiveItems,
bool shareForWrite,
bool stdInMode,
/* const UString & stdInFileName, */
bool stdOutMode,
const CObjectVector<CDirItem> &dirItems,
bool sfxMode,
const UString &sfxModule,
const CRecordVector<UInt64> &volumesSizes,
CTempFiles &tempFiles,
CUpdateErrorInfo &errorInfo,
IUpdateCallbackUI *callback)
{
CMyComPtr<IOutArchive> outArchive;
if(archive != NULL)
{
CMyComPtr<IInArchive> archive2 = archive;
HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
if(result != S_OK)
throw kUpdateIsNotSupoorted;
}
else
{
RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));
#ifdef EXTERNAL_CODECS
{
CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
if (setCompressCodecsInfo)
{
RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
}
}
#endif
}
if (outArchive == 0)
throw kUpdateIsNotSupoorted;
NFileTimeType::EEnum fileTimeType;
UInt32 value;
RINOK(outArchive->GetFileTimeType(&value));
switch(value)
{
case NFileTimeType::kWindows:
case NFileTimeType::kDOS:
case NFileTimeType::kUnix:
fileTimeType = NFileTimeType::EEnum(value);
break;
default:
return E_FAIL;
}
CObjectVector<CUpdatePair> updatePairs;
GetUpdatePairInfoList(dirItems, archiveItems, fileTimeType, updatePairs); // must be done only once!!!
CObjectVector<CUpdatePair2> updatePairs2;
UpdateProduce(updatePairs, actionSet, updatePairs2);
UInt32 numFiles = 0;
for (int i = 0; i < updatePairs2.Size(); i++)
if (updatePairs2[i].NewData)
numFiles++;
RINOK(callback->SetNumFiles(numFiles));
CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
updateCallbackSpec->ShareForWrite = shareForWrite;
updateCallbackSpec->StdInMode = stdInMode;
updateCallbackSpec->Callback = callback;
updateCallbackSpec->DirItems = &dirItems;
updateCallbackSpec->ArchiveItems = &archiveItems;
updateCallbackSpec->UpdatePairs = &updatePairs2;
CMyComPtr<ISequentialOutStream> outStream;
const UString &archiveName = archivePath.GetFinalPath();
if (!stdOutMode)
{
UString resultPath;
int pos;
if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
throw 1417161;
NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
}
COutFileStream *outStreamSpec = NULL;
COutMultiVolStream *volStreamSpec = NULL;
if (volumesSizes.Size() == 0)
{
if (stdOutMode)
outStream = new CStdOutFileStream;
else
{
outStreamSpec = new COutFileStream;
outStream = outStreamSpec;
bool isOK = false;
UString realPath;
for (int i = 0; i < (1 << 16); i++)
{
if (archivePath.Temp)
{
if (i > 0)
{
wchar_t s[32];
ConvertUInt64ToString(i, s);
archivePath.TempPostfix = s;
}
realPath = archivePath.GetTempPath();
}
else
realPath = archivePath.GetFinalPath();
if (outStreamSpec->Create(realPath, false))
{
tempFiles.Paths.Add(realPath);
isOK = true;
break;
}
if (::GetLastError() != ERROR_FILE_EXISTS)
break;
if (!archivePath.Temp)
break;
}
if (!isOK)
{
errorInfo.SystemError = ::GetLastError();
errorInfo.FileName = realPath;
errorInfo.Message = L"Can not open file";
return E_FAIL;
}
}
}
else
{
if (stdOutMode)
return E_FAIL;
volStreamSpec = new COutMultiVolStream;
outStream = volStreamSpec;
volStreamSpec->Sizes = volumesSizes;
volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
volStreamSpec->TempFiles = &tempFiles;
volStreamSpec->Init();
/*
updateCallbackSpec->VolumesSizes = volumesSizes;
updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
if (!archivePath.VolExtension.IsEmpty())
updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
*/
}
RINOK(SetProperties(outArchive, compressionMethod.Properties));
if (sfxMode)
{
CInFileStream *sfxStreamSpec = new CInFileStream;
CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
if (!sfxStreamSpec->Open(sfxModule))
{
errorInfo.SystemError = ::GetLastError();
errorInfo.Message = L"Can't open sfx module";
errorInfo.FileName = sfxModule;
return E_FAIL;
}
CMyComPtr<ISequentialOutStream> sfxOutStream;
COutFileStream *outStreamSpec = NULL;
if (volumesSizes.Size() == 0)
sfxOutStream = outStream;
else
{
outStreamSpec = new COutFileStream;
sfxOutStream = outStreamSpec;
UString realPath = archivePath.GetFinalPath();
if (!outStreamSpec->Create(realPath, false))
{
errorInfo.SystemError = ::GetLastError();
errorInfo.FileName = realPath;
errorInfo.Message = L"Can not open file";
return E_FAIL;
}
}
RINOK(CopyBlock(sfxStream, sfxOutStream));
if (outStreamSpec)
{
RINOK(outStreamSpec->Close());
}
}
HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
callback->Finilize();
RINOK(result);
if (outStreamSpec)
result = outStreamSpec->Close();
else if (volStreamSpec)
result = volStreamSpec->Close();
return result;
}
HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
IInArchive *archive,
const UString &defaultItemName,
const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
CObjectVector<CArchiveItem> &archiveItems)
{
archiveItems.Clear();
UInt32 numItems;
RINOK(archive->GetNumberOfItems(&numItems));
archiveItems.Reserve(numItems);
for(UInt32 i = 0; i < numItems; i++)
{
CArchiveItem ai;
RINOK(GetArchiveItemPath(archive, i, ai.Name));
RINOK(IsArchiveItemFolder(archive, i, ai.IsDirectory));
ai.Censored = censor.CheckPath(ai.Name.IsEmpty() ? defaultItemName : ai.Name, !ai.IsDirectory);
RINOK(GetArchiveItemFileTime(archive, i,
archiveFileInfo.LastWriteTime, ai.LastWriteTime));
CPropVariant propertySize;
RINOK(archive->GetProperty(i, kpidSize, &propertySize));
ai.SizeIsDefined = (propertySize.vt != VT_EMPTY);
if (ai.SizeIsDefined)
ai.Size = ConvertPropVariantToUInt64(propertySize);
ai.IndexInServer = i;
archiveItems.Add(ai);
}
return S_OK;
}
static HRESULT UpdateWithItemLists(
CCodecs *codecs,
CUpdateOptions &options,
IInArchive *archive,
const CObjectVector<CArchiveItem> &archiveItems,
const CObjectVector<CDirItem> &dirItems,
CTempFiles &tempFiles,
CUpdateErrorInfo &errorInfo,
IUpdateCallbackUI2 *callback)
{
for(int i = 0; i < options.Commands.Size(); i++)
{
CUpdateArchiveCommand &command = options.Commands[i];
if (options.StdOutMode)
{
RINOK(callback->StartArchive(0, archive != 0));
}
else
{
RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
i == 0 && options.UpdateArchiveItself && archive != 0));
}
RINOK(Compress(
codecs,
command.ActionSet, archive,
options.MethodMode,
command.ArchivePath,
archiveItems,
options.OpenShareForWrite,
options.StdInMode,
/* options.StdInFileName, */
options.StdOutMode,
dirItems,
options.SfxMode, options.SfxModule,
options.VolumesSizes,
tempFiles,
errorInfo, callback));
RINOK(callback->FinishArchive());
}
return S_OK;
}
#ifdef _WIN32
class CCurrentDirRestorer
{
UString m_CurrentDirectory;
public:
CCurrentDirRestorer()
{ NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
~CCurrentDirRestorer()
{ RestoreDirectory();}
bool RestoreDirectory()
{ return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
};
#endif
struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
{
IUpdateCallbackUI2 *Callback;
HRESULT CheckBreak() { return Callback->CheckBreak(); }
};
HRESULT UpdateArchive(
CCodecs *codecs,
const NWildcard::CCensor &censor,
CUpdateOptions &options,
CUpdateErrorInfo &errorInfo,
IOpenCallbackUI *openCallback,
IUpdateCallbackUI2 *callback)
{
if (options.StdOutMode && options.EMailMode)
return E_FAIL;
if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
return E_NOTIMPL;
if (options.SfxMode)
{
CProperty property;
property.Name = L"rsfx";
property.Value = L"on";
options.MethodMode.Properties.Add(property);
if (options.SfxModule.IsEmpty())
{
errorInfo.Message = L"sfx file is not specified";
return E_FAIL;
}
UString name = options.SfxModule;
if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
{
errorInfo.Message = L"can't find specified sfx module";
return E_FAIL;
}
}
const UString archiveName = options.ArchivePath.GetFinalPath();
UString defaultItemName;
NFind::CFileInfoW archiveFileInfo;
CArchiveLink archiveLink;
IInArchive *archive = 0;
if (NFind::FindFile(archiveName, archiveFileInfo))
{
if (archiveFileInfo.IsDirectory())
throw "there is no such archive";
if (options.VolumesSizes.Size() > 0)
return E_NOTIMPL;
HRESULT result = MyOpenArchive(codecs, archiveName, archiveLink, openCallback);
RINOK(callback->OpenResult(archiveName, result));
RINOK(result);
if (archiveLink.VolumePaths.Size() > 1)
{
errorInfo.SystemError = (DWORD)E_NOTIMPL;
errorInfo.Message = L"Updating for multivolume archives is not implemented";
return E_NOTIMPL;
}
archive = archiveLink.GetArchive();
defaultItemName = archiveLink.GetDefaultItemName();
}
else
{
/*
if (archiveType.IsEmpty())
throw "type of archive is not specified";
*/
}
CObjectVector<CDirItem> dirItems;
if (options.StdInMode)
{
CDirItem item;
item.FullPath = item.Name = options.StdInFileName;
item.Size = (UInt64)(Int64)-1;
item.Attributes = 0;
SYSTEMTIME st;
FILETIME ft;
GetSystemTime(&st);
SystemTimeToFileTime(&st, &ft);
item.CreationTime = item.LastAccessTime = item.LastWriteTime = ft;
dirItems.Add(item);
}
else
{
bool needScanning = false;
for(int i = 0; i < options.Commands.Size(); i++)
if (options.Commands[i].ActionSet.NeedScanning())
needScanning = true;
if (needScanning)
{
CEnumDirItemUpdateCallback enumCallback;
enumCallback.Callback = callback;
RINOK(callback->StartScanning());
UStringVector errorPaths;
CRecordVector<DWORD> errorCodes;
HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
for (int i = 0; i < errorPaths.Size(); i++)
{
RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
}
if(res != S_OK)
{
errorInfo.Message = L"Scanning error";
// errorInfo.FileName = errorPath;
return res;
}
RINOK(callback->FinishScanning());
}
}
UString tempDirPrefix;
bool usesTempDir = false;
#ifdef _WIN32
NDirectory::CTempDirectoryW tempDirectory;
if (options.EMailMode && options.EMailRemoveAfter)
{
tempDirectory.Create(kTempFolderPrefix);
tempDirPrefix = tempDirectory.GetPath();
NormalizeDirPathPrefix(tempDirPrefix);
usesTempDir = true;
}
#endif
CTempFiles tempFiles;
bool createTempFile = false;
if(!options.StdOutMode && options.UpdateArchiveItself)
{
CArchivePath &ap = options.Commands[0].ArchivePath;
ap = options.ArchivePath;
// if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
if ((archive != 0 || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
{
createTempFile = true;
ap.Temp = true;
if (!options.WorkingDir.IsEmpty())
{
ap.TempPrefix = options.WorkingDir;
NormalizeDirPathPrefix(ap.TempPrefix);
}
}
}
for(int i = 0; i < options.Commands.Size(); i++)
{
CArchivePath &ap = options.Commands[i].ArchivePath;
if (usesTempDir)
{
// Check it
ap.Prefix = tempDirPrefix;
// ap.Temp = true;
// ap.TempPrefix = tempDirPrefix;
}
if (i > 0 || !createTempFile)
{
const UString &path = ap.GetFinalPath();
if (NFind::DoesFileExist(path))
{
errorInfo.SystemError = 0;
errorInfo.Message = L"File already exists";
errorInfo.FileName = path;
return E_FAIL;
}
}
}
CObjectVector<CArchiveItem> archiveItems;
if (archive != NULL)
{
RINOK(EnumerateInArchiveItems(censor,
archive, defaultItemName, archiveFileInfo, archiveItems));
}
RINOK(UpdateWithItemLists(codecs, options, archive, archiveItems, dirItems,
tempFiles, errorInfo, callback));
if (archive != NULL)
{
RINOK(archiveLink.Close());
archiveLink.Release();
}
tempFiles.Paths.Clear();
if(createTempFile)
{
try
{
CArchivePath &ap = options.Commands[0].ArchivePath;
const UString &tempPath = ap.GetTempPath();
if (archive != NULL)
if (!NDirectory::DeleteFileAlways(archiveName))
{
errorInfo.SystemError = ::GetLastError();
errorInfo.Message = L"delete file error";
errorInfo.FileName = archiveName;
return E_FAIL;
}
if (!NDirectory::MyMoveFile(tempPath, archiveName))
{
errorInfo.SystemError = ::GetLastError();
errorInfo.Message = L"move file error";
errorInfo.FileName = tempPath;
errorInfo.FileName2 = archiveName;
return E_FAIL;
}
}
catch(...)
{
throw;
}
}
#ifdef _WIN32
if (options.EMailMode)
{
NDLL::CLibrary mapiLib;
if (!mapiLib.Load(TEXT("Mapi32.dll")))
{
errorInfo.SystemError = ::GetLastError();
errorInfo.Message = L"can not load Mapi32.dll";
return E_FAIL;
}
LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)
mapiLib.GetProcAddress("MAPISendDocuments");
if (fnSend == 0)
{
errorInfo.SystemError = ::GetLastError();
errorInfo.Message = L"can not find MAPISendDocuments function";
return E_FAIL;
}
UStringVector fullPaths;
int i;
for(i = 0; i < options.Commands.Size(); i++)
{
CArchivePath &ap = options.Commands[i].ArchivePath;
UString arcPath;
if(!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
{
errorInfo.SystemError = ::GetLastError();
return E_FAIL;
}
fullPaths.Add(arcPath);
}
CCurrentDirRestorer curDirRestorer;
for(i = 0; i < fullPaths.Size(); i++)
{
UString arcPath = fullPaths[i];
UString fileName = ExtractFileNameFromPath(arcPath);
AString path = GetAnsiString(arcPath);
AString name = GetAnsiString(fileName);
// Warning!!! MAPISendDocuments function changes Current directory
fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
}
}
#endif
return S_OK;
}
@@ -0,0 +1,165 @@
// Update.h
#ifndef __UPDATE_H
#define __UPDATE_H
#include "Common/Wildcard.h"
#include "Windows/FileFind.h"
#include "../../Archive/IArchive.h"
#include "UpdateAction.h"
#include "ArchiveOpenCallback.h"
#include "UpdateCallback.h"
#include "Property.h"
#include "LoadCodecs.h"
struct CArchivePath
{
UString Prefix; // path(folder) prefix including slash
UString Name; // base name
UString BaseExtension; // archive type extension or "exe" extension
UString VolExtension; // archive type extension for volumes
bool Temp;
UString TempPrefix; // path(folder) for temp location
UString TempPostfix;
CArchivePath(): Temp(false) {};
void ParseFromPath(const UString &path)
{
SplitPathToParts(path, Prefix, Name);
if (Name.IsEmpty())
return;
int dotPos = Name.ReverseFind(L'.');
if (dotPos <= 0)
return;
if (dotPos == Name.Length() - 1)
{
Name = Name.Left(dotPos);
BaseExtension.Empty();
return;
}
if (BaseExtension.CompareNoCase(Name.Mid(dotPos + 1)) == 0)
{
BaseExtension = Name.Mid(dotPos + 1);
Name = Name.Left(dotPos);
}
else
BaseExtension.Empty();
}
UString GetPathWithoutExt() const
{
return Prefix + Name;
}
UString GetFinalPath() const
{
UString path = GetPathWithoutExt();
if (!BaseExtension.IsEmpty())
path += UString(L'.') + BaseExtension;
return path;
}
UString GetTempPath() const
{
UString path = TempPrefix + Name;
if (!BaseExtension.IsEmpty())
path += UString(L'.') + BaseExtension;
path += L".tmp";
path += TempPostfix;
return path;
}
};
struct CUpdateArchiveCommand
{
UString UserArchivePath;
CArchivePath ArchivePath;
NUpdateArchive::CActionSet ActionSet;
};
struct CCompressionMethodMode
{
int FormatIndex;
CObjectVector<CProperty> Properties;
CCompressionMethodMode(): FormatIndex(-1) {}
};
struct CUpdateOptions
{
CCompressionMethodMode MethodMode;
CObjectVector<CUpdateArchiveCommand> Commands;
bool UpdateArchiveItself;
CArchivePath ArchivePath;
bool SfxMode;
UString SfxModule;
bool OpenShareForWrite;
bool StdInMode;
UString StdInFileName;
bool StdOutMode;
bool EMailMode;
bool EMailRemoveAfter;
UString EMailAddress;
UString WorkingDir;
bool Init(const CCodecs *codecs, const UString &arcPath, const UString &arcType);
CUpdateOptions():
UpdateArchiveItself(true),
SfxMode(false),
StdInMode(false),
StdOutMode(false),
EMailMode(false),
EMailRemoveAfter(false),
OpenShareForWrite(false)
{};
CRecordVector<UInt64> VolumesSizes;
};
struct CErrorInfo
{
DWORD SystemError;
UString FileName;
UString FileName2;
UString Message;
// UStringVector ErrorPaths;
// CRecordVector<DWORD> ErrorCodes;
CErrorInfo(): SystemError(0) {};
};
struct CUpdateErrorInfo: public CErrorInfo
{
};
#define INTERFACE_IUpdateCallbackUI2(x) \
INTERFACE_IUpdateCallbackUI(x) \
virtual HRESULT OpenResult(const wchar_t *name, HRESULT result) x; \
virtual HRESULT StartScanning() x; \
virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \
virtual HRESULT FinishScanning() x; \
virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \
virtual HRESULT FinishArchive() x; \
struct IUpdateCallbackUI2: public IUpdateCallbackUI
{
INTERFACE_IUpdateCallbackUI2(=0)
};
HRESULT UpdateArchive(
CCodecs *codecs,
const NWildcard::CCensor &censor,
CUpdateOptions &options,
CUpdateErrorInfo &errorInfo,
IOpenCallbackUI *openCallback,
IUpdateCallbackUI2 *callback);
#endif
@@ -0,0 +1,64 @@
// UpdateAction.cpp
#include "StdAfx.h"
#include "UpdateAction.h"
namespace NUpdateArchive {
const CActionSet kAddActionSet =
{
NPairAction::kCopy,
NPairAction::kCopy,
NPairAction::kCompress,
NPairAction::kCompress,
NPairAction::kCompress,
NPairAction::kCompress,
NPairAction::kCompress
};
const CActionSet kUpdateActionSet =
{
NPairAction::kCopy,
NPairAction::kCopy,
NPairAction::kCompress,
NPairAction::kCopy,
NPairAction::kCompress,
NPairAction::kCopy,
NPairAction::kCompress
};
const CActionSet kFreshActionSet =
{
NPairAction::kCopy,
NPairAction::kCopy,
NPairAction::kIgnore,
NPairAction::kCopy,
NPairAction::kCompress,
NPairAction::kCopy,
NPairAction::kCompress
};
const CActionSet kSynchronizeActionSet =
{
NPairAction::kCopy,
NPairAction::kIgnore,
NPairAction::kCompress,
NPairAction::kCopy,
NPairAction::kCompress,
NPairAction::kCopy,
NPairAction::kCompress,
};
const CActionSet kDeleteActionSet =
{
NPairAction::kCopy,
NPairAction::kIgnore,
NPairAction::kIgnore,
NPairAction::kIgnore,
NPairAction::kIgnore,
NPairAction::kIgnore,
NPairAction::kIgnore
};
}
@@ -0,0 +1,57 @@
// UpdateAction.h
#ifndef __UPDATE_ACTION_H
#define __UPDATE_ACTION_H
namespace NUpdateArchive {
namespace NPairState
{
const int kNumValues = 7;
enum EEnum
{
kNotMasked = 0,
kOnlyInArchive,
kOnlyOnDisk,
kNewInArchive,
kOldInArchive,
kSameFiles,
kUnknowNewerFiles
};
}
namespace NPairAction
{
enum EEnum
{
kIgnore = 0,
kCopy,
kCompress,
kCompressAsAnti
};
}
struct CActionSet
{
NPairAction::EEnum StateActions[NPairState::kNumValues];
bool NeedScanning() const
{
int i;
for (i = 0; i < NPairState::kNumValues; i++)
if (StateActions[i] == NPairAction::kCompress)
return true;
for (i = 1; i < NPairState::kNumValues; i++)
if (StateActions[i] != NPairAction::kIgnore)
return true;
return false;
}
};
extern const CActionSet kAddActionSet;
extern const CActionSet kUpdateActionSet;
extern const CActionSet kFreshActionSet;
extern const CActionSet kSynchronizeActionSet;
extern const CActionSet kDeleteActionSet;
};
#endif
@@ -0,0 +1,267 @@
// UpdateCallback.cpp
#include "StdAfx.h"
#include "UpdateCallback.h"
#include "Common/StringConvert.h"
#include "Common/IntToString.h"
#include "Common/Defs.h"
#include "Common/ComTry.h"
#include "Windows/PropVariant.h"
#include "../../Common/FileStreams.h"
using namespace NWindows;
CArchiveUpdateCallback::CArchiveUpdateCallback():
Callback(0),
ShareForWrite(false),
StdInMode(false),
DirItems(0),
ArchiveItems(0),
UpdatePairs(0)
{}
STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
{
COM_TRY_BEGIN
return Callback->SetTotal(size);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
{
COM_TRY_BEGIN
return Callback->SetCompleted(completeValue);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
{
COM_TRY_BEGIN
return Callback->SetRatioInfo(inSize, outSize);
COM_TRY_END
}
/*
STATPROPSTG kProperties[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidIsFolder, VT_BOOL},
{ NULL, kpidSize, VT_UI8},
{ NULL, kpidLastAccessTime, VT_FILETIME},
{ NULL, kpidCreationTime, VT_FILETIME},
{ NULL, kpidLastWriteTime, VT_FILETIME},
{ NULL, kpidAttributes, VT_UI4},
{ NULL, kpidIsAnti, VT_BOOL}
};
*/
STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
{
return E_NOTIMPL;
/*
return CStatPropEnumerator::CreateEnumerator(kProperties,
sizeof(kProperties) / sizeof(kProperties[0]), enumerator);
*/
}
STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
{
COM_TRY_BEGIN
RINOK(Callback->CheckBreak());
const CUpdatePair2 &updatePair = (*UpdatePairs)[index];
if(newData != NULL)
*newData = BoolToInt(updatePair.NewData);
if(newProperties != NULL)
*newProperties = BoolToInt(updatePair.NewProperties);
if(indexInArchive != NULL)
{
if (updatePair.ExistInArchive)
{
if (ArchiveItems == 0)
*indexInArchive = updatePair.ArchiveItemIndex;
else
*indexInArchive = (*ArchiveItems)[updatePair.ArchiveItemIndex].IndexInServer;
}
else
*indexInArchive = UInt32(-1);
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
const CUpdatePair2 &updatePair = (*UpdatePairs)[index];
NWindows::NCOM::CPropVariant propVariant;
if (propID == kpidIsAnti)
{
propVariant = updatePair.IsAnti;
propVariant.Detach(value);
return S_OK;
}
if (updatePair.IsAnti)
{
switch(propID)
{
case kpidIsFolder:
case kpidPath:
break;
case kpidSize:
propVariant = (UInt64)0;
propVariant.Detach(value);
return S_OK;
default:
propVariant.Detach(value);
return S_OK;
}
}
if(updatePair.ExistOnDisk)
{
const CDirItem &dirItem = (*DirItems)[updatePair.DirItemIndex];
switch(propID)
{
case kpidPath:
propVariant = dirItem.Name;
break;
case kpidIsFolder:
propVariant = dirItem.IsDirectory();
break;
case kpidSize:
propVariant = dirItem.Size;
break;
case kpidAttributes:
propVariant = dirItem.Attributes;
break;
case kpidLastAccessTime:
propVariant = dirItem.LastAccessTime;
break;
case kpidCreationTime:
propVariant = dirItem.CreationTime;
break;
case kpidLastWriteTime:
propVariant = dirItem.LastWriteTime;
break;
}
}
else
{
if (propID == kpidPath)
{
if (updatePair.NewNameIsDefined)
{
propVariant = updatePair.NewName;
propVariant.Detach(value);
return S_OK;
}
}
if (updatePair.ExistInArchive && Archive)
{
UInt32 indexInArchive;
if (ArchiveItems == 0)
indexInArchive = updatePair.ArchiveItemIndex;
else
indexInArchive = (*ArchiveItems)[updatePair.ArchiveItemIndex].IndexInServer;
return Archive->GetProperty(indexInArchive, propID, value);
}
}
propVariant.Detach(value);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
{
COM_TRY_BEGIN
const CUpdatePair2 &updatePair = (*UpdatePairs)[index];
if(!updatePair.NewData)
return E_FAIL;
RINOK(Callback->CheckBreak());
RINOK(Callback->Finilize());
if(updatePair.IsAnti)
{
return Callback->GetStream((*ArchiveItems)[updatePair.ArchiveItemIndex].Name, true);
}
const CDirItem &dirItem = (*DirItems)[updatePair.DirItemIndex];
RINOK(Callback->GetStream(dirItem.Name, false));
if(dirItem.IsDirectory())
return S_OK;
if (StdInMode)
{
CStdInFileStream *inStreamSpec = new CStdInFileStream;
CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
*inStream = inStreamLoc.Detach();
}
else
{
CInFileStream *inStreamSpec = new CInFileStream;
CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
UString path = DirPrefix + dirItem.FullPath;
if(!inStreamSpec->OpenShared(path, ShareForWrite))
{
return Callback->OpenFileError(path, ::GetLastError());
}
*inStream = inStreamLoc.Detach();
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult)
{
COM_TRY_BEGIN
return Callback->SetOperationResult(operationResult);
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
{
if (VolumesSizes.Size() == 0)
return S_FALSE;
if (index >= (UInt32)VolumesSizes.Size())
index = VolumesSizes.Size() - 1;
*size = VolumesSizes[index];
return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
{
COM_TRY_BEGIN
wchar_t temp[32];
ConvertUInt64ToString(index + 1, temp);
UString res = temp;
while (res.Length() < 2)
res = UString(L'0') + res;
UString fileName = VolName;
fileName += L'.';
fileName += res;
fileName += VolExt;
COutFileStream *streamSpec = new COutFileStream;
CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
if(!streamSpec->Create(fileName, false))
return ::GetLastError();
*volumeStream = streamLoc.Detach();
return S_OK;
COM_TRY_END
}
STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
{
COM_TRY_BEGIN
return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
COM_TRY_END
}
@@ -0,0 +1,82 @@
// UpdateCallback.h
#ifndef __UPDATECALLBACK_H
#define __UPDATECALLBACK_H
#include "Common/MyCom.h"
#include "Common/MyString.h"
#include "../../IPassword.h"
#include "../../ICoder.h"
#include "../Common/UpdatePair.h"
#include "../Common/UpdateProduce.h"
#define INTERFACE_IUpdateCallbackUI(x) \
virtual HRESULT SetTotal(UInt64 size) x; \
virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \
virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \
virtual HRESULT CheckBreak() x; \
virtual HRESULT Finilize() x; \
virtual HRESULT SetNumFiles(UInt64 numFiles) x; \
virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \
virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \
virtual HRESULT SetOperationResult(Int32 operationResult) x; \
virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \
// virtual HRESULT CloseProgress() { return S_OK; };
struct IUpdateCallbackUI
{
INTERFACE_IUpdateCallbackUI(=0)
};
class CArchiveUpdateCallback:
public IArchiveUpdateCallback2,
public ICryptoGetTextPassword2,
public ICompressProgressInfo,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP3(
IArchiveUpdateCallback2,
ICryptoGetTextPassword2,
ICompressProgressInfo)
// IProgress
STDMETHOD(SetTotal)(UInt64 size);
STDMETHOD(SetCompleted)(const UInt64 *completeValue);
STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
// IUpdateCallback
STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);
STDMETHOD(GetUpdateItemInfo)(UInt32 index,
Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
STDMETHOD(SetOperationResult)(Int32 operationResult);
STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
public:
CRecordVector<UInt64> VolumesSizes;
UString VolName;
UString VolExt;
IUpdateCallbackUI *Callback;
UString DirPrefix;
bool ShareForWrite;
bool StdInMode;
const CObjectVector<CDirItem> *DirItems;
const CObjectVector<CArchiveItem> *ArchiveItems;
const CObjectVector<CUpdatePair2> *UpdatePairs;
CMyComPtr<IInArchive> Archive;
CArchiveUpdateCallback();
};
#endif
@@ -0,0 +1,166 @@
// UpdatePair.cpp
#include "StdAfx.h"
#include <time.h>
#include "Common/Defs.h"
#include "Common/Wildcard.h"
#include "Windows/Time.h"
#include "UpdatePair.h"
#include "SortUtils.h"
using namespace NWindows;
using namespace NTime;
static int MyCompareTime(NFileTimeType::EEnum fileTimeType,
const FILETIME &time1, const FILETIME &time2)
{
switch(fileTimeType)
{
case NFileTimeType::kWindows:
return ::CompareFileTime(&time1, &time2);
case NFileTimeType::kUnix:
{
UInt32 unixTime1, unixTime2;
if (!FileTimeToUnixTime(time1, unixTime1))
{
unixTime1 = 0;
// throw 4191614;
}
if (!FileTimeToUnixTime(time2, unixTime2))
{
unixTime2 = 0;
// throw 4191615;
}
return MyCompare(unixTime1, unixTime2);
}
case NFileTimeType::kDOS:
{
UInt32 dosTime1, dosTime2;
FileTimeToDosTime(time1, dosTime1);
FileTimeToDosTime(time2, dosTime2);
/*
if (!FileTimeToDosTime(time1, dosTime1))
throw 4191616;
if (!FileTimeToDosTime(time2, dosTime2))
throw 4191617;
*/
return MyCompare(dosTime1, dosTime2);
}
}
throw 4191618;
}
static const wchar_t *kDuplicateFileNameMessage = L"Duplicate filename:";
/*
static const char *kNotCensoredCollisionMessaged = "Internal file name collision:\n";
static const char *kSameTimeChangedSizeCollisionMessaged =
"Collision between files with same date/time and different sizes:\n";
*/
static void TestDuplicateString(const UStringVector &strings, const CIntVector &indices)
{
for(int i = 0; i + 1 < indices.Size(); i++)
if (CompareFileNames(strings[indices[i]], strings[indices[i + 1]]) == 0)
{
UString message = kDuplicateFileNameMessage;
message += L"\n";
message += strings[indices[i]];
message += L"\n";
message += strings[indices[i + 1]];
throw message;
}
}
void GetUpdatePairInfoList(
const CObjectVector<CDirItem> &dirItems,
const CObjectVector<CArchiveItem> &archiveItems,
NFileTimeType::EEnum fileTimeType,
CObjectVector<CUpdatePair> &updatePairs)
{
CIntVector dirIndices, archiveIndices;
UStringVector dirNames, archiveNames;
int numDirItems = dirItems.Size();
int i;
for(i = 0; i < numDirItems; i++)
dirNames.Add(dirItems[i].Name);
SortFileNames(dirNames, dirIndices);
TestDuplicateString(dirNames, dirIndices);
int numArchiveItems = archiveItems.Size();
for(i = 0; i < numArchiveItems; i++)
archiveNames.Add(archiveItems[i].Name);
SortFileNames(archiveNames, archiveIndices);
TestDuplicateString(archiveNames, archiveIndices);
int dirItemIndex = 0, archiveItemIndex = 0;
CUpdatePair pair;
while(dirItemIndex < numDirItems && archiveItemIndex < numArchiveItems)
{
int dirItemIndex2 = dirIndices[dirItemIndex],
archiveItemIndex2 = archiveIndices[archiveItemIndex];
const CDirItem &dirItem = dirItems[dirItemIndex2];
const CArchiveItem &archiveItem = archiveItems[archiveItemIndex2];
int compareResult = CompareFileNames(dirItem.Name, archiveItem.Name);
if (compareResult < 0)
{
pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
pair.DirItemIndex = dirItemIndex2;
dirItemIndex++;
}
else if (compareResult > 0)
{
pair.State = archiveItem.Censored ?
NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked;
pair.ArchiveItemIndex = archiveItemIndex2;
archiveItemIndex++;
}
else
{
if (!archiveItem.Censored)
throw 1082022;; // TTString(kNotCensoredCollisionMessaged + dirItem.Name);
pair.DirItemIndex = dirItemIndex2;
pair.ArchiveItemIndex = archiveItemIndex2;
switch (MyCompareTime(fileTimeType, dirItem.LastWriteTime, archiveItem.LastWriteTime))
{
case -1:
pair.State = NUpdateArchive::NPairState::kNewInArchive;
break;
case 1:
pair.State = NUpdateArchive::NPairState::kOldInArchive;
break;
default:
if (archiveItem.SizeIsDefined)
if (dirItem.Size != archiveItem.Size)
// throw 1082034; // kSameTimeChangedSizeCollisionMessaged;
pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
else
pair.State = NUpdateArchive::NPairState::kSameFiles;
else
pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
}
dirItemIndex++;
archiveItemIndex++;
}
updatePairs.Add(pair);
}
for(;dirItemIndex < numDirItems; dirItemIndex++)
{
pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
pair.DirItemIndex = dirIndices[dirItemIndex];
updatePairs.Add(pair);
}
for(;archiveItemIndex < numArchiveItems; archiveItemIndex++)
{
int archiveItemIndex2 = archiveIndices[archiveItemIndex];
const CArchiveItem &archiveItem = archiveItems[archiveItemIndex2];
pair.State = archiveItem.Censored ?
NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked;
pair.ArchiveItemIndex = archiveItemIndex2;
updatePairs.Add(pair);
}
}
@@ -0,0 +1,24 @@
// UpdatePair.h
#ifndef __UPDATE_PAIR_H
#define __UPDATE_PAIR_H
#include "DirItem.h"
#include "UpdateAction.h"
#include "../../Archive/IArchive.h"
struct CUpdatePair
{
NUpdateArchive::NPairState::EEnum State;
int ArchiveItemIndex;
int DirItemIndex;
};
void GetUpdatePairInfoList(
const CObjectVector<CDirItem> &dirItems,
const CObjectVector<CArchiveItem> &archiveItems,
NFileTimeType::EEnum fileTimeType,
CObjectVector<CUpdatePair> &updatePairs);
#endif
@@ -0,0 +1,63 @@
// UpdateProduce.cpp
#include "StdAfx.h"
#include "UpdateProduce.h"
using namespace NUpdateArchive;
static const char *kUpdateActionSetCollision =
"Internal collision in update action set";
void UpdateProduce(
const CObjectVector<CUpdatePair> &updatePairs,
const NUpdateArchive::CActionSet &actionSet,
CObjectVector<CUpdatePair2> &operationChain)
{
for(int i = 0; i < updatePairs.Size(); i++)
{
// CUpdateArchiveRange aRange;
const CUpdatePair &pair = updatePairs[i];
CUpdatePair2 pair2;
pair2.IsAnti = false;
pair2.ArchiveItemIndex = pair.ArchiveItemIndex;
pair2.DirItemIndex = pair.DirItemIndex;
pair2.ExistInArchive = (pair.State != NPairState::kOnlyOnDisk);
pair2.ExistOnDisk = (pair.State != NPairState::kOnlyInArchive && pair.State != NPairState::kNotMasked);
switch(actionSet.StateActions[pair.State])
{
case NPairAction::kIgnore:
/*
if (pair.State != NPairState::kOnlyOnDisk)
IgnoreArchiveItem(m_ArchiveItems[pair.ArchiveItemIndex]);
// cout << "deleting";
*/
break;
case NPairAction::kCopy:
{
if (pair.State == NPairState::kOnlyOnDisk)
throw kUpdateActionSetCollision;
pair2.NewData = pair2.NewProperties = false;
operationChain.Add(pair2);
break;
}
case NPairAction::kCompress:
{
if (pair.State == NPairState::kOnlyInArchive ||
pair.State == NPairState::kNotMasked)
throw kUpdateActionSetCollision;
pair2.NewData = pair2.NewProperties = true;
operationChain.Add(pair2);
break;
}
case NPairAction::kCompressAsAnti:
{
pair2.IsAnti = true;
pair2.NewData = pair2.NewProperties = true;
operationChain.Add(pair2);
break;
}
}
}
}
@@ -0,0 +1,31 @@
// UpdateProduce.h
#ifndef __UPDATE_PRODUCE_H
#define __UPDATE_PRODUCE_H
#include "UpdatePair.h"
struct CUpdatePair2
{
// bool OperationIsCompress;
bool NewData;
bool NewProperties;
bool ExistInArchive;
bool ExistOnDisk;
bool IsAnti;
int ArchiveItemIndex;
int DirItemIndex;
bool NewNameIsDefined;
UString NewName;
CUpdatePair2(): NewNameIsDefined(false) {}
};
void UpdateProduce(
const CObjectVector<CUpdatePair> &updatePairs,
const NUpdateArchive::CActionSet &actionSet,
CObjectVector<CUpdatePair2> &operationChain);
#endif
@@ -0,0 +1,64 @@
// WorkDir.cpp
#include "StdAfx.h"
#include "WorkDir.h"
#include "Common/StringConvert.h"
#include "Common/Wildcard.h"
#include "Windows/FileName.h"
#include "Windows/FileDir.h"
static inline UINT GetCurrentCodePage()
{ return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
using namespace NWindows;
using namespace NFile;
using namespace NName;
UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path)
{
NWorkDir::NMode::EEnum mode = workDirInfo.Mode;
if (workDirInfo.ForRemovableOnly)
{
mode = NWorkDir::NMode::kCurrent;
UString prefix = path.Left(3);
if (prefix[1] == L':' && prefix[2] == L'\\')
{
UINT driveType = GetDriveType(GetSystemString(prefix, GetCurrentCodePage()));
if (driveType == DRIVE_CDROM || driveType == DRIVE_REMOVABLE)
mode = workDirInfo.Mode;
}
/*
CParsedPath parsedPath;
parsedPath.ParsePath(archiveName);
UINT driveType = GetDriveType(parsedPath.Prefix);
if ((driveType != DRIVE_CDROM) && (driveType != DRIVE_REMOVABLE))
mode = NZipSettings::NWorkDir::NMode::kCurrent;
*/
}
switch(mode)
{
case NWorkDir::NMode::kCurrent:
{
return ExtractDirPrefixFromPath(path);
}
case NWorkDir::NMode::kSpecified:
{
UString tempDir = workDirInfo.Path;
NormalizeDirPathPrefix(tempDir);
return tempDir;
}
default:
{
UString tempDir;
if(!NFile::NDirectory::MyGetTempPath(tempDir))
throw 141717;
return tempDir;
}
}
}
@@ -0,0 +1,10 @@
// WorkDir.h
#ifndef __WORKDIR_H
#define __WORKDIR_H
#include "ZipRegistry.h"
UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path);
#endif
@@ -0,0 +1,98 @@
// ZipRegistry.h
#ifndef __ZIPREGISTRY_H
#define __ZIPREGISTRY_H
#include "Common/MyString.h"
#include "Common/Types.h"
#include "ExtractMode.h"
namespace NExtract
{
struct CInfo
{
NPathMode::EEnum PathMode;
NOverwriteMode::EEnum OverwriteMode;
UStringVector Paths;
bool ShowPassword;
};
}
namespace NCompression {
struct CFormatOptions
{
CSysString FormatID;
UString Options;
UString Method;
UString EncryptionMethod;
UInt32 Level;
UInt32 Dictionary;
UInt32 Order;
UInt32 BlockLogSize;
UInt32 NumThreads;
void ResetForLevelChange()
{
BlockLogSize = NumThreads = Level = Dictionary = Order = UInt32(-1);
Method.Empty();
// EncryptionMethod.Empty();
// Options.Empty();
}
CFormatOptions() { ResetForLevelChange(); }
};
struct CInfo
{
UStringVector HistoryArchives;
UInt32 Level;
UString ArchiveType;
CObjectVector<CFormatOptions> FormatOptionsVector;
bool ShowPassword;
bool EncryptHeaders;
};
}
namespace NWorkDir{
namespace NMode
{
enum EEnum
{
kSystem,
kCurrent,
kSpecified
};
}
struct CInfo
{
NMode::EEnum Mode;
UString Path;
bool ForRemovableOnly;
void SetForRemovableOnlyDefault() { ForRemovableOnly = true; }
void SetDefault()
{
Mode = NMode::kSystem;
Path.Empty();
SetForRemovableOnlyDefault();
}
};
}
void SaveExtractionInfo(const NExtract::CInfo &info);
void ReadExtractionInfo(NExtract::CInfo &info);
void SaveCompressionInfo(const NCompression::CInfo &info);
void ReadCompressionInfo(NCompression::CInfo &info);
void SaveWorkDirInfo(const NWorkDir::CInfo &info);
void ReadWorkDirInfo(NWorkDir::CInfo &info);
void SaveCascadedMenu(bool enabled);
bool ReadCascadedMenu();
void SaveContextMenuStatus(UInt32 value);
bool ReadContextMenuStatus(UInt32 &value);
#endif