mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-06 11:14:58 -06:00
549 lines
16 KiB
C++
Executable File
549 lines
16 KiB
C++
Executable File
// ZipHandlerOut.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Common/ComTry.h"
|
|
#include "Common/StringConvert.h"
|
|
#include "Common/StringToInt.h"
|
|
|
|
#include "Windows/PropVariant.h"
|
|
#include "Windows/Time.h"
|
|
|
|
#include "../../IPassword.h"
|
|
|
|
#include "../../Common/OutBuffer.h"
|
|
|
|
#include "../../Crypto/WzAes.h"
|
|
|
|
#include "../Common/ItemNameUtils.h"
|
|
#include "../Common/ParseProperties.h"
|
|
|
|
#include "ZipHandler.h"
|
|
#include "ZipUpdate.h"
|
|
|
|
using namespace NWindows;
|
|
using namespace NCOM;
|
|
using namespace NTime;
|
|
|
|
namespace NArchive {
|
|
namespace NZip {
|
|
|
|
static const UInt32 kLzAlgoX1 = 0;
|
|
static const UInt32 kLzAlgoX5 = 1;
|
|
|
|
static const UInt32 kDeflateNumPassesX1 = 1;
|
|
static const UInt32 kDeflateNumPassesX7 = 3;
|
|
static const UInt32 kDeflateNumPassesX9 = 10;
|
|
|
|
static const UInt32 kDeflateNumFastBytesX1 = 32;
|
|
static const UInt32 kDeflateNumFastBytesX7 = 64;
|
|
static const UInt32 kDeflateNumFastBytesX9 = 128;
|
|
|
|
static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
|
|
static const wchar_t *kLzmaMatchFinderX5 = L"BT4";
|
|
|
|
static const UInt32 kLzmaNumFastBytesX1 = 32;
|
|
static const UInt32 kLzmaNumFastBytesX7 = 64;
|
|
|
|
static const UInt32 kLzmaDicSizeX1 = 1 << 16;
|
|
static const UInt32 kLzmaDicSizeX3 = 1 << 20;
|
|
static const UInt32 kLzmaDicSizeX5 = 1 << 24;
|
|
static const UInt32 kLzmaDicSizeX7 = 1 << 25;
|
|
static const UInt32 kLzmaDicSizeX9 = 1 << 26;
|
|
|
|
static const UInt32 kPpmdMemSizeX1 = (1 << 20);
|
|
static const UInt32 kPpmdMemSizeX3 = (1 << 22);
|
|
static const UInt32 kPpmdMemSizeX5 = (1 << 24);
|
|
static const UInt32 kPpmdMemSizeX7 = (1 << 26);
|
|
static const UInt32 kPpmdMemSizeX9 = (1 << 27);
|
|
|
|
static const UInt32 kPpmdOrderX1 = 4;
|
|
static const UInt32 kPpmdOrderX3 = 6;
|
|
static const UInt32 kPpmdOrderX5 = 8;
|
|
static const UInt32 kPpmdOrderX7 = 10;
|
|
static const UInt32 kPpmdOrderX9 = 16;
|
|
|
|
static const UInt32 kBZip2NumPassesX1 = 1;
|
|
static const UInt32 kBZip2NumPassesX7 = 2;
|
|
static const UInt32 kBZip2NumPassesX9 = 7;
|
|
|
|
static const UInt32 kBZip2DicSizeX1 = 100000;
|
|
static const UInt32 kBZip2DicSizeX3 = 500000;
|
|
static const UInt32 kBZip2DicSizeX5 = 900000;
|
|
|
|
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
|
|
{
|
|
*timeType = NFileTimeType::kDOS;
|
|
return S_OK;
|
|
}
|
|
|
|
static bool IsAsciiString(const UString &s)
|
|
{
|
|
for (int i = 0; i < s.Length(); i++)
|
|
{
|
|
wchar_t c = s[i];
|
|
if (c < 0x20 || c > 0x7F)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define COM_TRY_BEGIN2 try {
|
|
#define COM_TRY_END2 } \
|
|
catch(const CSystemException &e) { return e.ErrorCode; } \
|
|
catch(...) { return E_OUTOFMEMORY; }
|
|
|
|
static HRESULT GetTime(IArchiveUpdateCallback *callback, int index, PROPID propID, FILETIME &filetime)
|
|
{
|
|
filetime.dwHighDateTime = filetime.dwLowDateTime = 0;
|
|
NCOM::CPropVariant prop;
|
|
RINOK(callback->GetProperty(index, propID, &prop));
|
|
if (prop.vt == VT_FILETIME)
|
|
filetime = prop.filetime;
|
|
else if (prop.vt != VT_EMPTY)
|
|
return E_INVALIDARG;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
|
|
IArchiveUpdateCallback *callback)
|
|
{
|
|
COM_TRY_BEGIN2
|
|
CObjectVector<CUpdateItem> updateItems;
|
|
bool thereAreAesUpdates = false;
|
|
for (UInt32 i = 0; i < numItems; i++)
|
|
{
|
|
CUpdateItem ui;
|
|
Int32 newData;
|
|
Int32 newProperties;
|
|
UInt32 indexInArchive;
|
|
if (!callback)
|
|
return E_FAIL;
|
|
RINOK(callback->GetUpdateItemInfo(i, &newData, &newProperties, &indexInArchive));
|
|
ui.NewProperties = IntToBool(newProperties);
|
|
ui.NewData = IntToBool(newData);
|
|
ui.IndexInArchive = indexInArchive;
|
|
ui.IndexInClient = i;
|
|
bool existInArchive = (indexInArchive != (UInt32)-1);
|
|
if (existInArchive && newData)
|
|
if (m_Items[indexInArchive].IsAesEncrypted())
|
|
thereAreAesUpdates = true;
|
|
|
|
if (IntToBool(newProperties))
|
|
{
|
|
UString name;
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(callback->GetProperty(i, kpidAttrib, &prop));
|
|
if (prop.vt == VT_EMPTY)
|
|
ui.Attributes = 0;
|
|
else if (prop.vt != VT_UI4)
|
|
return E_INVALIDARG;
|
|
else
|
|
ui.Attributes = prop.ulVal;
|
|
}
|
|
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(callback->GetProperty(i, kpidPath, &prop));
|
|
if (prop.vt == VT_EMPTY)
|
|
name.Empty();
|
|
else if (prop.vt != VT_BSTR)
|
|
return E_INVALIDARG;
|
|
else
|
|
name = prop.bstrVal;
|
|
}
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(callback->GetProperty(i, kpidIsDir, &prop));
|
|
if (prop.vt == VT_EMPTY)
|
|
ui.IsDir = false;
|
|
else if (prop.vt != VT_BOOL)
|
|
return E_INVALIDARG;
|
|
else
|
|
ui.IsDir = (prop.boolVal != VARIANT_FALSE);
|
|
}
|
|
|
|
{
|
|
CPropVariant prop;
|
|
RINOK(callback->GetProperty(i, kpidTimeType, &prop));
|
|
if (prop.vt == VT_UI4)
|
|
ui.NtfsTimeIsDefined = (prop.ulVal == NFileTimeType::kWindows);
|
|
else
|
|
ui.NtfsTimeIsDefined = m_WriteNtfsTimeExtra;
|
|
}
|
|
RINOK(GetTime(callback, i, kpidMTime, ui.NtfsMTime));
|
|
RINOK(GetTime(callback, i, kpidATime, ui.NtfsATime));
|
|
RINOK(GetTime(callback, i, kpidCTime, ui.NtfsCTime));
|
|
|
|
{
|
|
FILETIME localFileTime = { 0, 0 };
|
|
if (ui.NtfsMTime.dwHighDateTime != 0 ||
|
|
ui.NtfsMTime.dwLowDateTime != 0)
|
|
if (!FileTimeToLocalFileTime(&ui.NtfsMTime, &localFileTime))
|
|
return E_INVALIDARG;
|
|
FileTimeToDosTime(localFileTime, ui.Time);
|
|
}
|
|
|
|
name = NItemName::MakeLegalName(name);
|
|
bool needSlash = ui.IsDir;
|
|
const wchar_t kSlash = L'/';
|
|
if (!name.IsEmpty())
|
|
{
|
|
if (name[name.Length() - 1] == kSlash)
|
|
{
|
|
if (!ui.IsDir)
|
|
return E_INVALIDARG;
|
|
needSlash = false;
|
|
}
|
|
}
|
|
if (needSlash)
|
|
name += kSlash;
|
|
|
|
bool tryUtf8 = true;
|
|
if (m_ForseLocal || !m_ForseUtf8)
|
|
{
|
|
bool defaultCharWasUsed;
|
|
ui.Name = UnicodeStringToMultiByte(name, CP_OEMCP, '_', defaultCharWasUsed);
|
|
tryUtf8 = (!m_ForseLocal && (defaultCharWasUsed ||
|
|
MultiByteToUnicodeString(ui.Name, CP_OEMCP) != name));
|
|
}
|
|
|
|
if (tryUtf8)
|
|
{
|
|
int i;
|
|
for (i = 0; i < name.Length() && (unsigned)name[i] < 0x80; i++);
|
|
ui.IsUtf8 = (i != name.Length());
|
|
if (!ConvertUnicodeToUTF8(name, ui.Name))
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (ui.Name.Length() >= (1 << 16))
|
|
return E_INVALIDARG;
|
|
|
|
ui.IndexInClient = i;
|
|
/*
|
|
if (existInArchive)
|
|
{
|
|
const CItemEx &itemInfo = m_Items[indexInArchive];
|
|
// ui.Commented = itemInfo.IsCommented();
|
|
ui.Commented = false;
|
|
if (ui.Commented)
|
|
{
|
|
ui.CommentRange.Position = itemInfo.GetCommentPosition();
|
|
ui.CommentRange.Size = itemInfo.CommentSize;
|
|
}
|
|
}
|
|
else
|
|
ui.Commented = false;
|
|
*/
|
|
}
|
|
if (IntToBool(newData))
|
|
{
|
|
UInt64 size;
|
|
{
|
|
NCOM::CPropVariant prop;
|
|
RINOK(callback->GetProperty(i, kpidSize, &prop));
|
|
if (prop.vt != VT_UI8)
|
|
return E_INVALIDARG;
|
|
size = prop.uhVal.QuadPart;
|
|
}
|
|
ui.Size = size;
|
|
}
|
|
updateItems.Add(ui);
|
|
}
|
|
|
|
CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
|
|
{
|
|
CMyComPtr<IArchiveUpdateCallback> udateCallBack2(callback);
|
|
udateCallBack2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
|
|
}
|
|
CCompressionMethodMode options;
|
|
|
|
if (getTextPassword)
|
|
{
|
|
CMyComBSTR password;
|
|
Int32 passwordIsDefined;
|
|
RINOK(getTextPassword->CryptoGetTextPassword2(&passwordIsDefined, &password));
|
|
options.PasswordIsDefined = IntToBool(passwordIsDefined);
|
|
if (options.PasswordIsDefined)
|
|
{
|
|
options.IsAesMode = (m_ForceAesMode ? m_IsAesMode : thereAreAesUpdates);
|
|
options.AesKeyMode = m_AesKeyMode;
|
|
|
|
if (!IsAsciiString((const wchar_t *)password))
|
|
return E_INVALIDARG;
|
|
if (options.IsAesMode)
|
|
{
|
|
if (options.Password.Length() > NCrypto::NWzAes::kPasswordSizeMax)
|
|
return E_INVALIDARG;
|
|
}
|
|
options.Password = UnicodeStringToMultiByte((const wchar_t *)password, CP_OEMCP);
|
|
}
|
|
}
|
|
else
|
|
options.PasswordIsDefined = false;
|
|
|
|
int level = m_Level;
|
|
if (level < 0)
|
|
level = 5;
|
|
|
|
Byte mainMethod;
|
|
if (m_MainMethod < 0)
|
|
mainMethod = (Byte)(((level == 0) ?
|
|
NFileHeader::NCompressionMethod::kStored :
|
|
NFileHeader::NCompressionMethod::kDeflated));
|
|
else
|
|
mainMethod = (Byte)m_MainMethod;
|
|
options.MethodSequence.Add(mainMethod);
|
|
if (mainMethod != NFileHeader::NCompressionMethod::kStored)
|
|
options.MethodSequence.Add(NFileHeader::NCompressionMethod::kStored);
|
|
bool isDeflate = (mainMethod == NFileHeader::NCompressionMethod::kDeflated) ||
|
|
(mainMethod == NFileHeader::NCompressionMethod::kDeflated64);
|
|
bool isLZMA = (mainMethod == NFileHeader::NCompressionMethod::kLZMA);
|
|
bool isLz = (isLZMA || isDeflate);
|
|
options.NumPasses = m_NumPasses;
|
|
options.DicSize = m_DicSize;
|
|
options.NumFastBytes = m_NumFastBytes;
|
|
options.NumMatchFinderCycles = m_NumMatchFinderCycles;
|
|
options.NumMatchFinderCyclesDefined = m_NumMatchFinderCyclesDefined;
|
|
options.Algo = m_Algo;
|
|
options.MemSize = m_MemSize;
|
|
options.Order = m_Order;
|
|
#ifndef _7ZIP_ST
|
|
options.NumThreads = _numThreads;
|
|
#endif
|
|
if (isLz)
|
|
{
|
|
if (isDeflate)
|
|
{
|
|
if (options.NumPasses == 0xFFFFFFFF)
|
|
options.NumPasses = (level >= 9 ? kDeflateNumPassesX9 :
|
|
(level >= 7 ? kDeflateNumPassesX7 :
|
|
kDeflateNumPassesX1));
|
|
if (options.NumFastBytes == 0xFFFFFFFF)
|
|
options.NumFastBytes = (level >= 9 ? kDeflateNumFastBytesX9 :
|
|
(level >= 7 ? kDeflateNumFastBytesX7 :
|
|
kDeflateNumFastBytesX1));
|
|
}
|
|
else if (isLZMA)
|
|
{
|
|
if (options.DicSize == 0xFFFFFFFF)
|
|
options.DicSize =
|
|
(level >= 9 ? kLzmaDicSizeX9 :
|
|
(level >= 7 ? kLzmaDicSizeX7 :
|
|
(level >= 5 ? kLzmaDicSizeX5 :
|
|
(level >= 3 ? kLzmaDicSizeX3 :
|
|
kLzmaDicSizeX1))));
|
|
|
|
if (options.NumFastBytes == 0xFFFFFFFF)
|
|
options.NumFastBytes = (level >= 7 ? kLzmaNumFastBytesX7 :
|
|
kLzmaNumFastBytesX1);
|
|
|
|
options.MatchFinder =
|
|
(level >= 5 ? kLzmaMatchFinderX5 :
|
|
kLzmaMatchFinderX1);
|
|
}
|
|
|
|
if (options.Algo == 0xFFFFFFFF)
|
|
options.Algo = (level >= 5 ? kLzAlgoX5 :
|
|
kLzAlgoX1);
|
|
}
|
|
if (mainMethod == NFileHeader::NCompressionMethod::kBZip2)
|
|
{
|
|
if (options.NumPasses == 0xFFFFFFFF)
|
|
options.NumPasses = (level >= 9 ? kBZip2NumPassesX9 :
|
|
(level >= 7 ? kBZip2NumPassesX7 :
|
|
kBZip2NumPassesX1));
|
|
if (options.DicSize == 0xFFFFFFFF)
|
|
options.DicSize = (level >= 5 ? kBZip2DicSizeX5 :
|
|
(level >= 3 ? kBZip2DicSizeX3 :
|
|
kBZip2DicSizeX1));
|
|
}
|
|
if (mainMethod == NFileHeader::NCompressionMethod::kPPMd)
|
|
{
|
|
if (options.MemSize == 0xFFFFFFFF)
|
|
options.MemSize =
|
|
(level >= 9 ? kPpmdMemSizeX9 :
|
|
(level >= 7 ? kPpmdMemSizeX7 :
|
|
(level >= 5 ? kPpmdMemSizeX5 :
|
|
(level >= 3 ? kPpmdMemSizeX3 :
|
|
kPpmdMemSizeX1))));
|
|
|
|
if (options.Order == 0xFFFFFFFF)
|
|
options.Order =
|
|
(level >= 9 ? kPpmdOrderX9 :
|
|
(level >= 7 ? kPpmdOrderX7 :
|
|
(level >= 5 ? kPpmdOrderX5 :
|
|
(level >= 3 ? kPpmdOrderX3 :
|
|
kPpmdOrderX1))));
|
|
|
|
options.Algo = 0;
|
|
}
|
|
|
|
return Update(
|
|
EXTERNAL_CODECS_VARS
|
|
m_Items, updateItems, outStream,
|
|
m_Archive.IsOpen() ? &m_Archive : NULL, &options, callback);
|
|
COM_TRY_END2
|
|
}
|
|
|
|
STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
|
|
{
|
|
#ifndef _7ZIP_ST
|
|
const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
|
|
_numThreads = numProcessors;
|
|
#endif
|
|
InitMethodProperties();
|
|
for (int i = 0; i < numProperties; i++)
|
|
{
|
|
UString name = UString(names[i]);
|
|
name.MakeUpper();
|
|
if (name.IsEmpty())
|
|
return E_INVALIDARG;
|
|
|
|
const PROPVARIANT &prop = values[i];
|
|
|
|
if (name[0] == L'X')
|
|
{
|
|
UInt32 level = 9;
|
|
RINOK(ParsePropValue(name.Mid(1), prop, level));
|
|
m_Level = level;
|
|
continue;
|
|
}
|
|
else if (name == L"M")
|
|
{
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
UString m = prop.bstrVal;
|
|
m.MakeUpper();
|
|
if (m == L"COPY") m_MainMethod = NFileHeader::NCompressionMethod::kStored;
|
|
else if (m == L"DEFLATE") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated;
|
|
else if (m == L"DEFLATE64") m_MainMethod = NFileHeader::NCompressionMethod::kDeflated64;
|
|
else if (m == L"BZIP2") m_MainMethod = NFileHeader::NCompressionMethod::kBZip2;
|
|
else if (m == L"LZMA") m_MainMethod = NFileHeader::NCompressionMethod::kLZMA;
|
|
else if (m == L"PPMD") m_MainMethod = NFileHeader::NCompressionMethod::kPPMd;
|
|
else return E_INVALIDARG;
|
|
}
|
|
else if (prop.vt == VT_UI4)
|
|
{
|
|
switch(prop.ulVal)
|
|
{
|
|
case NFileHeader::NCompressionMethod::kStored:
|
|
case NFileHeader::NCompressionMethod::kDeflated:
|
|
case NFileHeader::NCompressionMethod::kDeflated64:
|
|
case NFileHeader::NCompressionMethod::kBZip2:
|
|
case NFileHeader::NCompressionMethod::kLZMA:
|
|
m_MainMethod = (Byte)prop.ulVal;
|
|
break;
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
else if (name.Left(2) == L"EM")
|
|
{
|
|
if (prop.vt == VT_BSTR)
|
|
{
|
|
UString valueString = prop.bstrVal;
|
|
valueString.MakeUpper();
|
|
if (valueString.Left(3) == L"AES")
|
|
{
|
|
valueString = valueString.Mid(3);
|
|
if (valueString == L"128")
|
|
m_AesKeyMode = 1;
|
|
else if (valueString == L"192")
|
|
m_AesKeyMode = 2;
|
|
else if (valueString == L"256" || valueString.IsEmpty())
|
|
m_AesKeyMode = 3;
|
|
else
|
|
return E_INVALIDARG;
|
|
m_IsAesMode = true;
|
|
m_ForceAesMode = true;
|
|
}
|
|
else if (valueString == L"ZIPCRYPTO")
|
|
{
|
|
m_IsAesMode = false;
|
|
m_ForceAesMode = true;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
else if (name[0] == L'D')
|
|
{
|
|
UInt32 dicSize = kBZip2DicSizeX5;
|
|
RINOK(ParsePropDictionaryValue(name.Mid(1), prop, dicSize));
|
|
m_DicSize = dicSize;
|
|
}
|
|
else if (name.Left(3) == L"MEM")
|
|
{
|
|
UInt32 memSize = kPpmdMemSizeX5;
|
|
RINOK(ParsePropDictionaryValue(name.Mid(3), prop, memSize));
|
|
m_MemSize = memSize;
|
|
}
|
|
else if (name[0] == L'O')
|
|
{
|
|
UInt32 order = kPpmdOrderX5;
|
|
RINOK(ParsePropValue(name.Mid(1), prop, order));
|
|
m_Order = order;
|
|
}
|
|
else if (name.Left(4) == L"PASS")
|
|
{
|
|
UInt32 num = kDeflateNumPassesX9;
|
|
RINOK(ParsePropValue(name.Mid(4), prop, num));
|
|
m_NumPasses = num;
|
|
}
|
|
else if (name.Left(2) == L"FB")
|
|
{
|
|
UInt32 num = kDeflateNumFastBytesX9;
|
|
RINOK(ParsePropValue(name.Mid(2), prop, num));
|
|
m_NumFastBytes = num;
|
|
}
|
|
else if (name.Left(2) == L"MC")
|
|
{
|
|
UInt32 num = 0xFFFFFFFF;
|
|
RINOK(ParsePropValue(name.Mid(2), prop, num));
|
|
m_NumMatchFinderCycles = num;
|
|
m_NumMatchFinderCyclesDefined = true;
|
|
}
|
|
else if (name.Left(2) == L"MT")
|
|
{
|
|
#ifndef _7ZIP_ST
|
|
RINOK(ParseMtProp(name.Mid(2), prop, numProcessors, _numThreads));
|
|
#endif
|
|
}
|
|
else if (name.Left(1) == L"A")
|
|
{
|
|
UInt32 num = kLzAlgoX5;
|
|
RINOK(ParsePropValue(name.Mid(1), prop, num));
|
|
m_Algo = num;
|
|
}
|
|
else if (name.CompareNoCase(L"TC") == 0)
|
|
{
|
|
RINOK(SetBoolProperty(m_WriteNtfsTimeExtra, prop));
|
|
}
|
|
else if (name.CompareNoCase(L"CL") == 0)
|
|
{
|
|
RINOK(SetBoolProperty(m_ForseLocal, prop));
|
|
if (m_ForseLocal)
|
|
m_ForseUtf8 = false;
|
|
}
|
|
else if (name.CompareNoCase(L"CU") == 0)
|
|
{
|
|
RINOK(SetBoolProperty(m_ForseUtf8, prop));
|
|
if (m_ForseUtf8)
|
|
m_ForseLocal = false;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|