diff --git a/DiscImageChef.Filesystems/AmigaDOS.cs b/DiscImageChef.Filesystems/AmigaDOS.cs index b79f837cf..f2797a9e9 100644 --- a/DiscImageChef.Filesystems/AmigaDOS.cs +++ b/DiscImageChef.Filesystems/AmigaDOS.cs @@ -34,6 +34,7 @@ using System; using System.Text; using System.Collections.Generic; using DiscImageChef.Console; +using System.Runtime.InteropServices; namespace DiscImageChef.Filesystems { @@ -57,6 +58,7 @@ namespace DiscImageChef.Filesystems /// /// Boot block, first 2 sectors /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct BootBlock { /// @@ -72,11 +74,13 @@ namespace DiscImageChef.Filesystems /// public uint root_ptr; /// - /// Offset 0x0C, Boot code, til completion + /// Offset 0x0C, Boot code, til completion. Size is intentionally incorrect to allow marshaling to work. /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public byte[] bootCode; } + [StructLayout(LayoutKind.Sequential, Pack = 1)] struct RootBlock { /// @@ -104,8 +108,10 @@ namespace DiscImageChef.Filesystems /// public uint checksum; /// - /// 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. /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public uint[] hashTable; /// /// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid @@ -114,6 +120,7 @@ namespace DiscImageChef.Filesystems /// /// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)] public uint[] bitmapPages; /// /// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block @@ -132,9 +139,14 @@ namespace DiscImageChef.Filesystems /// public uint rTicks; /// - /// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 32 bytes + /// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 31 bytes /// - public string diskName; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] + public byte[] diskName; + /// + /// Offset 0x18+hashTableSize*4+151, unused + /// + public byte padding; /// /// Offset 0x18+hashTableSize*4+152, unused /// @@ -185,6 +197,12 @@ namespace DiscImageChef.Filesystems 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) { if(partitionStart >= partitionEnd) @@ -192,139 +210,183 @@ namespace DiscImageChef.Filesystems 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(sector); - uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); - - if((magic & 0x6D754600) != 0x6D754600 && - (magic & 0x444F5300) != 0x444F5300) + // Not FFS or MuFS? + if((bblk.diskType & FFS_Mask) != FFS_Mask && + (bblk.diskType & MuFS_Mask) != MuFS_Mask) return false; - ulong root_ptr = BigEndianBitConverter.ToUInt32(sector, 0x08); - DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", root_ptr); + // Clear checksum on sector + 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; - - DicConsole.DebugWriteLine("AmigaDOS plugin", "Nonetheless, going to block {0} for Rootblock", root_ptr); + b_root_ptr = bblk.root_ptr + partitionStart; + DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", b_root_ptr); } - else - root_ptr += partitionStart; - if(root_ptr >= partitionEnd) - return false; + ulong[] root_ptrs = { b_root_ptr + partitionStart, ((partitionEnd - partitionStart) + 1) / 2 + partitionStart - 2, + ((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); - uint hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); + // So to handle even number of sectors + foreach(ulong root_ptr in root_ptrs) + { + if(root_ptr >= partitionEnd || root_ptr < partitionStart) + continue; - uint blockSize = 0x18 + hashTableSize * 4 + 196; - uint sectorsPerBlock = (uint)(blockSize / sector.Length); - if(blockSize % sector.Length > 0) - sectorsPerBlock++; + DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", root_ptr); - sector = imagePlugin.ReadSectors(root_ptr, sectorsPerBlock); + sector = imagePlugin.ReadSector(root_ptr); - //if((0x18 + hashTableSize * 4 + 196) > sector.Length) - // return false; + rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); + 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) { StringBuilder sbInformation = new StringBuilder(); + xmlFSType = new Schemas.FileSystemType(); + information = null; + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] BootBlockSectors = imagePlugin.ReadSectors(0 + partitionStart, 2); - ulong root_ptr = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x08); - DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", root_ptr); + BootBlock bootBlk = BigEndianMarshal.ByteArrayToStructureBigEndian(BootBlockSectors); + 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; - - DicConsole.DebugWriteLine("AmigaDOS plugin", "Nonetheless, going to block {0} for Rootblock", root_ptr); + b_root_ptr = bootBlk.root_ptr + partitionStart; + DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", b_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(); - xmlFSType = new Schemas.FileSystemType(); + byte[] RootBlockSector = null; - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + bool root_found = false; + uint blockSize = 0; - bootBlk.diskType = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x00); - bootBlk.checksum = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x04); - bootBlk.root_ptr = BigEndianBitConverter.ToUInt32(BootBlockSectors, 0x08); - bootBlk.bootCode = new byte[BootBlockSectors.Length - 0x0C]; - Array.Copy(BootBlockSectors, 0x0C, bootBlk.bootCode, 0, BootBlockSectors.Length - 0x0C); + // So to handle even number of sectors + foreach(ulong root_ptr in root_ptrs) + { + if(root_ptr >= partitionEnd || root_ptr < partitionStart) + continue; - DicConsole.DebugWriteLine("AmigaDOS plugin", "Stored boot blocks checksum is 0x{0:X8}", bootBlk.checksum); - 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()); + DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", root_ptr); - rootBlk.type = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x00); - 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]; + RootBlockSector = imagePlugin.ReadSector(root_ptr); - for(int i = 0; i < rootBlk.hashTableSize; i++) - rootBlk.hashTable[i] = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x18 + i * 4); + rootBlk.type = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x00); + 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.bitmapPages = new uint[25]; + rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(RootBlockSector, 0x0C); - for(int i = 0; i < 25; i++) - rootBlk.bitmapPages[i] = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 4 + i * 4)); + DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); - rootBlk.bitmapExtensionBlock = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 104)); - rootBlk.rDays = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 108)); - rootBlk.rMins = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 112)); - rootBlk.rTicks = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 116)); + blockSize = (rootBlk.hashTableSize + 56) * 4; + uint sectorsPerBlock = (uint)(blockSize / RootBlockSector.Length); - Array.Copy(RootBlockSector, 0x18 + rootBlk.hashTableSize * 4 + 120, diskName, 0, 32); - rootBlk.diskName = StringHandlers.PascalToString(diskName, CurrentEncoding); + DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); + DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); - rootBlk.reserved1 = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 152)); - rootBlk.reserved2 = BigEndianBitConverter.ToUInt32(RootBlockSector, (int)(0x18 + rootBlk.hashTableSize * 4 + 156)); - 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)); + if(blockSize % RootBlockSector.Length > 0) + sectorsPerBlock++; - DicConsole.DebugWriteLine("AmigaDOS plugin", "Stored root block checksum is 0x{0:X8}", rootBlk.checksum); - DicConsole.DebugWriteLine("AmigaDOS plugin", "Probably incorrect calculated root block checksum is 0x{0:X8}", AmigaChecksum(RootBlockSector)); + if(root_ptr + sectorsPerBlock >= partitionEnd) + 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) { @@ -354,11 +416,11 @@ namespace DiscImageChef.Filesystems break; case 6: sbInformation.Append("Amiga Original File System with long filenames"); - xmlFSType.Type = "Amiga OFS"; + xmlFSType.Type = "Amiga OFS2"; break; case 7: sbInformation.Append("Amiga Fast File System with long filenames"); - xmlFSType.Type = "Amiga FFS"; + xmlFSType.Type = "Amiga FFS2"; break; } @@ -367,13 +429,16 @@ namespace DiscImageChef.Filesystems sbInformation.AppendLine(); - if((bootBlk.diskType & 0xFF) == 6 || (bootBlk.diskType & 0xFF) == 7) - { - sbInformation.AppendLine("AFFS v2, following information may be completely incorrect or garbage."); - xmlFSType.Type = "Amiga FFS2"; - } + sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); - 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) sbInformation.AppendLine("Volume bitmap is valid"); @@ -384,10 +449,14 @@ namespace DiscImageChef.Filesystems if((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) 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 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("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); information = sbInformation.ToString(); 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.ModificationDateSpecified = true; xmlFSType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; - xmlFSType.Clusters = (long)((partitionEnd - partitionStart) + 1); - xmlFSType.ClusterSize = (int)imagePlugin.GetSectorSize(); + xmlFSType.Clusters = blocks; + 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(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) { - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; uint sum = 0; 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()