mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-11 00:07:09 -06:00
637 lines
16 KiB
C++
Executable File
637 lines
16 KiB
C++
Executable File
// Compress.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include <mapi.h>
|
|
|
|
#include "Compress.h"
|
|
#include "CompressDialog.h"
|
|
#include "resource.h"
|
|
|
|
#include "Common/StringConvert.h"
|
|
#include "Common/IntToString.h"
|
|
|
|
#include "Windows/FileName.h"
|
|
#include "Windows/FileFind.h"
|
|
#include "Windows/FileDir.h"
|
|
#include "Windows/Thread.h"
|
|
#include "Windows/COM.h"
|
|
#include "Windows/PropVariant.h"
|
|
|
|
#include "../../FileManager/ProgramLocation.h"
|
|
#include "../../FileManager/FormatUtils.h"
|
|
#include "../../FileManager/UpdateCallback100.h"
|
|
|
|
#include "../Agent/Agent.h"
|
|
#include "../Common/UpdateAction.h"
|
|
#include "../Common/WorkDir.h"
|
|
#include "../Common/ZipRegistry.h"
|
|
#include "../Common/OpenArchive.h"
|
|
#include "../Resource/Extract/resource.h"
|
|
#include "../Explorer/MyMessages.h"
|
|
|
|
using namespace NWindows;
|
|
using namespace NFile;
|
|
using namespace NDirectory;
|
|
using namespace NName;
|
|
|
|
static LPCWSTR kTempArchivePrefix = L"7zA";
|
|
static LPCWSTR kTempFolderPrefix = L"7zE";
|
|
static LPCWSTR kDefaultSfxModule = L"7zC.sfx";
|
|
|
|
static void SplitString(const UString &srcString, UStringVector &destStrings)
|
|
{
|
|
destStrings.Clear();
|
|
for (int pos = 0; pos < srcString.Length();)
|
|
{
|
|
int spacePos = srcString.Find(L' ', pos);
|
|
if (spacePos < 0)
|
|
{
|
|
destStrings.Add(srcString.Mid(pos));
|
|
return;
|
|
}
|
|
if (spacePos != pos)
|
|
destStrings.Add(srcString.Mid(pos, spacePos - pos));
|
|
pos = spacePos + 1;
|
|
}
|
|
}
|
|
|
|
static bool ParseNumberString(const UString &string, UINT32 &number)
|
|
{
|
|
wchar_t *endPtr;
|
|
number = wcstoul(string, &endPtr, 10);
|
|
return (endPtr - string == string.Length());
|
|
}
|
|
|
|
static void SetOptions(const UString &options,
|
|
CObjectVector<CMyComBSTR> &realNames,
|
|
std::vector<NCOM::CPropVariant> &values)
|
|
{
|
|
UStringVector strings;
|
|
SplitString(options, strings);
|
|
for(int i = 0; i < strings.Size(); i++)
|
|
{
|
|
const UString &s = strings[i];
|
|
int index = s.Find(L'=');
|
|
CMyComBSTR name;
|
|
NCOM::CPropVariant propVariant;
|
|
if (index < 0)
|
|
name = s;
|
|
else
|
|
{
|
|
name = s.Left(index);
|
|
UString value = s.Mid(index + 1);
|
|
if (!value.IsEmpty())
|
|
{
|
|
UINT32 number;
|
|
if (ParseNumberString(value, number))
|
|
propVariant = number;
|
|
else
|
|
propVariant = value;
|
|
}
|
|
}
|
|
realNames.Add(name);
|
|
values.push_back(propVariant);
|
|
}
|
|
}
|
|
|
|
static HRESULT SetOutProperties(IOutFolderArchive * outArchive,
|
|
bool is7z,
|
|
UINT32 level,
|
|
const UString &method,
|
|
UINT32 dictionary,
|
|
bool orderMode,
|
|
UINT32 order,
|
|
bool solidModeIsAllowed, bool solidMode,
|
|
bool multiThreadIsAllowed, bool multiThread,
|
|
bool encryptHeadersIsAllowed, bool encryptHeaders,
|
|
bool sfxMode,
|
|
const UString &options)
|
|
{
|
|
CMyComPtr<ISetProperties> setProperties;
|
|
if (outArchive->QueryInterface(&setProperties) == S_OK)
|
|
{
|
|
CObjectVector<CMyComBSTR> realNames;
|
|
std::vector<NCOM::CPropVariant> values;
|
|
if (level != (UINT32)(INT32)-1)
|
|
{
|
|
CMyComBSTR comBSTR = L"x";
|
|
realNames.Add(comBSTR);
|
|
values.push_back(NCOM::CPropVariant((UINT32)level));
|
|
}
|
|
if (!method.IsEmpty())
|
|
{
|
|
CMyComBSTR comBSTR;
|
|
if (is7z)
|
|
comBSTR = L"0";
|
|
else
|
|
comBSTR = L"m";
|
|
realNames.Add(comBSTR);
|
|
values.push_back(NCOM::CPropVariant(method));
|
|
}
|
|
if (dictionary != (UINT32)(INT32)-1)
|
|
{
|
|
CMyComBSTR comBSTR;
|
|
if (is7z)
|
|
if (orderMode)
|
|
comBSTR = L"0mem";
|
|
else
|
|
comBSTR = L"0d";
|
|
else
|
|
if (orderMode)
|
|
comBSTR = L"mem";
|
|
else
|
|
comBSTR = L"d";
|
|
realNames.Add(comBSTR);
|
|
wchar_t s[32];
|
|
ConvertUINT64ToString(dictionary, s);
|
|
wcscat(s, L"B");
|
|
values.push_back(NCOM::CPropVariant(s));
|
|
}
|
|
if (order != (UINT32)(INT32)-1)
|
|
{
|
|
CMyComBSTR comBSTR;
|
|
if (is7z)
|
|
if (orderMode)
|
|
comBSTR = L"0o";
|
|
else
|
|
comBSTR = L"0fb";
|
|
else
|
|
if (orderMode)
|
|
comBSTR = L"o";
|
|
else
|
|
comBSTR = L"fb";
|
|
realNames.Add(comBSTR);
|
|
values.push_back(NCOM::CPropVariant((UINT32)order));
|
|
}
|
|
|
|
if (sfxMode)
|
|
{
|
|
realNames.Add(L"rsfx");
|
|
values.push_back(NCOM::CPropVariant(L"on"));
|
|
}
|
|
|
|
if (encryptHeadersIsAllowed)
|
|
{
|
|
if (encryptHeaders)
|
|
{
|
|
realNames.Add(L"he");
|
|
values.push_back(NCOM::CPropVariant(L"on"));
|
|
}
|
|
}
|
|
|
|
// Solid
|
|
if (solidModeIsAllowed)
|
|
{
|
|
realNames.Add(L"s");
|
|
values.push_back(NCOM::CPropVariant(solidMode ? L"on": L"off"));
|
|
}
|
|
if (multiThreadIsAllowed)
|
|
{
|
|
realNames.Add(L"mt");
|
|
values.push_back(NCOM::CPropVariant(multiThread ? L"on": L"off"));
|
|
}
|
|
|
|
// Options
|
|
SetOptions(options, realNames, values);
|
|
|
|
std::vector<BSTR> names;
|
|
for(int i = 0; i < realNames.Size(); i++)
|
|
names.push_back(realNames[i]);
|
|
RINOK(setProperties->SetProperties(&names.front(),
|
|
&values.front(), names.size()));
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
struct CThreadUpdateCompress
|
|
{
|
|
CMyComPtr<IOutFolderArchive> OutArchive;
|
|
UString LibPath;
|
|
CLSID ClassID;
|
|
UString OutArchivePath;
|
|
BYTE ActionSetByte[NUpdateArchive::NPairState::kNumValues];
|
|
bool SfxMode;
|
|
UString SfxModule;
|
|
|
|
|
|
UStringVector FileNames;
|
|
CRecordVector<const wchar_t *> FileNamePointers;
|
|
CMyComPtr<IFolderArchiveUpdateCallback> UpdateCallback;
|
|
CUpdateCallback100Imp *UpdateCallbackSpec;
|
|
HRESULT Result;
|
|
|
|
DWORD Process()
|
|
{
|
|
NCOM::CComInitializer comInitializer;
|
|
UpdateCallbackSpec->ProgressDialog.WaitCreating();
|
|
try
|
|
{
|
|
Result = OutArchive->DoOperation(
|
|
LibPath, &ClassID,
|
|
OutArchivePath, ActionSetByte,
|
|
(SfxMode ? (const wchar_t *)SfxModule: NULL),
|
|
UpdateCallback);
|
|
}
|
|
catch(const UString &s)
|
|
{
|
|
MyMessageBox(s);
|
|
Result = E_FAIL;
|
|
}
|
|
catch(...)
|
|
{
|
|
Result = E_FAIL;
|
|
}
|
|
UpdateCallbackSpec->ProgressDialog.MyClose();
|
|
return 0;
|
|
}
|
|
|
|
static DWORD WINAPI MyThreadFunction(void *param)
|
|
{
|
|
return ((CThreadUpdateCompress *)param)->Process();
|
|
}
|
|
};
|
|
|
|
static UString MakeFullArchiveName(const UString &name,
|
|
const UString &extension, bool sfx)
|
|
{
|
|
if (sfx)
|
|
{
|
|
UString sfxExt = L".exe";
|
|
if (sfxExt.CollateNoCase(name.Right(sfxExt.Length())) == 0)
|
|
return name;
|
|
return name + sfxExt;
|
|
}
|
|
|
|
if (extension.IsEmpty())
|
|
return name;
|
|
if (name.IsEmpty())
|
|
return name;
|
|
if (name[name.Length() - 1] == '.')
|
|
return name.Left(name.Length() - 1);
|
|
int slash1Pos = name.ReverseFind(L'\\');
|
|
int slash2Pos = name.ReverseFind(L'/');
|
|
int slashPos = MyMax(slash1Pos, slash2Pos);
|
|
int dotPos = name.ReverseFind(L'.');
|
|
if (dotPos >= 0 && (dotPos > slashPos || slashPos < 0))
|
|
return name;
|
|
return name + UString(L'.') + extension;
|
|
}
|
|
|
|
HRESULT CompressArchive(
|
|
const UString &archivePath,
|
|
const UStringVector &fileNames,
|
|
const UString &archiveType,
|
|
bool email,
|
|
bool showDialog)
|
|
{
|
|
if (fileNames.Size() == 0)
|
|
return S_OK;
|
|
|
|
CObjectVector<CArchiverInfo> archivers;
|
|
ReadArchiverInfoList(archivers);
|
|
|
|
CArchiverInfo archiverInfo;
|
|
UString password;
|
|
bool encryptHeadersIsAllowed = false;
|
|
bool encryptHeaders = false;
|
|
const NUpdateArchive::CActionSet *actionSet;
|
|
NCompressDialog::CInfo compressInfo;
|
|
|
|
UString tempDirPath;
|
|
UString currentDirPrefix;
|
|
bool needTempFile = true;
|
|
NDirectory::CTempDirectoryW tempDirectory;
|
|
UString archiveName;
|
|
int pos = archivePath.ReverseFind(L'\\');
|
|
if (pos < 0)
|
|
{
|
|
archiveName = archivePath;
|
|
MyGetCurrentDirectory(currentDirPrefix);
|
|
}
|
|
else
|
|
{
|
|
currentDirPrefix = archivePath.Left(pos + 1);
|
|
archiveName = archivePath.Mid(pos + 1);
|
|
}
|
|
|
|
if (email)
|
|
{
|
|
tempDirectory.Create(kTempFolderPrefix);
|
|
currentDirPrefix = tempDirectory.GetPath();
|
|
NormalizeDirPathPrefix(currentDirPrefix);
|
|
needTempFile = false;
|
|
}
|
|
|
|
if (showDialog)
|
|
{
|
|
bool oneFile = false;
|
|
NFind::CFileInfoW fileInfo;
|
|
if (!NFind::FindFile(fileNames.Front(), fileInfo))
|
|
return ::GetLastError();
|
|
if (fileNames.Size() == 1)
|
|
oneFile = !fileInfo.IsDirectory();
|
|
|
|
CCompressDialog dialog;
|
|
for(int i = 0; i < archivers.Size(); i++)
|
|
{
|
|
const CArchiverInfo &archiverInfo = archivers[i];
|
|
if (archiverInfo.UpdateEnabled &&
|
|
(oneFile || !archiverInfo.KeepName))
|
|
dialog.m_ArchiverInfoList.Add(archiverInfo);
|
|
}
|
|
if(dialog.m_ArchiverInfoList.Size() == 0)
|
|
{
|
|
MyMessageBox(L"No Update Engines");
|
|
return E_FAIL;
|
|
}
|
|
dialog.m_Info.ArchiveName = archiveName;
|
|
dialog.OriginalFileName = fileInfo.Name;
|
|
|
|
dialog.m_Info.CurrentDirPrefix = currentDirPrefix;
|
|
dialog.m_Info.SFXMode = false;
|
|
dialog.m_Info.Solid = true;
|
|
dialog.m_Info.MultiThread = false;
|
|
|
|
dialog.m_Info.KeepName = !oneFile;
|
|
|
|
if(dialog.Create(0) != IDOK)
|
|
return S_OK;
|
|
|
|
if (dialog.m_Info.VolumeSizeIsDefined)
|
|
{
|
|
MyMessageBox(L"Splitting to volumes is not supported");
|
|
return E_FAIL;
|
|
}
|
|
|
|
switch(dialog.m_Info.UpdateMode)
|
|
{
|
|
case NCompressDialog::NUpdateMode::kAdd:
|
|
actionSet = &NUpdateArchive::kAddActionSet;
|
|
break;
|
|
case NCompressDialog::NUpdateMode::kUpdate:
|
|
actionSet = &NUpdateArchive::kUpdateActionSet;
|
|
break;
|
|
case NCompressDialog::NUpdateMode::kFresh:
|
|
actionSet = &NUpdateArchive::kFreshActionSet;
|
|
break;
|
|
case NCompressDialog::NUpdateMode::kSynchronize:
|
|
actionSet = &NUpdateArchive::kSynchronizeActionSet;
|
|
break;
|
|
default:
|
|
throw 1091756;
|
|
}
|
|
archiverInfo = dialog.m_ArchiverInfoList[dialog.m_Info.ArchiverInfoIndex];
|
|
password = GetUnicodeString(dialog.Password);
|
|
encryptHeadersIsAllowed = dialog.EncryptHeadersIsAllowed;
|
|
encryptHeaders = dialog.EncryptHeaders;
|
|
compressInfo = dialog.m_Info;
|
|
compressInfo.ArchiveName = MakeFullArchiveName(
|
|
compressInfo.ArchiveName,
|
|
archiverInfo.GetMainExtension(), compressInfo.SFXMode);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
for(i = 0; i < archivers.Size(); i++)
|
|
{
|
|
if (archivers[i].Name.CollateNoCase(archiveType) == 0)
|
|
{
|
|
archiverInfo = archivers[i];
|
|
break;
|
|
}
|
|
}
|
|
if (i == archivers.Size())
|
|
{
|
|
MyMessageBox(L"No archiver");
|
|
return E_FAIL;
|
|
}
|
|
actionSet = &NUpdateArchive::kAddActionSet;
|
|
bool is7z = (archiveType.CollateNoCase(L"7z") == 0);
|
|
compressInfo.SolidIsAllowed = is7z;
|
|
compressInfo.Solid = true;
|
|
compressInfo.MultiThreadIsAllowed = is7z;
|
|
compressInfo.MultiThread = false;
|
|
compressInfo.SFXMode = false;
|
|
compressInfo.KeepName = false;
|
|
compressInfo.ArchiveName = archiveName;
|
|
compressInfo.CurrentDirPrefix = currentDirPrefix;
|
|
compressInfo.Level = 5;
|
|
}
|
|
UString arcPath;
|
|
if (!compressInfo.GetFullPathName(arcPath))
|
|
{
|
|
MyMessageBox(L"Incorrect archive path");
|
|
return E_FAIL;
|
|
}
|
|
if (compressInfo.ArchiveName.Find('\\') >= 0)
|
|
{
|
|
needTempFile = true;
|
|
}
|
|
|
|
// MessageBox(0, arcPath, 0, 0);
|
|
|
|
NWorkDir::CInfo workDirInfo;
|
|
ReadWorkDirInfo(workDirInfo);
|
|
UString workDir = GetWorkDir(workDirInfo, arcPath);
|
|
NFile::NDirectory::CreateComplexDirectory(workDir);
|
|
|
|
NFile::NDirectory::CTempFileW tempFile;
|
|
UString tempFileName;
|
|
if (needTempFile)
|
|
{
|
|
if (tempFile.Create(workDir, kTempArchivePrefix, tempFileName) == 0)
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
tempFileName = arcPath;
|
|
|
|
|
|
/*
|
|
const CLSID &classID =
|
|
dialog.m_ArchiverInfoList[dialog.m_Info.ArchiverInfoIndex].ClassID;
|
|
*/
|
|
NFind::CFileInfoW fileInfo;
|
|
|
|
CMyComPtr<IOutFolderArchive> outArchive;
|
|
|
|
CMyComPtr<IInFolderArchive> archiveHandler;
|
|
if(NFind::FindFile(arcPath, fileInfo))
|
|
{
|
|
if (fileInfo.IsDirectory())
|
|
{
|
|
MyMessageBox(L"There is a folder with such name");
|
|
return E_FAIL;
|
|
}
|
|
CAgent *agentSpec = new CAgent;
|
|
archiveHandler = agentSpec;
|
|
// CLSID realClassID;
|
|
CMyComBSTR archiveType;
|
|
HRESULT result = agentSpec->Open(
|
|
GetUnicodeString(arcPath), &archiveType, NULL);
|
|
if (result == S_FALSE)
|
|
{
|
|
MyMessageBox(IDS_OPEN_IS_NOT_SUPORTED_ARCHIVE, 0x02000604);
|
|
return E_FAIL;
|
|
}
|
|
/*
|
|
HRESULT result = OpenArchive(arcPath, &archiveHandler,
|
|
archiverInfoResult, defaultName, NULL);
|
|
if (result == S_FALSE)
|
|
{
|
|
MyMessageBox(IDS_OPEN_IS_NOT_SUPORTED_ARCHIVE, 0x02000604);
|
|
return E_FAIL;
|
|
}
|
|
*/
|
|
if (result != S_OK)
|
|
{
|
|
MyMessageBox(L"Open error");
|
|
return E_FAIL;
|
|
}
|
|
if (archiverInfo.Name.CollateNoCase((const wchar_t *)archiveType) != 0)
|
|
{
|
|
MyMessageBox(L"Type of existing archive differs from specified type");
|
|
return E_FAIL;
|
|
}
|
|
result = archiveHandler.QueryInterface(IID_IOutFolderArchive, &outArchive);
|
|
if(result != S_OK)
|
|
{
|
|
MyMessageBox(MyFormatNew(IDS_CANT_UPDATE_ARCHIVE, 0x02000602,
|
|
GetUnicodeString(arcPath)));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CAgent *agentSpec = new CAgent;
|
|
outArchive = agentSpec;
|
|
}
|
|
|
|
CRecordVector<const wchar_t *> fileNamePointers;
|
|
fileNamePointers.Reserve(fileNames.Size());
|
|
|
|
int i;
|
|
for(i = 0; i < fileNames.Size(); i++)
|
|
fileNamePointers.Add(fileNames[i]);
|
|
|
|
outArchive->SetFolder(NULL);
|
|
|
|
// Don't uses CurrentFolder here, since files are absolute paths;
|
|
// MyGetCurrentDirectory(aCurrentFolder);
|
|
|
|
UINT codePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
|
outArchive->SetFiles(L"",
|
|
&fileNamePointers.Front(), fileNamePointers.Size());
|
|
|
|
CThreadUpdateCompress updater;
|
|
for (i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
|
|
updater.ActionSetByte[i] = actionSet->StateActions[i];
|
|
updater.UpdateCallbackSpec = new CUpdateCallback100Imp;
|
|
updater.UpdateCallback = updater.UpdateCallbackSpec;
|
|
updater.OutArchive = outArchive;
|
|
// updater.SrcFolderPrefix = srcPanel._currentFolderPrefix;
|
|
|
|
UString title = LangLoadStringW(IDS_PROGRESS_COMPRESSING, 0x02000DC0);
|
|
updater.UpdateCallbackSpec->Init(0, !password.IsEmpty(), password);
|
|
|
|
// UINT32 level = MyMin(compressInfo.Level, UINT32(9));
|
|
UINT32 level = compressInfo.Level;
|
|
HRESULT result = SetOutProperties(outArchive,
|
|
archiverInfo.Name.CompareNoCase(L"7z") == 0,
|
|
level,
|
|
compressInfo.Method,
|
|
compressInfo.Dictionary,
|
|
compressInfo.OrderMode, compressInfo.Order,
|
|
compressInfo.SolidIsAllowed, compressInfo.Solid,
|
|
compressInfo.MultiThreadIsAllowed, compressInfo.MultiThread,
|
|
encryptHeadersIsAllowed, encryptHeaders,
|
|
compressInfo.SFXMode,
|
|
GetUnicodeString(compressInfo.Options));
|
|
|
|
if (result != S_OK)
|
|
{
|
|
if (result != E_ABORT)
|
|
ShowErrorMessage(result);
|
|
return result;
|
|
}
|
|
|
|
|
|
UString sfxModule;
|
|
if (compressInfo.SFXMode)
|
|
{
|
|
UString sfxModule2;
|
|
LPCWSTR path = NULL;
|
|
UString sfxModule3;
|
|
if (GetProgramFolderPath(sfxModule3))
|
|
path = sfxModule3;
|
|
if (!NDirectory::MySearchPath(path, kDefaultSfxModule, NULL, sfxModule2))
|
|
{
|
|
MyMessageBox(L"can't find sfx module");
|
|
return E_FAIL;
|
|
}
|
|
sfxModule = sfxModule2;
|
|
}
|
|
|
|
updater.OutArchivePath = GetUnicodeString(tempFileName, codePage);
|
|
updater.SfxMode = compressInfo.SFXMode;
|
|
updater.SfxModule = sfxModule;
|
|
updater.LibPath = GetUnicodeString(archiverInfo.FilePath);
|
|
updater.ClassID = archiverInfo.ClassID;
|
|
|
|
CThread thread;
|
|
if (!thread.Create(CThreadUpdateCompress::MyThreadFunction, &updater))
|
|
throw 271824;
|
|
updater.UpdateCallbackSpec->StartProgressDialog(title);
|
|
result = updater.Result;
|
|
|
|
updater.UpdateCallback.Release();
|
|
|
|
updater.OutArchive.Release();
|
|
outArchive.Release();
|
|
|
|
if (result != S_OK)
|
|
{
|
|
if (result != E_ABORT)
|
|
ShowErrorMessage(result);
|
|
return result;
|
|
}
|
|
|
|
if(archiveHandler)
|
|
{
|
|
archiveHandler->Close();
|
|
if (!DeleteFileAlways(arcPath))
|
|
{
|
|
ShowLastErrorMessage();
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
if (needTempFile)
|
|
{
|
|
tempFile.DisableDeleting();
|
|
if (!NDirectory::MyMoveFile(tempFileName, arcPath))
|
|
{
|
|
ShowLastErrorMessage();
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (email)
|
|
{
|
|
NDLL::CLibrary mapiLib;
|
|
if (!mapiLib.Load(TEXT("Mapi32.dll")))
|
|
return E_FAIL;
|
|
LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)
|
|
mapiLib.GetProcAddress("MAPISendDocuments");
|
|
if (fnSend == 0)
|
|
return E_FAIL;
|
|
|
|
UString fileName;
|
|
GetOnlyName(arcPath, fileName);
|
|
AString path = GetAnsiString(arcPath);
|
|
AString name = GetAnsiString(fileName);
|
|
fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|