2017-05-19 20:28:49 +01:00
// /***************************************************************************
2016-07-28 18:13:49 +01:00
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : VHD.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Disc image plugins.
//
// --[ Description ] ----------------------------------------------------------
//
// Manages Connectix and Microsoft Virtual PC disk images.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2017-05-19 20:28:49 +01:00
// Copyright © 2011-2017 Natalia Portillo
2016-07-28 18:13:49 +01:00
// ****************************************************************************/
2015-04-22 19:24:29 +01:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Text ;
2015-04-24 01:52:26 +01:00
using System.Runtime.InteropServices ;
2015-10-18 22:04:03 +01:00
using DiscImageChef.Console ;
2015-11-23 21:44:58 +00:00
using DiscImageChef.CommonTypes ;
2016-09-05 17:37:31 +01:00
using DiscImageChef.Filters ;
2015-04-22 19:24:29 +01:00
namespace DiscImageChef.ImagePlugins
{
/// <summary>
/// Supports Connectix/Microsoft Virtual PC hard disk image format
/// Until Virtual PC 5 there existed no format, and the hard disk image was
/// merely a sector by sector (RAW) image with a resource fork giving
/// information to Virtual PC itself.
/// </summary>
public class VHD : ImagePlugin
{
#region Internal Structures
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
struct HardDiskFooter
{
/// <summary>
/// Offset 0x00, File magic number, <see cref="ImageCookie"/>
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong cookie ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x08, Specific feature support
/// </summary>
2016-07-28 22:25:26 +01:00
public uint features ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x0C, File format version
/// </summary>
2016-07-28 22:25:26 +01:00
public uint version ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x10, Offset from beginning of file to next structure
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong offset ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x18, Creation date seconds since 2000/01/01 00:00:00 UTC
/// </summary>
2016-07-28 22:25:26 +01:00
public uint timestamp ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x1C, Application that created this disk image
/// </summary>
2016-07-28 22:25:26 +01:00
public uint creatorApplication ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x20, Version of the application that created this disk image
/// </summary>
2016-07-28 22:25:26 +01:00
public uint creatorVersion ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x24, Host operating system of the application that created this disk image
/// </summary>
2016-07-28 22:25:26 +01:00
public uint creatorHostOS ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x28, Original hard disk size, in bytes
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong originalSize ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x30, Current hard disk size, in bytes
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong currentSize ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x38, CHS geometry
/// Cylinder mask = 0xFFFF0000
/// Heads mask = 0x0000FF00
/// Sectors mask = 0x000000FF
/// </summary>
2016-07-28 22:25:26 +01:00
public uint diskGeometry ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x3C, Disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
public uint diskType ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x40, Checksum for this structure
/// </summary>
2016-07-28 22:25:26 +01:00
public uint checksum ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Offset 0x44, UUID, used to associate parent with differencing disk images
/// </summary>
public Guid uniqueId ;
/// <summary>
/// Offset 0x54, If set, system is saved, so compaction and expansion cannot be performed
/// </summary>
public byte savedState ;
/// <summary>
/// Offset 0x55, 427 bytes reserved, should contain zeros.
/// </summary>
public byte [ ] reserved ;
}
struct ParentLocatorEntry
{
/// <summary>
2015-04-24 01:52:26 +01:00
/// Offset 0x00, Describes the platform specific type this entry belongs to
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint platformCode ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-24 01:52:26 +01:00
/// Offset 0x04, Describes the number of 512 bytes sectors used by this entry
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint platformDataSpace ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-24 01:52:26 +01:00
/// Offset 0x08, Describes this entry's size in bytes
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint platformDataLength ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-24 01:52:26 +01:00
/// Offset 0x0c, Reserved
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint reserved ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-24 01:52:26 +01:00
/// Offset 0x10, Offset on disk image this entry resides on
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong platformDataOffset ;
2015-04-22 19:24:29 +01:00
}
struct DynamicDiskHeader
{
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x00, Header magic, <see cref="DynamicCookie"/>
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong cookie ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x08, Offset to next structure on disk image.
2015-04-22 19:24:29 +01:00
/// Currently unused, 0xFFFFFFFF
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong dataOffset ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x10, Offset of the Block Allocation Table (BAT)
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public ulong tableOffset ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x18, Version of this header
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint headerVersion ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x1C, Maximum entries present in the BAT
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint maxTableEntries ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x20, Size of a block in bytes
2015-04-22 19:24:29 +01:00
/// Should always be a power of two of 512
/// </summary>
2016-07-28 22:25:26 +01:00
public uint blockSize ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x24, Checksum of this header
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint checksum ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x28, UUID of parent disk image for differencing type
2015-04-22 19:24:29 +01:00
/// </summary>
public Guid parentID ;
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x38, Timestamp of parent disk image
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint parentTimestamp ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x3C, Reserved
2015-04-22 19:24:29 +01:00
/// </summary>
2016-07-28 22:25:26 +01:00
public uint reserved ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x40, 512 bytes UTF-16 of parent disk image filename
2015-04-22 19:24:29 +01:00
/// </summary>
public string parentName ;
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x240, Parent disk image locator entry, <see cref="ParentLocatorEntry"/>
2015-04-22 19:24:29 +01:00
/// </summary>
2015-04-24 01:52:26 +01:00
public ParentLocatorEntry [ ] locatorEntries ;
2015-04-22 19:24:29 +01:00
/// <summary>
2015-04-22 19:32:51 +01:00
/// Offset 0x300, 256 reserved bytes
2015-04-22 19:24:29 +01:00
/// </summary>
public byte [ ] reserved2 ;
}
2015-04-22 19:32:51 +01:00
2015-04-24 01:52:26 +01:00
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BATSector
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
2016-07-28 22:25:26 +01:00
public uint [ ] blockPointer ;
2015-04-24 01:52:26 +01:00
}
2015-04-22 19:24:29 +01:00
#endregion
#region Internal Constants
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
/// <summary>
/// File magic number, "conectix"
/// </summary>
2016-07-28 22:25:26 +01:00
const ulong ImageCookie = 0x636F6E6563746978 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Dynamic disk header magic, "cxsparse"
/// </summary>
2016-07-28 22:25:26 +01:00
const ulong DynamicCookie = 0x6378737061727365 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image is candidate for deletion on shutdown
/// </summary>
2016-07-28 22:25:26 +01:00
const uint FeaturesTemporary = 0x00000001 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Unknown, set from Virtual PC for Mac 7 onwards
/// </summary>
2016-07-28 22:25:26 +01:00
const uint FeaturesReserved = 0x00000002 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Unknown
/// </summary>
2016-07-28 22:25:26 +01:00
const uint FeaturesUnknown = 0x00000100 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Only known version
/// </summary>
2016-07-28 22:25:26 +01:00
const uint Version1 = 0x00010000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Created by Virtual PC, "vpc "
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorVirtualPC = 0x76706320 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Created by Virtual Server, "vs "
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorVirtualServer = 0x76732020 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Created by QEMU, "qemu"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorQEMU = 0x71656D75 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Created by VirtualBox, "vbox"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorVirtualBox = 0x76626F78 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created by Virtual Server 2004
/// </summary>
2016-07-28 22:25:26 +01:00
const uint VersionVirtualServer2004 = 0x00010000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created by Virtual PC 2004
/// </summary>
2016-07-28 22:25:26 +01:00
const uint VersionVirtualPC2004 = 0x00050000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created by Virtual PC 2007
/// </summary>
2016-07-28 22:25:26 +01:00
const uint VersionVirtualPC2007 = 0x00050003 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created by Virtual PC for Mac 5, 6 or 7
/// </summary>
2016-07-28 22:25:26 +01:00
const uint VersionVirtualPCMac = 0x00040000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created in Windows, "Wi2k"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorWindows = 0x5769326B ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image created in Macintosh, "Mac "
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorMacintosh = 0x4D616320 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Seen in Virtual PC for Mac for dynamic and fixed images
/// </summary>
2016-07-28 22:25:26 +01:00
const uint CreatorMacintoshOld = 0x00000000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Disk image type is none, useless?
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeNone = 0 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Deprecated disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeDeprecated1 = 1 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Fixed disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeFixed = 2 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Dynamic disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeDynamic = 3 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Differencing disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeDifferencing = 4 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Deprecated disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeDeprecated2 = 5 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Deprecated disk image type
/// </summary>
2016-07-28 22:25:26 +01:00
const uint typeDeprecated3 = 6 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Means platform locator is unused
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeUnused = 0x00000000 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores a relative path string for Windows, unknown locale used, deprecated, "Wi2r"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeWindowsRelative = 0x57693272 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores an absolute path string for Windows, unknown locale used, deprecated, "Wi2k"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeWindowsAbsolute = 0x5769326B ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores a relative path string for Windows in UTF-16, "W2ru"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeWindowsRelativeU = 0x57327275 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores an absolute path string for Windows in UTF-16, "W2ku"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeWindowsAbsoluteU = 0x57326B75 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores a Mac OS alias as a blob, "Mac "
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeMacintoshAlias = 0x4D616320 ;
2015-04-22 19:24:29 +01:00
/// <summary>
/// Stores a Mac OS X URI (RFC-2396) absolute path in UTF-8, "MacX"
/// </summary>
2016-07-28 22:25:26 +01:00
const uint platformCodeMacintoshURI = 0x4D616358 ;
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
#endregion
#region Internal variables
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
HardDiskFooter thisFooter ;
DynamicDiskHeader thisDynamic ;
DateTime thisDateTime ;
DateTime parentDateTime ;
2016-09-05 17:37:31 +01:00
Filter thisFilter ;
2016-07-28 22:25:26 +01:00
uint [ ] blockAllocationTable ;
uint bitmapSize ;
2015-04-24 03:43:44 +01:00
byte [ ] [ ] locatorEntriesData ;
ImagePlugin parentImage ;
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
#endregion
2015-10-05 19:45:07 +01:00
public VHD ( )
2015-04-22 19:24:29 +01:00
{
Name = "VirtualPC" ;
PluginUUID = new Guid ( "8014d88f-64cd-4484-9441-7635c632958a" ) ;
ImageInfo = new ImageInfo ( ) ;
ImageInfo . readableSectorTags = new List < SectorTagType > ( ) ;
2016-01-16 03:54:55 +00:00
ImageInfo . readableMediaTags = new List < MediaTagType > ( ) ;
2015-04-22 19:24:29 +01:00
ImageInfo . imageHasPartitions = false ;
ImageInfo . imageHasSessions = false ;
ImageInfo . imageVersion = null ;
ImageInfo . imageApplication = null ;
ImageInfo . imageApplicationVersion = null ;
ImageInfo . imageCreator = null ;
ImageInfo . imageComments = null ;
2016-01-16 03:54:55 +00:00
ImageInfo . mediaManufacturer = null ;
ImageInfo . mediaModel = null ;
ImageInfo . mediaSerialNumber = null ;
ImageInfo . mediaBarcode = null ;
ImageInfo . mediaPartNumber = null ;
ImageInfo . mediaSequence = 0 ;
ImageInfo . lastMediaSequence = 0 ;
2015-04-22 19:24:29 +01:00
ImageInfo . driveManufacturer = null ;
ImageInfo . driveModel = null ;
ImageInfo . driveSerialNumber = null ;
2016-08-18 00:05:24 +01:00
ImageInfo . driveFirmwareRevision = null ;
2015-04-22 19:24:29 +01:00
}
#region public methods
2015-04-22 19:32:51 +01:00
2016-09-05 17:37:31 +01:00
public override bool IdentifyImage ( Filter imageFilter )
2015-04-22 19:24:29 +01:00
{
2016-09-05 17:37:31 +01:00
Stream imageStream = imageFilter . GetDataForkStream ( ) ;
2016-07-28 22:25:26 +01:00
ulong headerCookie ;
ulong footerCookie ;
2015-04-22 19:24:29 +01:00
byte [ ] headerCookieBytes = new byte [ 8 ] ;
byte [ ] footerCookieBytes = new byte [ 8 ] ;
2016-04-19 02:11:47 +01:00
if ( ( imageStream . Length % 2 ) = = 0 )
2015-04-22 19:24:29 +01:00
imageStream . Seek ( - 512 , SeekOrigin . End ) ;
else
imageStream . Seek ( - 511 , SeekOrigin . End ) ;
imageStream . Read ( footerCookieBytes , 0 , 8 ) ;
imageStream . Seek ( 0 , SeekOrigin . Begin ) ;
imageStream . Read ( headerCookieBytes , 0 , 8 ) ;
BigEndianBitConverter . IsLittleEndian = BitConverter . IsLittleEndian ;
headerCookie = BigEndianBitConverter . ToUInt64 ( headerCookieBytes , 0 ) ;
footerCookie = BigEndianBitConverter . ToUInt64 ( footerCookieBytes , 0 ) ;
return ( headerCookie = = ImageCookie | | footerCookie = = ImageCookie ) ;
}
2016-09-05 17:37:31 +01:00
public override bool OpenImage ( Filter imageFilter )
2015-04-22 19:24:29 +01:00
{
2016-09-05 17:37:31 +01:00
Stream imageStream = imageFilter . GetDataForkStream ( ) ;
2015-04-22 19:24:29 +01:00
byte [ ] header = new byte [ 512 ] ;
byte [ ] footer ;
imageStream . Seek ( 0 , SeekOrigin . Begin ) ;
imageStream . Read ( header , 0 , 512 ) ;
2016-04-19 02:11:47 +01:00
if ( ( imageStream . Length % 2 ) = = 0 )
2015-04-22 19:24:29 +01:00
{
footer = new byte [ 512 ] ;
imageStream . Seek ( - 512 , SeekOrigin . End ) ;
imageStream . Read ( footer , 0 , 512 ) ;
}
else
{
footer = new byte [ 511 ] ;
imageStream . Seek ( - 511 , SeekOrigin . End ) ;
imageStream . Read ( footer , 0 , 511 ) ;
}
BigEndianBitConverter . IsLittleEndian = BitConverter . IsLittleEndian ;
2016-07-28 22:25:26 +01:00
uint headerChecksum = BigEndianBitConverter . ToUInt32 ( header , 0x40 ) ;
uint footerChecksum = BigEndianBitConverter . ToUInt32 ( footer , 0x40 ) ;
ulong headerCookie = BigEndianBitConverter . ToUInt64 ( header , 0 ) ;
ulong footerCookie = BigEndianBitConverter . ToUInt64 ( footer , 0 ) ;
2015-04-22 19:24:29 +01:00
header [ 0x40 ] = 0 ;
header [ 0x41 ] = 0 ;
header [ 0x42 ] = 0 ;
header [ 0x43 ] = 0 ;
footer [ 0x40 ] = 0 ;
footer [ 0x41 ] = 0 ;
footer [ 0x42 ] = 0 ;
footer [ 0x43 ] = 0 ;
2016-07-28 22:25:26 +01:00
uint headerCalculatedChecksum = VHDChecksum ( header ) ;
uint footerCalculatedChecksum = VHDChecksum ( footer ) ;
2015-04-22 19:24:29 +01:00
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}" , headerChecksum , headerCalculatedChecksum ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Header checksum = 0x{0:X8}, calculated = 0x{1:X8}" , footerChecksum , footerCalculatedChecksum ) ;
2015-04-22 19:24:29 +01:00
byte [ ] usableHeader ;
2016-07-28 22:25:26 +01:00
uint usableChecksum ;
2015-04-22 19:24:29 +01:00
2016-04-19 02:11:47 +01:00
if ( headerCookie = = ImageCookie & & headerChecksum = = headerCalculatedChecksum )
2015-04-22 19:24:29 +01:00
{
usableHeader = header ;
usableChecksum = headerChecksum ;
}
2016-04-19 02:11:47 +01:00
else if ( footerCookie = = ImageCookie & & footerChecksum = = footerCalculatedChecksum )
2015-04-22 19:24:29 +01:00
{
usableHeader = footer ;
usableChecksum = footerChecksum ;
}
else
throw new ImageNotSupportedException ( "(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened." ) ;
thisFooter = new HardDiskFooter ( ) ;
thisFooter . cookie = BigEndianBitConverter . ToUInt64 ( usableHeader , 0x00 ) ;
thisFooter . features = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x08 ) ;
thisFooter . version = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x0C ) ;
thisFooter . offset = BigEndianBitConverter . ToUInt64 ( usableHeader , 0x10 ) ;
thisFooter . timestamp = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x18 ) ;
thisFooter . creatorApplication = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x1C ) ;
thisFooter . creatorVersion = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x20 ) ;
thisFooter . creatorHostOS = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x24 ) ;
thisFooter . originalSize = BigEndianBitConverter . ToUInt64 ( usableHeader , 0x28 ) ;
thisFooter . currentSize = BigEndianBitConverter . ToUInt64 ( usableHeader , 0x30 ) ;
thisFooter . diskGeometry = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x38 ) ;
thisFooter . diskType = BigEndianBitConverter . ToUInt32 ( usableHeader , 0x3C ) ;
thisFooter . checksum = usableChecksum ;
thisFooter . uniqueId = BigEndianBitConverter . ToGuid ( usableHeader , 0x44 ) ;
thisFooter . savedState = usableHeader [ 0x54 ] ;
thisFooter . reserved = new byte [ usableHeader . Length - 0x55 ] ;
Array . Copy ( usableHeader , 0x55 , thisFooter . reserved , 0 , ( usableHeader . Length - 0x55 ) ) ;
thisDateTime = new DateTime ( 2000 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
thisDateTime = thisDateTime . AddSeconds ( thisFooter . timestamp ) ;
2015-10-18 22:04:03 +01:00
Checksums . SHA1Context sha1Ctx = new Checksums . SHA1Context ( ) ;
sha1Ctx . Init ( ) ;
sha1Ctx . Update ( thisFooter . reserved ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.cookie = 0x{0:X8}" , thisFooter . cookie ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.features = 0x{0:X8}" , thisFooter . features ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.version = 0x{0:X8}" , thisFooter . version ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.offset = {0}" , thisFooter . offset ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.timestamp = 0x{0:X8} ({1})" , thisFooter . timestamp , thisDateTime ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.creatorApplication = 0x{0:X8} (\"{1}\")" , thisFooter . creatorApplication ,
Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisFooter . creatorApplication ) ) ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.creatorVersion = 0x{0:X8}" , thisFooter . creatorVersion ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.creatorHostOS = 0x{0:X8} (\"{1}\")" , thisFooter . creatorHostOS ,
Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisFooter . creatorHostOS ) ) ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.originalSize = {0}" , thisFooter . originalSize ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.currentSize = {0}" , thisFooter . currentSize ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.diskGeometry = 0x{0:X8} (C/H/S: {1}/{2}/{3})" , thisFooter . diskGeometry ,
( thisFooter . diskGeometry & 0xFFFF0000 ) > > 16 , ( thisFooter . diskGeometry & 0xFF00 ) > > 8 , ( thisFooter . diskGeometry & 0xFF ) ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.diskType = 0x{0:X8}" , thisFooter . diskType ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.checksum = 0x{0:X8}" , thisFooter . checksum ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.uniqueId = {0}" , thisFooter . uniqueId ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.savedState = 0x{0:X2}" , thisFooter . savedState ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "footer.reserved's SHA1 = 0x{0}" , sha1Ctx . End ( ) ) ;
2015-04-22 19:24:29 +01:00
2016-04-19 02:11:47 +01:00
if ( thisFooter . version = = Version1 )
2015-04-22 19:24:29 +01:00
ImageInfo . imageVersion = "1.0" ;
else
2016-07-28 22:25:26 +01:00
throw new ImageNotSupportedException ( string . Format ( "(VirtualPC plugin): Unknown image type {0} found. Please submit a bug with an example image." , thisFooter . diskType ) ) ;
2015-04-22 19:24:29 +01:00
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorApplication )
2015-04-22 19:24:29 +01:00
{
case CreatorQEMU :
{
ImageInfo . imageApplication = "QEMU" ;
// QEMU always set same version
ImageInfo . imageApplicationVersion = "Unknown" ;
break ;
}
case CreatorVirtualBox :
{
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplicationVersion = string . Format ( "{0}.{1:D2}" , ( thisFooter . creatorVersion & 0xFFFF0000 ) > > 16 , ( thisFooter . creatorVersion & 0x0000FFFF ) ) ;
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorHostOS )
2015-04-22 19:24:29 +01:00
{
case CreatorMacintosh :
case CreatorMacintoshOld :
ImageInfo . imageApplication = "VirtualBox for Mac" ;
break ;
case CreatorWindows :
// VirtualBox uses Windows creator for any other OS
ImageInfo . imageApplication = "VirtualBox" ;
break ;
default :
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplication = string . Format ( "VirtualBox for unknown OS \"{0}\"" , Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisFooter . creatorHostOS ) ) ) ;
2015-04-22 19:24:29 +01:00
break ;
}
break ;
}
case CreatorVirtualServer :
{
ImageInfo . imageApplication = "Microsoft Virtual Server" ;
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorVersion )
2015-04-22 19:24:29 +01:00
{
case VersionVirtualServer2004 :
ImageInfo . imageApplicationVersion = "2004" ;
break ;
default :
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplicationVersion = string . Format ( "Unknown version 0x{0:X8}" , thisFooter . creatorVersion ) ;
2015-04-22 19:24:29 +01:00
break ;
}
break ;
}
case CreatorVirtualPC :
{
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorHostOS )
2015-04-22 19:24:29 +01:00
{
case CreatorMacintosh :
case CreatorMacintoshOld :
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorVersion )
2015-04-22 19:24:29 +01:00
{
case VersionVirtualPCMac :
ImageInfo . imageApplication = "Connectix Virtual PC" ;
ImageInfo . imageApplicationVersion = "5, 6 or 7" ;
break ;
default :
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplicationVersion = string . Format ( "Unknown version 0x{0:X8}" , thisFooter . creatorVersion ) ;
2015-04-22 19:24:29 +01:00
break ;
}
break ;
case CreatorWindows :
2016-04-19 02:11:47 +01:00
switch ( thisFooter . creatorVersion )
2015-04-22 19:24:29 +01:00
{
case VersionVirtualPCMac :
ImageInfo . imageApplication = "Connectix Virtual PC" ;
ImageInfo . imageApplicationVersion = "5, 6 or 7" ;
break ;
case VersionVirtualPC2004 :
ImageInfo . imageApplication = "Microsoft Virtual PC" ;
ImageInfo . imageApplicationVersion = "2004" ;
break ;
case VersionVirtualPC2007 :
ImageInfo . imageApplication = "Microsoft Virtual PC" ;
ImageInfo . imageApplicationVersion = "2007" ;
break ;
default :
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplicationVersion = string . Format ( "Unknown version 0x{0:X8}" , thisFooter . creatorVersion ) ;
2015-04-22 19:24:29 +01:00
break ;
}
break ;
default :
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplication = string . Format ( "Virtual PC for unknown OS \"{0}\"" , Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisFooter . creatorHostOS ) ) ) ;
ImageInfo . imageApplicationVersion = string . Format ( "Unknown version 0x{0:X8}" , thisFooter . creatorVersion ) ;
2015-04-22 19:24:29 +01:00
break ;
}
break ;
}
default :
{
2016-07-28 22:25:26 +01:00
ImageInfo . imageApplication = string . Format ( "Unknown application \"{0}\"" , Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisFooter . creatorHostOS ) ) ) ;
ImageInfo . imageApplicationVersion = string . Format ( "Unknown version 0x{0:X8}" , thisFooter . creatorVersion ) ;
2015-04-22 19:24:29 +01:00
break ;
}
}
2016-09-05 17:37:31 +01:00
thisFilter = imageFilter ;
2015-04-22 19:24:29 +01:00
ImageInfo . imageSize = thisFooter . currentSize ;
2015-04-22 19:32:51 +01:00
ImageInfo . sectors = thisFooter . currentSize / 512 ;
2015-04-22 19:24:29 +01:00
ImageInfo . sectorSize = 512 ;
2016-09-05 17:37:31 +01:00
ImageInfo . imageCreationTime = imageFilter . GetCreationTime ( ) ;
2015-04-22 19:24:29 +01:00
ImageInfo . imageLastModificationTime = thisDateTime ;
2016-09-05 17:37:31 +01:00
ImageInfo . imageName = Path . GetFileNameWithoutExtension ( imageFilter . GetFilename ( ) ) ;
2016-04-19 02:11:47 +01:00
2017-08-02 23:01:11 +01:00
ImageInfo . cylinders = ( thisFooter . diskGeometry & 0xFFFF0000 ) > > 16 ;
ImageInfo . heads = ( thisFooter . diskGeometry & 0xFF00 ) > > 8 ;
ImageInfo . sectorsPerTrack = ( thisFooter . diskGeometry & 0xFF ) ;
if ( thisFooter . diskType = = typeDynamic | | thisFooter . diskType = = typeDifferencing )
2015-04-24 01:52:26 +01:00
{
imageStream . Seek ( ( long ) thisFooter . offset , SeekOrigin . Begin ) ;
byte [ ] dynamicBytes = new byte [ 1024 ] ;
imageStream . Read ( dynamicBytes , 0 , 1024 ) ;
2016-07-28 22:25:26 +01:00
uint dynamicChecksum = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x24 ) ;
2015-04-24 01:52:26 +01:00
dynamicBytes [ 0x24 ] = 0 ;
dynamicBytes [ 0x25 ] = 0 ;
dynamicBytes [ 0x26 ] = 0 ;
dynamicBytes [ 0x27 ] = 0 ;
2016-07-28 22:25:26 +01:00
uint dynamicChecksumCalculated = VHDChecksum ( dynamicBytes ) ;
2015-04-24 01:52:26 +01:00
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Dynamic header checksum = 0x{0:X8}, calculated = 0x{1:X8}" , dynamicChecksum , dynamicChecksumCalculated ) ;
2015-04-24 01:52:26 +01:00
2016-04-19 02:11:47 +01:00
if ( dynamicChecksum ! = dynamicChecksumCalculated )
2015-04-24 01:52:26 +01:00
throw new ImageNotSupportedException ( "(VirtualPC plugin): Both header and footer are corrupt, image cannot be opened." ) ;
thisDynamic = new DynamicDiskHeader ( ) ;
thisDynamic . locatorEntries = new ParentLocatorEntry [ 8 ] ;
thisDynamic . reserved2 = new byte [ 256 ] ;
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < 8 ; i + + )
2015-04-24 01:52:26 +01:00
thisDynamic . locatorEntries [ i ] = new ParentLocatorEntry ( ) ;
thisDynamic . cookie = BigEndianBitConverter . ToUInt64 ( dynamicBytes , 0x00 ) ;
thisDynamic . dataOffset = BigEndianBitConverter . ToUInt64 ( dynamicBytes , 0x08 ) ;
thisDynamic . tableOffset = BigEndianBitConverter . ToUInt64 ( dynamicBytes , 0x10 ) ;
thisDynamic . headerVersion = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x18 ) ;
thisDynamic . maxTableEntries = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x1C ) ;
thisDynamic . blockSize = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x20 ) ;
thisDynamic . checksum = dynamicChecksum ;
thisDynamic . parentID = BigEndianBitConverter . ToGuid ( dynamicBytes , 0x28 ) ;
thisDynamic . parentTimestamp = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x38 ) ;
thisDynamic . reserved = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x3C ) ;
thisDynamic . parentName = Encoding . BigEndianUnicode . GetString ( dynamicBytes , 0x40 , 512 ) ;
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < 8 ; i + + )
2015-04-24 01:52:26 +01:00
{
thisDynamic . locatorEntries [ i ] . platformCode = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x240 + 0x00 + 24 * i ) ;
thisDynamic . locatorEntries [ i ] . platformDataSpace = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x240 + 0x04 + 24 * i ) ;
thisDynamic . locatorEntries [ i ] . platformDataLength = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x240 + 0x08 + 24 * i ) ;
thisDynamic . locatorEntries [ i ] . reserved = BigEndianBitConverter . ToUInt32 ( dynamicBytes , 0x240 + 0x0C + 24 * i ) ;
thisDynamic . locatorEntries [ i ] . platformDataOffset = BigEndianBitConverter . ToUInt64 ( dynamicBytes , 0x240 + 0x10 + 24 * i ) ;
}
Array . Copy ( dynamicBytes , 0x300 , thisDynamic . reserved2 , 0 , 256 ) ;
parentDateTime = new DateTime ( 2000 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
parentDateTime = parentDateTime . AddSeconds ( thisDynamic . parentTimestamp ) ;
2015-10-18 22:04:03 +01:00
sha1Ctx = new Checksums . SHA1Context ( ) ;
sha1Ctx . Init ( ) ;
sha1Ctx . Update ( thisDynamic . reserved2 ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.cookie = 0x{0:X8}" , thisDynamic . cookie ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.dataOffset = {0}" , thisDynamic . dataOffset ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.tableOffset = {0}" , thisDynamic . tableOffset ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.headerVersion = 0x{0:X8}" , thisDynamic . headerVersion ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.maxTableEntries = {0}" , thisDynamic . maxTableEntries ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.blockSize = {0}" , thisDynamic . blockSize ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.checksum = 0x{0:X8}" , thisDynamic . checksum ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.parentID = {0}" , thisDynamic . parentID ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.parentTimestamp = 0x{0:X8} ({1})" , thisDynamic . parentTimestamp , parentDateTime ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.reserved = 0x{0:X8}" , thisDynamic . reserved ) ;
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < 8 ; i + + )
2015-04-24 01:52:26 +01:00
{
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}].platformCode = 0x{1:X8} (\"{2}\")" , i , thisDynamic . locatorEntries [ i ] . platformCode ,
Encoding . ASCII . GetString ( BigEndianBitConverter . GetBytes ( thisDynamic . locatorEntries [ i ] . platformCode ) ) ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}].platformDataSpace = {1}" , i , thisDynamic . locatorEntries [ i ] . platformDataSpace ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}].platformDataLength = {1}" , i , thisDynamic . locatorEntries [ i ] . platformDataLength ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}].reserved = 0x{1:X8}" , i , thisDynamic . locatorEntries [ i ] . reserved ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}].platformDataOffset = {1}" , i , thisDynamic . locatorEntries [ i ] . platformDataOffset ) ;
2015-04-24 01:52:26 +01:00
}
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.parentName = \"{0}\"" , thisDynamic . parentName ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.reserved2's SHA1 = 0x{0}" , sha1Ctx . End ( ) ) ;
2015-04-24 01:52:26 +01:00
2016-04-19 02:11:47 +01:00
if ( thisDynamic . headerVersion ! = Version1 )
2016-07-28 22:25:26 +01:00
throw new ImageNotSupportedException ( string . Format ( "(VirtualPC plugin): Unknown image type {0} found. Please submit a bug with an example image." , thisFooter . diskType ) ) ;
2015-04-24 01:52:26 +01:00
DateTime startTime = DateTime . UtcNow ;
blockAllocationTable = new uint [ thisDynamic . maxTableEntries ] ;
// Safe and slow code. It takes 76,572 ms to fill a 30720 entries BAT
/ *
byte [ ] bat = new byte [ thisDynamic . maxTableEntries * 4 ] ;
imageStream . Seek ( ( long ) thisDynamic . tableOffset , SeekOrigin . Begin ) ;
imageStream . Read ( bat , 0 , ( int ) ( thisDynamic . maxTableEntries * 4 ) ) ;
for ( int i = 0 ; i < thisDynamic . maxTableEntries ; i + + )
blockAllocationTable [ i ] = BigEndianBitConverter . ToUInt32 ( bat , 4 * i ) ;
2015-10-18 22:04:03 +01:00
DateTime endTime = DateTime . UtcNow ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Filling the BAT took {0} seconds" , ( endTime - startTime ) . TotalSeconds ) ;
2015-04-24 01:52:26 +01:00
* /
// How many sectors uses the BAT
2016-07-28 22:25:26 +01:00
uint batSectorCount = ( uint ) Math . Ceiling ( ( ( double ) thisDynamic . maxTableEntries * 4 ) / 512 ) ;
2015-04-24 01:52:26 +01:00
byte [ ] batSectorBytes = new byte [ 512 ] ;
BATSector batSector = new BATSector ( ) ;
// Unsafe and fast code. It takes 4 ms to fill a 30720 entries BAT
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < batSectorCount ; i + + )
2015-04-24 01:52:26 +01:00
{
imageStream . Seek ( ( long ) thisDynamic . tableOffset + i * 512 , SeekOrigin . Begin ) ;
imageStream . Read ( batSectorBytes , 0 , 512 ) ;
// This does the big-endian trick but reverses the order of elements also
Array . Reverse ( batSectorBytes ) ;
GCHandle handle = GCHandle . Alloc ( batSectorBytes , GCHandleType . Pinned ) ;
batSector = ( BATSector ) Marshal . PtrToStructure ( handle . AddrOfPinnedObject ( ) , typeof ( BATSector ) ) ;
handle . Free ( ) ;
// This restores the order of elements
Array . Reverse ( batSector . blockPointer ) ;
2016-04-19 02:11:47 +01:00
if ( blockAllocationTable . Length > = ( i * 512 ) / 4 + 512 / 4 )
2015-04-24 01:52:26 +01:00
Array . Copy ( batSector . blockPointer , 0 , blockAllocationTable , ( i * 512 ) / 4 , 512 / 4 ) ;
else
Array . Copy ( batSector . blockPointer , 0 , blockAllocationTable , ( i * 512 ) / 4 , blockAllocationTable . Length - ( i * 512 ) / 4 ) ;
}
2015-10-18 22:04:03 +01:00
DateTime endTime = DateTime . UtcNow ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Filling the BAT took {0} seconds" , ( endTime - startTime ) . TotalSeconds ) ;
2015-04-24 01:52:26 +01:00
// Too noisy
/ *
for ( int i = 0 ; i < thisDynamic . maxTableEntries ; i + + )
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "blockAllocationTable[{0}] = {1}" , i , blockAllocationTable [ i ] ) ;
* /
2015-04-24 01:52:26 +01:00
// Get the roundest number of sectors needed to store the block bitmap
2015-04-24 03:43:44 +01:00
bitmapSize = ( uint ) Math . Ceiling ( (
2015-04-24 01:52:26 +01:00
// How many sectors do a block store
2015-04-24 03:43:44 +01:00
( ( double ) thisDynamic . blockSize / 512 )
2015-04-24 01:52:26 +01:00
// 1 bit per sector on the bitmap
/ 8
// and aligned to 512 byte boundary
/ 512 ) ) ;
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Bitmap is {0} sectors" , bitmapSize ) ;
2015-04-24 01:52:26 +01:00
}
2015-12-05 17:21:47 +00:00
ImageInfo . xmlMediaType = XmlMediaType . BlockMedia ;
2016-04-19 02:11:47 +01:00
switch ( thisFooter . diskType )
2015-04-22 19:24:29 +01:00
{
case typeFixed :
2015-04-24 01:52:26 +01:00
case typeDynamic :
2015-04-22 19:24:29 +01:00
{
// Nothing to do here, really.
return true ;
}
case typeDifferencing :
{
2015-04-24 03:43:44 +01:00
locatorEntriesData = new byte [ 8 ] [ ] ;
2016-04-19 02:11:47 +01:00
for ( int i = 0 ; i < 8 ; i + + )
2015-04-24 03:43:44 +01:00
{
2016-04-19 02:11:47 +01:00
if ( thisDynamic . locatorEntries [ i ] . platformCode ! = 0x00000000 )
2015-04-24 03:43:44 +01:00
{
locatorEntriesData [ i ] = new byte [ thisDynamic . locatorEntries [ i ] . platformDataLength ] ;
imageStream . Seek ( ( long ) thisDynamic . locatorEntries [ i ] . platformDataOffset , SeekOrigin . Begin ) ;
imageStream . Read ( locatorEntriesData [ i ] , 0 , ( int ) thisDynamic . locatorEntries [ i ] . platformDataLength ) ;
2016-04-19 02:11:47 +01:00
switch ( thisDynamic . locatorEntries [ i ] . platformCode )
2015-04-24 03:43:44 +01:00
{
2015-10-18 22:04:03 +01:00
case platformCodeWindowsAbsolute :
case platformCodeWindowsRelative :
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}] = \"{1}\"" , i , Encoding . ASCII . GetString ( locatorEntriesData [ i ] ) ) ;
break ;
case platformCodeWindowsAbsoluteU :
case platformCodeWindowsRelativeU :
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}] = \"{1}\"" , i , Encoding . BigEndianUnicode . GetString ( locatorEntriesData [ i ] ) ) ;
break ;
case platformCodeMacintoshURI :
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}] = \"{1}\"" , i , Encoding . UTF8 . GetString ( locatorEntriesData [ i ] ) ) ;
break ;
default :
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dynamic.locatorEntries[{0}] =" , i ) ;
PrintHex . PrintHexArray ( locatorEntriesData [ i ] , 64 ) ;
break ;
2015-04-24 03:43:44 +01:00
}
}
}
int currentLocator = 0 ;
bool locatorFound = false ;
string parentPath = null ;
2017-09-11 21:17:47 +01:00
FiltersList filters ;
2015-04-24 03:43:44 +01:00
2016-04-19 02:11:47 +01:00
while ( ! locatorFound & & currentLocator < 8 )
2015-04-24 03:43:44 +01:00
{
2016-04-19 02:11:47 +01:00
switch ( thisDynamic . locatorEntries [ currentLocator ] . platformCode )
2015-04-24 03:43:44 +01:00
{
case platformCodeWindowsAbsolute :
case platformCodeWindowsRelative :
parentPath = Encoding . ASCII . GetString ( locatorEntriesData [ currentLocator ] ) ;
break ;
case platformCodeWindowsAbsoluteU :
case platformCodeWindowsRelativeU :
parentPath = Encoding . BigEndianUnicode . GetString ( locatorEntriesData [ currentLocator ] ) ;
break ;
case platformCodeMacintoshURI :
parentPath = Uri . UnescapeDataString ( Encoding . UTF8 . GetString ( locatorEntriesData [ currentLocator ] ) ) ;
2016-04-19 02:11:47 +01:00
if ( parentPath . StartsWith ( "file://localhost" , StringComparison . InvariantCulture ) )
2015-04-24 03:43:44 +01:00
parentPath = parentPath . Remove ( 0 , 16 ) ;
else
{
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Unsupported protocol classified found in URI parent path: \"{0}\"" , parentPath ) ;
2015-04-24 03:43:44 +01:00
parentPath = null ;
}
break ;
}
2016-04-19 02:11:47 +01:00
if ( parentPath ! = null )
2015-04-24 03:43:44 +01:00
{
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Possible parent path: \"{0}\"" , parentPath ) ;
2017-09-11 21:17:47 +01:00
Filter parentFilter = new FiltersList ( ) . GetFilter ( Path . Combine ( imageFilter . GetParentFolder ( ) , parentPath ) ) ;
2015-04-24 03:43:44 +01:00
2016-09-05 17:37:31 +01:00
if ( parentFilter ! = null )
locatorFound = true ;
2015-04-24 03:43:44 +01:00
2016-04-19 02:11:47 +01:00
if ( ! locatorFound )
2015-04-24 03:43:44 +01:00
parentPath = null ;
}
currentLocator + + ;
}
2016-04-19 02:11:47 +01:00
if ( ! locatorFound | | parentPath = = null )
2015-04-24 03:43:44 +01:00
throw new FileNotFoundException ( "(VirtualPC plugin): Cannot find parent file for differencing disk image" ) ;
else
{
2015-10-05 19:45:07 +01:00
parentImage = new VHD ( ) ;
2017-09-11 21:17:47 +01:00
Filter parentFilter = new FiltersList ( ) . GetFilter ( Path . Combine ( imageFilter . GetParentFolder ( ) , parentPath ) ) ;
2016-09-05 17:37:31 +01:00
if ( parentFilter = = null )
throw new ImageNotSupportedException ( "(VirtualPC plugin): Cannot find parent image filter" ) ;
2016-04-19 02:11:47 +01:00
/ * PluginBase plugins = new PluginBase ( ) ;
plugins . RegisterAllPlugins ( ) ;
if ( ! plugins . ImagePluginsList . TryGetValue ( Name . ToLower ( ) , out parentImage ) )
throw new SystemException ( "(VirtualPC plugin): Unable to open myself" ) ; * /
2015-04-24 03:43:44 +01:00
2016-09-05 17:37:31 +01:00
if ( ! parentImage . IdentifyImage ( parentFilter ) )
2015-04-24 03:43:44 +01:00
throw new ImageNotSupportedException ( "(VirtualPC plugin): Parent image is not a Virtual PC disk image" ) ;
2016-09-05 17:37:31 +01:00
if ( ! parentImage . OpenImage ( parentFilter ) )
2015-04-24 03:43:44 +01:00
throw new ImageNotSupportedException ( "(VirtualPC plugin): Cannot open parent disk image" ) ;
// While specification says that parent and child disk images should contain UUID relationship
// in reality it seems that old differencing disk images stored a parent UUID that, nonetheless
// the parent never stored itself. So the only real way to know that images are related is
// because the parent IS found and SAME SIZE. Ugly...
// More funny even, tested parent images show an empty host OS, and child images a correct one.
2016-04-19 02:11:47 +01:00
if ( parentImage . GetSectors ( ) ! = GetSectors ( ) )
2015-04-24 03:43:44 +01:00
throw new ImageNotSupportedException ( "(VirtualPC plugin): Parent image is of different size" ) ;
}
return true ;
2015-04-22 19:24:29 +01:00
}
case typeDeprecated1 :
case typeDeprecated2 :
case typeDeprecated3 :
{
throw new ImageNotSupportedException ( "(VirtualPC plugin): Deprecated image type found. Please submit a bug with an example image." ) ;
}
default :
{
2016-07-28 22:25:26 +01:00
throw new ImageNotSupportedException ( string . Format ( "(VirtualPC plugin): Unknown image type {0} found. Please submit a bug with an example image." , thisFooter . diskType ) ) ;
2015-04-22 19:24:29 +01:00
}
}
}
public override bool ImageHasPartitions ( )
{
return false ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override ulong GetImageSize ( )
{
return ImageInfo . imageSize ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override ulong GetSectors ( )
{
return ImageInfo . sectors ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override uint GetSectorSize ( )
{
return ImageInfo . sectorSize ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetImageFormat ( )
{
2016-04-19 02:11:47 +01:00
switch ( thisFooter . diskType )
2015-04-22 19:24:29 +01:00
{
case typeFixed :
return "Virtual PC fixed size disk image" ;
case typeDynamic :
return "Virtual PC dynamic size disk image" ;
case typeDifferencing :
return "Virtual PC differencing disk image" ;
default :
return "Virtual PC disk image" ;
}
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetImageVersion ( )
{
return ImageInfo . imageVersion ;
}
public override string GetImageApplication ( )
{
return ImageInfo . imageApplication ;
}
public override string GetImageApplicationVersion ( )
{
return ImageInfo . imageApplicationVersion ;
}
public override string GetImageCreator ( )
{
return ImageInfo . imageCreator ;
}
public override DateTime GetImageCreationTime ( )
{
return ImageInfo . imageCreationTime ;
}
public override DateTime GetImageLastModificationTime ( )
{
return ImageInfo . imageLastModificationTime ;
}
public override string GetImageName ( )
{
return ImageInfo . imageName ;
}
2016-01-16 03:54:55 +00:00
public override MediaType GetMediaType ( )
2015-04-22 19:24:29 +01:00
{
2016-01-16 03:54:55 +00:00
return MediaType . GENERIC_HDD ;
2015-04-22 19:24:29 +01:00
}
public override byte [ ] ReadSector ( ulong sectorAddress )
{
2016-04-19 02:11:47 +01:00
switch ( thisFooter . diskType )
2015-04-24 03:43:44 +01:00
{
case typeDifferencing :
{
// Block number for BAT searching
2016-07-28 22:25:26 +01:00
uint blockNumber = ( uint ) Math . Floor ( ( double ) ( sectorAddress / ( thisDynamic . blockSize / 512 ) ) ) ;
2015-04-24 03:43:44 +01:00
// Sector number inside of block
2016-07-28 22:25:26 +01:00
uint sectorInBlock = ( uint ) ( sectorAddress % ( thisDynamic . blockSize / 512 ) ) ;
2015-04-24 03:43:44 +01:00
byte [ ] bitmap = new byte [ bitmapSize * 512 ] ;
// Offset of block in file
2016-07-28 22:25:26 +01:00
uint blockOffset = blockAllocationTable [ blockNumber ] * 512 ;
2015-04-24 03:43:44 +01:00
int bitmapByte = ( int ) Math . Floor ( ( double ) sectorInBlock / 8 ) ;
int bitmapBit = ( int ) ( sectorInBlock % 8 ) ;
2016-09-05 17:37:31 +01:00
Stream thisStream = thisFilter . GetDataForkStream ( ) ;
2015-04-24 03:43:44 +01:00
thisStream . Seek ( blockOffset , SeekOrigin . Begin ) ;
thisStream . Read ( bitmap , 0 , ( int ) bitmapSize * 512 ) ;
byte mask = ( byte ) ( 1 < < ( 7 - bitmapBit ) ) ;
bool dirty = false | | ( bitmap [ bitmapByte ] & mask ) = = mask ;
/ *
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "bitmapSize = {0}" , bitmapSize ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "blockNumber = {0}" , blockNumber ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "sectorInBlock = {0}" , sectorInBlock ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "blockOffset = {0}" , blockOffset ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "bitmapByte = {0}" , bitmapByte ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "bitmapBit = {0}" , bitmapBit ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "mask = 0x{0:X2}" , mask ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "dirty = 0x{0}" , dirty ) ;
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "bitmap = " ) ;
PrintHex . PrintHexArray ( bitmap , 64 ) ;
2015-04-24 03:43:44 +01:00
* /
// Sector has been written, read from child image
2016-04-19 02:11:47 +01:00
if ( dirty )
2015-04-24 03:43:44 +01:00
{
/ * Too noisy
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Sector {0} is dirty" , sectorAddress ) ;
2015-04-24 03:43:44 +01:00
* /
byte [ ] data = new byte [ 512 ] ;
2016-07-28 22:25:26 +01:00
uint sectorOffset = blockAllocationTable [ blockNumber ] + bitmapSize + sectorInBlock ;
2016-09-05 17:37:31 +01:00
thisStream = thisFilter . GetDataForkStream ( ) ;
2015-04-24 03:43:44 +01:00
2016-07-28 22:25:26 +01:00
thisStream . Seek ( ( sectorOffset * 512 ) , SeekOrigin . Begin ) ;
2015-04-24 03:43:44 +01:00
thisStream . Read ( data , 0 , 512 ) ;
return data ;
}
/ * Too noisy
2015-10-18 22:04:03 +01:00
DicConsole . DebugWriteLine ( "VirtualPC plugin" , "Sector {0} is clean" , sectorAddress ) ;
2015-04-24 03:43:44 +01:00
* /
// Read sector from parent image
return parentImage . ReadSector ( sectorAddress ) ;
}
default :
return ReadSectors ( sectorAddress , 1 ) ;
}
2015-04-22 19:24:29 +01:00
}
public override byte [ ] ReadSectors ( ulong sectorAddress , uint length )
{
2016-04-19 02:11:47 +01:00
switch ( thisFooter . diskType )
2015-04-22 19:24:29 +01:00
{
case typeFixed :
{
2016-09-05 17:37:31 +01:00
Stream thisStream ;
2015-04-24 03:43:44 +01:00
2015-04-22 19:24:29 +01:00
byte [ ] data = new byte [ 512 * length ] ;
2016-09-05 17:37:31 +01:00
thisStream = thisFilter . GetDataForkStream ( ) ;
2015-04-22 19:24:29 +01:00
thisStream . Seek ( ( long ) ( sectorAddress * 512 ) , SeekOrigin . Begin ) ;
thisStream . Read ( data , 0 , ( int ) ( 512 * length ) ) ;
return data ;
}
2016-04-19 02:11:47 +01:00
// Contrary to Microsoft's specifications that tell us to check the bitmap
// and in case of unused sector just return zeros, as blocks are allocated
// as a whole, this would waste time and miss cache, so we read any sector
// as long as it is in the block.
2015-04-22 19:24:29 +01:00
case typeDynamic :
{
2016-09-05 17:37:31 +01:00
Stream thisStream ;
2015-04-24 03:43:44 +01:00
2015-04-24 01:52:26 +01:00
// Block number for BAT searching
2016-07-28 22:25:26 +01:00
uint blockNumber = ( uint ) Math . Floor ( ( double ) ( sectorAddress / ( thisDynamic . blockSize / 512 ) ) ) ;
2015-04-24 01:52:26 +01:00
// Sector number inside of block
2016-07-28 22:25:26 +01:00
uint sectorInBlock = ( uint ) ( sectorAddress % ( thisDynamic . blockSize / 512 ) ) ;
2015-04-24 01:52:26 +01:00
// How many sectors before reaching end of block
2016-07-28 22:25:26 +01:00
uint remainingInBlock = ( thisDynamic . blockSize / 512 ) - sectorInBlock ;
2015-04-24 01:52:26 +01:00
// Data that can be read in this block
byte [ ] prefix ;
// Data that needs to be read from another block
byte [ ] suffix = null ;
// How many sectors to read from this block
2016-07-28 22:25:26 +01:00
uint sectorsToReadHere ;
2015-04-24 01:52:26 +01:00
// Asked to read more sectors than are remaining in block
2016-04-19 02:11:47 +01:00
if ( length > remainingInBlock )
2015-04-24 01:52:26 +01:00
{
suffix = ReadSectors ( sectorAddress + remainingInBlock , length - remainingInBlock ) ;
sectorsToReadHere = remainingInBlock ;
}
else
sectorsToReadHere = length ;
// Offset of sector in file
2016-07-28 22:25:26 +01:00
uint sectorOffset = blockAllocationTable [ blockNumber ] + bitmapSize + sectorInBlock ;
2015-04-24 01:52:26 +01:00
prefix = new byte [ sectorsToReadHere * 512 ] ;
// 0xFFFFFFFF means unallocated
2016-04-19 02:11:47 +01:00
if ( sectorOffset ! = 0xFFFFFFFF )
2015-04-24 01:52:26 +01:00
{
2016-09-05 17:37:31 +01:00
thisStream = thisFilter . GetDataForkStream ( ) ;
2016-07-28 22:25:26 +01:00
thisStream . Seek ( ( sectorOffset * 512 ) , SeekOrigin . Begin ) ;
2015-04-24 01:52:26 +01:00
thisStream . Read ( prefix , 0 , ( int ) ( 512 * sectorsToReadHere ) ) ;
}
// If it is unallocated, just fill with zeroes
else
Array . Clear ( prefix , 0 , prefix . Length ) ;
// If we needed to read from another block, join all the data
2016-04-19 02:11:47 +01:00
if ( suffix ! = null )
2015-04-24 01:52:26 +01:00
{
byte [ ] data = new byte [ 512 * length ] ;
Array . Copy ( prefix , 0 , data , 0 , prefix . Length ) ;
Array . Copy ( suffix , 0 , data , prefix . Length , suffix . Length ) ;
return data ;
}
return prefix ;
2015-04-22 19:24:29 +01:00
}
case typeDifferencing :
{
2015-04-24 03:43:44 +01:00
// As on differencing images, each independent sector can be read from child or parent
// image, we must read sector one by one
byte [ ] fullData = new byte [ 512 * length ] ;
2016-04-19 02:11:47 +01:00
for ( ulong i = 0 ; i < length ; i + + )
2015-04-24 03:43:44 +01:00
{
byte [ ] oneSector = ReadSector ( sectorAddress + i ) ;
Array . Copy ( oneSector , 0 , fullData , ( int ) ( i * 512 ) , 512 ) ;
}
return fullData ;
2015-04-22 19:24:29 +01:00
}
case typeDeprecated1 :
case typeDeprecated2 :
case typeDeprecated3 :
{
throw new ImageNotSupportedException ( "(VirtualPC plugin): Deprecated image type found. Please submit a bug with an example image." ) ;
}
default :
{
2016-07-28 22:25:26 +01:00
throw new ImageNotSupportedException ( string . Format ( "(VirtualPC plugin): Unknown image type {0} found. Please submit a bug with an example image." , thisFooter . diskType ) ) ;
2015-04-22 19:24:29 +01:00
}
}
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
#endregion
#region private methods
2015-04-22 19:32:51 +01:00
2016-07-28 22:25:26 +01:00
static uint VHDChecksum ( byte [ ] data )
2015-04-22 19:24:29 +01:00
{
2016-07-28 22:25:26 +01:00
uint checksum = 0 ;
2016-04-19 02:11:47 +01:00
foreach ( byte b in data )
2015-04-22 19:24:29 +01:00
checksum + = b ;
return ~ checksum ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
#endregion
#region Unsupported features
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetImageComments ( )
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override byte [ ] ReadDiskTag ( MediaTagType tag )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorTag ( ulong sectorAddress , SectorTagType tag )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSector ( ulong sectorAddress , uint track )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorTag ( ulong sectorAddress , uint track , SectorTagType tag )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorsTag ( ulong sectorAddress , uint length , SectorTagType tag )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectors ( ulong sectorAddress , uint length , uint track )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorsTag ( ulong sectorAddress , uint length , uint track , SectorTagType tag )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorLong ( ulong sectorAddress )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorLong ( ulong sectorAddress , uint track )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorsLong ( ulong sectorAddress , uint length )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override byte [ ] ReadSectorsLong ( ulong sectorAddress , uint length , uint track )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override string GetMediaManufacturer ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override string GetMediaModel ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override string GetMediaSerialNumber ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override string GetMediaBarcode ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override string GetMediaPartNumber ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override int GetMediaSequence ( )
2015-04-22 19:24:29 +01:00
{
return 0 ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override int GetLastDiskSequence ( )
{
return 0 ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetDriveManufacturer ( )
{
return null ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetDriveModel ( )
{
return null ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override string GetDriveSerialNumber ( )
{
return null ;
}
2015-04-22 19:32:51 +01:00
2016-07-28 22:25:26 +01:00
public override List < Partition > GetPartitions ( )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-24 04:01:58 +01:00
public override List < Track > GetTracks ( )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-24 04:01:58 +01:00
public override List < Track > GetSessionTracks ( Session session )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-24 04:01:58 +01:00
public override List < Track > GetSessionTracks ( ushort session )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-24 04:01:58 +01:00
public override List < Session > GetSessions ( )
2015-04-22 19:24:29 +01:00
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override bool? VerifySector ( ulong sectorAddress )
{
return null ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override bool? VerifySector ( ulong sectorAddress , uint track )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override bool? VerifySectors ( ulong sectorAddress , uint length , out List < ulong > FailingLBAs , out List < ulong > UnknownLBAs )
{
FailingLBAs = new List < ulong > ( ) ;
UnknownLBAs = new List < ulong > ( ) ;
2016-04-19 02:11:47 +01:00
for ( ulong i = 0 ; i < ImageInfo . sectors ; i + + )
2015-04-22 19:24:29 +01:00
UnknownLBAs . Add ( i ) ;
return null ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
public override bool? VerifySectors ( ulong sectorAddress , uint length , uint track , out List < ulong > FailingLBAs , out List < ulong > UnknownLBAs )
{
throw new FeatureUnsupportedImageException ( "Feature not supported by image format" ) ;
}
2015-04-22 19:32:51 +01:00
2016-01-16 03:54:55 +00:00
public override bool? VerifyMediaImage ( )
2015-04-22 19:24:29 +01:00
{
return null ;
}
2015-04-22 19:32:51 +01:00
2015-04-22 19:24:29 +01:00
#endregion
}
}