physfs/src/lzma/CPP/7zip/Archive/7z/7zHandler.cpp

794 lines
20 KiB
C++

// 7zHandler.cpp
#include "StdAfx.h"
#include "7zHandler.h"
#include "7zProperties.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/ComTry.h"
#include "../../../Windows/Defs.h"
#include "../Common/ItemNameUtils.h"
#ifdef _7Z_VOL
#include "../Common/MultiStream.h"
#endif
#ifdef __7Z_SET_PROPERTIES
#ifdef EXTRACT_ONLY
#include "../Common/ParseProperties.h"
#endif
#endif
#ifdef COMPRESS_MT
#include "../../../Windows/System.h"
#endif
using namespace NWindows;
extern UString ConvertMethodIdToString(UInt64 id);
namespace NArchive {
namespace N7z {
CHandler::CHandler()
{
_crcSize = 4;
#ifdef EXTRACT_ONLY
#ifdef COMPRESS_MT
_numThreads = NWindows::NSystem::GetNumberOfProcessors();
#endif
#else
Init();
#endif
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems =
#ifdef _7Z_VOL
_refs.Size();
#else
*numItems = _database.Files.Size();
#endif
return S_OK;
}
#ifdef _SFX
IMP_IInArchive_ArcProps_NO
STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */)
{
return E_NOTIMPL;
}
STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */,
BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */)
{
return E_NOTIMPL;
}
#else
STATPROPSTG kArcProps[] =
{
{ NULL, kpidMethod, VT_BSTR},
{ NULL, kpidSolid, VT_BOOL},
{ NULL, kpidNumBlocks, VT_UI4}
};
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
switch(propID)
{
case kpidMethod:
{
UString resString;
CRecordVector<UInt64> ids;
int i;
for (i = 0; i < _database.Folders.Size(); i++)
{
const CFolder &f = _database.Folders[i];
for (int j = f.Coders.Size() - 1; j >= 0; j--)
ids.AddToUniqueSorted(f.Coders[j].MethodID);
}
for (i = 0; i < ids.Size(); i++)
{
UInt64 id = ids[i];
UString methodName;
/* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName);
if (methodName.IsEmpty())
methodName = ConvertMethodIdToString(id);
if (!resString.IsEmpty())
resString += L' ';
resString += methodName;
}
prop = resString;
break;
}
case kpidSolid: prop = _database.IsSolid(); break;
case kpidNumBlocks: prop = (UInt32)_database.Folders.Size(); break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
IMP_IInArchive_ArcProps
#endif
static void MySetFileTime(bool timeDefined, FILETIME unixTime, NWindows::NCOM::CPropVariant &prop)
{
if (timeDefined)
prop = unixTime;
}
#ifndef _SFX
static UString ConvertUInt32ToString(UInt32 value)
{
wchar_t buffer[32];
ConvertUInt64ToString(value, buffer);
return buffer;
}
static UString GetStringForSizeValue(UInt32 value)
{
for (int i = 31; i >= 0; i--)
if ((UInt32(1) << i) == value)
return ConvertUInt32ToString(i);
UString result;
if (value % (1 << 20) == 0)
{
result += ConvertUInt32ToString(value >> 20);
result += L"m";
}
else if (value % (1 << 10) == 0)
{
result += ConvertUInt32ToString(value >> 10);
result += L"k";
}
else
{
result += ConvertUInt32ToString(value);
result += L"b";
}
return result;
}
static const UInt64 k_Copy = 0x0;
static const UInt64 k_LZMA = 0x030101;
static const UInt64 k_PPMD = 0x030401;
static wchar_t GetHex(Byte value)
{
return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10)));
}
static inline UString GetHex2(Byte value)
{
UString result;
result += GetHex((Byte)(value >> 4));
result += GetHex((Byte)(value & 0xF));
return result;
}
#endif
static const UInt64 k_AES = 0x06F10701;
#ifndef _SFX
static inline UInt32 GetUInt32FromMemLE(const Byte *p)
{
return p[0] | (((UInt32)p[1]) << 8) | (((UInt32)p[2]) << 16) | (((UInt32)p[3]) << 24);
}
#endif
bool CHandler::IsEncrypted(UInt32 index2) const
{
CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
if (folderIndex != kNumNoIndex)
{
const CFolder &folderInfo = _database.Folders[folderIndex];
for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
if (folderInfo.Coders[i].MethodID == k_AES)
return true;
}
return false;
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
/*
const CRef2 &ref2 = _refs[index];
if (ref2.Refs.IsEmpty())
return E_FAIL;
const CRef &ref = ref2.Refs.Front();
*/
#ifdef _7Z_VOL
const CRef &ref = _refs[index];
const CVolume &volume = _volumes[ref.VolumeIndex];
const CArchiveDatabaseEx &_database = volume.Database;
UInt32 index2 = ref.ItemIndex;
const CFileItem &item = _database.Files[index2];
#else
const CFileItem &item = _database.Files[index];
UInt32 index2 = index;
#endif
switch(propID)
{
case kpidPath:
{
if (!item.Name.IsEmpty())
prop = NItemName::GetOSName(item.Name);
break;
}
case kpidIsFolder:
prop = item.IsDirectory;
break;
case kpidSize:
{
prop = item.UnPackSize;
// prop = ref2.UnPackSize;
break;
}
case kpidPosition:
{
/*
if (ref2.Refs.Size() > 1)
prop = ref2.StartPos;
else
*/
if (item.IsStartPosDefined)
prop = item.StartPos;
break;
}
case kpidPackedSize:
{
// prop = ref2.PackSize;
{
CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
if (folderIndex != kNumNoIndex)
{
if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2)
prop = _database.GetFolderFullPackSize(folderIndex);
/*
else
prop = (UInt64)0;
*/
}
else
prop = (UInt64)0;
}
break;
}
case kpidLastAccessTime:
MySetFileTime(item.IsLastAccessTimeDefined, item.LastAccessTime, prop);
break;
case kpidCreationTime:
MySetFileTime(item.IsCreationTimeDefined, item.CreationTime, prop);
break;
case kpidLastWriteTime:
MySetFileTime(item.IsLastWriteTimeDefined, item.LastWriteTime, prop);
break;
case kpidAttributes:
if (item.AreAttributesDefined)
prop = item.Attributes;
break;
case kpidCRC:
if (item.IsFileCRCDefined)
prop = item.FileCRC;
break;
case kpidEncrypted:
{
prop = IsEncrypted(index2);
break;
}
#ifndef _SFX
case kpidMethod:
{
CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
if (folderIndex != kNumNoIndex)
{
const CFolder &folderInfo = _database.Folders[folderIndex];
UString methodsString;
for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
{
const CCoderInfo &coderInfo = folderInfo.Coders[i];
if (!methodsString.IsEmpty())
methodsString += L' ';
{
UString methodName;
bool methodIsKnown = FindMethod(
EXTERNAL_CODECS_VARS
coderInfo.MethodID, methodName);
if (methodIsKnown)
{
methodsString += methodName;
if (coderInfo.MethodID == k_LZMA)
{
if (coderInfo.Properties.GetCapacity() >= 5)
{
methodsString += L":";
UInt32 dicSize = GetUInt32FromMemLE(
((const Byte *)coderInfo.Properties + 1));
methodsString += GetStringForSizeValue(dicSize);
}
}
else if (coderInfo.MethodID == k_PPMD)
{
if (coderInfo.Properties.GetCapacity() >= 5)
{
Byte order = *(const Byte *)coderInfo.Properties;
methodsString += L":o";
methodsString += ConvertUInt32ToString(order);
methodsString += L":mem";
UInt32 dicSize = GetUInt32FromMemLE(
((const Byte *)coderInfo.Properties + 1));
methodsString += GetStringForSizeValue(dicSize);
}
}
else if (coderInfo.MethodID == k_AES)
{
if (coderInfo.Properties.GetCapacity() >= 1)
{
methodsString += L":";
const Byte *data = (const Byte *)coderInfo.Properties;
Byte firstByte = *data++;
UInt32 numCyclesPower = firstByte & 0x3F;
methodsString += ConvertUInt32ToString(numCyclesPower);
/*
if ((firstByte & 0xC0) != 0)
{
methodsString += L":";
return S_OK;
UInt32 saltSize = (firstByte >> 7) & 1;
UInt32 ivSize = (firstByte >> 6) & 1;
if (coderInfo.Properties.GetCapacity() >= 2)
{
Byte secondByte = *data++;
saltSize += (secondByte >> 4);
ivSize += (secondByte & 0x0F);
}
}
*/
}
}
else
{
if (coderInfo.Properties.GetCapacity() > 0)
{
methodsString += L":[";
for (size_t bi = 0; bi < coderInfo.Properties.GetCapacity(); bi++)
{
if (bi > 5 && bi + 1 < coderInfo.Properties.GetCapacity())
{
methodsString += L"..";
break;
}
else
methodsString += GetHex2(coderInfo.Properties[bi]);
}
methodsString += L"]";
}
}
}
else
{
methodsString += ConvertMethodIdToString(coderInfo.MethodID);
}
}
}
prop = methodsString;
}
}
break;
case kpidBlock:
{
CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
if (folderIndex != kNumNoIndex)
prop = (UInt32)folderIndex;
}
break;
case kpidPackedSize0:
case kpidPackedSize1:
case kpidPackedSize2:
case kpidPackedSize3:
case kpidPackedSize4:
{
CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
if (folderIndex != kNumNoIndex)
{
const CFolder &folderInfo = _database.Folders[folderIndex];
if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0))
{
prop = _database.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0);
}
else
prop = (UInt64)0;
}
else
prop = (UInt64)0;
}
break;
#endif
case kpidIsAnti:
prop = item.IsAnti;
break;
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
#ifdef _7Z_VOL
static const wchar_t *kExt = L"7z";
static const wchar_t *kAfterPart = L".7z";
class CVolumeName
{
bool _first;
UString _unchangedPart;
UString _changedPart;
UString _afterPart;
public:
bool InitName(const UString &name)
{
_first = true;
int dotPos = name.ReverseFind('.');
UString basePart = name;
if (dotPos >= 0)
{
UString ext = name.Mid(dotPos + 1);
if (ext.CompareNoCase(kExt)==0 ||
ext.CompareNoCase(L"EXE") == 0)
{
_afterPart = kAfterPart;
basePart = name.Left(dotPos);
}
}
int numLetters = 1;
bool splitStyle = false;
if (basePart.Right(numLetters) == L"1")
{
while (numLetters < basePart.Length())
{
if (basePart[basePart.Length() - numLetters - 1] != '0')
break;
numLetters++;
}
}
else
return false;
_unchangedPart = basePart.Left(basePart.Length() - numLetters);
_changedPart = basePart.Right(numLetters);
return true;
}
UString GetNextName()
{
UString newName;
// if (_newStyle || !_first)
{
int i;
int numLetters = _changedPart.Length();
for (i = numLetters - 1; i >= 0; i--)
{
wchar_t c = _changedPart[i];
if (c == L'9')
{
c = L'0';
newName = c + newName;
if (i == 0)
newName = UString(L'1') + newName;
continue;
}
c++;
newName = UString(c) + newName;
i--;
for (; i >= 0; i--)
newName = _changedPart[i] + newName;
break;
}
_changedPart = newName;
}
_first = false;
return _unchangedPart + _changedPart + _afterPart;
}
};
#endif
STDMETHODIMP CHandler::Open(IInStream *stream,
const UInt64 *maxCheckStartPosition,
IArchiveOpenCallback *openArchiveCallback)
{
COM_TRY_BEGIN
Close();
#ifndef _SFX
_fileInfoPopIDs.Clear();
#endif
try
{
CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;
#ifdef _7Z_VOL
CVolumeName seqName;
CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
#endif
#ifndef _NO_CRYPTO
CMyComPtr<ICryptoGetTextPassword> getTextPassword;
if (openArchiveCallback)
{
openArchiveCallbackTemp.QueryInterface(
IID_ICryptoGetTextPassword, &getTextPassword);
}
#endif
#ifdef _7Z_VOL
if (openArchiveCallback)
{
openArchiveCallbackTemp.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);
}
for (;;)
{
CMyComPtr<IInStream> inStream;
if (!_volumes.IsEmpty())
{
if (!openVolumeCallback)
break;
if(_volumes.Size() == 1)
{
UString baseName;
{
NCOM::CPropVariant prop;
RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
if (prop.vt != VT_BSTR)
break;
baseName = prop.bstrVal;
}
seqName.InitName(baseName);
}
UString fullName = seqName.GetNextName();
HRESULT result = openVolumeCallback->GetStream(fullName, &inStream);
if (result == S_FALSE)
break;
if (result != S_OK)
return result;
if (!stream)
break;
}
else
inStream = stream;
CInArchive archive;
RINOK(archive.Open(inStream, maxCheckStartPosition));
_volumes.Add(CVolume());
CVolume &volume = _volumes.Back();
CArchiveDatabaseEx &database = volume.Database;
volume.Stream = inStream;
volume.StartRef2Index = _refs.Size();
HRESULT result = archive.ReadDatabase(database
#ifndef _NO_CRYPTO
, getTextPassword
#endif
);
if (result != S_OK)
{
_volumes.Clear();
return result;
}
database.Fill();
for(int i = 0; i < database.Files.Size(); i++)
{
CRef refNew;
refNew.VolumeIndex = _volumes.Size() - 1;
refNew.ItemIndex = i;
_refs.Add(refNew);
/*
const CFileItem &file = database.Files[i];
int j;
*/
/*
for (j = _refs.Size() - 1; j >= 0; j--)
{
CRef2 &ref2 = _refs[j];
const CRef &ref = ref2.Refs.Back();
const CVolume &volume2 = _volumes[ref.VolumeIndex];
const CArchiveDatabaseEx &database2 = volume2.Database;
const CFileItem &file2 = database2.Files[ref.ItemIndex];
if (file2.Name.CompareNoCase(file.Name) == 0)
{
if (!file.IsStartPosDefined)
continue;
if (file.StartPos != ref2.StartPos + ref2.UnPackSize)
continue;
ref2.Refs.Add(refNew);
break;
}
}
*/
/*
j = -1;
if (j < 0)
{
CRef2 ref2New;
ref2New.Refs.Add(refNew);
j = _refs.Add(ref2New);
}
CRef2 &ref2 = _refs[j];
ref2.UnPackSize += file.UnPackSize;
ref2.PackSize += database.GetFilePackSize(i);
if (ref2.Refs.Size() == 1 && file.IsStartPosDefined)
ref2.StartPos = file.StartPos;
*/
}
if (database.Files.Size() != 1)
break;
const CFileItem &file = database.Files.Front();
if (!file.IsStartPosDefined)
break;
}
#else
CInArchive archive;
RINOK(archive.Open(stream, maxCheckStartPosition));
HRESULT result = archive.ReadDatabase(
EXTERNAL_CODECS_VARS
_database
#ifndef _NO_CRYPTO
, getTextPassword
#endif
);
RINOK(result);
_database.Fill();
_inStream = stream;
#endif
}
catch(...)
{
Close();
return S_FALSE;
}
// _inStream = stream;
#ifndef _SFX
FillPopIDs();
#endif
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Close()
{
COM_TRY_BEGIN
#ifdef _7Z_VOL
_volumes.Clear();
_refs.Clear();
#else
_inStream.Release();
_database.Clear();
#endif
return S_OK;
COM_TRY_END
}
#ifdef _7Z_VOL
STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
{
if (index != 0)
return E_INVALIDARG;
*stream = 0;
CMultiStream *streamSpec = new CMultiStream;
CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
UInt64 pos = 0;
const UString *fileName;
for (int i = 0; i < _refs.Size(); i++)
{
const CRef &ref = _refs[i];
const CVolume &volume = _volumes[ref.VolumeIndex];
const CArchiveDatabaseEx &database = volume.Database;
const CFileItem &file = database.Files[ref.ItemIndex];
if (i == 0)
fileName = &file.Name;
else
if (fileName->Compare(file.Name) != 0)
return S_FALSE;
if (!file.IsStartPosDefined)
return S_FALSE;
if (file.StartPos != pos)
return S_FALSE;
CNum folderIndex = database.FileIndexToFolderIndexMap[ref.ItemIndex];
if (folderIndex == kNumNoIndex)
{
if (file.UnPackSize != 0)
return E_FAIL;
continue;
}
if (database.NumUnPackStreamsVector[folderIndex] != 1)
return S_FALSE;
const CFolder &folder = database.Folders[folderIndex];
if (folder.Coders.Size() != 1)
return S_FALSE;
const CCoderInfo &coder = folder.Coders.Front();
if (coder.NumInStreams != 1 || coder.NumOutStreams != 1)
return S_FALSE;
if (coder.MethodID != k_Copy)
return S_FALSE;
pos += file.UnPackSize;
CMultiStream::CSubStreamInfo subStreamInfo;
subStreamInfo.Stream = volume.Stream;
subStreamInfo.Pos = database.GetFolderStreamPos(folderIndex, 0);
subStreamInfo.Size = file.UnPackSize;
streamSpec->Streams.Add(subStreamInfo);
}
streamSpec->Init();
*stream = streamTemp.Detach();
return S_OK;
}
#endif
#ifdef __7Z_SET_PROPERTIES
#ifdef EXTRACT_ONLY
STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
{
COM_TRY_BEGIN
#ifdef COMPRESS_MT
const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
_numThreads = numProcessors;
#endif
for (int i = 0; i < numProperties; i++)
{
UString name = names[i];
name.MakeUpper();
if (name.IsEmpty())
return E_INVALIDARG;
const PROPVARIANT &value = values[i];
UInt32 number;
int index = ParseStringToUInt32(name, number);
if (index == 0)
{
if(name.Left(2).CompareNoCase(L"MT") == 0)
{
#ifdef COMPRESS_MT
RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
#endif
continue;
}
else
return E_INVALIDARG;
}
}
return S_OK;
COM_TRY_END
}
#endif
#endif
IMPL_ISetCompressCodecsInfo
}}