// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : Structs.cs // Author(s) : Natalia Portillo // // Component : CP/M filesystem plugin. // // --[ Description ] ---------------------------------------------------------- // // CP/M filesystem structures. // // --[ 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-2022 Natalia Portillo // ****************************************************************************/ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; // ReSharper disable NotAccessedField.Local namespace Aaru.Filesystems; public sealed partial class CPM { /// Most of the times this structure is hard wired or generated by CP/M, not stored on disk [SuppressMessage("ReSharper", "InconsistentNaming")] class DiscParameterBlock { /// First byte of allocation bitmap public byte al0; /// Second byte of allocation bitmap public byte al1; /// Block mask public byte blm; /// Block shift public byte bsh; /// Checksum vector size public ushort cks; /// Directory entries - 1 public ushort drm; /// Blocks on disk - 1 public ushort dsm; /// Extent mask public byte exm; /// Reserved tracks public ushort off; /// Physical sector mask public byte phm; /// Physical sector shift public byte psh; /// Sectors per track public ushort spt; } /// Amstrad superblock, for PCW [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct AmstradSuperBlock { /// Format ID. 0 single-side, 3 double-side. 1 and 2 are for CPC but they don't use the superblock public readonly byte format; /// Gives information about side ordering public readonly byte sidedness; /// Tracks per side, aka, cylinders public readonly byte tps; /// Sectors per track public readonly byte spt; /// Physical sector shift public readonly byte psh; /// Reserved tracks public readonly byte off; /// Block size shift public readonly byte bsh; /// How many blocks does the directory take public readonly byte dirBlocks; /// GAP#3 length (intersector) public readonly byte gapLen; /// GAP#4 length (end-of-track) public readonly byte formatGap; /// Must be 0 public readonly byte zero1; /// Must be 0 public readonly byte zero2; /// Must be 0 public readonly byte zero3; /// Must be 0 public readonly byte zero4; /// Must be 0 public readonly byte zero5; /// Indicates machine the boot code following the superblock is designed to boot public readonly byte fiddle; } /// Superblock found on CP/M-86 hard disk volumes [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct HardDiskSuperBlock { /// Value so the sum of all the superblock's sector bytes taken as 16-bit values gives 0 public readonly ushort checksum; /// Copyright string [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1F)] public readonly byte[] copyright; /// First cylinder of disk where this volume resides public readonly ushort firstCylinder; /// How many cylinders does this volume span public readonly ushort cylinders; /// Heads on hard disk public readonly byte heads; /// Sectors per track public readonly byte sectorsPerTrack; /// Flags, only use by CCP/M where bit 0 equals verify on write public readonly byte flags; /// Sector size / 128 public readonly byte recordsPerSector; /// /// /// public readonly ushort spt; /// /// /// public readonly byte bsh; /// /// /// public readonly byte blm; /// /// /// public readonly byte exm; /// /// /// public readonly ushort dsm; /// /// /// public readonly ushort drm; /// /// /// public readonly ushort al0; /// /// /// public readonly ushort al1; /// /// /// public readonly ushort cks; /// /// /// public readonly ushort off; /// Must be zero public readonly ushort zero1; /// Must be zero public readonly ushort zero2; /// Must be zero public readonly ushort zero3; /// Must be zero public readonly ushort zero4; /// How many 128 bytes are in a block public readonly ushort recordsPerBlock; /// Maximum number of bad blocks in the bad block list public readonly ushort badBlockWordsMax; /// Used number of bad blocks in the bad block list public readonly ushort badBlockWords; /// First block after the blocks reserved for bad block substitution public readonly ushort firstSub; } /// Volume label entry [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct LabelEntry { /// Must be 0x20 public readonly byte signature; /// Label in ASCII [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] public readonly byte[] label; /// /// Label flags. Bit 0 = label exists, bit 4 = creation timestamp, bit 5 = modification timestamp, bit 6 = access /// timestamp, bit 7 = password enabled /// public readonly byte flags; /// Password decoder byte public readonly byte passwordDecoder; /// Must be 0 public readonly ushort reserved; /// Password XOR'ed with [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] password; /// Label creation time [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] ctime; /// Label modification time [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] mtime; } /// CP/M 3 timestamp entry [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct DateEntry { /// Must be 0x21 public readonly byte signature; /// File 1 create/access timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date1; /// File 1 modification timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date2; /// File 1 password mode public readonly byte mode1; public readonly byte zero1; /// File 2 create/access timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date3; /// File 2 modification timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date4; /// File 2 password mode public readonly byte mode2; public readonly byte zero2; /// File 3 create/access timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date5; /// File 3 modification timestamp [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] date6; /// File 3 password mode public readonly byte mode3; public readonly ushort zero3; } /// Password entry [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct PasswordEntry { /// 16 + user number public readonly byte userNumber; /// Filename [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] filename; /// Extension [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public readonly byte[] extension; /// Password mode. Bit 7 = required for read, bit 6 = required for write, bit 5 = required for delete public readonly byte mode; /// Password decoder byte public readonly byte passwordDecoder; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public readonly byte[] reserved; /// Password XOR'ed with [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] password; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] reserved2; } /// Timestamp for Z80DOS or DOS+ [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct TrdPartyDateEntry { /// Must be 0x21 public readonly byte signature; public readonly byte zero; /// Creation year for file 1 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public readonly byte[] create1; /// Modification time for file 1 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] modify1; /// Access time for file 1 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] access1; /// Creation year for file 2 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public readonly byte[] create2; /// Modification time for file 2 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] modify2; /// Access time for file 2 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] access2; /// Creation year for file 3 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public readonly byte[] create3; /// Modification time for file 3 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] modify3; /// Access time for file 3 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public readonly byte[] access3; } /// Directory entry for <256 allocation blocks [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct DirectoryEntry { /// User number. Bit 7 set in CP/M 1 means hidden public readonly byte statusUser; /// Filename and bit 7 as flags [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] filename; /// Extension and bit 7 as flags [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public readonly byte[] extension; /// Low byte of extent number public readonly byte extentCounter; /// /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. /// public readonly byte lastRecordBytes; /// High byte of extent number public readonly byte extentCounterHigh; /// How many records are used in this entry. 0x80 if all are used. public readonly byte records; /// Allocation blocks [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] allocations; } /// Directory entry for >256 allocation blocks [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct DirectoryEntry16 { /// User number. Bit 7 set in CP/M 1 means hidden public readonly byte statusUser; /// Filename and bit 7 as flags [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly byte[] filename; /// Extension and bit 7 as flags [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public readonly byte[] extension; /// Low byte of extent number public readonly byte extentCounter; /// /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. /// public readonly byte lastRecordBytes; /// High byte of extent number public readonly byte extentCounterHigh; /// How many records are used in this entry. 0x80 if all are used. public readonly byte records; /// Allocation blocks [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly ushort[] allocations; } }