Files
Aaru/Aaru.Filesystems/CPM/Info.cs

1255 lines
58 KiB
C#
Raw Normal View History

// /***************************************************************************
2020-02-27 12:31:25 +00:00
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Info.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : CP/M filesystem plugin.
//
// --[ Description ] ----------------------------------------------------------
//
// Identifies the CP/M filesystem 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 <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2025-08-14 02:49:52 +01:00
// Copyright © 2011-2025 Natalia Portillo
// ****************************************************************************/
2017-12-19 19:33:46 +00:00
using System;
using System.IO;
using System.Linq;
using System.Text;
2020-02-27 00:33:26 +00:00
using Aaru.CommonTypes;
using Aaru.CommonTypes.Interfaces;
using Aaru.Console;
using Aaru.Helpers;
2017-12-21 14:30:38 +00:00
using Schemas;
2020-07-20 15:43:52 +01:00
namespace Aaru.Filesystems
{
2020-07-22 13:20:25 +01:00
public sealed partial class CPM
{
2021-08-17 13:56:05 +01:00
/// <inheritdoc />
public bool Identify(IMediaImage imagePlugin, Partition partition)
{
// This will only continue on devices with a chance to have ever been used by CP/M while failing on all others
// It's ugly, but will stop a lot of false positives
switch(imagePlugin.Info.MediaType)
{
case MediaType.Unknown:
case MediaType.Apple32SS:
case MediaType.Apple32DS:
case MediaType.Apple33SS:
case MediaType.Apple33DS:
case MediaType.DOS_525_SS_DD_8:
case MediaType.DOS_525_SS_DD_9:
case MediaType.DOS_525_DS_DD_8:
case MediaType.DOS_525_DS_DD_9:
case MediaType.DOS_525_HD:
case MediaType.DOS_35_SS_DD_8:
case MediaType.DOS_35_SS_DD_9:
case MediaType.DOS_35_DS_DD_8:
case MediaType.DOS_35_DS_DD_9:
case MediaType.DOS_35_HD:
case MediaType.DOS_35_ED:
case MediaType.IBM23FD:
case MediaType.IBM33FD_128:
case MediaType.IBM33FD_256:
case MediaType.IBM33FD_512:
case MediaType.IBM43FD_128:
case MediaType.IBM43FD_256:
case MediaType.IBM53FD_256:
case MediaType.IBM53FD_512:
case MediaType.IBM53FD_1024:
case MediaType.RX01:
case MediaType.RX02:
case MediaType.RX03:
case MediaType.RX50:
case MediaType.ACORN_525_SS_SD_40:
case MediaType.ACORN_525_SS_SD_80:
case MediaType.ACORN_525_SS_DD_40:
case MediaType.ACORN_525_SS_DD_80:
case MediaType.ACORN_525_DS_DD:
case MediaType.ATARI_525_SD:
case MediaType.ATARI_525_ED:
case MediaType.ATARI_525_DD:
case MediaType.CBM_35_DD:
case MediaType.CBM_1540:
case MediaType.CBM_1540_Ext:
case MediaType.CBM_1571:
case MediaType.NEC_8_SD:
case MediaType.NEC_8_DD:
case MediaType.NEC_525_SS:
case MediaType.NEC_525_DS:
case MediaType.NEC_525_HD:
case MediaType.NEC_35_HD_8:
case MediaType.NEC_35_HD_15:
case MediaType.SHARP_525_9:
case MediaType.SHARP_35_9:
case MediaType.ECMA_99_8:
case MediaType.ECMA_99_15:
case MediaType.ECMA_99_26:
case MediaType.ECMA_54:
case MediaType.ECMA_59:
case MediaType.ECMA_66:
case MediaType.ECMA_69_8:
case MediaType.ECMA_69_15:
case MediaType.ECMA_69_26:
case MediaType.ECMA_70:
case MediaType.ECMA_78:
case MediaType.ECMA_78_2:
case MediaType.Apricot_35:
case MediaType.CompactFloppy:
case MediaType.DemiDiskette:
case MediaType.QuickDisk:
case MediaType.Wafer:
case MediaType.ZXMicrodrive:
case MediaType.AppleProfile:
case MediaType.AppleWidget:
case MediaType.AppleHD20:
case MediaType.RA60:
case MediaType.RA80:
case MediaType.RA81:
case MediaType.RC25:
case MediaType.RD31:
case MediaType.RD32:
case MediaType.RD51:
case MediaType.RD52:
case MediaType.RD53:
case MediaType.RD54:
case MediaType.RK06:
case MediaType.RK06_18:
case MediaType.RK07:
case MediaType.RK07_18:
case MediaType.RM02:
case MediaType.RM03:
case MediaType.RM05:
case MediaType.RP02:
case MediaType.RP02_18:
case MediaType.RP03:
case MediaType.RP03_18:
case MediaType.RP04:
case MediaType.RP04_18:
case MediaType.RP05:
case MediaType.RP05_18:
case MediaType.RP06:
case MediaType.RP06_18:
case MediaType.GENERIC_HDD:
2021-10-05 19:24:39 +01:00
case MediaType.FlashDrive:
2021-10-06 14:31:03 +01:00
case MediaType.MetaFloppy_Mod_I:
case MediaType.MetaFloppy_Mod_II: break;
2017-12-19 20:33:03 +00:00
default: return false;
}
2017-12-19 20:33:03 +00:00
// This will try to identify a CP/M filesystem
// However as it contains no identification marks whatsoever it's more something of trial-and-error
// As anything can happen, better try{}catch{} than sorry ;)
try
{
byte[] sector;
2018-06-22 08:08:38 +01:00
ulong sectorSize;
ulong firstDirectorySector;
byte[] directory = null;
2020-07-20 21:11:32 +01:00
_workingDefinition = null;
_label = null;
// Try Amstrad superblock
2020-07-20 21:11:32 +01:00
if(!_cpmFound)
{
// Read CHS = {0,0,1}
2017-07-19 16:37:11 +01:00
sector = imagePlugin.ReadSector(0 + partition.Start);
int amsSbOffset = 0;
2018-06-20 22:22:21 +01:00
uint sig1 = BitConverter.ToUInt32(sector, 0x2B);
uint sig2 = BitConverter.ToUInt32(sector, 0x33) & 0x00FFFFFF;
uint sig3 = BitConverter.ToUInt32(sector, 0x7C);
// PCW16 extended boot record
2020-02-29 18:03:35 +00:00
if(sig1 == 0x4D2F5043 &&
sig2 == 0x004B5344 &&
sig3 == sig1)
amsSbOffset = 0x80;
// Read the superblock
2018-06-22 08:08:38 +01:00
AmstradSuperBlock amsSb =
Marshal.ByteArrayToStructureLittleEndian<AmstradSuperBlock>(sector, amsSbOffset, 16);
// Check that format byte and sidedness indicate the same number of sizes
2020-02-29 18:03:35 +00:00
if((amsSb.format == 0 && (amsSb.sidedness & 0x02) == 0) ||
(amsSb.format == 2 && (amsSb.sidedness & 0x02) == 1) ||
(amsSb.format == 2 && (amsSb.sidedness & 0x02) == 2))
{
// Calculate device limits
2018-06-22 08:08:38 +01:00
ulong sides = (ulong)(amsSb.format == 0 ? 1 : 2);
ulong sectorCount = (ulong)(amsSb.tps * amsSb.spt * (byte)sides);
sectorSize = (ulong)(128 << amsSb.psh);
// Compare device limits from superblock to real limits
2020-02-29 18:03:35 +00:00
if(sectorSize == imagePlugin.Info.SectorSize &&
sectorCount == imagePlugin.Info.Sectors)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector = (ulong)(amsSb.off * amsSb.spt);
// Build a DiscParameterBlock
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = sectorCount == 1440 ? (byte)0xF0 : (byte)0xC0,
spt = amsSb.spt,
bsh = amsSb.bsh
};
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
for(int i = 0; i < _dpb.bsh; i++)
_dpb.blm += (byte)Math.Pow(2, i);
2017-12-19 20:33:03 +00:00
if(sectorCount >= 1440)
{
2020-07-20 21:11:32 +01:00
_dpb.cks = 0x40;
_dpb.drm = 0xFF;
}
else
{
2020-07-20 21:11:32 +01:00
_dpb.cks = 0x10;
_dpb.drm = 0x3F;
}
2018-06-22 08:08:38 +01:00
2020-07-20 21:11:32 +01:00
_dpb.dsm = 0; // I don't care
_dpb.exm = sectorCount == 2880 ? (byte)1 : (byte)0;
_dpb.off = amsSb.off;
_dpb.psh = amsSb.psh;
2017-12-19 20:33:03 +00:00
2020-07-20 21:11:32 +01:00
for(int i = 0; i < _dpb.psh; i++)
_dpb.phm += (byte)Math.Pow(2, i);
2020-02-29 18:03:35 +00:00
2021-08-17 14:27:19 +01:00
_dpb.spt = (ushort)(amsSb.spt * (sectorSize / 128));
uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize);
2020-02-29 18:03:35 +00:00
2017-12-19 20:33:03 +00:00
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start,
directoryLength);
// Build a CP/M disk definition
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = amsSb.tps,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "Amstrad PCW superblock",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = amsSb.spt,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[amsSb.spt]
2020-02-29 18:03:35 +00:00
}
};
2020-02-29 18:03:35 +00:00
for(int si = 0; si < amsSb.spt; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
if(amsSb.format == 2)
{
switch(amsSb.sidedness & 0x02)
{
case 1:
2020-07-20 21:11:32 +01:00
_workingDefinition.order = "SIDES";
2020-02-29 18:03:35 +00:00
break;
case 2:
2020-07-20 21:11:32 +01:00
_workingDefinition.order = "CYLINDERS";
2020-02-29 18:03:35 +00:00
break;
default:
2020-07-20 21:11:32 +01:00
_workingDefinition.order = null;
2020-02-29 18:03:35 +00:00
break;
}
2020-07-20 21:11:32 +01:00
_workingDefinition.side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[amsSb.spt]
2020-02-29 18:03:35 +00:00
};
for(int si = 0; si < amsSb.spt; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2020-02-29 18:03:35 +00:00
else
2020-07-20 21:11:32 +01:00
_workingDefinition.order = null;
2020-07-20 21:11:32 +01:00
_workingDefinition.skew = 2;
_workingDefinition.sofs = 0;
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Found Amstrad superblock.");
}
}
}
// Try CP/M-86 superblock for hard disks
2020-07-20 21:11:32 +01:00
if(!_cpmFound)
{
// Read CHS = {0,0,4}
2017-07-19 16:37:11 +01:00
sector = imagePlugin.ReadSector(3 + partition.Start);
ushort sum = 0;
// Sum of all 16-bit words that make this sector must be 0
2020-02-29 18:03:35 +00:00
for(int i = 0; i < sector.Length; i += 2)
sum += BitConverter.ToUInt16(sector, i);
// It may happen that there is a corrupted superblock
// Better to ignore corrupted than to false positive the rest
if(sum == 0)
{
// Read the superblock
HardDiskSuperBlock hddSb = Marshal.ByteArrayToStructureLittleEndian<HardDiskSuperBlock>(sector);
// Calculate volume size
sectorSize = (ulong)(hddSb.recordsPerSector * 128);
ulong sectorsInPartition = (ulong)(hddSb.cylinders * hddSb.heads * hddSb.sectorsPerTrack);
2020-02-29 18:03:35 +00:00
2017-12-19 20:33:03 +00:00
ulong startingSector =
2020-02-29 18:03:35 +00:00
(ulong)(((hddSb.firstCylinder * hddSb.heads) + hddSb.heads) * hddSb.sectorsPerTrack);
// If volume size corresponds with working partition (this variant will be inside MBR partitioning)
2018-06-22 08:08:38 +01:00
if(sectorSize == imagePlugin.Info.SectorSize &&
startingSector == partition.Start &&
2017-07-19 16:37:11 +01:00
sectorsInPartition + partition.Start <= partition.End)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector = (ulong)(hddSb.off * hddSb.sectorsPerTrack);
// Build a DiscParameterBlock
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = (byte)hddSb.al0,
al1 = (byte)hddSb.al1,
blm = hddSb.blm,
bsh = hddSb.bsh,
cks = hddSb.cks,
drm = hddSb.drm,
dsm = hddSb.dsm,
exm = hddSb.exm,
off = hddSb.off,
2020-02-29 18:03:35 +00:00
// Needed?
phm = 0,
2020-02-29 18:03:35 +00:00
// Needed?
2020-07-20 04:34:16 +01:00
psh = 0,
spt = hddSb.spt
};
2021-08-17 14:27:19 +01:00
uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize);
2020-02-29 18:03:35 +00:00
2017-12-19 20:33:03 +00:00
directory = imagePlugin.ReadSectors(firstDirectorySector + partition.Start,
directoryLength);
2020-02-29 18:03:35 +00:00
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 hard disk superblock.");
// Build a CP/M disk definition
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "HIGH",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = hddSb.cylinders,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 hard disk superblock",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = hddSb.sectorsPerTrack,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[hddSb.sectorsPerTrack]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "SIDES",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[hddSb.sectorsPerTrack]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
for(int si = 0; si < hddSb.sectorsPerTrack; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < hddSb.spt; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
}
}
// Try CP/M-86 format ID for floppies
2020-07-20 21:11:32 +01:00
if(!_cpmFound)
{
// Read CHS = {0,0,1}
2017-07-19 16:37:11 +01:00
sector = imagePlugin.ReadSector(0 + partition.Start);
byte formatByte;
// Check for alternate location of format ID
2020-02-29 18:03:35 +00:00
if(sector.Last() == 0x00 ||
sector.Last() == 0xFF)
if(sector[0x40] == 0x94 ||
sector[0x40] == 0x26)
formatByte = sector[0x40];
else
formatByte = sector.Last();
else
formatByte = sector.Last();
uint firstDirectorySector86 = 0;
// Check format ID
// If it is one of the known IDs, check disk size corresponds to the one we expect
// If so, build a DiscParameterBlock and a CP/M disk definition
// Will not work on over-formatted disks (40 cylinder volume on an 80 cylinder disk,
// something that happens a lot in IBM PC 5.25" disks)
switch((FormatByte)formatByte)
{
case FormatByte.k160:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 320)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 8;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0xC0,
al1 = 0,
blm = 7,
bsh = 3,
cks = 0x10,
drm = 0x3F,
dsm = 0x9B,
exm = 0,
off = 1,
phm = 3,
psh = 2,
spt = 8 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 40,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 8,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[8]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 8; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.k320:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 640)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 16;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0x80,
al1 = 0,
blm = 0x0F,
bsh = 4,
cks = 0x10,
drm = 0x3F,
dsm = 0x9D,
exm = 1,
off = 2,
phm = 3,
psh = 2,
spt = 8 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 40,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 8,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[8]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "SIDES",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[8]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 8; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 8; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.k360:
case FormatByte.k360Alt:
case FormatByte.k360Alt2:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 720)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 36;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0x80,
al1 = 0,
blm = 0x0F,
bsh = 4,
cks = 0x10,
drm = 0x3F,
dsm = 0, // Unknown. Needed?
exm = 1,
off = 4,
phm = 3,
psh = 2,
spt = 9 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 40,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 9,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "SIDES",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.k720:
case FormatByte.k720Alt:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 1440)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 36;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0xF0,
al1 = 0,
blm = 0x0F,
bsh = 4,
cks = 0x40,
drm = 0xFF,
dsm = 0x15E,
exm = 0,
off = 4,
phm = 3,
psh = 2,
spt = 9 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 80,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 9,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "SIDES",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.f720:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 1440)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 18;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0xF0,
al1 = 0,
blm = 0x0F,
bsh = 4,
cks = 0x40,
drm = 0xFF,
dsm = 0x162,
exm = 0,
off = 2,
phm = 3,
psh = 2,
spt = 9 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 80,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 9,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "CYLINDERS",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[9]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 9; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.f1200:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 2400)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 30;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0xC0,
al1 = 0,
blm = 0x1F,
bsh = 5,
cks = 0x40,
drm = 0xFF,
dsm = 0x127,
exm = 1,
off = 2,
phm = 3,
psh = 2,
spt = 15 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "HIGH",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 80,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 15,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[15]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "CYLINDERS",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[15]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 15; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 15; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
case FormatByte.f1440:
2020-02-29 18:03:35 +00:00
if(imagePlugin.Info.SectorSize == 512 &&
imagePlugin.Info.Sectors == 2880)
{
2020-07-20 21:11:32 +01:00
_cpmFound = true;
firstDirectorySector86 = 36;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = 0xC0,
al1 = 0,
blm = 0x1F,
bsh = 5,
cks = 0x40,
drm = 0xFF,
dsm = 0x162,
exm = 1,
off = 2,
phm = 3,
psh = 2,
spt = 18 * 4
};
2020-07-20 21:11:32 +01:00
_workingDefinition = new CpmDefinition
{
2020-07-20 21:11:32 +01:00
al0 = _dpb.al0,
al1 = _dpb.al1,
2020-07-20 04:34:16 +01:00
bitrate = "LOW",
2020-07-20 21:11:32 +01:00
blm = _dpb.blm,
bsh = _dpb.bsh,
2020-07-20 04:34:16 +01:00
bytesPerSector = 512,
cylinders = 80,
2020-07-20 21:11:32 +01:00
drm = _dpb.drm,
dsm = _dpb.dsm,
2020-07-20 04:34:16 +01:00
encoding = "MFM",
evenOdd = false,
2020-07-20 21:11:32 +01:00
exm = _dpb.exm,
2020-07-20 04:34:16 +01:00
label = null,
comment = "CP/M-86 floppy identifier",
2020-07-20 21:11:32 +01:00
ofs = _dpb.off,
2020-07-20 04:34:16 +01:00
sectorsPerTrack = 18,
side1 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 0,
sectorIds = new int[18]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
order = "CYLINDERS",
side2 = new Side
2020-02-29 18:03:35 +00:00
{
2020-07-20 04:34:16 +01:00
sideId = 1,
sectorIds = new int[18]
2020-02-29 18:03:35 +00:00
},
2020-07-20 04:34:16 +01:00
skew = 0,
sofs = 0
};
2017-12-19 20:33:03 +00:00
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 18; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side1.sectorIds[si] = si + 1;
2020-02-29 18:03:35 +00:00
for(int si = 0; si < 18; si++)
2020-07-20 21:11:32 +01:00
_workingDefinition.side2.sectorIds[si] = si + 1;
}
2017-12-19 20:33:03 +00:00
break;
}
2020-07-20 21:11:32 +01:00
if(_cpmFound)
{
2021-08-17 14:27:19 +01:00
uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / imagePlugin.Info.SectorSize);
directory = imagePlugin.ReadSectors(firstDirectorySector86 + partition.Start, directoryLength);
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 floppy identifier.");
}
}
// One of the few CP/M filesystem marks has been found, try for correcteness checking the whole directory
2020-07-20 21:11:32 +01:00
if(_cpmFound)
{
if(CheckDir(directory))
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "First directory block seems correct.");
2020-02-29 18:03:35 +00:00
return true;
}
2020-07-20 21:11:32 +01:00
_cpmFound = false;
}
// Try all definitions
2020-07-20 21:11:32 +01:00
if(!_cpmFound)
{
// Load all definitions
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Trying to load definitions.");
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(LoadDefinitions() &&
_definitions?.definitions != null &&
_definitions.definitions.Count > 0)
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Trying all known definitions.");
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
foreach(CpmDefinition def in from def in _definitions.definitions let sectors =
(ulong)(def.cylinders * def.sides * def.sectorsPerTrack)
2018-06-22 08:08:38 +01:00
where sectors == imagePlugin.Info.Sectors &&
2020-02-29 18:03:35 +00:00
def.bytesPerSector == imagePlugin.Info.SectorSize select def)
{
// Definition seems to describe current disk, at least, same number of volume sectors and bytes per sector
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Trying definition \"{0}\"", def.comment);
ulong offset;
2020-02-29 18:03:35 +00:00
if(def.sofs != 0)
offset = (ulong)def.sofs;
else
offset = (ulong)(def.ofs * def.sectorsPerTrack);
2021-08-17 14:27:19 +01:00
int dirLen = (def.drm + 1) * 32 / def.bytesPerSector;
if(def.sides == 1)
{
2020-07-20 21:11:32 +01:00
_sectorMask = new int[def.side1.sectorIds.Length];
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
for(int m = 0; m < _sectorMask.Length; m++)
_sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0];
}
else
{
// Head changes after every track
if(string.Compare(def.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0)
{
2020-07-20 21:11:32 +01:00
_sectorMask = new int[def.side1.sectorIds.Length + def.side2.sectorIds.Length];
2020-02-29 18:03:35 +00:00
for(int m = 0; m < def.side1.sectorIds.Length; m++)
2020-07-20 21:11:32 +01:00
_sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0];
2020-02-29 18:03:35 +00:00
// Skip first track (first side)
for(int m = 0; m < def.side2.sectorIds.Length; m++)
2020-07-20 21:11:32 +01:00
_sectorMask[m + def.side1.sectorIds.Length] =
2021-08-17 14:27:19 +01:00
def.side2.sectorIds[m] - def.side2.sectorIds[0] +
def.side1.sectorIds.Length;
}
2020-02-29 18:03:35 +00:00
// Head changes after whole side
else if(string.Compare(def.order, "CYLINDERS",
StringComparison.InvariantCultureIgnoreCase) == 0)
{
for(int m = 0; m < def.side1.sectorIds.Length; m++)
2020-07-20 21:11:32 +01:00
_sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0];
2020-02-29 18:03:35 +00:00
// Skip first track (first side) and first track (second side)
for(int m = 0; m < def.side1.sectorIds.Length; m++)
2020-07-20 21:11:32 +01:00
_sectorMask[m + def.side1.sectorIds.Length] =
2021-08-17 14:27:19 +01:00
def.side1.sectorIds[m] - def.side1.sectorIds[0] +
2020-07-20 04:34:16 +01:00
def.side1.sectorIds.Length + def.side2.sectorIds.Length;
}
2020-02-29 18:03:35 +00:00
// TODO: Implement COLUMBIA ordering
else if(string.Compare(def.order, "COLUMBIA",
StringComparison.InvariantCultureIgnoreCase) == 0)
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin",
2020-02-29 18:03:35 +00:00
"Don't know how to handle COLUMBIA ordering, not proceeding with this definition.");
continue;
}
2020-02-29 18:03:35 +00:00
// TODO: Implement EAGLE ordering
2018-06-22 08:08:38 +01:00
else if(string.Compare(def.order, "EAGLE",
StringComparison.InvariantCultureIgnoreCase) == 0)
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin",
2020-02-29 18:03:35 +00:00
"Don't know how to handle EAGLE ordering, not proceeding with this definition.");
continue;
}
else
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin",
2020-02-29 18:03:35 +00:00
"Unknown order type \"{0}\", not proceeding with this definition.",
def.order);
continue;
}
}
// Read the directory marked by this definition
2020-02-29 18:03:35 +00:00
var ms = new MemoryStream();
for(int p = 0; p < dirLen; p++)
{
byte[] dirSector =
2020-07-20 04:34:16 +01:00
imagePlugin.ReadSector((ulong)((int)offset + (int)partition.Start +
2021-08-17 14:27:19 +01:00
(p / _sectorMask.Length * _sectorMask.Length) +
2020-07-20 21:11:32 +01:00
_sectorMask[p % _sectorMask.Length]));
2020-02-29 18:03:35 +00:00
ms.Write(dirSector, 0, dirSector.Length);
}
2017-12-19 20:33:03 +00:00
directory = ms.ToArray();
if(def.evenOdd)
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin",
2020-02-29 18:03:35 +00:00
"Definition contains EVEN-ODD field, with unknown meaning, detection may be wrong.");
// Complement of the directory bytes if needed
if(def.complement)
2018-06-22 08:08:38 +01:00
for(int b = 0; b < directory.Length; b++)
directory[b] = (byte)(~directory[b] & 0xFF);
// Check the directory
if(CheckDir(directory))
{
2020-02-27 23:48:41 +00:00
AaruConsole.DebugWriteLine("CP/M Plugin", "Definition \"{0}\" has a correct directory",
2020-02-29 18:03:35 +00:00
def.comment);
// Build a Disc Parameter Block
2020-07-20 21:11:32 +01:00
_workingDefinition = def;
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
_dpb = new DiscParameterBlock
{
2020-07-20 04:34:16 +01:00
al0 = (byte)def.al0,
al1 = (byte)def.al1,
blm = (byte)def.blm,
bsh = (byte)def.bsh,
2020-02-29 18:03:35 +00:00
// Needed?
2020-07-20 04:34:16 +01:00
cks = 0,
drm = (ushort)def.drm,
dsm = (ushort)def.dsm,
exm = (byte)def.exm,
off = (ushort)def.ofs,
2021-08-17 14:27:19 +01:00
spt = (ushort)(def.sectorsPerTrack * def.bytesPerSector / 128)
};
2020-02-29 18:03:35 +00:00
switch(def.bytesPerSector)
{
case 128:
2020-07-20 21:11:32 +01:00
_dpb.psh = 0;
_dpb.phm = 0;
2020-02-29 18:03:35 +00:00
break;
case 256:
2020-07-20 21:11:32 +01:00
_dpb.psh = 1;
_dpb.phm = 1;
2020-02-29 18:03:35 +00:00
break;
case 512:
2020-07-20 21:11:32 +01:00
_dpb.psh = 2;
_dpb.phm = 3;
2020-02-29 18:03:35 +00:00
break;
case 1024:
2020-07-20 21:11:32 +01:00
_dpb.psh = 3;
_dpb.phm = 7;
2020-02-29 18:03:35 +00:00
break;
case 2048:
2020-07-20 21:11:32 +01:00
_dpb.psh = 4;
_dpb.phm = 15;
2020-02-29 18:03:35 +00:00
break;
case 4096:
2020-07-20 21:11:32 +01:00
_dpb.psh = 5;
_dpb.phm = 31;
2020-02-29 18:03:35 +00:00
break;
case 8192:
2020-07-20 21:11:32 +01:00
_dpb.psh = 6;
_dpb.phm = 63;
2020-02-29 18:03:35 +00:00
break;
case 16384:
2020-07-20 21:11:32 +01:00
_dpb.psh = 7;
_dpb.phm = 127;
2020-02-29 18:03:35 +00:00
break;
case 32768:
2020-07-20 21:11:32 +01:00
_dpb.psh = 8;
_dpb.phm = 255;
2020-02-29 18:03:35 +00:00
break;
}
2020-07-20 21:11:32 +01:00
_cpmFound = true;
_workingDefinition = def;
return true;
}
2020-07-20 21:11:32 +01:00
_label = null;
_labelCreationDate = null;
_labelUpdateDate = null;
}
}
}
// Clear class variables
2020-07-20 21:11:32 +01:00
_cpmFound = false;
_workingDefinition = null;
_dpb = null;
_label = null;
_standardTimestamps = false;
_thirdPartyTimestamps = false;
2020-02-29 18:03:35 +00:00
return false;
}
* TODO: * README.md: * DiscImageChef.sln: * DiscImageChef/Commands/Ls.cs: * DiscImageChef.Filters/GZip.cs: * DiscImageChef.DiscImages/BLU.cs: * DiscImageChef.DiscImages/VHD.cs: * DiscImageChef.DiscImages/VDI.cs: * DiscImageChef.DiscImages/QED.cs: * DiscImageChef.DiscImages/DIM.cs: * DiscImageChef.DiscImages/GDI.cs: * DiscImageChef.Filters/Filter.cs: * DiscImageChef/Commands/Decode.cs: * DiscImageChef.DiscImages/QCOW.cs: * DiscImageChef.Filters/Filters.cs: * DiscImageChef/Core/Statistics.cs: * DiscImageChef.DiscImages/VHDX.cs: * DiscImageChef.DiscImages/Nero.cs: * DiscImageChef/Commands/Verify.cs: * DiscImageChef.DiscImages/UDIF.cs: * DiscImageChef/Commands/Compare.cs: * DiscImageChef/Commands/Analyze.cs: * DiscImageChef.DiscImages/QCOW2.cs: * DiscImageChef/Commands/Entropy.cs: * DiscImageChef/Commands/Formats.cs: * DiscImageChef/Commands/PrintHex.cs: * DiscImageChef.DiscImages/VMware.cs: * DiscImageChef.Settings/Settings.cs: * DiscImageChef/DetectImageFormat.cs: * DiscImageChef/DiscImageChef.csproj: * DiscImageChef.DiscImages/CDRDAO.cs: * DiscImageChef.DiscImages/CPCDSK.cs: * DiscImageChef/Commands/Checksum.cs: * DiscImageChef.DiscImages/CopyQM.cs: * DiscImageChef.DiscImages/CDRWin.cs: * DiscImageChef/Commands/Configure.cs: * DiscImageChef/Commands/DumpMedia.cs: * DiscImageChef/Commands/Statistics.cs: * DiscImageChef.Filters/ZZZNoFilter.cs: * DiscImageChef.DiscImages/TeleDisk.cs: * DiscImageChef.DiscImages/Apple2MG.cs: * DiscImageChef.Filters/OffsetStream.cs: * DiscImageChef.DiscImages/Parallels.cs: * DiscImageChef/Commands/ExtractFiles.cs: * DiscImageChef.DiscImages/DiskCopy42.cs: * DiscImageChef.DiscImages/Alcohol120.cs: * DiscImageChef.DiscImages/ZZZRawImage.cs: * DiscImageChef/Commands/CreateSidecar.cs: * DiscImageChef.DiscImages/ImagePlugin.cs: * DiscImageChef.DiscImages/BlindWrite5.cs: * DiscImageChef.DiscImages/BlindWrite4.cs: * DiscImageChef.Filters/ForcedSeekStream.cs: * DiscImageChef.Filters/Properties/AssemblyInfo.cs: * DiscImageChef.Filters/DiscImageChef.Filters.csproj: * DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj: Added filters. * DiscImageChef.Filesystems/CPM/Info.cs: Do not throw identification exceptions. * DiscImageChef/Plugins.cs: Sorted plugins lists.
2016-09-05 17:37:31 +01:00
catch
{
* TODO: * README.md: * DiscImageChef.sln: * DiscImageChef/Commands/Ls.cs: * DiscImageChef.Filters/GZip.cs: * DiscImageChef.DiscImages/BLU.cs: * DiscImageChef.DiscImages/VHD.cs: * DiscImageChef.DiscImages/VDI.cs: * DiscImageChef.DiscImages/QED.cs: * DiscImageChef.DiscImages/DIM.cs: * DiscImageChef.DiscImages/GDI.cs: * DiscImageChef.Filters/Filter.cs: * DiscImageChef/Commands/Decode.cs: * DiscImageChef.DiscImages/QCOW.cs: * DiscImageChef.Filters/Filters.cs: * DiscImageChef/Core/Statistics.cs: * DiscImageChef.DiscImages/VHDX.cs: * DiscImageChef.DiscImages/Nero.cs: * DiscImageChef/Commands/Verify.cs: * DiscImageChef.DiscImages/UDIF.cs: * DiscImageChef/Commands/Compare.cs: * DiscImageChef/Commands/Analyze.cs: * DiscImageChef.DiscImages/QCOW2.cs: * DiscImageChef/Commands/Entropy.cs: * DiscImageChef/Commands/Formats.cs: * DiscImageChef/Commands/PrintHex.cs: * DiscImageChef.DiscImages/VMware.cs: * DiscImageChef.Settings/Settings.cs: * DiscImageChef/DetectImageFormat.cs: * DiscImageChef/DiscImageChef.csproj: * DiscImageChef.DiscImages/CDRDAO.cs: * DiscImageChef.DiscImages/CPCDSK.cs: * DiscImageChef/Commands/Checksum.cs: * DiscImageChef.DiscImages/CopyQM.cs: * DiscImageChef.DiscImages/CDRWin.cs: * DiscImageChef/Commands/Configure.cs: * DiscImageChef/Commands/DumpMedia.cs: * DiscImageChef/Commands/Statistics.cs: * DiscImageChef.Filters/ZZZNoFilter.cs: * DiscImageChef.DiscImages/TeleDisk.cs: * DiscImageChef.DiscImages/Apple2MG.cs: * DiscImageChef.Filters/OffsetStream.cs: * DiscImageChef.DiscImages/Parallels.cs: * DiscImageChef/Commands/ExtractFiles.cs: * DiscImageChef.DiscImages/DiskCopy42.cs: * DiscImageChef.DiscImages/Alcohol120.cs: * DiscImageChef.DiscImages/ZZZRawImage.cs: * DiscImageChef/Commands/CreateSidecar.cs: * DiscImageChef.DiscImages/ImagePlugin.cs: * DiscImageChef.DiscImages/BlindWrite5.cs: * DiscImageChef.DiscImages/BlindWrite4.cs: * DiscImageChef.Filters/ForcedSeekStream.cs: * DiscImageChef.Filters/Properties/AssemblyInfo.cs: * DiscImageChef.Filters/DiscImageChef.Filters.csproj: * DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj: Added filters. * DiscImageChef.Filesystems/CPM/Info.cs: Do not throw identification exceptions. * DiscImageChef/Plugins.cs: Sorted plugins lists.
2016-09-05 17:37:31 +01:00
//throw ex;
return false;
}
}
2021-08-17 13:56:05 +01:00
/// <inheritdoc />
2017-12-26 08:01:40 +00:00
public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information,
2020-02-29 18:03:35 +00:00
Encoding encoding)
{
2018-06-22 08:08:38 +01:00
Encoding = encoding ?? Encoding.GetEncoding("IBM437");
information = "";
2020-02-29 18:03:35 +00:00
// As the identification is so complex, just call Identify() and relay on its findings
2020-02-29 18:03:35 +00:00
if(!Identify(imagePlugin, partition) ||
2020-07-20 21:11:32 +01:00
!_cpmFound ||
_workingDefinition == null ||
_dpb == null)
2020-02-29 18:03:35 +00:00
return;
2020-02-29 18:03:35 +00:00
var sb = new StringBuilder();
sb.AppendLine("CP/M filesystem");
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(!string.IsNullOrEmpty(_workingDefinition.comment))
sb.AppendFormat("Identified as {0}", _workingDefinition.comment).AppendLine();
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
sb.AppendFormat("Volume block is {0} bytes", 128 << _dpb.bsh).AppendLine();
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(_dpb.dsm > 0)
sb.AppendFormat("Volume contains {0} blocks ({1} bytes)", _dpb.dsm, _dpb.dsm * (128 << _dpb.bsh)).
2020-02-29 18:03:35 +00:00
AppendLine();
2020-07-20 21:11:32 +01:00
sb.AppendFormat("Volume contains {0} directory entries", _dpb.drm + 1).AppendLine();
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(_workingDefinition.sofs > 0)
sb.AppendFormat("Volume reserves {0} sectors for system", _workingDefinition.sofs).AppendLine();
else
2017-12-19 20:33:03 +00:00
sb.AppendFormat("Volume reserves {1} tracks ({0} sectors) for system",
2020-07-20 21:11:32 +01:00
_workingDefinition.ofs * _workingDefinition.sectorsPerTrack, _workingDefinition.ofs).
2020-02-29 18:03:35 +00:00
AppendLine();
2020-07-20 21:11:32 +01:00
if(_workingDefinition.side1.sectorIds.Length >= 2)
2017-07-10 21:37:31 +01:00
{
2020-07-20 21:11:32 +01:00
int interleaveSide1 = _workingDefinition.side1.sectorIds[1] - _workingDefinition.side1.sectorIds[0];
2020-02-29 18:03:35 +00:00
2017-07-10 21:37:31 +01:00
if(interleaveSide1 > 1)
sb.AppendFormat("Side 0 uses {0}:1 software interleaving", interleaveSide1).AppendLine();
}
2017-12-19 20:33:03 +00:00
2020-07-20 21:11:32 +01:00
if(_workingDefinition.sides == 2)
{
2020-07-20 21:11:32 +01:00
if(_workingDefinition.side2.sectorIds.Length >= 2)
2017-07-10 21:37:31 +01:00
{
2020-07-20 21:11:32 +01:00
int interleaveSide2 = _workingDefinition.side2.sectorIds[1] - _workingDefinition.side2.sectorIds[0];
2020-02-29 18:03:35 +00:00
2017-07-10 21:37:31 +01:00
if(interleaveSide2 > 1)
sb.AppendFormat("Side 1 uses {0}:1 software interleaving", interleaveSide2).AppendLine();
}
2018-06-22 08:08:38 +01:00
2020-07-20 21:11:32 +01:00
switch(_workingDefinition.order)
{
case "SIDES":
sb.AppendLine("Head changes after each whole track");
2020-02-29 18:03:35 +00:00
break;
case "CYLINDERS":
sb.AppendLine("Head changes after whole side");
2020-02-29 18:03:35 +00:00
break;
default:
2020-07-20 21:11:32 +01:00
sb.AppendFormat("Unknown how {0} side ordering works", _workingDefinition.order).AppendLine();
2020-02-29 18:03:35 +00:00
break;
}
}
2020-07-20 21:11:32 +01:00
if(_workingDefinition.skew > 0)
sb.AppendFormat("Device uses {0}:1 hardware interleaving", _workingDefinition.skew).AppendLine();
2020-07-20 21:11:32 +01:00
if(_workingDefinition.sofs > 0)
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H SOFS {7}", _dpb.bsh,
_dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.sofs).
2020-02-29 18:03:35 +00:00
AppendLine();
else
2020-07-20 21:11:32 +01:00
sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H OFS {7}", _dpb.bsh,
_dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.ofs).
2020-02-29 18:03:35 +00:00
AppendLine();
2020-07-20 21:11:32 +01:00
if(_label != null)
sb.AppendFormat("Volume label {0}", _label).AppendLine();
2020-07-20 21:11:32 +01:00
if(_standardTimestamps)
2020-02-29 18:03:35 +00:00
sb.AppendLine("Volume uses standard CP/M timestamps");
2020-07-20 21:11:32 +01:00
if(_thirdPartyTimestamps)
2020-02-29 18:03:35 +00:00
sb.AppendLine("Volume uses third party timestamps");
2020-07-20 21:11:32 +01:00
if(_labelCreationDate != null)
sb.AppendFormat("Volume created on {0}", DateHandlers.CpmToDateTime(_labelCreationDate)).AppendLine();
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(_labelUpdateDate != null)
sb.AppendFormat("Volume updated on {0}", DateHandlers.CpmToDateTime(_labelUpdateDate)).AppendLine();
2018-06-22 08:08:38 +01:00
XmlFsType = new FileSystemType();
2020-07-20 21:11:32 +01:00
XmlFsType.Bootable |= _workingDefinition.sofs > 0 || _workingDefinition.ofs > 0;
XmlFsType.ClusterSize = (uint)(128 << _dpb.bsh);
2020-02-29 18:03:35 +00:00
2020-07-20 21:11:32 +01:00
if(_dpb.dsm > 0)
XmlFsType.Clusters = _dpb.dsm;
2020-02-29 18:03:35 +00:00
else
XmlFsType.Clusters = partition.End - partition.Start;
2020-07-20 21:11:32 +01:00
if(_labelCreationDate != null)
{
2020-07-20 21:11:32 +01:00
XmlFsType.CreationDate = DateHandlers.CpmToDateTime(_labelCreationDate);
2017-12-26 08:01:40 +00:00
XmlFsType.CreationDateSpecified = true;
}
2018-06-22 08:08:38 +01:00
2020-07-20 21:11:32 +01:00
if(_labelUpdateDate != null)
{
2020-07-20 21:11:32 +01:00
XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(_labelUpdateDate);
2017-12-26 08:01:40 +00:00
XmlFsType.ModificationDateSpecified = true;
}
2018-06-22 08:08:38 +01:00
XmlFsType.Type = "CP/M";
2020-07-20 21:11:32 +01:00
XmlFsType.VolumeName = _label;
information = sb.ToString();
}
}
2017-12-19 20:33:03 +00:00
}