mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-12 07:09:54 -06:00
245 lines
6.4 KiB
C++
Executable File
245 lines
6.4 KiB
C++
Executable File
// HfsHandler.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Common/ComTry.h"
|
|
#include "Windows/PropVariant.h"
|
|
#include "../../Common/StreamUtils.h"
|
|
#include "HfsHandler.h"
|
|
|
|
namespace NArchive {
|
|
namespace NHfs {
|
|
|
|
STATPROPSTG kProps[] =
|
|
{
|
|
{ NULL, kpidPath, VT_BSTR},
|
|
{ NULL, kpidIsDir, VT_BOOL},
|
|
{ NULL, kpidSize, VT_UI8},
|
|
{ NULL, kpidPackSize, VT_UI8},
|
|
{ NULL, kpidCTime, VT_FILETIME},
|
|
{ NULL, kpidMTime, VT_FILETIME},
|
|
{ NULL, kpidATime, VT_FILETIME}
|
|
};
|
|
|
|
STATPROPSTG kArcProps[] =
|
|
{
|
|
{ NULL, kpidMethod, VT_BSTR},
|
|
{ NULL, kpidClusterSize, VT_UI4},
|
|
{ NULL, kpidFreeSpace, VT_UI8},
|
|
{ NULL, kpidCTime, VT_FILETIME},
|
|
{ NULL, kpidMTime, VT_FILETIME}
|
|
};
|
|
|
|
IMP_IInArchive_Props
|
|
IMP_IInArchive_ArcProps
|
|
|
|
static void HfsTimeToProp(UInt32 hfsTime, NWindows::NCOM::CPropVariant &prop)
|
|
{
|
|
FILETIME ft;
|
|
HfsTimeToFileTime(hfsTime, ft);
|
|
prop = ft;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NWindows::NCOM::CPropVariant prop;
|
|
switch(propID)
|
|
{
|
|
case kpidMethod: prop = _db.Header.IsHfsX() ? L"HFSX" : L"HFS+"; break;
|
|
case kpidClusterSize: prop = (UInt32)1 << _db.Header.BlockSizeLog; break;
|
|
case kpidFreeSpace: prop = (UInt64)_db.Header.NumFreeBlocks << _db.Header.BlockSizeLog; break;
|
|
case kpidMTime: HfsTimeToProp(_db.Header.MTime, prop); break;
|
|
case kpidCTime:
|
|
{
|
|
FILETIME localFt, ft;
|
|
HfsTimeToFileTime(_db.Header.CTime, localFt);
|
|
if (LocalFileTimeToFileTime(&localFt, &ft))
|
|
prop = ft;
|
|
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 CItem &item = _db.Items[index];
|
|
switch(propID)
|
|
{
|
|
case kpidPath: prop = _db.GetItemPath(index); break;
|
|
case kpidIsDir: prop = item.IsDir(); break;
|
|
|
|
case kpidCTime: HfsTimeToProp(item.CTime, prop); break;
|
|
case kpidMTime: HfsTimeToProp(item.MTime, prop); break;
|
|
case kpidATime: HfsTimeToProp(item.ATime, prop); break;
|
|
|
|
case kpidPackSize: if (!item.IsDir()) prop = (UInt64)item.NumBlocks << _db.Header.BlockSizeLog; break;
|
|
case kpidSize: if (!item.IsDir()) prop = item.Size; break;
|
|
}
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
class CProgressImp: public CProgressVirt
|
|
{
|
|
CMyComPtr<IArchiveOpenCallback> _callback;
|
|
public:
|
|
HRESULT SetTotal(UInt64 numFiles);
|
|
HRESULT SetCompleted(UInt64 numFiles);
|
|
CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {}
|
|
};
|
|
|
|
HRESULT CProgressImp::SetTotal(UInt64 numFiles)
|
|
{
|
|
if (_callback)
|
|
return _callback->SetTotal(&numFiles, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CProgressImp::SetCompleted(UInt64 numFiles)
|
|
{
|
|
if (_callback)
|
|
return _callback->SetCompleted(&numFiles, NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Open(IInStream *inStream,
|
|
const UInt64 * /* maxCheckStartPosition */,
|
|
IArchiveOpenCallback *callback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
Close();
|
|
try
|
|
{
|
|
CProgressImp progressImp(callback);
|
|
HRESULT res = _db.Open(inStream, &progressImp);
|
|
if (res == E_ABORT)
|
|
return res;
|
|
if (res != S_OK)
|
|
return S_FALSE;
|
|
_stream = inStream;
|
|
}
|
|
catch(...) { return S_FALSE; }
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_stream.Release();
|
|
_db.Clear();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
|
Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
|
{
|
|
COM_TRY_BEGIN
|
|
bool testMode = (_aTestMode != 0);
|
|
bool allFilesMode = (numItems == UInt32(-1));
|
|
if (allFilesMode)
|
|
numItems = _db.Items.Size();
|
|
if (numItems == 0)
|
|
return S_OK;
|
|
UInt32 i;
|
|
UInt64 totalSize = 0;
|
|
for (i = 0; i < numItems; i++)
|
|
{
|
|
const CItem &item = _db.Items[allFilesMode ? i : indices[i]];
|
|
if (!item.IsDir())
|
|
totalSize += item.Size;
|
|
}
|
|
RINOK(extractCallback->SetTotal(totalSize));
|
|
|
|
UInt64 currentTotalSize = 0, currentItemSize = 0;
|
|
|
|
CByteBuffer buf;
|
|
const UInt32 kBufSize = (1 << 16);
|
|
buf.SetCapacity(kBufSize);
|
|
|
|
for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize)
|
|
{
|
|
RINOK(extractCallback->SetCompleted(¤tTotalSize));
|
|
Int32 index = allFilesMode ? i : indices[i];
|
|
const CItem &item = _db.Items[index];
|
|
currentItemSize = 0;
|
|
if (!item.IsDir())
|
|
currentItemSize = item.Size;
|
|
|
|
CMyComPtr<ISequentialOutStream> realOutStream;
|
|
Int32 askMode = testMode ?
|
|
NArchive::NExtract::NAskMode::kTest :
|
|
NArchive::NExtract::NAskMode::kExtract;
|
|
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
|
|
|
if (item.IsDir())
|
|
{
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
|
|
continue;
|
|
}
|
|
if (!testMode && (!realOutStream))
|
|
continue;
|
|
RINOK(extractCallback->PrepareOperation(askMode));
|
|
UInt64 pos = 0;
|
|
int res = NArchive::NExtract::NOperationResult::kOK;
|
|
int i;
|
|
for (i = 0; i < item.Extents.Size(); i++)
|
|
{
|
|
if (item.Size == pos)
|
|
break;
|
|
if (res != NArchive::NExtract::NOperationResult::kOK)
|
|
break;
|
|
const CExtent &e = item.Extents[i];
|
|
RINOK(_stream->Seek((UInt64)e.Pos << _db.Header.BlockSizeLog, STREAM_SEEK_SET, NULL));
|
|
UInt64 extentSize = (UInt64)e.NumBlocks << _db.Header.BlockSizeLog;
|
|
for (;;)
|
|
{
|
|
if (extentSize == 0)
|
|
break;
|
|
UInt64 rem = item.Size - pos;
|
|
if (rem == 0)
|
|
{
|
|
if (extentSize >= (UInt64)((UInt32)1 << _db.Header.BlockSizeLog))
|
|
res = NArchive::NExtract::NOperationResult::kDataError;
|
|
break;
|
|
}
|
|
UInt32 curSize = kBufSize;
|
|
if (curSize > rem)
|
|
curSize = (UInt32)rem;
|
|
if (curSize > extentSize)
|
|
curSize = (UInt32)extentSize;
|
|
RINOK(ReadStream_FALSE(_stream, buf, curSize));
|
|
if (realOutStream)
|
|
{
|
|
RINOK(WriteStream(realOutStream, buf, curSize));
|
|
}
|
|
pos += curSize;
|
|
extentSize -= curSize;
|
|
UInt64 processed = currentTotalSize + pos;
|
|
RINOK(extractCallback->SetCompleted(&processed));
|
|
}
|
|
}
|
|
if (i != item.Extents.Size() || item.Size != pos)
|
|
res = NArchive::NExtract::NOperationResult::kDataError;
|
|
realOutStream.Release();
|
|
RINOK(extractCallback->SetOperationResult(res));
|
|
}
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
|
{
|
|
*numItems = _db.Items.Size();
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|