| // 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) |
| |
| }} |