Implemented support for Virtual PC differencing (undo) disk

images.
This commit is contained in:
2015-04-24 03:43:44 +01:00
parent d2c51e62df
commit 5f06fbe855
3 changed files with 198 additions and 12 deletions

View File

@@ -1,3 +1,9 @@
2015-04-24 Natalia Portillo <claunia@claunia.com>
* ImagePlugins/VHD.cs:
Implemented support for Virtual PC differencing (undo) disk
images.
2015-04-24 Natalia Portillo <claunia@claunia.com>
* ImagePlugins/VHD.cs:

View File

@@ -354,15 +354,14 @@ namespace DiscImageChef.ImagePlugins
#region Internal variables
HardDiskFooter thisFooter;
HardDiskFooter parentFooter;
DynamicDiskHeader thisDynamic;
DynamicDiskHeader parentDynamic;
DateTime thisDateTime;
DateTime parentDateTime;
string thisPath;
string parentPath;
UInt32[] blockAllocationTable;
UInt32 bitmapSize;
byte[][] locatorEntriesData;
ImagePlugin parentImage;
#endregion
@@ -785,9 +784,9 @@ namespace DiscImageChef.ImagePlugins
}*/
// Get the roundest number of sectors needed to store the block bitmap
bitmapSize = (uint)Math.Ceiling((double)(
bitmapSize = (uint)Math.Ceiling((
// How many sectors do a block store
(thisDynamic.blockSize / 512)
((double)thisDynamic.blockSize / 512)
// 1 bit per sector on the bitmap
/ 8
// and aligned to 512 byte boundary
@@ -807,8 +806,106 @@ namespace DiscImageChef.ImagePlugins
}
case typeDifferencing:
{
throw new NotImplementedException("(VirtualPC plugin): Differencing disk images not yet supported");
locatorEntriesData = new byte[8][];
for (int i = 0; i < 8; i++)
{
if (thisDynamic.locatorEntries[i].platformCode != 0x00000000)
{
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);
if (MainClass.isDebug)
{
switch (thisDynamic.locatorEntries[i].platformCode)
{
case platformCodeWindowsAbsolute:
case platformCodeWindowsRelative:
Console.WriteLine("DEBUG (VirtualPC plugin): dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.ASCII.GetString(locatorEntriesData[i]));
break;
case platformCodeWindowsAbsoluteU:
case platformCodeWindowsRelativeU:
Console.WriteLine("DEBUG (VirtualPC plugin): dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.BigEndianUnicode.GetString(locatorEntriesData[i]));
break;
case platformCodeMacintoshURI:
Console.WriteLine("DEBUG (VirtualPC plugin): dynamic.locatorEntries[{0}] = \"{1}\"", i, Encoding.UTF8.GetString(locatorEntriesData[i]));
break;
default:
Console.WriteLine("DEBUG (VirtualPC plugin): dynamic.locatorEntries[{0}] =", i);
PrintHex.PrintHexArray(locatorEntriesData[i], 64);
break;
}
}
}
}
int currentLocator = 0;
bool locatorFound = false;
string parentPath = null;
while (!locatorFound && currentLocator < 8)
{
switch (thisDynamic.locatorEntries[currentLocator].platformCode)
{
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]));
if (parentPath.StartsWith("file://localhost", StringComparison.InvariantCulture))
parentPath = parentPath.Remove(0, 16);
else
{
if (MainClass.isDebug)
Console.WriteLine("DEBUG (VirtualPC plugin) Unsupported protocol classified found in URI parent path: \"{0}\"", parentPath);
parentPath = null;
}
break;
}
if (parentPath != null)
{
if (MainClass.isDebug)
Console.WriteLine("DEBUG (VirtualPC plugin) Possible parent path: \"{0}\"", parentPath);
locatorFound |= File.Exists(parentPath);
if (!locatorFound)
parentPath = null;
}
currentLocator++;
}
if (!locatorFound || parentPath == null)
throw new FileNotFoundException("(VirtualPC plugin): Cannot find parent file for differencing disk image");
else
{
PluginBase plugins = new PluginBase();
plugins.RegisterAllPlugins();
if (!plugins.ImagePluginsList.TryGetValue(Name.ToLower(), out parentImage))
throw new SystemException("(VirtualPC plugin): Unable to open myself");
if (!parentImage.IdentifyImage(parentPath))
throw new ImageNotSupportedException("(VirtualPC plugin): Parent image is not a Virtual PC disk image");
if (!parentImage.OpenImage(parentPath))
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.
if (parentImage.GetSectors() != GetSectors())
throw new ImageNotSupportedException("(VirtualPC plugin): Parent image is of different size");
}
return true;
}
case typeDeprecated1:
case typeDeprecated2:
@@ -900,17 +997,90 @@ namespace DiscImageChef.ImagePlugins
public override byte[] ReadSector(ulong sectorAddress)
{
return ReadSectors(sectorAddress, 1);
switch (thisFooter.diskType)
{
case typeDifferencing:
{
// Block number for BAT searching
UInt32 blockNumber = (uint)Math.Floor((double)(sectorAddress / (thisDynamic.blockSize / 512)));
// Sector number inside of block
UInt32 sectorInBlock = (uint)(sectorAddress % (thisDynamic.blockSize / 512));
byte[] bitmap = new byte[bitmapSize * 512];
// Offset of block in file
UInt32 blockOffset = blockAllocationTable[blockNumber] * 512;
int bitmapByte = (int)Math.Floor((double)sectorInBlock / 8);
int bitmapBit = (int)(sectorInBlock % 8);
FileStream thisStream = new FileStream(thisPath, FileMode.Open, FileAccess.Read);
thisStream.Seek(blockOffset, SeekOrigin.Begin);
thisStream.Read(bitmap, 0, (int)bitmapSize * 512);
thisStream.Close();
byte mask = (byte)(1 << (7 - bitmapBit));
bool dirty = false || (bitmap[bitmapByte] & mask) == mask;
/*
if (MainClass.isDebug)
{
Console.WriteLine("DEBUG (VirtualPC plugin): bitmapSize = {0}", bitmapSize);
Console.WriteLine("DEBUG (VirtualPC plugin): blockNumber = {0}", blockNumber);
Console.WriteLine("DEBUG (VirtualPC plugin): sectorInBlock = {0}", sectorInBlock);
Console.WriteLine("DEBUG (VirtualPC plugin): blockOffset = {0}", blockOffset);
Console.WriteLine("DEBUG (VirtualPC plugin): bitmapByte = {0}", bitmapByte);
Console.WriteLine("DEBUG (VirtualPC plugin): bitmapBit = {0}", bitmapBit);
Console.WriteLine("DEBUG (VirtualPC plugin): mask = 0x{0:X2}", mask);
Console.WriteLine("DEBUG (VirtualPC plugin): dirty = 0x{0}", dirty);
Console.WriteLine("DEBUG (VirtualPC plugin): bitmap = ");
PrintHex.PrintHexArray(bitmap, 64);
}
*/
// Sector has been written, read from child image
if (dirty)
{
/* Too noisy
if (MainClass.isDebug)
Console.WriteLine("DEBUG (VirtualPC plugin): Sector {0} is dirty", sectorAddress);
*/
byte[] data = new byte[512];
UInt32 sectorOffset = blockAllocationTable[blockNumber] + bitmapSize + sectorInBlock;
thisStream = new FileStream(thisPath, FileMode.Open, FileAccess.Read);
thisStream.Seek((long)(sectorOffset * 512), SeekOrigin.Begin);
thisStream.Read(data, 0, 512);
thisStream.Close();
return data;
}
/* Too noisy
if (MainClass.isDebug)
Console.WriteLine("DEBUG (VirtualPC plugin): Sector {0} is clean", sectorAddress);
*/
// Read sector from parent image
return parentImage.ReadSector(sectorAddress);
}
default:
return ReadSectors(sectorAddress, 1);
}
}
public override byte[] ReadSectors(ulong sectorAddress, uint length)
{
FileStream thisStream;
switch (thisFooter.diskType)
{
case typeFixed:
{
FileStream thisStream;
byte[] data = new byte[512 * length];
thisStream = new FileStream(thisPath, FileMode.Open, FileAccess.Read);
@@ -926,6 +1096,8 @@ namespace DiscImageChef.ImagePlugins
// as long as it is in the block.
case typeDynamic:
{
FileStream thisStream;
// Block number for BAT searching
UInt32 blockNumber = (uint)Math.Floor((double)(sectorAddress / (thisDynamic.blockSize / 512)));
// Sector number inside of block
@@ -979,7 +1151,15 @@ namespace DiscImageChef.ImagePlugins
}
case typeDifferencing:
{
throw new NotImplementedException("(VirtualPC plugin): Differencing disk images not yet supported");
// 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];
for (ulong i = 0; i < length; i++)
{
byte[] oneSector = ReadSector(sectorAddress + i);
Array.Copy(oneSector, 0, fullData, (int)(i * 512), 512);
}
return fullData;
}
case typeDeprecated1:
case typeDeprecated2:

View File

@@ -37,7 +37,7 @@ Supported disk image formats
* TeleDisk (without compression)
* Nero Burning ROM (both image formats)
* Apple 2IMG (used with Apple // emulators)
* Virtual PC fixed size disk images
* Virtual PC fixed size, dynamic size and differencing (undo) disk images
Supported partitioning schemes
==============================