// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : Apricot.cs // Author(s) : Natalia Portillo // // Component : Component // // --[ Description ] ---------------------------------------------------------- // // Description // // --[ 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-2017 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using DiscImageChef.CommonTypes; using DiscImageChef.ImagePlugins; using DiscImageChef.Console; namespace DiscImageChef.PartPlugins { public class Apricot : PartPlugin { readonly string[] operatingSystemCodes = { "Invalid", "MS-DOS", "UCSD Pascal", "CP/M", "Concurrent CP/M" }; readonly string[] bootTypeCodes = { "Non-bootable", "Apricot & XI RAM BIOS", "Generic ROM BIOS", "Apricot & XI ROM BIOS", "Apricot Portable ROM BIOS", "Apricot F1 ROM BIOS" }; readonly string[] diskTypeCodes = { "MF1DD 70-track", "MF1DD", "MF2DD", "Winchester 5M", "Winchester 10M", "Winchester 20M" }; readonly string[] printDevices = { "Parallel", "Serial" }; readonly int[] lineModes = { 256, 200 }; readonly int[] lineWidths = { 80, 40 }; readonly int[] baudRates = { 50, 75, 110, 134, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 19200 }; readonly double[] stopBits = { 1, 1.5, 2 }; readonly string[] parityTypes = { "None", "Odd", "Even", "Mark", "Space" }; public Apricot() { Name = "ACT Apricot partitions"; PluginUUID = new Guid("8CBF5864-7B5A-47A0-8CEB-199C74FA22DE"); } public override bool GetInformation(ImagePlugin imagePlugin, out List partitions, ulong sectorOffset) { partitions = new List(); // I think Apricot can't chain partitions so. if(sectorOffset != 0) return false; byte[] sector = imagePlugin.ReadSector(0); if(sector.Length < 512) return false; ApricotLabel label = new ApricotLabel(); IntPtr lblPtr = Marshal.AllocHGlobal(512); Marshal.Copy(sector, 0, lblPtr, 512); label = (ApricotLabel)Marshal.PtrToStructure(lblPtr, typeof(ApricotLabel)); Marshal.FreeHGlobal(lblPtr); // Not much to check but... ulong deviceSectors = imagePlugin.GetSectors(); ulong deviceSizeAccordingToLabel = label.cylinders * label.heads * label.spt; if(label.operatingSystem > 4 || label.bootType > 5 || label.partitionCount > 8 || deviceSizeAccordingToLabel > deviceSectors || label.firstDataBlock > deviceSectors) return false; DicConsole.DebugWriteLine("Apricot partitions", "label.version = \"{0}\"", StringHandlers.CToString(label.version)); DicConsole.DebugWriteLine("Apricot partitions", "label.operatingSystem = {0} ({1})", label.operatingSystem, label.operatingSystem < operatingSystemCodes.Length ? operatingSystemCodes[label.operatingSystem] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.writeProtected = {0}", label.writeProtected); DicConsole.DebugWriteLine("Apricot partitions", "label.copyProtected = {0}", label.copyProtected); DicConsole.DebugWriteLine("Apricot partitions", "label.bootType = {0} ({1})", label.bootType, label.bootType < bootTypeCodes.Length ? bootTypeCodes[label.bootType] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.partitionCount = {0}", label.partitionCount); DicConsole.DebugWriteLine("Apricot partitions", "label.winchester = {0}", label.winchester); DicConsole.DebugWriteLine("Apricot partitions", "label.sectorSize = {0}", label.sectorSize); DicConsole.DebugWriteLine("Apricot partitions", "label.spt = {0}", label.spt); DicConsole.DebugWriteLine("Apricot partitions", "label.cylinders = {0}", label.cylinders); DicConsole.DebugWriteLine("Apricot partitions", "label.heads = {0}", label.heads); DicConsole.DebugWriteLine("Apricot partitions", "label.interleave = {0}", label.interleave); DicConsole.DebugWriteLine("Apricot partitions", "label.skew = {0}", label.skew); DicConsole.DebugWriteLine("Apricot partitions", "label.bootLocation = {0}", label.bootLocation); DicConsole.DebugWriteLine("Apricot partitions", "label.bootSize = {0}", label.bootSize); DicConsole.DebugWriteLine("Apricot partitions", "label.bootAddress = 0x{0:X8}", label.bootAddress); DicConsole.DebugWriteLine("Apricot partitions", "label.bootOffset:label.bootSegment = {0:X4}:{1:X4}", label.bootOffset, label.bootSegment); DicConsole.DebugWriteLine("Apricot partitions", "label.firstDataBlock = {0}", label.firstDataBlock); DicConsole.DebugWriteLine("Apricot partitions", "label.generation = {0}", label.generation); DicConsole.DebugWriteLine("Apricot partitions", "label.copyCount = {0}", label.copyCount); DicConsole.DebugWriteLine("Apricot partitions", "label.maxCopies = {0}", label.maxCopies); DicConsole.DebugWriteLine("Apricot partitions", "label.serialNumber = \"{0}\"", StringHandlers.CToString(label.serialNumber)); DicConsole.DebugWriteLine("Apricot partitions", "label.partNumber = \"{0}\"", StringHandlers.CToString(label.partNumber)); DicConsole.DebugWriteLine("Apricot partitions", "label.copyright = \"{0}\"", StringHandlers.CToString(label.copyright)); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.bps = {0}", label.mainBPB.bps); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.spc = {0}", label.mainBPB.spc); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.rsectors = {0}", label.mainBPB.rsectors); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.fats_no = {0}", label.mainBPB.fats_no); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.root_ent = {0}", label.mainBPB.root_ent); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.sectors = {0}", label.mainBPB.sectors); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.media = {0}", label.mainBPB.media); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.spfat = {0}", label.mainBPB.spfat); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.diskType = {0} ({1})", label.mainBPB.diskType, label.mainBPB.diskType < diskTypeCodes.Length ? diskTypeCodes[label.mainBPB.diskType] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.mainBPB.startSector = {0}", label.mainBPB.startSector); DicConsole.DebugWriteLine("Apricot partitions", "label.fontName = \"{0}\"", StringHandlers.CToString(label.fontName)); DicConsole.DebugWriteLine("Apricot partitions", "label.keyboardName = \"{0}\"", StringHandlers.CToString(label.keyboardName)); DicConsole.DebugWriteLine("Apricot partitions", "label.biosMajorVersion = {0}", label.biosMajorVersion); DicConsole.DebugWriteLine("Apricot partitions", "label.biosMinorVersion = {0}", label.biosMinorVersion); DicConsole.DebugWriteLine("Apricot partitions", "label.diagnosticsFlag = {0}", label.diagnosticsFlag); DicConsole.DebugWriteLine("Apricot partitions", "label.prnDevice = {0} ({1})", label.prnDevice, label.prnDevice < printDevices.Length ? printDevices[label.prnDevice] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.bellVolume = {0}", label.bellVolume); DicConsole.DebugWriteLine("Apricot partitions", "label.enableCache = {0}", label.enableCache); DicConsole.DebugWriteLine("Apricot partitions", "label.enableGraphics = {0}", label.enableGraphics); DicConsole.DebugWriteLine("Apricot partitions", "label.dosLength = {0}", label.dosLength); DicConsole.DebugWriteLine("Apricot partitions", "label.fontLength = {0}", label.fontLength); DicConsole.DebugWriteLine("Apricot partitions", "label.keyboardLength = {0}", label.keyboardLength); DicConsole.DebugWriteLine("Apricot partitions", "label.dosStart = {0}", label.dosStart); DicConsole.DebugWriteLine("Apricot partitions", "label.fontStart = {0}", label.fontStart); DicConsole.DebugWriteLine("Apricot partitions", "label.keyboardStart = {0}", label.keyboardStart); DicConsole.DebugWriteLine("Apricot partitions", "label.keyboardVolume = {0}", label.keyboardVolume); DicConsole.DebugWriteLine("Apricot partitions", "label.autorepeat = {0}", label.autorepeat); DicConsole.DebugWriteLine("Apricot partitions", "label.autorepeatLeadIn = {0}", label.autorepeatLeadIn); DicConsole.DebugWriteLine("Apricot partitions", "label.autorepeatInterval = {0}", label.autorepeatInterval); DicConsole.DebugWriteLine("Apricot partitions", "label.microscreenMode = {0}", label.microscreenMode); DicConsole.DebugWriteLine("Apricot partitions", "label.spareKeyboard is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareKeyboard)); DicConsole.DebugWriteLine("Apricot partitions", "label.lineMode = {0} ({1} lines)", label.lineMode, label.lineMode < lineModes.Length ? lineModes[label.lineMode] : 0); DicConsole.DebugWriteLine("Apricot partitions", "label.lineWidth = {0} ({1} columns)", label.lineWidth, label.lineWidth < lineWidths.Length ? lineWidths[label.lineWidth] : 0); DicConsole.DebugWriteLine("Apricot partitions", "label.imageOff = {0}", label.imageOff); DicConsole.DebugWriteLine("Apricot partitions", "label.spareScreen is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareScreen)); DicConsole.DebugWriteLine("Apricot partitions", "label.txBaudRate = {0} ({1} bps)", label.txBaudRate, label.txBaudRate < baudRates.Length ? baudRates[label.txBaudRate] : 0); DicConsole.DebugWriteLine("Apricot partitions", "label.rxBaudRate = {0} ({1} bps)", label.rxBaudRate, label.rxBaudRate < baudRates.Length ? baudRates[label.rxBaudRate] : 0); DicConsole.DebugWriteLine("Apricot partitions", "label.txBits = {0}", label.txBits); DicConsole.DebugWriteLine("Apricot partitions", "label.rxBits = {0}", label.rxBits); DicConsole.DebugWriteLine("Apricot partitions", "label.stopBits = {0} ({1} bits)", label.stopBits, label.stopBits < stopBits.Length ? stopBits[label.stopBits] : 0); DicConsole.DebugWriteLine("Apricot partitions", "label.parityCheck = {0}", label.parityCheck); DicConsole.DebugWriteLine("Apricot partitions", "label.parityType = {0} ({1})", label.parityType, label.parityType < parityTypes.Length ? parityTypes[label.parityType] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.txXonXoff = {0}", label.txXonXoff); DicConsole.DebugWriteLine("Apricot partitions", "label.rxXonXoff = {0}", label.rxXonXoff); DicConsole.DebugWriteLine("Apricot partitions", "label.xonCharacter = {0}", label.xonCharacter); DicConsole.DebugWriteLine("Apricot partitions", "label.xoffCharacter = {0}", label.xoffCharacter); DicConsole.DebugWriteLine("Apricot partitions", "label.rxXonXoffBuffer = {0}", label.rxXonXoffBuffer); DicConsole.DebugWriteLine("Apricot partitions", "label.dtrDsr = {0}", label.dtrDsr); DicConsole.DebugWriteLine("Apricot partitions", "label.ctsRts = {0}", label.ctsRts); DicConsole.DebugWriteLine("Apricot partitions", "label.nullsAfterCr = {0}", label.nullsAfterCr); DicConsole.DebugWriteLine("Apricot partitions", "label.nullsAfterFF = {0}", label.nullsAfterFF); DicConsole.DebugWriteLine("Apricot partitions", "label.lfAfterCRSerial = {0}", label.lfAfterCRSerial); DicConsole.DebugWriteLine("Apricot partitions", "label.biosErrorReportSerial = {0}", label.biosErrorReportSerial); DicConsole.DebugWriteLine("Apricot partitions", "label.spareSerial is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareSerial)); DicConsole.DebugWriteLine("Apricot partitions", "label.lfAfterCrParallel = {0}", label.lfAfterCrParallel); DicConsole.DebugWriteLine("Apricot partitions", "label.selectLine = {0}", label.selectLine); DicConsole.DebugWriteLine("Apricot partitions", "label.paperEmpty = {0}", label.paperEmpty); DicConsole.DebugWriteLine("Apricot partitions", "label.faultLine = {0}", label.faultLine); DicConsole.DebugWriteLine("Apricot partitions", "label.biosErrorReportParallel = {0}", label.biosErrorReportParallel); DicConsole.DebugWriteLine("Apricot partitions", "label.spareParallel is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareParallel)); DicConsole.DebugWriteLine("Apricot partitions", "label.spareWinchester is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareWinchester)); DicConsole.DebugWriteLine("Apricot partitions", "label.parkingEnabled = {0}", label.parkingEnabled); DicConsole.DebugWriteLine("Apricot partitions", "label.formatProtection = {0}", label.formatProtection); DicConsole.DebugWriteLine("Apricot partitions", "label.spareRamDisk is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spareRamDisk)); for(int i = 0; i < 32; i++) DicConsole.DebugWriteLine("Apricot partitions", "label.badBlocks[{1}] = {0}", label.badBlocks[i], i); for(int i = 0; i < 8; i++) { DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].bps = {0}", label.partitions[i].bps, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].spc = {0}", label.partitions[i].spc, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].rsectors = {0}", label.partitions[i].rsectors, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].fats_no = {0}", label.partitions[i].fats_no, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].root_ent = {0}", label.partitions[i].root_ent, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].sectors = {0}", label.partitions[i].sectors, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].media = {0}", label.partitions[i].media, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].spfat = {0}", label.partitions[i].spfat, i); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].diskType = {0} ({2})", label.partitions[i].diskType, i, label.partitions[i].diskType < diskTypeCodes.Length ? diskTypeCodes[label.partitions[i].diskType] : "Unknown"); DicConsole.DebugWriteLine("Apricot partitions", "label.partitions[{1}].startSector = {0}", label.partitions[i].startSector, i); } DicConsole.DebugWriteLine("Apricot partitions", "label.spare is null? = {0}", ArrayHelpers.ArrayIsNullOrEmpty(label.spare)); DicConsole.DebugWriteLine("Apricot partitions", "label.cpmDoubleSided = {0}", label.cpmDoubleSided); // Only hard disks can contain partitions if(!label.winchester) return false; for(byte i = 0; i < label.partitionCount; i++) { Partition part = new Partition { Start = label.partitions[i].startSector, Size = (ulong)(label.partitions[i].sectors * label.sectorSize), Length = label.partitions[i].sectors, Type = "ACT Apricot partition", Sequence = i, Scheme = Name, Offset = (ulong)(label.partitions[i].startSector * label.sectorSize) }; if(part.Start < deviceSectors && part.End < deviceSectors) partitions.Add(part); } return partitions.Count > 0; } /// Apricot Label. [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ApricotLabel { /// Version of format which created disk [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] version; /// Operating system. public byte operatingSystem; /// Software write protection. [MarshalAs(UnmanagedType.U1)] public bool writeProtected; /// Copy protected. [MarshalAs(UnmanagedType.U1)] public bool copyProtected; /// Boot type. public byte bootType; /// Partitions. public byte partitionCount; /// Is hard disk?. [MarshalAs(UnmanagedType.U1)] public bool winchester; /// Sector size. public ushort sectorSize; /// Sectors per track. public ushort spt; /// Tracks per side. public uint cylinders; /// Sides. public byte heads; /// Interleave factor. public byte interleave; /// Skew factor. public ushort skew; /// Sector where boot code starts. public uint bootLocation; /// Size in sectors of boot code. public ushort bootSize; /// Address at which to load boot code. public uint bootAddress; /// Offset where to jump to boot. public ushort bootOffset; /// Segment where to jump to boot. public ushort bootSegment; /// First data sector. public uint firstDataBlock; /// Generation. public ushort generation; /// Copy count. public ushort copyCount; /// Maximum number of copies. public ushort maxCopies; /// Serial number. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] serialNumber; /// Part number. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] partNumber; /// Copyright. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] public byte[] copyright; /// BPB for whole disk. public ApricotParameterBlock mainBPB; /// Name of FONT file. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] fontName; /// Name of KEYBOARD file. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] keyboardName; /// Minor BIOS version. public byte biosMinorVersion; /// Major BIOS version. public byte biosMajorVersion; /// Diagnostics enabled?. [MarshalAs(UnmanagedType.U1)] public bool diagnosticsFlag; /// Printer device. public byte prnDevice; /// Bell volume. public byte bellVolume; /// Cache enabled?. [MarshalAs(UnmanagedType.U1)] public bool enableCache; /// Graphics enabled?. [MarshalAs(UnmanagedType.U1)] public bool enableGraphics; /// Length in sectors of DOS. public byte dosLength; /// Length in sectors of FONT file. public byte fontLength; /// Length in sectors of KEYBOARD file. public byte keyboardLength; /// Starting sector of DOS. public ushort dosStart; /// Starting sector of FONT file. public ushort fontStart; /// Starting sector of KEYBOARD file. public ushort keyboardStart; /// Keyboard click volume. public byte keyboardVolume; /// Auto-repeat enabled?. [MarshalAs(UnmanagedType.U1)] public bool autorepeat; /// Auto-repeat lead-in. public byte autorepeatLeadIn; /// Auto-repeat interval. public byte autorepeatInterval; /// Microscreen mode. public byte microscreenMode; /// Spare area for keyboard values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] public byte[] spareKeyboard; /// Screen line mode. public byte lineMode; /// Screen line width. public byte lineWidth; /// Screen disabled?. [MarshalAs(UnmanagedType.U1)] public bool imageOff; /// Spare area for screen values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] public byte[] spareScreen; /// TX baud rate. public byte txBaudRate; /// RX baud rate. public byte rxBaudRate; /// TX bits. public byte txBits; /// RX bits. public byte rxBits; /// Stop bits. public byte stopBits; /// Parity enabled?. [MarshalAs(UnmanagedType.U1)] public bool parityCheck; /// Parity type. public byte parityType; /// Xon/Xoff enabled on TX. [MarshalAs(UnmanagedType.U1)] public bool txXonXoff; /// Xon/Xoff enabled on RX. [MarshalAs(UnmanagedType.U1)] public bool rxXonXoff; /// Xon character. public byte xonCharacter; /// Xoff character. public byte xoffCharacter; /// Xon/Xoff buffer on RX. public ushort rxXonXoffBuffer; /// DTR/DSR enabled?. [MarshalAs(UnmanagedType.U1)] public bool dtrDsr; /// CTS/RTS enabled?. [MarshalAs(UnmanagedType.U1)] public bool ctsRts; /// NULLs after CR. public byte nullsAfterCr; /// NULLs after 0xFF. public byte nullsAfterFF; /// Send LF after CR in serial port. [MarshalAs(UnmanagedType.U1)] public bool lfAfterCRSerial; /// BIOS error report in serial port. [MarshalAs(UnmanagedType.U1)] public bool biosErrorReportSerial; /// Spare area for serial port values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] public byte[] spareSerial; /// Send LF after CR in parallel port. [MarshalAs(UnmanagedType.U1)] public bool lfAfterCrParallel; /// Select line supported?. [MarshalAs(UnmanagedType.U1)] public bool selectLine; /// Paper empty supported?. [MarshalAs(UnmanagedType.U1)] public bool paperEmpty; /// Fault line supported?. [MarshalAs(UnmanagedType.U1)] public bool faultLine; /// BIOS error report in parallel port. [MarshalAs(UnmanagedType.U1)] public bool biosErrorReportParallel; /// Spare area for parallel port values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] public byte[] spareParallel; /// Spare area for Winchester values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] public byte[] spareWinchester; /// Parking enabled?. [MarshalAs(UnmanagedType.U1)] public bool parkingEnabled; /// Format protection?. [MarshalAs(UnmanagedType.U1)] public bool formatProtection; /// Spare area for RAM disk values expansion. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] spareRamDisk; /// List of bad blocks. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public ushort[] badBlocks; /// Array of partition BPBs. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public ApricotParameterBlock[] partitions; /// Spare area. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] public byte[] spare; /// CP/M double side indicator?. public bool cpmDoubleSided; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct ApricotParameterBlock { /// Bytes per sector public ushort bps; /// Sectors per cluster public byte spc; /// Reserved sectors between BPB and FAT public ushort rsectors; /// Number of FATs public byte fats_no; /// Number of entries on root directory public ushort root_ent; /// Sectors in volume public ushort sectors; /// Media descriptor public byte media; /// Sectors per FAT public ushort spfat; /// Disk type public byte diskType; /// Volume starting sector public ushort startSector; } } }