// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : Atari.cs // Author(s) : Natalia Portillo // // Component : Partitioning scheme plugins. // // --[ Description ] ---------------------------------------------------------- // // Manages Atari ST GEMDOS partitions. // // --[ 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 . // // ---------------------------------------------------------------------------- // Copyright © 2011-2019 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Text; using DiscImageChef.Checksums; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Interfaces; using DiscImageChef.Console; namespace DiscImageChef.Partitions { public class AtariPartitions : IPartition { const uint TypeGEMDOS = 0x0047454D; const uint TypeBigGEMDOS = 0x0042474D; const uint TypeExtended = 0x0058474D; const uint TypeLinux = 0x004C4E58; const uint TypeSwap = 0x00535750; const uint TypeRAW = 0x00524157; const uint TypeNetBSD = 0x004E4244; const uint TypeNetBSDSwap = 0x004E4253; const uint TypeSysV = 0x00554E58; const uint TypeMac = 0x004D4143; const uint TypeMinix = 0x004D4958; const uint TypeMinix2 = 0x004D4E58; public string Name => "Atari partitions"; public Guid Id => new Guid("d1dd0f24-ec39-4c4d-9072-be31919a3b5e"); public string Author => "Natalia Portillo"; public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { partitions = new List(); if(imagePlugin.Info.SectorSize < 512) return false; BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; byte[] sector = imagePlugin.ReadSector(sectorOffset); AtariTable table = new AtariTable { boot = new byte[342], icdEntries = new AtariEntry[8], unused = new byte[12], entries = new AtariEntry[4] }; Array.Copy(sector, 0, table.boot, 0, 342); for(int i = 0; i < 8; i++) { table.icdEntries[i].type = BigEndianBitConverter.ToUInt32(sector, 342 + i * 12 + 0); table.icdEntries[i].start = BigEndianBitConverter.ToUInt32(sector, 342 + i * 12 + 4); table.icdEntries[i].length = BigEndianBitConverter.ToUInt32(sector, 342 + i * 12 + 8); } Array.Copy(sector, 438, table.unused, 0, 12); table.size = BigEndianBitConverter.ToUInt32(sector, 450); for(int i = 0; i < 4; i++) { table.entries[i].type = BigEndianBitConverter.ToUInt32(sector, 454 + i * 12 + 0); table.entries[i].start = BigEndianBitConverter.ToUInt32(sector, 454 + i * 12 + 4); table.entries[i].length = BigEndianBitConverter.ToUInt32(sector, 454 + i * 12 + 8); } table.badStart = BigEndianBitConverter.ToUInt32(sector, 502); table.badLength = BigEndianBitConverter.ToUInt32(sector, 506); table.checksum = BigEndianBitConverter.ToUInt16(sector, 510); Sha1Context sha1Ctx = new Sha1Context(); sha1Ctx.Update(table.boot); DicConsole.DebugWriteLine("Atari partition plugin", "Boot code SHA1: {0}", sha1Ctx.End()); for(int i = 0; i < 8; i++) { DicConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].flag = 0x{1:X2}", i, (table.icdEntries[i].type & 0xFF000000) >> 24); DicConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].type = 0x{1:X6}", i, table.icdEntries[i].type & 0x00FFFFFF); DicConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].start = {1}", i, table.icdEntries[i].start); DicConsole.DebugWriteLine("Atari partition plugin", "table.icdEntries[{0}].length = {1}", i, table.icdEntries[i].length); } DicConsole.DebugWriteLine("Atari partition plugin", "table.size = {0}", table.size); for(int i = 0; i < 4; i++) { DicConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].flag = 0x{1:X2}", i, (table.entries[i].type & 0xFF000000) >> 24); DicConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].type = 0x{1:X6}", i, table.entries[i].type & 0x00FFFFFF); DicConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].start = {1}", i, table.entries[i].start); DicConsole.DebugWriteLine("Atari partition plugin", "table.entries[{0}].length = {1}", i, table.entries[i].length); } DicConsole.DebugWriteLine("Atari partition plugin", "table.badStart = {0}", table.badStart); DicConsole.DebugWriteLine("Atari partition plugin", "table.badLength = {0}", table.badLength); DicConsole.DebugWriteLine("Atari partition plugin", "table.checksum = 0x{0:X4}", table.checksum); bool validTable = false; ulong partitionSequence = 0; for(int i = 0; i < 4; i++) { uint type = table.entries[i].type & 0x00FFFFFF; switch(type) { case TypeGEMDOS: case TypeBigGEMDOS: case TypeLinux: case TypeSwap: case TypeRAW: case TypeNetBSD: case TypeNetBSDSwap: case TypeSysV: case TypeMac: case TypeMinix: case TypeMinix2: validTable = true; if(table.entries[i].start <= imagePlugin.Info.Sectors) { if(table.entries[i].start + table.entries[i].length > imagePlugin.Info.Sectors) DicConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); ulong sectorSize = imagePlugin.Info.SectorSize; if(sectorSize == 2448 || sectorSize == 2352) sectorSize = 2048; byte[] partType = new byte[3]; partType[0] = (byte)((type & 0xFF0000) >> 16); partType[1] = (byte)((type & 0x00FF00) >> 8); partType[2] = (byte)(type & 0x0000FF); Partition part = new Partition { Size = table.entries[i].length * sectorSize, Length = table.entries[i].length, Sequence = partitionSequence, Name = "", Offset = table.entries[i].start * sectorSize, Start = table.entries[i].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch(type) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } break; case TypeExtended: byte[] extendedSector = imagePlugin.ReadSector(table.entries[i].start); AtariTable extendedTable = new AtariTable(); extendedTable.entries = new AtariEntry[4]; for(int j = 0; j < 4; j++) { extendedTable.entries[j].type = BigEndianBitConverter.ToUInt32(extendedSector, 454 + j * 12 + 0); extendedTable.entries[j].start = BigEndianBitConverter.ToUInt32(extendedSector, 454 + j * 12 + 4); extendedTable.entries[j].length = BigEndianBitConverter.ToUInt32(extendedSector, 454 + j * 12 + 8); } for(int j = 0; j < 4; j++) { uint extendedType = extendedTable.entries[j].type & 0x00FFFFFF; if(extendedType != TypeGEMDOS && extendedType != TypeBigGEMDOS && extendedType != TypeLinux && extendedType != TypeSwap && extendedType != TypeRAW && extendedType != TypeNetBSD && extendedType != TypeNetBSDSwap && extendedType != TypeSysV && extendedType != TypeMac && extendedType != TypeMinix && extendedType != TypeMinix2) continue; validTable = true; if(extendedTable.entries[j].start > imagePlugin.Info.Sectors) continue; if(extendedTable.entries[j].start + extendedTable.entries[j].length > imagePlugin.Info.Sectors) DicConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); ulong sectorSize = imagePlugin.Info.SectorSize; if(sectorSize == 2448 || sectorSize == 2352) sectorSize = 2048; byte[] partType = new byte[3]; partType[0] = (byte)((extendedType & 0xFF0000) >> 16); partType[1] = (byte)((extendedType & 0x00FF00) >> 8); partType[2] = (byte)(extendedType & 0x0000FF); Partition part = new Partition { Size = extendedTable.entries[j].length * sectorSize, Length = extendedTable.entries[j].length, Sequence = partitionSequence, Name = "", Offset = extendedTable.entries[j].start * sectorSize, Start = extendedTable.entries[j].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch(extendedType) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } break; } } if(!validTable) return partitions.Count > 0; for(int i = 0; i < 8; i++) { uint type = table.icdEntries[i].type & 0x00FFFFFF; if(type != TypeGEMDOS && type != TypeBigGEMDOS && type != TypeLinux && type != TypeSwap && type != TypeRAW && type != TypeNetBSD && type != TypeNetBSDSwap && type != TypeSysV && type != TypeMac && type != TypeMinix && type != TypeMinix2) continue; if(table.icdEntries[i].start > imagePlugin.Info.Sectors) continue; if(table.icdEntries[i].start + table.icdEntries[i].length > imagePlugin.Info.Sectors) DicConsole.DebugWriteLine("Atari partition plugin", "WARNING: End of partition goes beyond device size"); ulong sectorSize = imagePlugin.Info.SectorSize; if(sectorSize == 2448 || sectorSize == 2352) sectorSize = 2048; byte[] partType = new byte[3]; partType[0] = (byte)((type & 0xFF0000) >> 16); partType[1] = (byte)((type & 0x00FF00) >> 8); partType[2] = (byte)(type & 0x0000FF); Partition part = new Partition { Size = table.icdEntries[i].length * sectorSize, Length = table.icdEntries[i].length, Sequence = partitionSequence, Name = "", Offset = table.icdEntries[i].start * sectorSize, Start = table.icdEntries[i].start, Type = Encoding.ASCII.GetString(partType), Scheme = Name }; switch(type) { case TypeGEMDOS: part.Description = "Atari GEMDOS partition"; break; case TypeBigGEMDOS: part.Description = "Atari GEMDOS partition bigger than 32 MiB"; break; case TypeLinux: part.Description = "Linux partition"; break; case TypeSwap: part.Description = "Swap partition"; break; case TypeRAW: part.Description = "RAW partition"; break; case TypeNetBSD: part.Description = "NetBSD partition"; break; case TypeNetBSDSwap: part.Description = "NetBSD swap partition"; break; case TypeSysV: part.Description = "Atari UNIX partition"; break; case TypeMac: part.Description = "Macintosh partition"; break; case TypeMinix: case TypeMinix2: part.Description = "MINIX partition"; break; default: part.Description = "Unknown partition type"; break; } partitions.Add(part); partitionSequence++; } return partitions.Count > 0; } /// /// Atari partition entry /// struct AtariEntry { /// /// First byte flag, three bytes type in ASCII. /// Flag bit 0 = active /// Flag bit 7 = bootable /// public uint type; /// /// Starting sector /// public uint start; /// /// Length in sectors /// public uint length; } struct AtariTable { /// /// Boot code for 342 bytes /// public byte[] boot; /// /// 8 extra entries for ICDPro driver /// public AtariEntry[] icdEntries; /// /// Unused, 12 bytes /// public byte[] unused; /// /// Disk size in sectors /// public uint size; /// /// 4 partition entries /// public AtariEntry[] entries; /// /// Starting sector of bad block list /// public uint badStart; /// /// Length in sectors of bad block list /// public uint badLength; /// /// Checksum for bootable disks /// public ushort checksum; } } }