📄 cabhandler.cpp
字号:
// CabHandler.cpp
#include "StdAfx.h"
#include "Common/StringConvert.h"
#include "Common/Defs.h"
#include "Common/UTFConvert.h"
#include "Common/ComTry.h"
#include "Common/IntToString.h"
#include "Windows/PropVariant.h"
#include "Windows/Time.h"
#include "CabHandler.h"
#include "CabBlockInStream.h"
#include "../../Common/ProgressUtils.h"
#include "../../Compress/Copy/CopyCoder.h"
#include "../../Compress/Deflate/DeflateDecoder.h"
#include "../../Compress/Lzx/LzxDecoder.h"
#include "../../Compress/Quantum/QuantumDecoder.h"
#include "../Common/ItemNameUtils.h"
using namespace NWindows;
namespace NArchive {
namespace NCab {
// #define _CAB_DETAILS
#ifdef _CAB_DETAILS
enum
{
kpidBlockReal = kpidUserDefined
};
#endif
STATPROPSTG kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidSize, VT_UI8},
{ NULL, kpidMTime, VT_FILETIME},
{ NULL, kpidAttrib, VT_UI4},
{ NULL, kpidMethod, VT_BSTR},
{ NULL, kpidBlock, VT_I4}
#ifdef _CAB_DETAILS
,
{ L"BlockReal", kpidBlockReal, VT_UI4},
{ NULL, kpidOffset, VT_UI4},
{ NULL, kpidVolume, VT_UI4}
#endif
};
static const wchar_t *kMethods[] =
{
L"None",
L"MSZip",
L"Quantum",
L"LZX"
};
static const int kNumMethods = sizeof(kMethods) / sizeof(kMethods[0]);
static const wchar_t *kUnknownMethod = L"Unknown";
STATPROPSTG kArcProps[] =
{
{ NULL, kpidMethod, VT_BSTR},
// { NULL, kpidSolid, VT_BOOL},
{ NULL, kpidNumBlocks, VT_UI4},
{ NULL, kpidNumVolumes, VT_UI4}
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
switch(propID)
{
case kpidMethod:
{
UString resString;
CRecordVector<Byte> ids;
int i;
for (int v = 0; v < m_Database.Volumes.Size(); v++)
{
const CDatabaseEx &de = m_Database.Volumes[v];
for (i = 0; i < de.Folders.Size(); i++)
ids.AddToUniqueSorted(de.Folders[i].GetCompressionMethod());
}
for (i = 0; i < ids.Size(); i++)
{
Byte id = ids[i];
UString method = (id < kNumMethods) ? kMethods[id] : kUnknownMethod;
if (!resString.IsEmpty())
resString += L' ';
resString += method;
}
prop = resString;
break;
}
// case kpidSolid: prop = _database.IsSolid(); break;
case kpidNumBlocks:
{
UInt32 numFolders = 0;
for (int v = 0; v < m_Database.Volumes.Size(); v++)
numFolders += m_Database.Volumes[v].Folders.Size();
prop = numFolders;
break;
}
case kpidNumVolumes:
{
prop = (UInt32)m_Database.Volumes.Size();
break;
}
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
const CMvItem &mvItem = m_Database.Items[index];
const CDatabaseEx &db = m_Database.Volumes[mvItem.VolumeIndex];
int itemIndex = mvItem.ItemIndex;
const CItem &item = db.Items[itemIndex];
switch(propID)
{
case kpidPath:
{
UString unicodeName;
if (item.IsNameUTF())
ConvertUTF8ToUnicode(item.Name, unicodeName);
else
unicodeName = MultiByteToUnicodeString(item.Name, CP_ACP);
prop = (const wchar_t *)NItemName::WinNameToOSName(unicodeName);
break;
}
case kpidIsDir: prop = item.IsDir(); break;
case kpidSize: prop = item.Size; break;
case kpidAttrib: prop = item.GetWinAttributes(); break;
case kpidMTime:
{
FILETIME localFileTime, utcFileTime;
if (NTime::DosTimeToFileTime(item.Time, localFileTime))
{
if (!LocalFileTimeToFileTime(&localFileTime, &utcFileTime))
utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
}
else
utcFileTime.dwHighDateTime = utcFileTime.dwLowDateTime = 0;
prop = utcFileTime;
break;
}
case kpidMethod:
{
UInt32 realFolderIndex = item.GetFolderIndex(db.Folders.Size());
const CFolder &folder = db.Folders[realFolderIndex];
int methodIndex = folder.GetCompressionMethod();
UString method = (methodIndex < kNumMethods) ? kMethods[methodIndex] : kUnknownMethod;
if (methodIndex == NHeader::NCompressionMethodMajor::kLZX ||
methodIndex == NHeader::NCompressionMethodMajor::kQuantum)
{
method += L":";
wchar_t temp[32];
ConvertUInt64ToString(folder.CompressionTypeMinor, temp);
method += temp;
}
prop = method;
break;
}
case kpidBlock: prop = (Int32)m_Database.GetFolderIndex(&mvItem); break;
#ifdef _CAB_DETAILS
case kpidBlockReal: prop = (UInt32)item.FolderIndex; break;
case kpidOffset: prop = (UInt32)item.Offset; break;
case kpidVolume: prop = (UInt32)mvItem.VolumeIndex; break;
#endif
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
/*
class CProgressImp: public CProgressVirt
{
CMyComPtr<IArchiveOpenCallback> m_OpenArchiveCallback;
public:
STDMETHOD(SetTotal)(const UInt64 *numFiles);
STDMETHOD(SetCompleted)(const UInt64 *numFiles);
void Init(IArchiveOpenCallback *openArchiveCallback)
{ m_OpenArchiveCallback = openArchiveCallback; }
};
STDMETHODIMP CProgressImp::SetTotal(const UInt64 *numFiles)
{
if (m_OpenArchiveCallback)
return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);
return S_OK;
}
STDMETHODIMP CProgressImp::SetCompleted(const UInt64 *numFiles)
{
if (m_OpenArchiveCallback)
return m_OpenArchiveCallback->SetCompleted(numFiles, NULL);
return S_OK;
}
*/
STDMETHODIMP CHandler::Open(IInStream *inStream,
const UInt64 *maxCheckStartPosition,
IArchiveOpenCallback *callback)
{
COM_TRY_BEGIN
Close();
HRESULT res = S_FALSE;
CInArchive archive;
CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
{
CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = callback;
openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);
}
CMyComPtr<IInStream> nextStream = inStream;
bool prevChecked = false;
UInt64 numItems = 0;
try
{
while(nextStream != 0)
{
CDatabaseEx db;
db.Stream = nextStream;
res = archive.Open(maxCheckStartPosition, db);
if (res == S_OK)
{
if (!m_Database.Volumes.IsEmpty())
{
const CDatabaseEx &dbPrev = m_Database.Volumes[prevChecked ? m_Database.Volumes.Size() - 1 : 0];
if (dbPrev.ArchiveInfo.SetID != db.ArchiveInfo.SetID ||
dbPrev.ArchiveInfo.CabinetNumber + (prevChecked ? 1: - 1) !=
db.ArchiveInfo.CabinetNumber)
res = S_FALSE;
}
}
if (res == S_OK)
m_Database.Volumes.Insert(prevChecked ? m_Database.Volumes.Size() : 0, db);
else if (res != S_FALSE)
return res;
else
{
if (m_Database.Volumes.IsEmpty())
return S_FALSE;
if (prevChecked)
break;
prevChecked = true;
}
numItems += db.Items.Size();
RINOK(callback->SetCompleted(&numItems, NULL));
nextStream = 0;
for (;;)
{
const COtherArchive *otherArchive = 0;
if (!prevChecked)
{
const CInArchiveInfo &ai = m_Database.Volumes.Front().ArchiveInfo;
if (ai.IsTherePrev())
otherArchive = &ai.PreviousArchive;
else
prevChecked = true;
}
if (otherArchive == 0)
{
const CInArchiveInfo &ai = m_Database.Volumes.Back().ArchiveInfo;
if (ai.IsThereNext())
otherArchive = &ai.NextArchive;
}
if (!otherArchive)
break;
const UString fullName = MultiByteToUnicodeString(otherArchive->FileName, CP_ACP);
HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream);
if (result == S_OK)
break;
if (result != S_FALSE)
return result;
if (prevChecked)
break;
prevChecked = true;
}
}
if (res == S_OK)
{
m_Database.FillSortAndShrink();
if (!m_Database.Check())
res = S_FALSE;
}
}
catch(...)
{
res = S_FALSE;
}
if (res != S_OK)
{
Close();
return res;
}
COM_TRY_END
return S_OK;
}
STDMETHODIMP CHandler::Close()
{
m_Database.Clear();
return S_OK;
}
class CCabFolderOutStream:
public ISequentialOutStream,
public CMyUnknownImp
{
public:
MY_UNKNOWN_IMP
STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
private:
const CMvDatabaseEx *m_Database;
const CRecordVector<bool> *m_ExtractStatuses;
int m_StartIndex;
int m_CurrentIndex;
CMyComPtr<IArchiveExtractCallback> m_ExtractCallback;
bool m_TestMode;
CMyComPtr<ISequentialOutStream> m_RealOutStream;
bool m_IsOk;
bool m_FileIsOpen;
UInt64 m_RemainFileSize;
UInt64 m_FolderSize;
UInt64 m_PosInFolder;
HRESULT OpenFile();
HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK);
public:
HRESULT WriteEmptyFiles();
void Init(
const CMvDatabaseEx *database,
const CRecordVector<bool> *extractStatuses,
int startIndex,
UInt64 folderSize,
IArchiveExtractCallback *extractCallback,
bool testMode);
HRESULT FlushCorrupted();
HRESULT Unsupported();
UInt64 GetRemain() const { return m_FolderSize - m_PosInFolder; }
UInt64 GetPosInFolder() const { return m_PosInFolder; }
};
void CCabFolderOutStream::Init(
const CMvDatabaseEx *database,
const CRecordVector<bool> *extractStatuses,
int startIndex,
UInt64 folderSize,
IArchiveExtractCallback *extractCallback,
bool testMode)
{
m_Database = database;
m_ExtractStatuses = extractStatuses;
m_StartIndex = startIndex;
m_FolderSize = folderSize;
m_ExtractCallback = extractCallback;
m_TestMode = testMode;
m_CurrentIndex = 0;
m_PosInFolder = 0;
m_FileIsOpen = false;
m_IsOk = true;
}
HRESULT CCabFolderOutStream::OpenFile()
{
Int32 askMode = (*m_ExtractStatuses)[m_CurrentIndex] ? (m_TestMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract) :
NExtract::NAskMode::kSkip;
RINOK(m_ExtractCallback->GetStream(m_StartIndex + m_CurrentIndex, &m_RealOutStream, askMode));
if (!m_RealOutStream && !m_TestMode)
askMode = NArchive::NExtract::NAskMode::kSkip;
return m_ExtractCallback->PrepareOperation(askMode);
}
HRESULT CCabFolderOutStream::WriteEmptyFiles()
{
if (m_FileIsOpen)
return S_OK;
for(;m_CurrentIndex < m_ExtractStatuses->Size(); m_CurrentIndex++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -