363 lines
7.1 KiB
C++
363 lines
7.1 KiB
C++
// VdiHandler.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
// #include <stdio.h>
|
|
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "../../Common/ComTry.h"
|
|
#include "../../Common/IntToString.h"
|
|
#include "../../Common/MyBuffer.h"
|
|
|
|
#include "../../Windows/PropVariant.h"
|
|
|
|
#include "../Common/RegisterArc.h"
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "HandlerCont.h"
|
|
|
|
#define Get32(p) GetUi32(p)
|
|
#define Get64(p) GetUi64(p)
|
|
|
|
using namespace NWindows;
|
|
|
|
namespace NArchive {
|
|
namespace NVdi {
|
|
|
|
#define SIGNATURE { 0x7F, 0x10, 0xDA, 0xBE }
|
|
|
|
static const Byte k_Signature[] = SIGNATURE;
|
|
|
|
static const unsigned k_ClusterBits = 20;
|
|
static const UInt32 k_ClusterSize = (UInt32)1 << k_ClusterBits;
|
|
static const UInt32 k_UnusedCluster = 0xFFFFFFFF;
|
|
|
|
// static const UInt32 kDiskType_Dynamic = 1;
|
|
// static const UInt32 kDiskType_Static = 2;
|
|
|
|
static const char * const kDiskTypes[] =
|
|
{
|
|
"0"
|
|
, "Dynamic"
|
|
, "Static"
|
|
};
|
|
|
|
class CHandler: public CHandlerImg
|
|
{
|
|
UInt32 _dataOffset;
|
|
CByteBuffer _table;
|
|
UInt64 _phySize;
|
|
UInt32 _imageType;
|
|
bool _isArc;
|
|
bool _unsupported;
|
|
|
|
HRESULT Seek(UInt64 offset)
|
|
{
|
|
_posInArc = offset;
|
|
return Stream->Seek(offset, STREAM_SEEK_SET, NULL);
|
|
}
|
|
|
|
HRESULT InitAndSeek()
|
|
{
|
|
_virtPos = 0;
|
|
return Seek(0);
|
|
}
|
|
|
|
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback);
|
|
|
|
public:
|
|
INTERFACE_IInArchive_Img(;)
|
|
|
|
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
|
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
|
|
};
|
|
|
|
|
|
STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize)
|
|
{
|
|
if (processedSize)
|
|
*processedSize = 0;
|
|
if (_virtPos >= _size)
|
|
return S_OK;
|
|
{
|
|
UInt64 rem = _size - _virtPos;
|
|
if (size > rem)
|
|
size = (UInt32)rem;
|
|
if (size == 0)
|
|
return S_OK;
|
|
}
|
|
|
|
{
|
|
UInt64 cluster = _virtPos >> k_ClusterBits;
|
|
UInt32 lowBits = (UInt32)_virtPos & (k_ClusterSize - 1);
|
|
{
|
|
UInt32 rem = k_ClusterSize - lowBits;
|
|
if (size > rem)
|
|
size = rem;
|
|
}
|
|
|
|
cluster <<= 2;
|
|
if (cluster < _table.Size())
|
|
{
|
|
const Byte *p = (const Byte *)_table + (size_t)cluster;
|
|
UInt32 v = Get32(p);
|
|
if (v != k_UnusedCluster)
|
|
{
|
|
UInt64 offset = _dataOffset + ((UInt64)v << k_ClusterBits);
|
|
offset += lowBits;
|
|
if (offset != _posInArc)
|
|
{
|
|
RINOK(Seek(offset));
|
|
}
|
|
HRESULT res = Stream->Read(data, size, &size);
|
|
_posInArc += size;
|
|
_virtPos += size;
|
|
if (processedSize)
|
|
*processedSize = size;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
memset(data, 0, size);
|
|
_virtPos += size;
|
|
if (processedSize)
|
|
*processedSize = size;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
|
|
static const Byte kProps[] =
|
|
{
|
|
kpidSize,
|
|
kpidPackSize
|
|
};
|
|
|
|
static const Byte kArcProps[] =
|
|
{
|
|
kpidHeadersSize,
|
|
kpidMethod
|
|
};
|
|
|
|
IMP_IInArchive_Props
|
|
IMP_IInArchive_ArcProps
|
|
|
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
|
|
switch (propID)
|
|
{
|
|
case kpidMainSubfile: prop = (UInt32)0; break;
|
|
case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
|
|
case kpidHeadersSize: prop = _dataOffset; break;
|
|
|
|
case kpidMethod:
|
|
{
|
|
char s[16];
|
|
const char *ptr;
|
|
if (_imageType < ARRAY_SIZE(kDiskTypes))
|
|
ptr = kDiskTypes[_imageType];
|
|
else
|
|
{
|
|
ConvertUInt32ToString(_imageType, s);
|
|
ptr = s;
|
|
}
|
|
prop = ptr;
|
|
break;
|
|
}
|
|
|
|
case kpidErrorFlags:
|
|
{
|
|
UInt32 v = 0;
|
|
if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
|
|
if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
|
|
// if (_headerError) v |= kpv_ErrorFlags_HeadersError;
|
|
if (!Stream && v == 0 && _isArc)
|
|
v = kpv_ErrorFlags_HeadersError;
|
|
if (v != 0)
|
|
prop = v;
|
|
break;
|
|
}
|
|
}
|
|
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
|
|
{
|
|
COM_TRY_BEGIN
|
|
NCOM::CPropVariant prop;
|
|
|
|
switch (propID)
|
|
{
|
|
case kpidSize: prop = _size; break;
|
|
case kpidPackSize: prop = _phySize - _dataOffset; break;
|
|
case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
|
|
}
|
|
|
|
prop.Detach(value);
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
static bool IsEmptyGuid(const Byte *data)
|
|
{
|
|
for (unsigned i = 0; i < 16; i++)
|
|
if (data[i] != 0)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback * /* openCallback */)
|
|
{
|
|
const unsigned kHeaderSize = 512;
|
|
Byte buf[kHeaderSize];
|
|
RINOK(ReadStream_FALSE(stream, buf, kHeaderSize));
|
|
|
|
if (memcmp(buf + 0x40, k_Signature, sizeof(k_Signature)) != 0)
|
|
return S_FALSE;
|
|
|
|
UInt32 version = Get32(buf + 0x44);
|
|
if (version >= 0x20000)
|
|
return S_FALSE;
|
|
|
|
UInt32 headerSize = Get32(buf + 0x48);
|
|
if (headerSize < 0x140 || headerSize > 0x1B8)
|
|
return S_FALSE;
|
|
|
|
_imageType = Get32(buf + 0x4C);
|
|
_dataOffset = Get32(buf + 0x158);
|
|
|
|
UInt32 tableOffset = Get32(buf + 0x154);
|
|
if (tableOffset < 0x200)
|
|
return S_FALSE;
|
|
|
|
UInt32 sectorSize = Get32(buf + 0x168);
|
|
if (sectorSize != 0x200)
|
|
return S_FALSE;
|
|
|
|
_size = Get64(buf + 0x170);
|
|
_isArc = true;
|
|
|
|
if (_imageType > 2)
|
|
{
|
|
_unsupported = true;
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (_dataOffset < tableOffset)
|
|
return S_FALSE;
|
|
|
|
UInt32 blockSize = Get32(buf + 0x178);
|
|
if (blockSize != ((UInt32)1 << k_ClusterBits))
|
|
{
|
|
_unsupported = true;
|
|
return S_FALSE;
|
|
}
|
|
|
|
UInt32 totalBlocks = Get32(buf + 0x180);
|
|
|
|
{
|
|
UInt64 size2 = (UInt64)totalBlocks << k_ClusterBits;
|
|
if (size2 < _size)
|
|
{
|
|
_unsupported = true;
|
|
return S_FALSE;
|
|
}
|
|
/*
|
|
if (size2 > _size)
|
|
_size = size2;
|
|
*/
|
|
}
|
|
|
|
if (headerSize >= 0x180)
|
|
{
|
|
if (!IsEmptyGuid(buf + 0x1A8) ||
|
|
!IsEmptyGuid(buf + 0x1B8))
|
|
{
|
|
_unsupported = true;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
UInt32 numAllocatedBlocks = Get32(buf + 0x184);
|
|
|
|
{
|
|
UInt32 tableReserved = _dataOffset - tableOffset;
|
|
if ((tableReserved >> 2) < totalBlocks)
|
|
return S_FALSE;
|
|
}
|
|
|
|
_phySize = _dataOffset + ((UInt64)numAllocatedBlocks << k_ClusterBits);
|
|
|
|
size_t numBytes = (size_t)totalBlocks * 4;
|
|
if ((numBytes >> 2) != totalBlocks)
|
|
{
|
|
_unsupported = true;
|
|
return S_FALSE;
|
|
}
|
|
|
|
_table.Alloc(numBytes);
|
|
RINOK(stream->Seek(tableOffset, STREAM_SEEK_SET, NULL));
|
|
RINOK(ReadStream_FALSE(stream, _table, numBytes));
|
|
|
|
const Byte *data = _table;
|
|
for (UInt32 i = 0; i < totalBlocks; i++)
|
|
{
|
|
UInt32 v = Get32(data + (size_t)i * 4);
|
|
if (v == k_UnusedCluster)
|
|
continue;
|
|
if (v >= numAllocatedBlocks)
|
|
return S_FALSE;
|
|
}
|
|
|
|
Stream = stream;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CHandler::Close()
|
|
{
|
|
_table.Free();
|
|
_phySize = 0;
|
|
_size = 0;
|
|
_isArc = false;
|
|
_unsupported = false;
|
|
|
|
_imgExt = NULL;
|
|
Stream.Release();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream)
|
|
{
|
|
COM_TRY_BEGIN
|
|
*stream = NULL;
|
|
if (_unsupported)
|
|
return S_FALSE;
|
|
CMyComPtr<ISequentialInStream> streamTemp = this;
|
|
RINOK(InitAndSeek());
|
|
*stream = streamTemp.Detach();
|
|
return S_OK;
|
|
COM_TRY_END
|
|
}
|
|
|
|
|
|
REGISTER_ARC_I(
|
|
"VDI", "vdi", NULL, 0xC9,
|
|
k_Signature,
|
|
0x40,
|
|
0,
|
|
NULL)
|
|
|
|
}}
|