// /*************************************************************************** // Aaru Data Preservation Suite // ---------------------------------------------------------------------------- // // Filename : PC98.cs // Author(s) : Natalia Portillo // // Component : Partitioning scheme plugins. // // --[ Description ] ---------------------------------------------------------- // // Manages NEC PC-9800 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-2025 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Aaru.Logging; using Marshal = Aaru.Helpers.Marshal; namespace Aaru.Partitions; /// /// Implements decoding of NEC PC-9800 partitions public sealed class PC98 : IPartition { const string MODULE_NAME = "PC-98 partitions plugin"; static string DecodePC98Sid(byte sid) { return (sid & 0x7F) switch { 0x01 => Localization.FAT12, 0x04 => Localization.PC_UX, 0x06 => Localization.N88_BASIC_86, // Supposedly for FAT16 < 32 MiB, seen in bigger partitions 0x11 or 0x21 => Localization.FAT16, 0x28 or 0x41 or 0x48 => Localization.Windows_Volume_Set, 0x44 => Localization.FreeBSD, 0x61 => Localization.FAT32, 0x62 => Localization.Linux, _ => Localization.Unknown_partition_type }; } #region Nested type: Partition [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct Partition { /// Some ID, if 0x80 bit is set, it is bootable public readonly byte dp_mid; /// Some ID, if 0x80 bit is set, it is active public readonly byte dp_sid; public readonly byte dp_dum1; public readonly byte dp_dum2; public readonly byte dp_ipl_sct; public readonly byte dp_ipl_head; public readonly ushort dp_ipl_cyl; public readonly byte dp_ssect; public readonly byte dp_shd; public readonly ushort dp_scyl; public readonly byte dp_esect; public readonly byte dp_ehd; public readonly ushort dp_ecyl; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public readonly byte[] dp_name; } #endregion #region Nested type: Table [StructLayout(LayoutKind.Sequential, Pack = 1)] readonly struct Table { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public readonly Partition[] entries; } #endregion #region IPartition Members /// public string Name => Localization.PC98_Name; /// public Guid Id => new("27333401-C7C2-447D-961C-22AD0641A09A"); /// public string Author => Authors.NATALIA_PORTILLO; /// public bool GetInformation(IMediaImage imagePlugin, out List partitions, ulong sectorOffset) { partitions = []; if(sectorOffset != 0) return false; ErrorNumber errno = imagePlugin.ReadSector(0, false, out byte[] bootSector, out _); if(errno != ErrorNumber.NoError || bootSector[^2] != 0x55 || bootSector[^1] != 0xAA) return false; errno = imagePlugin.ReadSector(1, false, out byte[] sector, out _); if(errno != ErrorNumber.NoError) return false; // Prevent false positives with some FAT BPBs if(Encoding.ASCII.GetString(bootSector, 0x36, 3) == Localization.FAT) return false; Table table = Marshal.ByteArrayToStructureLittleEndian(sector); ulong counter = 0; foreach(Partition entry in table.entries) { AaruLogging.Debug(MODULE_NAME, "entry.dp_mid = {0}", entry.dp_mid); AaruLogging.Debug(MODULE_NAME, "entry.dp_sid = {0}", entry.dp_sid); AaruLogging.Debug(MODULE_NAME, "entry.dp_dum1 = {0}", entry.dp_dum1); AaruLogging.Debug(MODULE_NAME, "entry.dp_dum2 = {0}", entry.dp_dum2); AaruLogging.Debug(MODULE_NAME, "entry.dp_ipl_sct = {0}", entry.dp_ipl_sct); AaruLogging.Debug(MODULE_NAME, "entry.dp_ipl_head = {0}", entry.dp_ipl_head); AaruLogging.Debug(MODULE_NAME, "entry.dp_ipl_cyl = {0}", entry.dp_ipl_cyl); AaruLogging.Debug(MODULE_NAME, "entry.dp_ssect = {0}", entry.dp_ssect); AaruLogging.Debug(MODULE_NAME, "entry.dp_shd = {0}", entry.dp_shd); AaruLogging.Debug(MODULE_NAME, "entry.dp_scyl = {0}", entry.dp_scyl); AaruLogging.Debug(MODULE_NAME, "entry.dp_esect = {0}", entry.dp_esect); AaruLogging.Debug(MODULE_NAME, "entry.dp_ehd = {0}", entry.dp_ehd); AaruLogging.Debug(MODULE_NAME, "entry.dp_ecyl = {0}", entry.dp_ecyl); AaruLogging.Debug(MODULE_NAME, "entry.dp_name = \"{0}\"", StringHandlers.CToString(entry.dp_name, Encoding.GetEncoding(932))); if(entry.dp_scyl == entry.dp_ecyl || entry.dp_ecyl <= 0 || entry.dp_scyl > imagePlugin.Info.Cylinders || entry.dp_ecyl > imagePlugin.Info.Cylinders || entry.dp_shd > imagePlugin.Info.Heads || entry.dp_ehd > imagePlugin.Info.Heads || entry.dp_ssect > imagePlugin.Info.SectorsPerTrack || entry.dp_esect > imagePlugin.Info.SectorsPerTrack) continue; var part = new CommonTypes.Partition { Start = CHS.ToLBA(entry.dp_scyl, entry.dp_shd, (uint)(entry.dp_ssect + 1), imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack), Type = DecodePC98Sid(entry.dp_sid), Name = StringHandlers.CToString(entry.dp_name, Encoding.GetEncoding(932)).Trim(), Sequence = counter, Scheme = Name }; part.Offset = part.Start * imagePlugin.Info.SectorSize; part.Length = CHS.ToLBA(entry.dp_ecyl, entry.dp_ehd, (uint)(entry.dp_esect + 1), imagePlugin.Info.Heads, imagePlugin.Info.SectorsPerTrack) - part.Start; part.Size = part.Length * imagePlugin.Info.SectorSize; AaruLogging.Debug(MODULE_NAME, "part.Start = {0}", part.Start); AaruLogging.Debug(MODULE_NAME, "part.Type = {0}", part.Type); AaruLogging.Debug(MODULE_NAME, "part.Name = {0}", part.Name); AaruLogging.Debug(MODULE_NAME, "part.Sequence = {0}", part.Sequence); AaruLogging.Debug(MODULE_NAME, "part.Offset = {0}", part.Offset); AaruLogging.Debug(MODULE_NAME, "part.Length = {0}", part.Length); AaruLogging.Debug(MODULE_NAME, "part.Size = {0}", part.Size); if((entry.dp_mid & 0x20) != 0x20 && (entry.dp_mid & 0x44) != 0x44 || part.Start >= imagePlugin.Info.Sectors || part.End > imagePlugin.Info.Sectors) continue; partitions.Add(part); counter++; } return partitions.Count > 0; } #endregion }