Use marshalling.

Corrected checksum calculation.
	Corrected root block location when boot block is not present
	or correct.
	Tested FFS2 values, no long show warning.
	Correct cluster size calculation.
This commit is contained in:
2017-07-19 05:21:25 +01:00
parent 34d5bb5bae
commit e3ce75b9a7

View File

@@ -34,6 +34,7 @@ using System;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using DiscImageChef.Console; using DiscImageChef.Console;
using System.Runtime.InteropServices;
namespace DiscImageChef.Filesystems namespace DiscImageChef.Filesystems
{ {
@@ -57,6 +58,7 @@ namespace DiscImageChef.Filesystems
/// <summary> /// <summary>
/// Boot block, first 2 sectors /// Boot block, first 2 sectors
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BootBlock struct BootBlock
{ {
/// <summary> /// <summary>
@@ -72,11 +74,13 @@ namespace DiscImageChef.Filesystems
/// </summary> /// </summary>
public uint root_ptr; public uint root_ptr;
/// <summary> /// <summary>
/// Offset 0x0C, Boot code, til completion /// Offset 0x0C, Boot code, til completion. Size is intentionally incorrect to allow marshaling to work.
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] bootCode; public byte[] bootCode;
} }
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct RootBlock struct RootBlock
{ {
/// <summary> /// <summary>
@@ -104,8 +108,10 @@ namespace DiscImageChef.Filesystems
/// </summary> /// </summary>
public uint checksum; public uint checksum;
/// <summary> /// <summary>
/// Offset 0x18, Hashtable, size = (block size / 4) - 56 or size = hashTableSize /// Offset 0x18, Hashtable, size = (block size / 4) - 56 or size = hashTableSize.
/// Size intentionally bad to allow marshalling to work.
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public uint[] hashTable; public uint[] hashTable;
/// <summary> /// <summary>
/// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid /// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid
@@ -114,6 +120,7 @@ namespace DiscImageChef.Filesystems
/// <summary> /// <summary>
/// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries /// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries
/// </summary> /// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
public uint[] bitmapPages; public uint[] bitmapPages;
/// <summary> /// <summary>
/// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block /// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block
@@ -132,9 +139,14 @@ namespace DiscImageChef.Filesystems
/// </summary> /// </summary>
public uint rTicks; public uint rTicks;
/// <summary> /// <summary>
/// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 32 bytes /// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 31 bytes
/// </summary> /// </summary>
public string diskName; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)]
public byte[] diskName;
/// <summary>
/// Offset 0x18+hashTableSize*4+151, unused
/// </summary>
public byte padding;
/// <summary> /// <summary>
/// Offset 0x18+hashTableSize*4+152, unused /// Offset 0x18+hashTableSize*4+152, unused
/// </summary> /// </summary>
@@ -185,6 +197,12 @@ namespace DiscImageChef.Filesystems
public uint sec_type; public uint sec_type;
} }
public const uint FFS_Mask = 0x444F5300;
public const uint MuFS_Mask = 0x6D754600;
public const uint TypeHeader = 2;
public const uint SubTypeRoot = 1;
public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd) public override bool Identify(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd)
{ {
if(partitionStart >= partitionEnd) if(partitionStart >= partitionEnd)
@@ -192,139 +210,183 @@ namespace DiscImageChef.Filesystems
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] sector = imagePlugin.ReadSector(0 + partitionStart); // Boot block is unless defined otherwise, 2 blocks
// Funny, you may need boot block to find root block if it's not in standard place just to know size of
// block size and then read the whole boot block.
// However while you can set a block size different from the sector size on formatting, the bootblock block
// size for floppies is the sector size, and for RDB is usually is the hard disk sector size,
// so this is not entirely wrong...
byte[] sector = imagePlugin.ReadSectors(0 + partitionStart, 2);
BootBlock bblk = BigEndianMarshal.ByteArrayToStructureBigEndian<BootBlock>(sector);
uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); // Not FFS or MuFS?
if((bblk.diskType & FFS_Mask) != FFS_Mask &&
if((magic & 0x6D754600) != 0x6D754600 && (bblk.diskType & MuFS_Mask) != MuFS_Mask)
(magic & 0x444F5300) != 0x444F5300)
return false; return false;
ulong root_ptr = BigEndianBitConverter.ToUInt32(sector, 0x08); // Clear checksum on sector
DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", root_ptr); sector[4] = sector[5] = sector[6] = sector[7] = 0;
uint bsum = AmigaBootChecksum(sector);
if(root_ptr == 0) DicConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum);
DicConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum);
ulong b_root_ptr = 0;
// If bootblock is correct, let's take its rootblock pointer
if(bsum == bblk.checksum)
{ {
root_ptr = (partitionEnd - partitionStart) / 2 + partitionStart; b_root_ptr = bblk.root_ptr + partitionStart;
DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", b_root_ptr);
DicConsole.DebugWriteLine("AmigaDOS plugin", "Nonetheless, going to block {0} for Rootblock", root_ptr);
} }
else
root_ptr += partitionStart;
if(root_ptr >= partitionEnd) ulong[] root_ptrs = { b_root_ptr + partitionStart, ((partitionEnd - partitionStart) + 1) / 2 + partitionStart - 2,
return false; ((partitionEnd - partitionStart) + 1) / 2 + partitionStart - 1, ((partitionEnd - partitionStart) + 1) / 2 + partitionStart};
sector = imagePlugin.ReadSector(root_ptr); RootBlock rblk = new RootBlock();
uint type = BigEndianBitConverter.ToUInt32(sector, 0x00); // So to handle even number of sectors
uint hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); foreach(ulong root_ptr in root_ptrs)
{
if(root_ptr >= partitionEnd || root_ptr < partitionStart)
continue;
uint blockSize = 0x18 + hashTableSize * 4 + 196; DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", root_ptr);
uint sectorsPerBlock = (uint)(blockSize / sector.Length);
if(blockSize % sector.Length > 0)
sectorsPerBlock++;
sector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock); sector = imagePlugin.ReadSector(root_ptr);
//if((0x18 + hashTableSize * 4 + 196) > sector.Length) rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00);
// return false; DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type);
if(rblk.type != TypeHeader)
continue;
uint sec_type = BigEndianBitConverter.ToUInt32(sector, (int)(0x18 + hashTableSize * 4 + 196)); rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C);
return type == 2 && sec_type == 1; DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize);
uint blockSize = (rblk.hashTableSize + 56) * 4;
uint sectorsPerBlock = (uint)(blockSize / sector.Length);
DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);
if(blockSize % sector.Length > 0)
sectorsPerBlock++;
if(root_ptr + sectorsPerBlock >= partitionEnd)
continue;
sector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock);
// Clear checksum on sector
rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20);
sector[20] = sector[21] = sector[22] = sector[23] = 0;
uint rsum = AmigaChecksum(sector);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);
rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type);
if(rblk.sec_type == SubTypeRoot && rblk.checksum == rsum)
return true;
}
return false;
} }
public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd, out string information) public override void GetInformation(ImagePlugins.ImagePlugin imagePlugin, ulong partitionStart, ulong partitionEnd, out string information)
{ {
StringBuilder sbInformation = new StringBuilder(); StringBuilder sbInformation = new StringBuilder();
xmlFSType = new Schemas.FileSystemType();
information = null;
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
byte[] BootBlockSectors = imagePlugin.ReadSectors(0 + partitionStart, 2); byte[] BootBlockSectors = imagePlugin.ReadSectors(0 + partitionStart, 2);
ulong root_ptr = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x08); BootBlock bootBlk = BigEndianMarshal.ByteArrayToStructureBigEndian<BootBlock>(BootBlockSectors);
DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", root_ptr); bootBlk.bootCode = new byte[BootBlockSectors.Length - 12];
Array.Copy(BootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length);
BootBlockSectors[4] = BootBlockSectors[5] = BootBlockSectors[6] = BootBlockSectors[7] = 0;
uint bsum = AmigaBootChecksum(BootBlockSectors);
if(root_ptr == 0) ulong b_root_ptr = 0;
// If bootblock is correct, let's take its rootblock pointer
if(bsum == bootBlk.checksum)
{ {
root_ptr = (partitionEnd - partitionStart) / 2 + partitionStart; b_root_ptr = bootBlk.root_ptr + partitionStart;
DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", b_root_ptr);
DicConsole.DebugWriteLine("AmigaDOS plugin", "Nonetheless, going to block {0} for Rootblock", root_ptr);
} }
else
root_ptr += partitionStart;
byte[] RootBlockSector = imagePlugin.ReadSector(root_ptr); ulong[] root_ptrs = { b_root_ptr + partitionStart, ((partitionEnd - partitionStart) + 1) / 2 + partitionStart - 2,
((partitionEnd - partitionStart) + 1) / 2 + partitionStart - 1, ((partitionEnd - partitionStart) + 1) / 2 + partitionStart};
uint hashTableSize = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x0C);
uint blockSize = 0x18 + hashTableSize * 4 + 196;
uint sectorsPerBlock = (uint)(blockSize / RootBlockSector.Length);
if(blockSize % RootBlockSector.Length > 0)
sectorsPerBlock++;
RootBlockSector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock);
byte[] diskName = new byte[32];
BootBlock bootBlk = new BootBlock();
RootBlock rootBlk = new RootBlock(); RootBlock rootBlk = new RootBlock();
xmlFSType = new Schemas.FileSystemType(); byte[] RootBlockSector = null;
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; bool root_found = false;
uint blockSize = 0;
bootBlk.diskType = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x00); // So to handle even number of sectors
bootBlk.checksum = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x04); foreach(ulong root_ptr in root_ptrs)
bootBlk.root_ptr = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x08); {
bootBlk.bootCode = new byte[BootBlockSectors.Length - 0x0C]; if(root_ptr >= partitionEnd || root_ptr < partitionStart)
Array.Copy(BootBlockSectors, 0x0C, bootBlk.bootCode, 0, BootBlockSectors.Length - 0x0C); continue;
DicConsole.DebugWriteLine("AmigaDOS plugin", "Stored boot blocks checksum is 0x{0:X8}", bootBlk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", root_ptr);
DicConsole.DebugWriteLine("AmigaDOS plugin", "Probably incorrect calculated boot blocks checksum is 0x{0:X8}", AmigaChecksum(RootBlockSector));
Checksums.SHA1Context sha1Ctx = new Checksums.SHA1Context();
sha1Ctx.Init();
sha1Ctx.Update(bootBlk.bootCode);
DicConsole.DebugWriteLine("AmigaDOS plugin", "Boot code SHA1 is {0}", sha1Ctx.End());
rootBlk.type = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x00); RootBlockSector = imagePlugin.ReadSector(root_ptr);
rootBlk.headerKey = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x04);
rootBlk.highSeq = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x08);
rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x0C);
rootBlk.firstData = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x10);
rootBlk.checksum = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x14);
rootBlk.hashTable = new uint[rootBlk.hashTableSize];
for(int i = 0; i < rootBlk.hashTableSize; i++) rootBlk.type = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x00);
rootBlk.hashTable[i] = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x18 + i * 4); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type);
if(rootBlk.type != TypeHeader)
continue;
rootBlk.bitmapFlag = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 0)); rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x0C);
rootBlk.bitmapPages = new uint[25];
for(int i = 0; i < 25; i++) DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize);
rootBlk.bitmapPages[i] = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 4 + i * 4));
rootBlk.bitmapExtensionBlock = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 104)); blockSize = (rootBlk.hashTableSize + 56) * 4;
rootBlk.rDays = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 108)); uint sectorsPerBlock = (uint)(blockSize / RootBlockSector.Length);
rootBlk.rMins = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 112));
rootBlk.rTicks = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 116));
Array.Copy(RootBlockSector, 0x18 + rootBlk.hashTableSize * 4 + 120, diskName, 0, 32); DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize);
rootBlk.diskName = StringHandlers.PascalToString(diskName, CurrentEncoding); DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock);
rootBlk.reserved1 = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 152)); if(blockSize % RootBlockSector.Length > 0)
rootBlk.reserved2 = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 156)); sectorsPerBlock++;
rootBlk.vDays = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 160));
rootBlk.vMins = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 164));
rootBlk.vTicks = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 168));
rootBlk.cDays = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 172));
rootBlk.cMins = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 176));
rootBlk.cTicks = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 180));
rootBlk.nextHash = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 184));
rootBlk.parentDir = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 188));
rootBlk.extension = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 192));
rootBlk.sec_type = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 196));
DicConsole.DebugWriteLine("AmigaDOS plugin", "Stored root block checksum is 0x{0:X8}", rootBlk.checksum); if(root_ptr + sectorsPerBlock >= partitionEnd)
DicConsole.DebugWriteLine("AmigaDOS plugin", "Probably incorrect calculated root block checksum is 0x{0:X8}", AmigaChecksum(RootBlockSector)); continue;
RootBlockSector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock);
// Clear checksum on sector
rootBlk.checksum = BigEndianBitConverter.ToUInt32(RootBlockSector, 20);
RootBlockSector[20] = RootBlockSector[21] = RootBlockSector[22] = RootBlockSector[23] = 0;
uint rsum = AmigaChecksum(RootBlockSector);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum);
rootBlk.sec_type = BigEndianBitConverter.ToUInt32(RootBlockSector, RootBlockSector.Length - 4);
DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type);
if(rootBlk.sec_type == SubTypeRoot && rootBlk.checksum == rsum)
{
RootBlockSector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock);
root_found = true;
break;
}
}
if(!root_found)
return;
rootBlk = MarshalRootBlock(RootBlockSector);
string diskName = StringHandlers.PascalToString(rootBlk.diskName, CurrentEncoding);
switch(bootBlk.diskType & 0xFF) switch(bootBlk.diskType & 0xFF)
{ {
@@ -354,11 +416,11 @@ namespace DiscImageChef.Filesystems
break; break;
case 6: case 6:
sbInformation.Append("Amiga Original File System with long filenames"); sbInformation.Append("Amiga Original File System with long filenames");
xmlFSType.Type = "Amiga OFS"; xmlFSType.Type = "Amiga OFS2";
break; break;
case 7: case 7:
sbInformation.Append("Amiga Fast File System with long filenames"); sbInformation.Append("Amiga Fast File System with long filenames");
xmlFSType.Type = "Amiga FFS"; xmlFSType.Type = "Amiga FFS2";
break; break;
} }
@@ -367,13 +429,16 @@ namespace DiscImageChef.Filesystems
sbInformation.AppendLine(); sbInformation.AppendLine();
if((bootBlk.diskType & 0xFF) == 6 || (bootBlk.diskType & 0xFF) == 7) sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine();
{
sbInformation.AppendLine("AFFS v2, following information may be completely incorrect or garbage.");
xmlFSType.Type = "Amiga FFS2";
}
sbInformation.AppendFormat("Volume name: {0}", rootBlk.diskName).AppendLine(); if(bootBlk.checksum == bsum)
{
Checksums.SHA1Context sha1Ctx = new Checksums.SHA1Context();
sha1Ctx.Init();
sha1Ctx.Update(bootBlk.bootCode);
sbInformation.AppendLine("Volume is bootable");
sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine();
}
if(rootBlk.bitmapFlag == 0xFFFFFFFF) if(rootBlk.bitmapFlag == 0xFFFFFFFF)
sbInformation.AppendLine("Volume bitmap is valid"); sbInformation.AppendLine("Volume bitmap is valid");
@@ -384,10 +449,14 @@ namespace DiscImageChef.Filesystems
if((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) if((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5)
sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine();
long blocks = (long)((((partitionEnd - partitionStart) + 1) * imagePlugin.ImageInfo.sectorSize) / blockSize);
sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine();
sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine();
sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)).AppendLine(); sbInformation.AppendFormat("Volume created on {0}", DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)).AppendLine();
sbInformation.AppendFormat("Volume last modified on {0}", DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)).AppendLine(); sbInformation.AppendFormat("Volume last modified on {0}", DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)).AppendLine();
sbInformation.AppendFormat("Volume root directory last modified on on {0}", DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)).AppendLine(); sbInformation.AppendFormat("Volume root directory last modified on on {0}", DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)).AppendLine();
sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine();
information = sbInformation.ToString(); information = sbInformation.ToString();
xmlFSType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); xmlFSType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks);
@@ -395,19 +464,50 @@ namespace DiscImageChef.Filesystems
xmlFSType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); xmlFSType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks);
xmlFSType.ModificationDateSpecified = true; xmlFSType.ModificationDateSpecified = true;
xmlFSType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; xmlFSType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF;
xmlFSType.Clusters = (long)((partitionEnd - partitionStart) + 1); xmlFSType.Clusters = blocks;
xmlFSType.ClusterSize = (int)imagePlugin.GetSectorSize(); xmlFSType.ClusterSize = (int)blockSize;
xmlFSType.VolumeName = diskName;
xmlFSType.Bootable = bsum == bootBlk.checksum;
// Useful as a serial
xmlFSType.VolumeSerial = string.Format("{0:X8}", rootBlk.checksum);
}
static RootBlock MarshalRootBlock(byte[] block)
{
byte[] tmp = new byte[228];
Array.Copy(block, 0, tmp, 0, 24);
Array.Copy(block, block.Length - 200, tmp, 28, 200);
RootBlock root = BigEndianMarshal.ByteArrayToStructureBigEndian<RootBlock>(tmp);
root.hashTable = new uint[(block.Length - 224) / 4];
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
for(int i = 0; i < root.hashTable.Length; i++)
root.hashTable[i] = BigEndianBitConverter.ToUInt32(block, 24 + i * 4);
return root;
} }
static uint AmigaChecksum(byte[] data) static uint AmigaChecksum(byte[] data)
{ {
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
uint sum = 0; uint sum = 0;
for(int i = 0; i < data.Length; i += 4) for(int i = 0; i < data.Length; i += 4)
sum += BigEndianBitConverter.ToUInt32(data, i); sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3]);
return sum; return (uint)-sum;
}
static uint AmigaBootChecksum(byte[] data)
{
uint sum, psum;
sum = 0;
for(int i = 0; i < data.Length; i+=4) {
psum = sum;
if((sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3])) < psum)
sum++;
}
return ~sum;
} }
public override Errno Mount() public override Errno Mount()