mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-07 11:14:58 -06:00
9.04 beta
This commit is contained in:
committed by
Kornel Lesiński
parent
8874e4fbc9
commit
829409452d
731
CPP/7zip/Archive/VhdHandler.cpp
Executable file
731
CPP/7zip/Archive/VhdHandler.cpp
Executable file
@@ -0,0 +1,731 @@
|
||||
// VhdHandler.cpp
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "../../../C/CpuArch.h"
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/ComTry.h"
|
||||
#include "Common/IntToString.h"
|
||||
#include "Common/MyString.h"
|
||||
|
||||
#include "Windows/PropVariant.h"
|
||||
|
||||
#include "../Common/LimitedStreams.h"
|
||||
#include "../Common/ProgressUtils.h"
|
||||
#include "../Common/RegisterArc.h"
|
||||
#include "../Common/StreamUtils.h"
|
||||
|
||||
#include "../Compress/CopyCoder.h"
|
||||
|
||||
#define Get16(p) GetBe16(p)
|
||||
#define Get32(p) GetBe32(p)
|
||||
#define Get64(p) GetBe64(p)
|
||||
|
||||
#define G32(p, dest) dest = Get32(p);
|
||||
#define G64(p, dest) dest = Get64(p);
|
||||
|
||||
using namespace NWindows;
|
||||
|
||||
namespace NArchive {
|
||||
namespace NVhd {
|
||||
|
||||
static const UInt32 kUnusedBlock = 0xFFFFFFFF;
|
||||
|
||||
static const UInt32 kDiskType_Fixed = 2;
|
||||
static const UInt32 kDiskType_Dynamic = 3;
|
||||
static const UInt32 kDiskType_Diff = 4;
|
||||
|
||||
static const char *kDiskTypes[] =
|
||||
{
|
||||
"0",
|
||||
"1",
|
||||
"Fixed",
|
||||
"Dynamic",
|
||||
"Differencing"
|
||||
};
|
||||
|
||||
struct CFooter
|
||||
{
|
||||
// UInt32 Features;
|
||||
// UInt32 FormatVersion;
|
||||
UInt64 DataOffset;
|
||||
UInt32 CTime;
|
||||
UInt32 CreatorApp;
|
||||
UInt32 CreatorVersion;
|
||||
UInt32 CreatorHostOS;
|
||||
// UInt64 OriginalSize;
|
||||
UInt64 CurrentSize;
|
||||
UInt32 DiskGeometry;
|
||||
UInt32 Type;
|
||||
Byte Id[16];
|
||||
Byte SavedState;
|
||||
|
||||
bool IsFixed() const { return Type == kDiskType_Fixed; }
|
||||
bool ThereIsDynamic() const { return Type == kDiskType_Dynamic || Type == kDiskType_Diff; }
|
||||
// bool IsSupported() const { return Type == kDiskType_Fixed || Type == kDiskType_Dynamic || Type == kDiskType_Diff; }
|
||||
UInt32 NumCyls() const { return DiskGeometry >> 16; }
|
||||
UInt32 NumHeads() const { return (DiskGeometry >> 8) & 0xFF; }
|
||||
UInt32 NumSectorsPerTrack() const { return DiskGeometry & 0xFF; }
|
||||
AString GetTypeString() const;
|
||||
bool Parse(const Byte *p);
|
||||
};
|
||||
|
||||
AString CFooter::GetTypeString() const
|
||||
{
|
||||
if (Type < sizeof(kDiskTypes) / sizeof(kDiskTypes[0]))
|
||||
return kDiskTypes[Type];
|
||||
char s[16];
|
||||
ConvertUInt32ToString(Type, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool CheckBlock(const Byte *p, unsigned size, unsigned checkSumOffset, unsigned zeroOffset)
|
||||
{
|
||||
UInt32 sum = 0;
|
||||
unsigned i;
|
||||
for (i = 0; i < checkSumOffset; i++)
|
||||
sum += p[i];
|
||||
for (i = checkSumOffset + 4; i < size; i++)
|
||||
sum += p[i];
|
||||
if (~sum != Get32(p + checkSumOffset))
|
||||
return false;
|
||||
for (i = zeroOffset; i < size; i++)
|
||||
if (p[i] != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFooter::Parse(const Byte *p)
|
||||
{
|
||||
if (memcmp(p, "conectix", 8) != 0)
|
||||
return false;
|
||||
// G32(p + 0x08, Features);
|
||||
// G32(p + 0x0C, FormatVersion);
|
||||
G64(p + 0x10, DataOffset);
|
||||
G32(p + 0x18, CTime);
|
||||
G32(p + 0x1C, CreatorApp);
|
||||
G32(p + 0x20, CreatorVersion);
|
||||
G32(p + 0x24, CreatorHostOS);
|
||||
// G64(p + 0x28, OriginalSize);
|
||||
G64(p + 0x30, CurrentSize);
|
||||
G32(p + 0x38, DiskGeometry);
|
||||
G32(p + 0x3C, Type);
|
||||
memcpy(Id, p + 0x44, 16);
|
||||
SavedState = p[0x54];
|
||||
return CheckBlock(p, 512, 0x40, 0x55);
|
||||
}
|
||||
|
||||
/*
|
||||
struct CParentLocatorEntry
|
||||
{
|
||||
UInt32 Code;
|
||||
UInt32 DataSpace;
|
||||
UInt32 DataLen;
|
||||
UInt64 DataOffset;
|
||||
|
||||
bool Parse(const Byte *p);
|
||||
};
|
||||
bool CParentLocatorEntry::Parse(const Byte *p)
|
||||
{
|
||||
G32(p + 0x00, Code);
|
||||
G32(p + 0x04, DataSpace);
|
||||
G32(p + 0x08, DataLen);
|
||||
G32(p + 0x10, DataOffset);
|
||||
return (Get32(p + 0x0C) == 0); // Resrved
|
||||
}
|
||||
*/
|
||||
|
||||
struct CDynHeader
|
||||
{
|
||||
// UInt64 DataOffset;
|
||||
UInt64 TableOffset;
|
||||
// UInt32 HeaderVersion;
|
||||
UInt32 NumBlocks;
|
||||
int BlockSizeLog;
|
||||
UInt32 ParentTime;
|
||||
Byte ParentId[16];
|
||||
UString ParentName;
|
||||
// CParentLocatorEntry ParentLocators[8];
|
||||
|
||||
bool Parse(const Byte *p);
|
||||
UInt32 NumBitMapSectors() const
|
||||
{
|
||||
UInt32 numSectorsInBlock = (1 << (BlockSizeLog - 9));
|
||||
return (numSectorsInBlock + 512 * 8 - 1) / (512 * 8);
|
||||
}
|
||||
};
|
||||
|
||||
static int GetLog(UInt32 num)
|
||||
{
|
||||
for (int i = 0; i < 31; i++)
|
||||
if (((UInt32)1 << i) == num)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CDynHeader::Parse(const Byte *p)
|
||||
{
|
||||
if (memcmp(p, "cxsparse", 8) != 0)
|
||||
return false;
|
||||
// G64(p + 0x08, DataOffset);
|
||||
G64(p + 0x10, TableOffset);
|
||||
// G32(p + 0x18, HeaderVersion);
|
||||
G32(p + 0x1C, NumBlocks);
|
||||
BlockSizeLog = GetLog(Get32(p + 0x20));
|
||||
if (BlockSizeLog < 9 || BlockSizeLog > 30)
|
||||
return false;
|
||||
G32(p + 0x38, ParentTime);
|
||||
if (Get32(p + 0x3C) != 0) // reserved
|
||||
return false;
|
||||
memcpy(ParentId, p + 0x28, 16);
|
||||
{
|
||||
const int kNameLength = 256;
|
||||
wchar_t *s = ParentName.GetBuffer(kNameLength);
|
||||
for (unsigned i = 0; i < kNameLength; i++)
|
||||
s[i] = Get16(p + 0x40 + i * 2);
|
||||
s[kNameLength] = 0;
|
||||
ParentName.ReleaseBuffer();
|
||||
}
|
||||
/*
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (!ParentLocators[i].Parse(p + 0x240 + i * 24))
|
||||
return false;
|
||||
*/
|
||||
return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24);
|
||||
}
|
||||
|
||||
class CHandler:
|
||||
public IInStream,
|
||||
public IInArchive,
|
||||
public IInArchiveGetStream,
|
||||
public CMyUnknownImp
|
||||
{
|
||||
UInt64 _virtPos;
|
||||
UInt64 _phyPos;
|
||||
UInt64 _phyLimit;
|
||||
|
||||
CFooter Footer;
|
||||
CDynHeader Dyn;
|
||||
CRecordVector<UInt32> Bat;
|
||||
CByteBuffer BitMap;
|
||||
UInt32 BitMapTag;
|
||||
UInt32 NumUsedBlocks;
|
||||
CMyComPtr<IInStream> Stream;
|
||||
CMyComPtr<IInStream> ParentStream;
|
||||
CHandler *Parent;
|
||||
|
||||
HRESULT Seek(UInt64 offset);
|
||||
HRESULT InitAndSeek();
|
||||
HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size);
|
||||
|
||||
bool NeedParent() const { return Footer.Type == kDiskType_Diff; }
|
||||
UInt64 GetSize() const { return Footer.CurrentSize; }
|
||||
UInt64 GetPackSize() const
|
||||
{ return Footer.ThereIsDynamic() ? ((UInt64)NumUsedBlocks << Dyn.BlockSizeLog) : Footer.CurrentSize; }
|
||||
|
||||
UString GetParentName() const
|
||||
{
|
||||
const CHandler *p = this;
|
||||
UString res;
|
||||
while (p && p->NeedParent())
|
||||
{
|
||||
if (!res.IsEmpty())
|
||||
res += L" -> ";
|
||||
res += p->Dyn.ParentName;
|
||||
p = p->Parent;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool IsOK() const
|
||||
{
|
||||
const CHandler *p = this;
|
||||
while (p->NeedParent())
|
||||
{
|
||||
p = p->Parent;
|
||||
if (p == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT Open3();
|
||||
HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level);
|
||||
|
||||
public:
|
||||
MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream)
|
||||
|
||||
INTERFACE_IInArchive(;)
|
||||
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
||||
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
|
||||
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
|
||||
};
|
||||
|
||||
HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); }
|
||||
|
||||
HRESULT CHandler::InitAndSeek()
|
||||
{
|
||||
if (ParentStream)
|
||||
{
|
||||
RINOK(Parent->InitAndSeek());
|
||||
}
|
||||
_virtPos = _phyPos = 0;
|
||||
BitMapTag = kUnusedBlock;
|
||||
BitMap.SetCapacity(Dyn.NumBitMapSectors() << 9);
|
||||
return Seek(0);
|
||||
}
|
||||
|
||||
HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size)
|
||||
{
|
||||
if (offset + size > _phyLimit)
|
||||
return S_FALSE;
|
||||
if (offset != _phyPos)
|
||||
{
|
||||
_phyPos = offset;
|
||||
RINOK(Seek(offset));
|
||||
}
|
||||
HRESULT res = ReadStream_FALSE(Stream, data, size);
|
||||
_phyPos += size;
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT CHandler::Open3()
|
||||
{
|
||||
RINOK(Stream->Seek(0, STREAM_SEEK_END, &_phyPos));
|
||||
if (_phyPos < 512)
|
||||
return S_FALSE;
|
||||
const UInt32 kDynSize = 1024;
|
||||
Byte buf[kDynSize];
|
||||
|
||||
_phyLimit = _phyPos;
|
||||
RINOK(ReadPhy(_phyLimit - 512, buf, 512));
|
||||
if (!Footer.Parse(buf))
|
||||
return S_FALSE;
|
||||
_phyLimit -= 512;
|
||||
|
||||
if (!Footer.ThereIsDynamic())
|
||||
return S_OK;
|
||||
|
||||
RINOK(ReadPhy(0, buf + 512, 512));
|
||||
if (memcmp(buf, buf + 512, 512) != 0)
|
||||
return S_FALSE;
|
||||
|
||||
RINOK(ReadPhy(Footer.DataOffset, buf, kDynSize));
|
||||
if (!Dyn.Parse(buf))
|
||||
return S_FALSE;
|
||||
|
||||
if (Dyn.NumBlocks >= (UInt32)1 << 31)
|
||||
return S_FALSE;
|
||||
if (GetSize() != ((UInt64)Dyn.NumBlocks << Dyn.BlockSizeLog))
|
||||
return S_FALSE;
|
||||
|
||||
Bat.Reserve(Dyn.NumBlocks);
|
||||
while ((UInt32)Bat.Size() < Dyn.NumBlocks)
|
||||
{
|
||||
RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, 512));
|
||||
for (UInt32 j = 0; j < 512; j += 4)
|
||||
{
|
||||
UInt32 v = Get32(buf + j);
|
||||
if (v != kUnusedBlock)
|
||||
NumUsedBlocks++;
|
||||
Bat.Add(v);
|
||||
if ((UInt32)Bat.Size() >= Dyn.NumBlocks)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize)
|
||||
{
|
||||
if (processedSize != NULL)
|
||||
*processedSize = 0;
|
||||
if (_virtPos >= GetSize())
|
||||
return (GetSize() == _virtPos) ? S_OK: E_FAIL;
|
||||
if (size == 0)
|
||||
return S_OK;
|
||||
UInt32 blockIndex = (UInt32)(_virtPos >> Dyn.BlockSizeLog);
|
||||
UInt32 blockSectIndex = Bat[blockIndex];
|
||||
UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog;
|
||||
UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
|
||||
size = MyMin(blockSize - offsetInBlock, size);
|
||||
|
||||
HRESULT res = S_OK;
|
||||
if (blockSectIndex == kUnusedBlock)
|
||||
{
|
||||
if (ParentStream)
|
||||
{
|
||||
RINOK(ParentStream->Seek(_virtPos, STREAM_SEEK_SET, NULL));
|
||||
res = ParentStream->Read(data, size, &size);
|
||||
}
|
||||
else
|
||||
memset(data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
UInt64 newPos = (UInt64)blockSectIndex << 9;
|
||||
if (BitMapTag != blockIndex)
|
||||
{
|
||||
RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.GetCapacity()));
|
||||
BitMapTag = blockIndex;
|
||||
}
|
||||
RINOK(ReadPhy(newPos + BitMap.GetCapacity() + offsetInBlock, data, size));
|
||||
for (UInt32 cur = 0; cur < size;)
|
||||
{
|
||||
UInt32 rem = MyMin(0x200 - (offsetInBlock & 0x1FF), size - cur);
|
||||
UInt32 bmi = offsetInBlock >> 9;
|
||||
if (((BitMap[bmi >> 3] >> (7 - (bmi & 7))) & 1) == 0)
|
||||
{
|
||||
if (ParentStream)
|
||||
{
|
||||
RINOK(ParentStream->Seek(_virtPos + cur, STREAM_SEEK_SET, NULL));
|
||||
RINOK(ReadStream_FALSE(ParentStream, (Byte *)data + cur, rem));
|
||||
}
|
||||
else
|
||||
{
|
||||
const Byte *p = (const Byte *)data + cur;
|
||||
for (UInt32 i = 0; i < rem; i++)
|
||||
if (p[i] != 0)
|
||||
return S_FALSE;
|
||||
}
|
||||
}
|
||||
offsetInBlock += rem;
|
||||
cur += rem;
|
||||
}
|
||||
}
|
||||
if (processedSize != NULL)
|
||||
*processedSize = size;
|
||||
_virtPos += size;
|
||||
return res;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
|
||||
{
|
||||
switch(seekOrigin)
|
||||
{
|
||||
case STREAM_SEEK_SET: _virtPos = offset; break;
|
||||
case STREAM_SEEK_CUR: _virtPos += offset; break;
|
||||
case STREAM_SEEK_END: _virtPos = GetSize() + offset; break;
|
||||
default: return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
if (newPosition)
|
||||
*newPosition = _virtPos;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
kpidParent = kpidUserDefined,
|
||||
kpidSavedState
|
||||
};
|
||||
|
||||
STATPROPSTG kArcProps[] =
|
||||
{
|
||||
{ NULL, kpidSize, VT_UI8},
|
||||
{ NULL, kpidCTime, VT_FILETIME},
|
||||
{ NULL, kpidClusterSize, VT_UI8},
|
||||
{ NULL, kpidMethod, VT_BSTR},
|
||||
{ L"Parent", kpidParent, VT_BSTR},
|
||||
{ NULL, kpidCreatorApp, VT_BSTR},
|
||||
{ NULL, kpidHostOS, VT_BSTR},
|
||||
{ L"Saved State", kpidSavedState, VT_BOOL},
|
||||
{ NULL, kpidId, VT_BSTR}
|
||||
};
|
||||
|
||||
STATPROPSTG kProps[] =
|
||||
{
|
||||
{ NULL, kpidSize, VT_UI8},
|
||||
{ NULL, kpidPackSize, VT_UI8},
|
||||
{ NULL, kpidCTime, VT_FILETIME}
|
||||
|
||||
/*
|
||||
{ NULL, kpidNumCyls, VT_UI4},
|
||||
{ NULL, kpidNumHeads, VT_UI4},
|
||||
{ NULL, kpidSectorsPerTrack, VT_UI4}
|
||||
*/
|
||||
};
|
||||
|
||||
IMP_IInArchive_Props
|
||||
IMP_IInArchive_ArcProps_WITH_NAME
|
||||
|
||||
// VHD start time: 2000-01-01
|
||||
static const UInt64 kVhdTimeStartValue = (UInt64)3600 * 24 * (399 * 365 + 24 * 4);
|
||||
|
||||
static void VhdTimeToFileTime(UInt32 vhdTime, NCOM::CPropVariant &prop)
|
||||
{
|
||||
FILETIME ft, utc;
|
||||
UInt64 v = (kVhdTimeStartValue + vhdTime) * 10000000;
|
||||
ft.dwLowDateTime = (DWORD)v;
|
||||
ft.dwHighDateTime = (DWORD)(v >> 32);
|
||||
// specification says that it's UTC time, but Virtual PC 6 writes local time. Why?
|
||||
LocalFileTimeToFileTime(&ft, &utc);
|
||||
prop = utc;
|
||||
}
|
||||
|
||||
static void StringToAString(char *dest, UInt32 s)
|
||||
{
|
||||
for (int i = 24; i >= 0; i -= 8)
|
||||
{
|
||||
Byte b = (Byte)((s >> i) & 0xFF);
|
||||
if (b < 0x20 || b > 0x7F)
|
||||
break;
|
||||
*dest++ = b;
|
||||
}
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
static void ConvertByteToHex(unsigned value, char *s)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
unsigned t = value & 0xF;
|
||||
value >>= 4;
|
||||
s[1 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
|
||||
}
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
NCOM::CPropVariant prop;
|
||||
switch(propID)
|
||||
{
|
||||
case kpidMainSubfile: prop = (UInt32)0; break;
|
||||
case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;
|
||||
case kpidClusterSize: if (Footer.ThereIsDynamic()) prop = (UInt32)1 << Dyn.BlockSizeLog; break;
|
||||
case kpidMethod:
|
||||
{
|
||||
AString s = Footer.GetTypeString();
|
||||
if (NeedParent())
|
||||
{
|
||||
s += " -> ";
|
||||
const CHandler *p = this;
|
||||
while (p != 0 && p->NeedParent())
|
||||
p = p->Parent;
|
||||
if (p == 0)
|
||||
s += '?';
|
||||
else
|
||||
s += p->Footer.GetTypeString();
|
||||
}
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
case kpidCreatorApp:
|
||||
{
|
||||
char s[16];
|
||||
StringToAString(s, Footer.CreatorApp);
|
||||
AString res = s;
|
||||
res.Trim();
|
||||
ConvertUInt32ToString(Footer.CreatorVersion >> 16, s);
|
||||
res += ' ';
|
||||
res += s;
|
||||
res += '.';
|
||||
ConvertUInt32ToString(Footer.CreatorVersion & 0xFFFF, s);
|
||||
res += s;
|
||||
prop = res;
|
||||
break;
|
||||
}
|
||||
case kpidHostOS:
|
||||
{
|
||||
if (Footer.CreatorHostOS == 0x5769326b)
|
||||
prop = "Windows";
|
||||
else
|
||||
{
|
||||
char s[16];
|
||||
StringToAString(s, Footer.CreatorHostOS);
|
||||
prop = s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kpidId:
|
||||
{
|
||||
char s[32 + 4];
|
||||
for (int i = 0; i < 16; i++)
|
||||
ConvertByteToHex(Footer.Id[i], s + i * 2);
|
||||
s[32] = 0;
|
||||
prop = s;
|
||||
break;
|
||||
}
|
||||
case kpidSavedState: prop = Footer.SavedState ? true : false; break;
|
||||
case kpidParent: if (NeedParent()) prop = GetParentName(); break;
|
||||
}
|
||||
prop.Detach(value);
|
||||
return S_OK;
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level)
|
||||
{
|
||||
Close();
|
||||
Stream = stream;
|
||||
if (level > 32)
|
||||
return S_FALSE;
|
||||
RINOK(Open3());
|
||||
if (child && memcmp(child->Dyn.ParentId, Footer.Id, 16) != 0)
|
||||
return S_FALSE;
|
||||
if (Footer.Type != kDiskType_Diff)
|
||||
return S_OK;
|
||||
CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
|
||||
if (openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback) != S_OK)
|
||||
return S_FALSE;
|
||||
CMyComPtr<IInStream> nextStream;
|
||||
HRESULT res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream);
|
||||
if (res == S_FALSE)
|
||||
return S_OK;
|
||||
RINOK(res);
|
||||
|
||||
Parent = new CHandler;
|
||||
ParentStream = Parent;
|
||||
return Parent->Open2(nextStream, this, openArchiveCallback, level + 1);
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Open(IInStream *stream,
|
||||
const UInt64 * /* maxCheckStartPosition */,
|
||||
IArchiveOpenCallback * openArchiveCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
{
|
||||
HRESULT res;
|
||||
try
|
||||
{
|
||||
res = Open2(stream, NULL, openArchiveCallback, 0);
|
||||
if (res == S_OK)
|
||||
return S_OK;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
Close();
|
||||
throw;
|
||||
}
|
||||
Close();
|
||||
return res;
|
||||
}
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Close()
|
||||
{
|
||||
Bat.Clear();
|
||||
NumUsedBlocks = 0;
|
||||
Parent = 0;
|
||||
Stream.Release();
|
||||
ParentStream.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
||||
{
|
||||
*numItems = 1;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
NWindows::NCOM::CPropVariant prop;
|
||||
|
||||
switch(propID)
|
||||
{
|
||||
case kpidSize: prop = GetSize(); break;
|
||||
case kpidPackSize: prop = GetPackSize(); break;
|
||||
case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;
|
||||
/*
|
||||
case kpidNumCyls: prop = Footer.NumCyls(); break;
|
||||
case kpidNumHeads: prop = Footer.NumHeads(); break;
|
||||
case kpidSectorsPerTrack: prop = Footer.NumSectorsPerTrack(); break;
|
||||
*/
|
||||
}
|
||||
prop.Detach(value);
|
||||
return S_OK;
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
||||
Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
if (numItems == UInt32(-1))
|
||||
numItems = 1;
|
||||
if (numItems == 0)
|
||||
return S_OK;
|
||||
if (numItems != 1 || indices[0] != 0)
|
||||
return E_INVALIDARG;
|
||||
|
||||
bool testMode = (_aTestMode != 0);
|
||||
|
||||
RINOK(extractCallback->SetTotal(GetSize()));
|
||||
CMyComPtr<ISequentialOutStream> outStream;
|
||||
Int32 askMode = testMode ?
|
||||
NArchive::NExtract::NAskMode::kTest :
|
||||
NArchive::NExtract::NAskMode::kExtract;
|
||||
RINOK(extractCallback->GetStream(0, &outStream, askMode));
|
||||
if (!testMode && !outStream)
|
||||
return S_OK;
|
||||
RINOK(extractCallback->PrepareOperation(askMode));
|
||||
|
||||
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
|
||||
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
||||
|
||||
CLocalProgress *lps = new CLocalProgress;
|
||||
CMyComPtr<ICompressProgressInfo> progress = lps;
|
||||
lps->Init(extractCallback, false);
|
||||
|
||||
int res = NArchive::NExtract::NOperationResult::kDataError;
|
||||
CMyComPtr<ISequentialInStream> inStream;
|
||||
HRESULT hres = GetStream(0, &inStream);
|
||||
if (hres == S_FALSE)
|
||||
res = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
|
||||
else
|
||||
{
|
||||
RINOK(hres);
|
||||
HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
|
||||
if (hres == S_OK)
|
||||
{
|
||||
if (copyCoderSpec->TotalSize == GetSize())
|
||||
res = NArchive::NExtract::NOperationResult::kOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hres != S_FALSE)
|
||||
{
|
||||
RINOK(hres);
|
||||
}
|
||||
}
|
||||
}
|
||||
outStream.Release();
|
||||
return extractCallback->SetOperationResult(res);
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
*stream = 0;
|
||||
if (Footer.IsFixed())
|
||||
{
|
||||
CLimitedInStream *streamSpec = new CLimitedInStream;
|
||||
CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
|
||||
streamSpec->SetStream(Stream);
|
||||
streamSpec->InitAndSeek(0, GetSize());
|
||||
RINOK(streamSpec->SeekToStart());
|
||||
*stream = streamTemp.Detach();
|
||||
return S_OK;
|
||||
}
|
||||
if (!Footer.ThereIsDynamic() || !IsOK())
|
||||
return S_FALSE;
|
||||
CMyComPtr<ISequentialInStream> streamTemp = this;
|
||||
RINOK(InitAndSeek());
|
||||
*stream = streamTemp.Detach();
|
||||
return S_OK;
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
static IInArchive *CreateArc() { return new CHandler; }
|
||||
|
||||
static CArcInfo g_ArcInfo =
|
||||
{ L"VHD", L"vhd", L".mbr", 0xDC, { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 }, 10, false, CreateArc, 0 };
|
||||
|
||||
REGISTER_ARC(Vhd)
|
||||
|
||||
}}
|
||||
Reference in New Issue
Block a user