Files
easy7zip/CPP/7zip/Archive/Hfs/HfsHandler.cpp
Igor Pavlov 173c07e166 4.59 beta
2016-05-28 00:15:56 +01:00

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(&currentTotalSize));
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;
}
}}