summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp')
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp734
1 files changed, 734 insertions, 0 deletions
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp
new file mode 100644
index 000000000..9d1c928e6
--- /dev/null
+++ b/src/libs/7zip/win/CPP/7zip/Archive/VhdHandler.cpp
@@ -0,0 +1,734 @@
+// 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 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 (Footer.CurrentSize == 0)
+ {
+ if (Dyn.NumBlocks != 0)
+ return S_FALSE;
+ }
+ else if (((Footer.CurrentSize - 1) >> Dyn.BlockSizeLog) + 1 != Dyn.NumBlocks)
+ 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 >= Footer.CurrentSize)
+ return (Footer.CurrentSize == _virtPos) ? S_OK: E_FAIL;
+ UInt64 rem = Footer.CurrentSize - _virtPos;
+ if (size > rem)
+ size = (UInt32)rem;
+ 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 = Footer.CurrentSize + 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 = Footer.CurrentSize; 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 testMode, IArchiveExtractCallback *extractCallback)
+{
+ COM_TRY_BEGIN
+ if (numItems == 0)
+ return S_OK;
+ if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
+ return E_INVALIDARG;
+
+ RINOK(extractCallback->SetTotal(Footer.CurrentSize));
+ CMyComPtr<ISequentialOutStream> outStream;
+ Int32 askMode = testMode ?
+ NExtract::NAskMode::kTest :
+ 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 = NExtract::NOperationResult::kDataError;
+ CMyComPtr<ISequentialInStream> inStream;
+ HRESULT hres = GetStream(0, &inStream);
+ if (hres == S_FALSE)
+ res = NExtract::NOperationResult::kUnSupportedMethod;
+ else
+ {
+ RINOK(hres);
+ HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
+ if (hres == S_OK)
+ {
+ if (copyCoderSpec->TotalSize == Footer.CurrentSize)
+ res = 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, Footer.CurrentSize);
+ 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)
+
+}}