public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = Encoding.GetEncoding("koi8-r"); byte[] sector = imagePlugin.ReadSector(0); BootBlock bb = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(sector); var sbInformation = new StringBuilder(); sbInformation.AppendLine("Alexander Osipov DOS file system"); XmlFsType = new FileSystemType { Type = "Alexander Osipov DOS file system", Clusters = imagePlugin.Info.Sectors, ClusterSize = imagePlugin.Info.SectorSize, Files = bb.files, FilesSpecified = true, FreeClusters = imagePlugin.Info.Sectors - bb.usedSectors, FreeClustersSpecified = true, VolumeName = StringHandlers.SpacePaddedToString(bb.volumeLabel, Encoding), Bootable = true }; sbInformation.AppendFormat("{0} files on volume", bb.files).AppendLine(); sbInformation.AppendFormat("{0} used sectors on volume", bb.usedSectors).AppendLine(); sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(bb.volumeLabel, Encoding)). AppendLine(); information = sbInformation.ToString(); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { // Does AO-DOS support hard disks? if (partition.Start > 0) { return(false); } // How is it really? if (imagePlugin.Info.SectorSize != 512) { return(false); } // Does AO-DOS support any other kind of disk? if (imagePlugin.Info.Sectors != 800 && imagePlugin.Info.Sectors != 1600) { return(false); } byte[] sector = imagePlugin.ReadSector(0); BootBlock bb = Marshal.ByteArrayToStructureLittleEndian <BootBlock>(sector); return(bb.identifier.SequenceEqual(_identifier)); }
public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Start >= partition.End) { return(false); } BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; // 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 + partition.Start, 2); BootBlock bblk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector); // AROS boot floppies... if (sector.Length >= 512 && sector[510] == 0x55 && sector[511] == 0xAA && (bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { sector = imagePlugin.ReadSectors(1 + partition.Start, 2); bblk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(sector); } // Not FFS or MuFS? if ((bblk.diskType & FFS_MASK) != FFS_MASK && (bblk.diskType & MUFS_MASK) != MUFS_MASK) { return(false); } // Clear checksum on sector sector[4] = sector[5] = sector[6] = sector[7] = 0; uint bsum = AmigaBootChecksum(sector); DicConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum); DicConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bblk.checksum) { bRootPtr = bblk.root_ptr + partition.Start; DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start - 2, (partition.End - partition.Start + 1) / 2 + partition.Start - 1, (partition.End - partition.Start + 1) / 2 + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start + 4 }; RootBlock rblk = new RootBlock(); // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); sector = imagePlugin.ReadSector(rootPtr); rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); DicConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type); if (rblk.type != TYPE_HEADER) { continue; } rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); 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 (rootPtr + sectorsPerBlock >= partition.End) { continue; } sector = imagePlugin.ReadSectors(rootPtr, 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 == SUBTYPE_ROOT && rblk.checksum == rsum) { return(true); } } return(false); }
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); StringBuilder sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = null; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] bootBlockSectors = imagePlugin.ReadSectors(0 + partition.Start, 2); BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian <BootBlock>(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); ulong bRootPtr = 0; // If bootblock is correct, let's take its rootblock pointer if (bsum == bootBlk.checksum) { bRootPtr = bootBlk.root_ptr + partition.Start; DicConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); } ulong[] rootPtrs = { bRootPtr + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start - 2, (partition.End - partition.Start + 1) / 2 + partition.Start - 1, (partition.End - partition.Start + 1) / 2 + partition.Start, (partition.End - partition.Start + 1) / 2 + partition.Start + 4 }; RootBlock rootBlk = new RootBlock(); byte[] rootBlockSector = null; bool rootFound = false; uint blockSize = 0; // So to handle even number of sectors foreach (ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) { DicConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); rootBlockSector = imagePlugin.ReadSector(rootPtr); rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); if (rootBlk.type != TYPE_HEADER) { continue; } rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); DicConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); blockSize = (rootBlk.hashTableSize + 56) * 4; uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); DicConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); DicConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); if (blockSize % rootBlockSector.Length > 0) { sectorsPerBlock++; } if (rootPtr + sectorsPerBlock >= partition.End) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, 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 != SUBTYPE_ROOT || rootBlk.checksum != rsum) { continue; } rootBlockSector = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock); rootFound = true; break; } if (!rootFound) { return; } rootBlk = MarshalRootBlock(rootBlockSector); string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); switch (bootBlk.diskType & 0xFF) { case 0: sbInformation.Append("Amiga Original File System"); XmlFsType.Type = "Amiga OFS"; break; case 1: sbInformation.Append("Amiga Fast File System"); XmlFsType.Type = "Amiga FFS"; break; case 2: sbInformation.Append("Amiga Original File System with international characters"); XmlFsType.Type = "Amiga OFS"; break; case 3: sbInformation.Append("Amiga Fast File System with international characters"); XmlFsType.Type = "Amiga FFS"; break; case 4: sbInformation.Append("Amiga Original File System with directory cache"); XmlFsType.Type = "Amiga OFS"; break; case 5: sbInformation.Append("Amiga Fast File System with directory cache"); XmlFsType.Type = "Amiga FFS"; break; case 6: sbInformation.Append("Amiga Original File System with long filenames"); XmlFsType.Type = "Amiga OFS2"; break; case 7: sbInformation.Append("Amiga Fast File System with long filenames"); XmlFsType.Type = "Amiga FFS2"; break; } if ((bootBlk.diskType & 0x6D754600) == 0x6D754600) { sbInformation.Append(", with multi-user patches"); } sbInformation.AppendLine(); sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); if (bootBlk.checksum == bsum) { Sha1Context sha1Ctx = new Sha1Context(); 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"); } if (rootBlk.bitmapExtensionBlock != 0x00000000 && rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) { sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); } if ((bootBlk.diskType & 0xFF) == 4 || (bootBlk.diskType & 0xFF) == 5) { sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); } ulong blocks = (partition.End - partition.Start + 1) * imagePlugin.Info.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); XmlFsType.CreationDateSpecified = true; XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); XmlFsType.ModificationDateSpecified = true; XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; XmlFsType.Clusters = blocks; XmlFsType.ClusterSize = blockSize; XmlFsType.VolumeName = diskName; XmlFsType.Bootable = bsum == bootBlk.checksum; // Useful as a serial XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; }
// TODO: BBC Master hard disks are untested... public bool Identify(IMediaImage imagePlugin, Partition partition) { if (partition.Start >= partition.End) { return(false); } ulong sbSector; uint sectorsToRead; if (imagePlugin.Info.SectorSize < 256) { return(false); } byte[] sector; GCHandle ptr; // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions if (partition.Start == 0) { sector = imagePlugin.ReadSector(0); byte oldChk0 = AcornMapChecksum(sector, 255); ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldMapSector0 oldMap0 = (OldMapSector0)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector0)); sector = imagePlugin.ReadSector(1); byte oldChk1 = AcornMapChecksum(sector, 255); ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldMapSector1 oldMap1 = (OldMapSector1)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector1)); DicConsole.DebugWriteLine("ADFS Plugin", "oldMap0.checksum = {0}", oldMap0.checksum); DicConsole.DebugWriteLine("ADFS Plugin", "oldChk0 = {0}", oldChk0); // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) if (oldMap0.checksum == oldChk0 && oldMap1.checksum != oldChk1 && sector.Length >= 512) { sector = imagePlugin.ReadSector(0); byte[] tmp = new byte[256]; Array.Copy(sector, 256, tmp, 0, 256); oldChk1 = AcornMapChecksum(tmp, 255); ptr = GCHandle.Alloc(tmp, GCHandleType.Pinned); oldMap1 = (OldMapSector1)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector1)); } DicConsole.DebugWriteLine("ADFS Plugin", "oldMap1.checksum = {0}", oldMap1.checksum); DicConsole.DebugWriteLine("ADFS Plugin", "oldChk1 = {0}", oldChk1); if (oldMap0.checksum == oldChk0 && oldMap1.checksum == oldChk1 && oldMap0.checksum != 0 && oldMap1.checksum != 0) { sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldDirectory oldRoot = (OldDirectory)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldDirectory)); byte dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x200 = {0}", oldRoot.header.magic); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x200 = {0}", oldRoot.tail.magic); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x200 = {0}", oldRoot.tail.checkByte); DicConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x200 = {0}", dirChk); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC || oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC) { return(true); } // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); oldRoot = (OldDirectory)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldDirectory)); dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x400 = {0}", oldRoot.header.magic); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x400 = {0}", oldRoot.tail.magic); DicConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x400 = {0}", oldRoot.tail.checkByte); DicConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x400 = {0}", dirChk); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC || oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC) { return(true); } } } // Partitioning or not, new formats follow: DiscRecord drSb; sector = imagePlugin.ReadSector(partition.Start); byte newChk = NewMapChecksum(sector); DicConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); DicConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } if (sbSector + partition.Start + sectorsToRead >= partition.End) { return(false); } byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead); int bootChk = 0; for (int i = 0; i < 0x1FF; i++) { bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; } DicConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); DicConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); if (newChk == sector[0] && newChk != 0) { ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); NewMap nmap = (NewMap)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(NewMap)); ptr.Free(); drSb = nmap.discRecord; } else if (bootChk == bootSector[0x1FF]) { ptr = GCHandle.Alloc(bootSector, GCHandleType.Pinned); BootBlock bBlock = (BootBlock)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(BootBlock)); ptr.Free(); drSb = bBlock.discRecord; } else { return(false); } DicConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); DicConsole.DebugWriteLine("ADFS Plugin", "IsNullOrEmpty(drSb.reserved) = {0}", ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)); if (drSb.log2secsize < 8 || drSb.log2secsize > 10) { return(false); } if (drSb.idlen < drSb.log2secsize + 3 || drSb.idlen > 19) { return(false); } if (drSb.disc_size_high >> drSb.log2secsize != 0) { return(false); } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) { return(false); } ulong bytes = drSb.disc_size_high; bytes *= 0x100000000; bytes += drSb.disc_size; return(bytes <= imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize); }
// TODO: Find root directory on volumes with DiscRecord // TODO: Support big directories (ADFS-G?) // TODO: Find the real freemap on volumes with DiscRecord, as DiscRecord's discid may be empty but this one isn't public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); StringBuilder sbInformation = new StringBuilder(); XmlFsType = new FileSystemType(); information = ""; ulong sbSector; byte[] sector; uint sectorsToRead; GCHandle ptr; ulong bytes; // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions if (partition.Start == 0) { sector = imagePlugin.ReadSector(0); byte oldChk0 = AcornMapChecksum(sector, 255); ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldMapSector0 oldMap0 = (OldMapSector0)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector0)); sector = imagePlugin.ReadSector(1); byte oldChk1 = AcornMapChecksum(sector, 255); ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldMapSector1 oldMap1 = (OldMapSector1)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector1)); // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) if (oldMap0.checksum == oldChk0 && oldMap1.checksum != oldChk1 && sector.Length >= 512) { sector = imagePlugin.ReadSector(0); byte[] tmp = new byte[256]; Array.Copy(sector, 256, tmp, 0, 256); oldChk1 = AcornMapChecksum(tmp, 255); ptr = GCHandle.Alloc(tmp, GCHandleType.Pinned); oldMap1 = (OldMapSector1)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldMapSector1)); } if (oldMap0.checksum == oldChk0 && oldMap1.checksum == oldChk1 && oldMap0.checksum != 0 && oldMap1.checksum != 0) { bytes = (ulong)((oldMap0.size[2] << 16) + (oldMap0.size[1] << 8) + oldMap0.size[0]) * 256; byte[] namebytes = new byte[10]; for (int i = 0; i < 5; i++) { namebytes[i * 2] = oldMap0.name[i]; namebytes[i * 2 + 1] = oldMap1.name[i]; } XmlFsType = new FileSystemType { Bootable = oldMap1.boot != 0, // Or not? Clusters = (long)(bytes / imagePlugin.Info.SectorSize), ClusterSize = (int)imagePlugin.Info.SectorSize, Type = "Acorn Advanced Disc Filing System" }; if (ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) { sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); OldDirectory oldRoot = (OldDirectory)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldDirectory)); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) { namebytes = oldRoot.tail.name; } else { // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; if (NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > OLD_DIRECTORY_SIZE) { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); sector = tmp; } ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); oldRoot = (OldDirectory)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(OldDirectory)); if (oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) { namebytes = oldRoot.tail.name; } else { sector = imagePlugin.ReadSectors(sbSector, sectorsToRead); if (sector.Length > NEW_DIRECTORY_SIZE) { byte[] tmp = new byte[NEW_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, NEW_DIRECTORY_SIZE - 41); Array.Copy(sector, sector.Length - 42, tmp, NEW_DIRECTORY_SIZE - 42, 41); sector = tmp; } ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); NewDirectory newRoot = (NewDirectory)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(NewDirectory)); if (newRoot.header.magic == NEW_DIR_MAGIC && newRoot.tail.magic == NEW_DIR_MAGIC) { namebytes = newRoot.tail.title; } } } } sbInformation.AppendLine("Acorn Advanced Disc Filing System"); sbInformation.AppendLine(); sbInformation.AppendFormat("{0} bytes per sector", imagePlugin.Info.SectorSize).AppendLine(); sbInformation.AppendFormat("Volume has {0} bytes", bytes).AppendLine(); sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(namebytes, Encoding)) .AppendLine(); if (oldMap1.discId > 0) { XmlFsType.VolumeSerial = $"{oldMap1.discId:X4}"; sbInformation.AppendFormat("Volume ID: {0:X4}", oldMap1.discId).AppendLine(); } if (!ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) { XmlFsType.VolumeName = StringHandlers.CToString(namebytes, Encoding); } information = sbInformation.ToString(); return; } } // Partitioning or not, new formats follow: DiscRecord drSb; sector = imagePlugin.ReadSector(partition.Start); byte newChk = NewMapChecksum(sector); DicConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); DicConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; if (BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) { sectorsToRead++; } byte[] bootSector = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead); int bootChk = 0; for (int i = 0; i < 0x1FF; i++) { bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; } DicConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); DicConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); if (newChk == sector[0] && newChk != 0) { ptr = GCHandle.Alloc(sector, GCHandleType.Pinned); NewMap nmap = (NewMap)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(NewMap)); ptr.Free(); drSb = nmap.discRecord; } else if (bootChk == bootSector[0x1FF]) { ptr = GCHandle.Alloc(bootSector, GCHandleType.Pinned); BootBlock bBlock = (BootBlock)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(BootBlock)); ptr.Free(); drSb = bBlock.discRecord; } else { return; } DicConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.spt = {0}", drSb.spt); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.heads = {0}", drSb.heads); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.density = {0}", drSb.density); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.log2bpmb = {0}", drSb.log2bpmb); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.skew = {0}", drSb.skew); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.bootoption = {0}", drSb.bootoption); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.lowsector = {0}", drSb.lowsector); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones = {0}", drSb.nzones); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.zone_spare = {0}", drSb.zone_spare); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.root = {0}", drSb.root); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_id = {0}", drSb.disc_id); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_name = {0}", StringHandlers.CToString(drSb.disc_name, Encoding)); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_type = {0}", drSb.disc_type); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.flags = {0}", drSb.flags); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones_high = {0}", drSb.nzones_high); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.format_version = {0}", drSb.format_version); DicConsole.DebugWriteLine("ADFS Plugin", "drSb.root_size = {0}", drSb.root_size); if (drSb.log2secsize < 8 || drSb.log2secsize > 10) { return; } if (drSb.idlen < drSb.log2secsize + 3 || drSb.idlen > 19) { return; } if (drSb.disc_size_high >> drSb.log2secsize != 0) { return; } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) { return; } bytes = drSb.disc_size_high; bytes *= 0x100000000; bytes += drSb.disc_size; ulong zones = drSb.nzones_high; zones *= 0x100000000; zones += drSb.nzones; if (bytes > imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) { return; } XmlFsType = new FileSystemType(); sbInformation.AppendLine("Acorn Advanced Disc Filing System"); sbInformation.AppendLine(); sbInformation.AppendFormat("Version {0}", drSb.format_version).AppendLine(); sbInformation.AppendFormat("{0} bytes per sector", 1 << drSb.log2secsize).AppendLine(); sbInformation.AppendFormat("{0} sectors per track", drSb.spt).AppendLine(); sbInformation.AppendFormat("{0} heads", drSb.heads).AppendLine(); sbInformation.AppendFormat("Density code: {0}", drSb.density).AppendLine(); sbInformation.AppendFormat("Skew: {0}", drSb.skew).AppendLine(); sbInformation.AppendFormat("Boot option: {0}", drSb.bootoption).AppendLine(); // TODO: What the hell is this field refering to? sbInformation.AppendFormat("Root starts at frag {0}", drSb.root).AppendLine(); //sbInformation.AppendFormat("Root is {0} bytes long", drSb.root_size).AppendLine(); sbInformation.AppendFormat("Volume has {0} bytes in {1} zones", bytes, zones).AppendLine(); sbInformation.AppendFormat("Volume flags: 0x{0:X4}", drSb.flags).AppendLine(); if (drSb.disc_id > 0) { XmlFsType.VolumeSerial = $"{drSb.disc_id:X4}"; sbInformation.AppendFormat("Volume ID: {0:X4}", drSb.disc_id).AppendLine(); } if (!ArrayHelpers.ArrayIsNullOrEmpty(drSb.disc_name)) { string discname = StringHandlers.CToString(drSb.disc_name, Encoding); XmlFsType.VolumeName = discname; sbInformation.AppendFormat("Volume name: {0}", discname).AppendLine(); } information = sbInformation.ToString(); XmlFsType.Bootable |= drSb.bootoption != 0; // Or not? XmlFsType.Clusters = (long)(bytes / (ulong)(1 << drSb.log2secsize)); XmlFsType.ClusterSize = 1 << drSb.log2secsize; XmlFsType.Type = "Acorn Advanced Disc Filing System"; }
internal static string GetBootBlockInformation(byte[] bbSector, Encoding encoding) { if (bbSector is null || bbSector.Length < 0x100) { return(null); } BootBlock bb = Marshal.ByteArrayToStructureBigEndian <BootBlock>(bbSector); if (bb.bbID != BB_MAGIC) { return(null); } var sb = new StringBuilder(); sb.AppendLine("Boot Block:"); if ((bb.bbVersion & 0x8000) > 0) { sb.AppendLine("Boot block is in new format."); if ((bb.bbVersion & 0x4000) > 0) { sb.AppendLine("Boot block should be executed."); if ((bb.bbVersion & 0x2000) > 0) { sb. AppendFormat("System heap will be extended by {0} bytes and a {1} fraction of the available RAM", bb.bbSysHeapExtra, bb.bbSysHeapFract).AppendLine(); } } } else if ((bb.bbVersion & 0xFF) == 0x0D) { sb.AppendLine("Boot block should be executed."); } if (bb.bbPageFlags > 0) { sb.AppendLine("Allocate secondary sound buffer at boot."); } else if (bb.bbPageFlags < 0) { sb.AppendLine("Allocate secondary sound and video buffers at boot."); } sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.bbSysName, encoding)).AppendLine(); sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.bbShellName, encoding)). AppendLine(); sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.bbDbg1Name, encoding)). AppendLine(); sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.bbDbg2Name, encoding)). AppendLine(); sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.bbScreenName, encoding)). AppendLine(); sb.AppendFormat("First program to execute at boot: {0}", StringHandlers.PascalToString(bb.bbHelloName, encoding)).AppendLine(); sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.bbScrapName, encoding)). AppendLine(); sb.AppendFormat("Maximum opened files: {0}", bb.bbCntFCBs * 4).AppendLine(); sb.AppendFormat("Event queue size: {0}", bb.bbCntEvts).AppendLine(); sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.bb128KSHeap).AppendLine(); sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.bb256KSHeap).AppendLine(); sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.bbSysHeapSize).AppendLine(); return(sb.ToString()); }