// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : CBM.cs // Author(s) : Natalia Portillo // // Component : Commodore file system plugin. // // --[ Description ] ---------------------------------------------------------- // // Identifies the Commodore file system and shows information. // // --[ 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-2020 Natalia Portillo // ****************************************************************************/ using System; using System.Runtime.InteropServices; using System.Text; using Claunia.Encoding; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Interfaces; using Schemas; using Encoding = System.Text.Encoding; using Marshal = DiscImageChef.Helpers.Marshal; namespace DiscImageChef.Filesystems { public class CBM : IFilesystem { public FileSystemType XmlFsType { get; private set; } public string Name => "Commodore file system"; public Guid Id => new Guid("D104744E-A376-450C-BAC0-1347C93F983B"); public Encoding Encoding { get; private set; } public string Author => "Natalia Portillo"; public bool Identify(IMediaImage imagePlugin, Partition partition) { if(partition.Start > 0) return false; if(imagePlugin.Info.SectorSize != 256) return false; if(imagePlugin.Info.Sectors != 683 && imagePlugin.Info.Sectors != 768 && imagePlugin.Info.Sectors != 1366 && imagePlugin.Info.Sectors != 3200) return false; byte[] sector; if(imagePlugin.Info.Sectors == 3200) { sector = imagePlugin.ReadSector(1560); CommodoreHeader cbmHdr = Marshal.ByteArrayToStructureLittleEndian(sector); if(cbmHdr.diskDosVersion == 0x44 && cbmHdr.dosVersion == 0x33 && cbmHdr.diskVersion == 0x44) return true; } else { sector = imagePlugin.ReadSector(357); CommodoreBam cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); if(cbmBam.dosVersion == 0x41 && (cbmBam.doubleSided == 0x00 || cbmBam.doubleSided == 0x80) && cbmBam.unused1 == 0x00 && cbmBam.directoryTrack == 0x12) return true; } return false; } public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, Encoding encoding) { Encoding = new PETSCII(); byte[] sector; StringBuilder sbInformation = new StringBuilder(); sbInformation.AppendLine("Commodore file system"); XmlFsType = new FileSystemType { Type = "Commodore file system", Clusters = imagePlugin.Info.Sectors, ClusterSize = 256 }; if(imagePlugin.Info.Sectors == 3200) { sector = imagePlugin.ReadSector(1560); CommodoreHeader cbmHdr = Marshal.ByteArrayToStructureLittleEndian(sector); sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmHdr.directoryTrack, cbmHdr.directorySector).AppendLine(); sbInformation .AppendFormat("Disk DOS Version: {0}", Encoding.ASCII.GetString(new[] {cbmHdr.diskDosVersion})) .AppendLine(); sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] {cbmHdr.dosVersion})) .AppendLine(); sbInformation.AppendFormat("Disk Version: {0}", Encoding.ASCII.GetString(new[] {cbmHdr.diskVersion})) .AppendLine(); sbInformation.AppendFormat("Disk ID: {0}", cbmHdr.diskId).AppendLine(); sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmHdr.name, Encoding)) .AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(cbmHdr.name, Encoding); XmlFsType.VolumeSerial = $"{cbmHdr.diskId}"; } else { sector = imagePlugin.ReadSector(357); CommodoreBam cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmBam.directoryTrack, cbmBam.directorySector).AppendLine(); sbInformation.AppendFormat("Disk DOS type: {0}", Encoding.ASCII.GetString(BitConverter.GetBytes(cbmBam.dosType))) .AppendLine(); sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] {cbmBam.dosVersion})) .AppendLine(); sbInformation.AppendFormat("Disk ID: {0}", cbmBam.diskId).AppendLine(); sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmBam.name, Encoding)) .AppendLine(); XmlFsType.VolumeName = StringHandlers.CToString(cbmBam.name, Encoding); XmlFsType.VolumeSerial = $"{cbmBam.diskId}"; } information = sbInformation.ToString(); } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct CommodoreBam { /// /// Track where directory starts /// public readonly byte directoryTrack; /// /// Sector where directory starts /// public readonly byte directorySector; /// /// Disk DOS version, 0x41 /// public readonly byte dosVersion; /// /// Set to 0x80 if 1571, 0x00 if not /// public readonly byte doubleSided; /// /// Block allocation map /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 140)] public readonly byte[] bam; /// /// Disk name /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] name; /// /// Filled with 0xA0 /// public readonly ushort fill1; /// /// Disk ID /// public readonly ushort diskId; /// /// Filled with 0xA0 /// public readonly byte fill2; /// /// DOS type /// public readonly ushort dosType; /// /// Filled with 0xA0 /// public readonly uint fill3; /// /// Unused /// public readonly byte unused1; /// /// Block allocation map for Dolphin DOS extended tracks /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public readonly byte[] dolphinBam; /// /// Block allocation map for Speed DOS extended tracks /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public readonly byte[] speedBam; /// /// Unused /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] public readonly byte[] unused2; /// /// Free sector count for second side in 1571 /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] public readonly byte[] freeCount; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct CommodoreHeader { /// /// Track where directory starts /// public readonly byte directoryTrack; /// /// Sector where directory starts /// public readonly byte directorySector; /// /// Disk DOS version, 0x44 /// public readonly byte diskDosVersion; /// /// Unusued /// public readonly byte unused1; /// /// Disk name /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] name; /// /// Filled with 0xA0 /// public readonly ushort fill1; /// /// Disk ID /// public readonly ushort diskId; /// /// Filled with 0xA0 /// public readonly byte fill2; /// /// DOS version ('3') /// public readonly byte dosVersion; /// /// Disk version ('D') /// public readonly byte diskVersion; /// /// Filled with 0xA0 /// public readonly short fill3; } } }