From 1be537379696401d09c44a9b8661f759fdbc604b Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Apr 2019 22:05:33 +0100 Subject: [PATCH] Move FAT's BPB detection to a separate method. --- .../.idea/contentModel.xml | 1 + .../DiscImageChef.Filesystems.csproj | 1 + DiscImageChef.Filesystems/FAT/BPB.cs | 747 ++++++++++++ DiscImageChef.Filesystems/FAT/Consts.cs | 20 + DiscImageChef.Filesystems/FAT/Info.cs | 1059 ++++------------- 5 files changed, 980 insertions(+), 848 deletions(-) create mode 100644 DiscImageChef.Filesystems/FAT/BPB.cs diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index cdca9fc82..cddd17616 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -1247,6 +1247,7 @@ + diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index 1b1004f0c..8449a4131 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -60,6 +60,7 @@ + diff --git a/DiscImageChef.Filesystems/FAT/BPB.cs b/DiscImageChef.Filesystems/FAT/BPB.cs new file mode 100644 index 000000000..ca5663ae6 --- /dev/null +++ b/DiscImageChef.Filesystems/FAT/BPB.cs @@ -0,0 +1,747 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : BPB.cs +// Author(s) : Natalia Portillo +// +// Component : Microsoft FAT filesystem plugin. +// +// --[ Description ] ---------------------------------------------------------- +// +// Identifies the Microsoft FAT Boot Parameter Block variant. +// +// --[ 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-2019 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.IO; +using System.Linq; +using System.Text; +using DiscImageChef.CommonTypes; +using DiscImageChef.CommonTypes.Enums; +using DiscImageChef.CommonTypes.Interfaces; +using DiscImageChef.Console; +using DiscImageChef.Helpers; + +namespace DiscImageChef.Filesystems.FAT +{ + public partial class FAT + { + static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, + Partition partition, + out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, + out AtariParameterBlock atariBpb, + out byte minBootNearJump, + out bool andosOemCorrect, out bool bootable) + { + fakeBpb = new BiosParameterBlockEbpb(); + minBootNearJump = 0; + andosOemCorrect = false; + bootable = false; + + humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); + atariBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + + ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; + + // Check clusters for Human68k are correct + bool humanClustersCorrect = humanBpb.clusters == 0 + ? humanBpb.big_clusters == expectedClusters + : humanBpb.clusters == expectedClusters; + + // Check OEM for Human68k is correct + bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && + bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && + bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && + bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && + bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && + bpbSector[17] >= 0x20; + + // Check correct branch for Human68k + bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE; + + DicConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); + DicConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); + DicConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); + + // If all Human68k checks are correct, it is a Human68k FAT16 + bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; + if(useHumanBpb) + { + DicConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); + + fakeBpb.jump = humanBpb.jump; + fakeBpb.oem_name = humanBpb.oem_name; + fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; + fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); + fakeBpb.fats_no = 2; + fakeBpb.root_ent = humanBpb.root_ent; + fakeBpb.media = humanBpb.media; + fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); + fakeBpb.boot_code = humanBpb.boot_code; + fakeBpb.sectors = humanBpb.clusters; + fakeBpb.big_sectors = humanBpb.big_clusters; + + return BpbKind.Human; + } + + MsxParameterBlock msxBpb = new MsxParameterBlock(); + BiosParameterBlock2 dos2Bpb = new BiosParameterBlock2(); + BiosParameterBlock30 dos30Bpb = new BiosParameterBlock30(); + BiosParameterBlock32 dos32Bpb = new BiosParameterBlock32(); + BiosParameterBlock33 dos33Bpb = new BiosParameterBlock33(); + BiosParameterBlockShortEbpb shortEbpb = new BiosParameterBlockShortEbpb(); + BiosParameterBlockEbpb ebpb = new BiosParameterBlockEbpb(); + Fat32ParameterBlockShort shortFat32Bpb = new Fat32ParameterBlockShort(); + Fat32ParameterBlock fat32Bpb = new Fat32ParameterBlock(); + ApricotLabel apricotBpb = new ApricotLabel(); + + bool useAtariBpb = false; + bool useMsxBpb = false; + bool useDos2Bpb = false; + bool useDos3Bpb = false; + bool useDos32Bpb = false; + bool useDos33Bpb = false; + bool userShortExtendedBpb = false; + bool useExtendedBpb = false; + bool useShortFat32 = false; + bool useLongFat32 = false; + bool useApricotBpb = false; + bool useDecRainbowBpb = false; + + if(imagePlugin.Info.SectorSize >= 256 && !useHumanBpb) + { + msxBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos2Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos30Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos33Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + shortEbpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + ebpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + fat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + apricotBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + + int bitsInBpsMsx = CountBits.Count(msxBpb.bps); + int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); + int bitsInBpsDos40 = CountBits.Count(ebpb.bps); + int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); + int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); + int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); + + bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || + msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; + bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || + dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || + dos33Bpb.spc == 64; + bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || + ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; + bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || + shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || + shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || + shortFat32Bpb.spc == 64; + bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || + fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || + fat32Bpb.spc == 64; + bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || + apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || + apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || + apricotBpb.mainBPB.spc == 64; + + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + { + atariBpb.sectors /= 4; + msxBpb.sectors /= 4; + dos2Bpb.sectors /= 4; + dos30Bpb.sectors /= 4; + dos32Bpb.sectors /= 4; + dos33Bpb.sectors /= 4; + dos33Bpb.big_sectors /= 4; + shortEbpb.sectors /= 4; + shortEbpb.big_sectors /= 4; + ebpb.sectors /= 4; + ebpb.big_sectors /= 4; + shortFat32Bpb.sectors /= 4; + shortFat32Bpb.big_sectors /= 4; + shortFat32Bpb.huge_sectors /= 4; + fat32Bpb.sectors /= 4; + fat32Bpb.big_sectors /= 4; + apricotBpb.mainBPB.sectors /= 4; + } + + andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && + dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && + dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && + dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; + + if(bitsInBpsFat32 == 1 && correctSpcFat32 && fat32Bpb.fats_no <= 2 && + fat32Bpb.sectors == 0 && + fat32Bpb.spfat == 0 && fat32Bpb.signature == 0x29 && + Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") + { + DicConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); + useLongFat32 = true; + minBootNearJump = 0x58; + return BpbKind.LongFat32; + } + + if(bitsInBpsFat32Short == 1 && correctSpcFat32Short && shortFat32Bpb.fats_no <= 2 && + shortFat32Bpb.sectors == 0 && shortFat32Bpb.spfat == 0 && + shortFat32Bpb.signature == 0x28) + { + DicConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); + useShortFat32 = shortFat32Bpb.big_sectors == 0 + ? shortFat32Bpb.huge_sectors <= partition.End - partition.Start + 1 + : shortFat32Bpb.big_sectors <= partition.End - partition.Start + 1; + minBootNearJump = 0x57; + return BpbKind.ShortFat32; + } + + if(bitsInBpsMsx == 1 && correctSpcMsx && + msxBpb.fats_no <= 2 && msxBpb.root_ent > 0 && + msxBpb.sectors <= partition.End - partition.Start + 1 && + msxBpb.spfat > 0 && + Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") + { + DicConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); + useMsxBpb = true; + } + else if(bitsInBpsApricot == 1 && correctSpcApricot && + apricotBpb.mainBPB.fats_no <= 2 && + apricotBpb.mainBPB.root_ent > 0 && + apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 && + apricotBpb.mainBPB.spfat > 0 && + apricotBpb.partitionCount == 0) + { + DicConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); + useApricotBpb = true; + } + else if(bitsInBpsDos40 == 1 && correctSpcDos40 && ebpb.fats_no <= 2 && ebpb.root_ent > 0 && + ebpb.spfat > 0 && (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) + { + if(ebpb.sectors == 0) + { + if(ebpb.big_sectors <= partition.End - partition.Start + 1) + if(ebpb.signature == 0x29 || andosOemCorrect) + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); + useExtendedBpb = true; + minBootNearJump = 0x3C; + } + else + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); + userShortExtendedBpb = true; + minBootNearJump = 0x29; + } + } + else if(ebpb.sectors <= partition.End - partition.Start + 1) + if(ebpb.signature == 0x29 || andosOemCorrect) + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); + useExtendedBpb = true; + minBootNearJump = 0x3C; + } + else + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); + userShortExtendedBpb = true; + minBootNearJump = 0x29; + } + } + else if(bitsInBpsDos33 == 1 && correctSpcDos33 && + dos33Bpb.rsectors < partition.End - partition.Start && + dos33Bpb.fats_no <= 2 && + dos33Bpb.root_ent > 0 && dos33Bpb.spfat > 0) + if(dos33Bpb.sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.big_sectors > 0 && + dos33Bpb.big_sectors <= partition.End - partition.Start + 1) + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); + useDos33Bpb = true; + minBootNearJump = 0x22; + } + else if(dos33Bpb.big_sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.sectors > 0 && + dos33Bpb.sectors <= partition.End - partition.Start + 1) + if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") + { + DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); + useAtariBpb = true; + } + else + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); + useDos33Bpb = true; + minBootNearJump = 0x22; + } + else + { + if(dos32Bpb.hsectors <= partition.Start && + dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); + useDos32Bpb = true; + minBootNearJump = 0x1E; + } + else if(dos30Bpb.sptrk > 0 && dos30Bpb.sptrk < 64 && dos30Bpb.heads > 0 && dos30Bpb.heads < 256) + if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") + { + DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); + useAtariBpb = true; + } + else + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); + useDos3Bpb = true; + minBootNearJump = 0x1C; + } + else + { + if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") + { + DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); + useAtariBpb = true; + } + else + { + DicConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); + useDos2Bpb = true; + minBootNearJump = 0x16; + } + } + } + } + + // DEC Rainbow, lacks a BPB but has a very concrete structure... + if(imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512 && !useAtariBpb && !useMsxBpb && + !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && + !userShortExtendedBpb && !useExtendedBpb && + !useHumanBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb) + { + // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) + byte z80Di = bpbSector[0]; + // First FAT1 sector resides at LBA 0x14 + byte[] fat1Sector0 = imagePlugin.ReadSector(0x14); + // First FAT2 sector resides at LBA 0x1A + byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A); + bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; + // Volume is software interleaved 2:1 + MemoryStream rootMs = new MemoryStream(); + foreach(byte[] tmp in from ulong rootSector in new[] {0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20} + select imagePlugin.ReadSector(rootSector)) rootMs.Write(tmp, 0, tmp.Length); + + byte[] rootDir = rootMs.ToArray(); + bool validRootDir = true; + + // Iterate all root directory + for(int e = 0; e < 96 * 32; e += 32) + { + for(int c = 0; c < 11; c++) + if(rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05 || + rootDir[c + e] == 0xFF || + rootDir[c + e] == 0x2E) + { + validRootDir = false; + break; + } + + if(!validRootDir) break; + } + + if(z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && + validRootDir) + { + useDecRainbowBpb = true; + DicConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); + fakeBpb.bps = 512; + fakeBpb.spc = 1; + fakeBpb.rsectors = 20; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 96; + fakeBpb.sectors = 800; + fakeBpb.media = 0xFA; + fakeBpb.sptrk = 10; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 3; + bootable = true; + fakeBpb.boot_code = bpbSector; + return BpbKind.DecRainbow; + } + } + + if(!useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && + !useDos33Bpb && + !useHumanBpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 && + !useApricotBpb && !useDecRainbowBpb) + { + byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start); + switch(fatSector[0]) + { + case 0xE5: + if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 2002; + fakeBpb.media = 0xE5; + fakeBpb.sptrk = 26; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } + + break; + case 0xFD: + if(imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 4; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 68; + fakeBpb.sectors = 4004; + fakeBpb.media = 0xFD; + fakeBpb.sptrk = 26; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 6; + } + else if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 4; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 68; + fakeBpb.sectors = 2002; + fakeBpb.media = 0xFD; + fakeBpb.sptrk = 26; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 6; + } + + break; + case 0xFE: + if(imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); + fakeBpb.bps = 512; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 320; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } + else if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 68; + fakeBpb.sectors = 2002; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 26; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 6; + } + else if(imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 1024; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 192; + fakeBpb.sectors = 1232; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 2; + } + else if(imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 1024; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 6192; + fakeBpb.sectors = 616; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + } + else if(imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 2; + fakeBpb.rsectors = 54; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 720; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 18; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 4; + } + else if(imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); + fakeBpb.bps = 512; + fakeBpb.spc = 2; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 112; + fakeBpb.sectors = 640; + fakeBpb.media = 0xFF; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } + + break; + case 0xFF: + if(imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) + { + DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); + fakeBpb.bps = 512; + fakeBpb.spc = 2; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 112; + fakeBpb.sectors = 640; + fakeBpb.media = 0xFF; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } + + break; + } + + // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code + bootable |= bpbSector[0] == 0xFA || + bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F || + bpbSector[0] == 0xE9 && + BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC; + fakeBpb.boot_code = bpbSector; + return BpbKind.Hardcoded; + } + + if(useExtendedBpb) + { + fakeBpb = ebpb; + return BpbKind.Extended; + } + + if(userShortExtendedBpb) + { + fakeBpb.jump = shortEbpb.jump; + fakeBpb.oem_name = shortEbpb.oem_name; + fakeBpb.bps = shortEbpb.bps; + fakeBpb.spc = shortEbpb.spc; + fakeBpb.rsectors = shortEbpb.rsectors; + fakeBpb.fats_no = shortEbpb.fats_no; + fakeBpb.root_ent = shortEbpb.root_ent; + fakeBpb.sectors = shortEbpb.sectors; + fakeBpb.media = shortEbpb.media; + fakeBpb.spfat = shortEbpb.spfat; + fakeBpb.sptrk = shortEbpb.sptrk; + fakeBpb.heads = shortEbpb.heads; + fakeBpb.hsectors = shortEbpb.hsectors; + fakeBpb.big_sectors = shortEbpb.big_sectors; + fakeBpb.drive_no = shortEbpb.drive_no; + fakeBpb.flags = shortEbpb.flags; + fakeBpb.signature = shortEbpb.signature; + fakeBpb.serial_no = shortEbpb.serial_no; + fakeBpb.boot_code = shortEbpb.boot_code; + fakeBpb.boot_signature = shortEbpb.boot_signature; + return BpbKind.ShortExtended; + } + + if(useDos33Bpb) + { + fakeBpb.jump = dos33Bpb.jump; + fakeBpb.oem_name = dos33Bpb.oem_name; + fakeBpb.bps = dos33Bpb.bps; + fakeBpb.spc = dos33Bpb.spc; + fakeBpb.rsectors = dos33Bpb.rsectors; + fakeBpb.fats_no = dos33Bpb.fats_no; + fakeBpb.root_ent = dos33Bpb.root_ent; + fakeBpb.sectors = dos33Bpb.sectors; + fakeBpb.media = dos33Bpb.media; + fakeBpb.spfat = dos33Bpb.spfat; + fakeBpb.sptrk = dos33Bpb.sptrk; + fakeBpb.heads = dos33Bpb.heads; + fakeBpb.hsectors = dos33Bpb.hsectors; + fakeBpb.big_sectors = dos33Bpb.big_sectors; + fakeBpb.boot_code = dos33Bpb.boot_code; + fakeBpb.boot_signature = dos33Bpb.boot_signature; + return BpbKind.Dos33; + } + + if(useDos32Bpb) + { + fakeBpb.jump = dos32Bpb.jump; + fakeBpb.oem_name = dos32Bpb.oem_name; + fakeBpb.bps = dos32Bpb.bps; + fakeBpb.spc = dos32Bpb.spc; + fakeBpb.rsectors = dos32Bpb.rsectors; + fakeBpb.fats_no = dos32Bpb.fats_no; + fakeBpb.root_ent = dos32Bpb.root_ent; + fakeBpb.sectors = dos32Bpb.sectors; + fakeBpb.media = dos32Bpb.media; + fakeBpb.spfat = dos32Bpb.spfat; + fakeBpb.sptrk = dos32Bpb.sptrk; + fakeBpb.heads = dos32Bpb.heads; + fakeBpb.hsectors = dos32Bpb.hsectors; + fakeBpb.boot_code = dos32Bpb.boot_code; + fakeBpb.boot_signature = dos32Bpb.boot_signature; + return BpbKind.Dos32; + } + + if(useDos3Bpb) + { + fakeBpb.jump = dos30Bpb.jump; + fakeBpb.oem_name = dos30Bpb.oem_name; + fakeBpb.bps = dos30Bpb.bps; + fakeBpb.spc = dos30Bpb.spc; + fakeBpb.rsectors = dos30Bpb.rsectors; + fakeBpb.fats_no = dos30Bpb.fats_no; + fakeBpb.root_ent = dos30Bpb.root_ent; + fakeBpb.sectors = dos30Bpb.sectors; + fakeBpb.media = dos30Bpb.media; + fakeBpb.spfat = dos30Bpb.spfat; + fakeBpb.sptrk = dos30Bpb.sptrk; + fakeBpb.heads = dos30Bpb.heads; + fakeBpb.hsectors = dos30Bpb.hsectors; + fakeBpb.boot_code = dos30Bpb.boot_code; + fakeBpb.boot_signature = dos30Bpb.boot_signature; + return BpbKind.Dos3; + } + + if(useDos2Bpb) + { + fakeBpb.jump = dos2Bpb.jump; + fakeBpb.oem_name = dos2Bpb.oem_name; + fakeBpb.bps = dos2Bpb.bps; + fakeBpb.spc = dos2Bpb.spc; + fakeBpb.rsectors = dos2Bpb.rsectors; + fakeBpb.fats_no = dos2Bpb.fats_no; + fakeBpb.root_ent = dos2Bpb.root_ent; + fakeBpb.sectors = dos2Bpb.sectors; + fakeBpb.media = dos2Bpb.media; + fakeBpb.spfat = dos2Bpb.spfat; + fakeBpb.boot_code = dos2Bpb.boot_code; + fakeBpb.boot_signature = dos2Bpb.boot_signature; + return BpbKind.Dos2; + } + + if(useMsxBpb) + { + fakeBpb.jump = msxBpb.jump; + fakeBpb.oem_name = msxBpb.oem_name; + fakeBpb.bps = msxBpb.bps; + fakeBpb.spc = msxBpb.spc; + fakeBpb.rsectors = msxBpb.rsectors; + fakeBpb.fats_no = msxBpb.fats_no; + fakeBpb.root_ent = msxBpb.root_ent; + fakeBpb.sectors = msxBpb.sectors; + fakeBpb.media = msxBpb.media; + fakeBpb.spfat = msxBpb.spfat; + fakeBpb.sptrk = msxBpb.sptrk; + fakeBpb.heads = msxBpb.heads; + fakeBpb.hsectors = msxBpb.hsectors; + fakeBpb.boot_code = msxBpb.boot_code; + fakeBpb.boot_signature = msxBpb.boot_signature; + fakeBpb.serial_no = msxBpb.serial_no; + // TODO: Is there any way to check this? + bootable = true; + return BpbKind.Msx; + } + + if(useAtariBpb) + { + fakeBpb.jump = atariBpb.jump; + fakeBpb.oem_name = atariBpb.oem_name; + fakeBpb.bps = atariBpb.bps; + fakeBpb.spc = atariBpb.spc; + fakeBpb.rsectors = atariBpb.rsectors; + fakeBpb.fats_no = atariBpb.fats_no; + fakeBpb.root_ent = atariBpb.root_ent; + fakeBpb.sectors = atariBpb.sectors; + fakeBpb.media = atariBpb.media; + fakeBpb.spfat = atariBpb.spfat; + fakeBpb.sptrk = atariBpb.sptrk; + fakeBpb.heads = atariBpb.heads; + fakeBpb.boot_code = atariBpb.boot_code; + return BpbKind.Atari; + } + + if(useApricotBpb) + { + fakeBpb.bps = apricotBpb.mainBPB.bps; + fakeBpb.spc = apricotBpb.mainBPB.spc; + fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; + fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; + fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; + fakeBpb.sectors = apricotBpb.mainBPB.sectors; + fakeBpb.media = apricotBpb.mainBPB.media; + fakeBpb.spfat = apricotBpb.mainBPB.spfat; + fakeBpb.sptrk = apricotBpb.spt; + bootable = apricotBpb.bootType > 0; + + if(apricotBpb.bootLocation > 0 && + apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) + fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation, + (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / + imagePlugin.Info.SectorSize); + return BpbKind.Apricot; + } + + return BpbKind.None; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.Filesystems/FAT/Consts.cs b/DiscImageChef.Filesystems/FAT/Consts.cs index ff5e29a41..82efebd50 100644 --- a/DiscImageChef.Filesystems/FAT/Consts.cs +++ b/DiscImageChef.Filesystems/FAT/Consts.cs @@ -147,5 +147,25 @@ namespace DiscImageChef.Filesystems.FAT Reserved = 0x80, LFN = 0x0F } + + enum BpbKind + { + None, + Hardcoded, + Atari, + Msx, + Dos2, + Dos3, + Dos32, + Dos33, + ShortExtended, + Extended, + ShortFat32, + LongFat32, + Andos, + Apricot, + DecRainbow, + Human + } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/FAT/Info.cs b/DiscImageChef.Filesystems/FAT/Info.cs index b2fb4223a..e21e34f79 100644 --- a/DiscImageChef.Filesystems/FAT/Info.cs +++ b/DiscImageChef.Filesystems/FAT/Info.cs @@ -344,869 +344,222 @@ namespace DiscImageChef.Filesystems.FAT StringBuilder sb = new StringBuilder(); XmlFsType = new FileSystemType(); - bool useAtariBpb = false; - bool useMsxBpb = false; - bool useDos2Bpb = false; - bool useDos3Bpb = false; - bool useDos32Bpb = false; - bool useDos33Bpb = false; - bool userShortExtendedBpb = false; - bool useExtendedBpb = false; - bool useShortFat32 = false; - bool useLongFat32 = false; - bool andosOemCorrect = false; - bool useApricotBpb = false; - bool useDecRainbowBpb = false; - bool useHumanBpb; - - AtariParameterBlock atariBpb = new AtariParameterBlock(); - MsxParameterBlock msxBpb = new MsxParameterBlock(); - BiosParameterBlock2 dos2Bpb = new BiosParameterBlock2(); - BiosParameterBlock30 dos30Bpb = new BiosParameterBlock30(); - BiosParameterBlock32 dos32Bpb = new BiosParameterBlock32(); - BiosParameterBlock33 dos33Bpb = new BiosParameterBlock33(); - BiosParameterBlockShortEbpb shortEbpb = new BiosParameterBlockShortEbpb(); - BiosParameterBlockEbpb ebpb = new BiosParameterBlockEbpb(); - Fat32ParameterBlockShort shortFat32Bpb = new Fat32ParameterBlockShort(); - Fat32ParameterBlock fat32Bpb = new Fat32ParameterBlock(); - ApricotLabel apricotBpb = new ApricotLabel(); - uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; byte[] bpbSector = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb); - HumanParameterBlock humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); + BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, + out BiosParameterBlockEbpb fakeBpb, + out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, + out byte minBootNearJump, out bool andosOemCorrect, + out bool bootable); - ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; - - // Check clusters for Human68k are correct - bool humanClustersCorrect = humanBpb.clusters == 0 - ? humanBpb.big_clusters == expectedClusters - : humanBpb.clusters == expectedClusters; - - // Check OEM for Human68k is correct - bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && - bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && - bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && - bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && - bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && - bpbSector[17] >= 0x20; - - // Check correct branch for Human68k - bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE; - - DicConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); - DicConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); - DicConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); - - // If all Human68k checks are correct, it is a Human68k FAT16 - useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; - if(useHumanBpb) DicConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); - - byte minBootNearJump = 0; - - if(imagePlugin.Info.SectorSize >= 256 && !useHumanBpb) - { - atariBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - msxBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos2Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos30Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos33Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - shortEbpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - ebpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - shortFat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - fat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - apricotBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - - int bitsInBpsAtari = CountBits.Count(atariBpb.bps); - int bitsInBpsMsx = CountBits.Count(msxBpb.bps); - int bitsInBpsDos20 = CountBits.Count(dos2Bpb.bps); - int bitsInBpsDos30 = CountBits.Count(dos30Bpb.bps); - int bitsInBpsDos32 = CountBits.Count(dos32Bpb.bps); - int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); - int bitsInBpsDos34 = CountBits.Count(shortEbpb.bps); - int bitsInBpsDos40 = CountBits.Count(ebpb.bps); - int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); - int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); - int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); - - bool correctSpcAtari = atariBpb.spc == 1 || atariBpb.spc == 2 || atariBpb.spc == 4 || - atariBpb.spc == 8 || atariBpb.spc == 16 || atariBpb.spc == 32 || - atariBpb.spc == 64; - bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || - msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; - bool correctSpcDos20 = dos2Bpb.spc == 1 || dos2Bpb.spc == 2 || dos2Bpb.spc == 4 || dos2Bpb.spc == 8 || - dos2Bpb.spc == 16 || dos2Bpb.spc == 32 || dos2Bpb.spc == 64; - bool correctSpcDos30 = dos30Bpb.spc == 1 || dos30Bpb.spc == 2 || dos30Bpb.spc == 4 || - dos30Bpb.spc == 8 || dos30Bpb.spc == 16 || dos30Bpb.spc == 32 || - dos30Bpb.spc == 64; - bool correctSpcDos32 = dos32Bpb.spc == 1 || dos32Bpb.spc == 2 || dos32Bpb.spc == 4 || - dos32Bpb.spc == 8 || dos32Bpb.spc == 16 || dos32Bpb.spc == 32 || - dos32Bpb.spc == 64; - bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || - dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || - dos33Bpb.spc == 64; - bool correctSpcDos34 = shortEbpb.spc == 1 || shortEbpb.spc == 2 || shortEbpb.spc == 4 || - shortEbpb.spc == 8 || shortEbpb.spc == 16 || shortEbpb.spc == 32 || - shortEbpb.spc == 64; - bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || - ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; - bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || - shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || - shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || - shortFat32Bpb.spc == 64; - bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || - fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || - fat32Bpb.spc == 64; - bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || - apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || - apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || - apricotBpb.mainBPB.spc == 64; - - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - atariBpb.sectors /= 4; - msxBpb.sectors /= 4; - dos2Bpb.sectors /= 4; - dos30Bpb.sectors /= 4; - dos32Bpb.sectors /= 4; - dos33Bpb.sectors /= 4; - dos33Bpb.big_sectors /= 4; - shortEbpb.sectors /= 4; - shortEbpb.big_sectors /= 4; - ebpb.sectors /= 4; - ebpb.big_sectors /= 4; - shortFat32Bpb.sectors /= 4; - shortFat32Bpb.big_sectors /= 4; - shortFat32Bpb.huge_sectors /= 4; - fat32Bpb.sectors /= 4; - fat32Bpb.big_sectors /= 4; - apricotBpb.mainBPB.sectors /= 4; - } - - andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && - dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && - dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && - dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; - - if(bitsInBpsFat32 == 1 && correctSpcFat32 && fat32Bpb.fats_no <= 2 && - fat32Bpb.sectors == 0 && - fat32Bpb.spfat == 0 && fat32Bpb.signature == 0x29 && - Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") - { - DicConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); - useLongFat32 = true; - minBootNearJump = 0x58; - } - else if(bitsInBpsFat32Short == 1 && correctSpcFat32Short && shortFat32Bpb.fats_no <= 2 && - shortFat32Bpb.sectors == 0 && shortFat32Bpb.spfat == 0 && - shortFat32Bpb.signature == 0x28) - { - DicConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); - useShortFat32 = shortFat32Bpb.big_sectors == 0 - ? shortFat32Bpb.huge_sectors <= partition.End - partition.Start + 1 - : shortFat32Bpb.big_sectors <= partition.End - partition.Start + 1; - minBootNearJump = 0x57; - } - else if(bitsInBpsMsx == 1 && - correctSpcMsx && msxBpb.fats_no <= 2 && msxBpb.root_ent > 0 && - msxBpb.sectors <= partition.End - partition.Start + 1 && - msxBpb.spfat > 0 && - Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") - { - DicConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); - useMsxBpb = true; - } - else if(bitsInBpsApricot == 1 && correctSpcApricot && - apricotBpb.mainBPB.fats_no <= 2 && - apricotBpb.mainBPB.root_ent > 0 && - apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 && - apricotBpb.mainBPB.spfat > 0 && - apricotBpb.partitionCount == 0) - { - DicConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); - useApricotBpb = true; - } - else if(bitsInBpsDos40 == 1 && correctSpcDos40 && ebpb.fats_no <= 2 && ebpb.root_ent > 0 && - ebpb.spfat > 0 && (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) - { - if(ebpb.sectors == 0) - { - if(ebpb.big_sectors <= partition.End - partition.Start + 1) - if(ebpb.signature == 0x29 || andosOemCorrect) - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); - useExtendedBpb = true; - minBootNearJump = 0x3C; - } - else - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); - userShortExtendedBpb = true; - minBootNearJump = 0x29; - } - } - else if(ebpb.sectors <= partition.End - partition.Start + 1) - if(ebpb.signature == 0x29 || andosOemCorrect) - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); - useExtendedBpb = true; - minBootNearJump = 0x3C; - } - else - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); - userShortExtendedBpb = true; - minBootNearJump = 0x29; - } - } - else if(bitsInBpsDos33 == 1 && correctSpcDos33 && - dos33Bpb.rsectors < partition.End - partition.Start && - dos33Bpb.fats_no <= 2 && - dos33Bpb.root_ent > 0 && dos33Bpb.spfat > 0) - if(dos33Bpb.sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.big_sectors > 0 && - dos33Bpb.big_sectors <= partition.End - partition.Start + 1) - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); - useDos33Bpb = true; - minBootNearJump = 0x22; - } - else if(dos33Bpb.big_sectors == 0 && dos33Bpb.hsectors <= partition.Start && dos33Bpb.sectors > 0 && - dos33Bpb.sectors <= partition.End - partition.Start + 1) - if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") - { - DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); - useAtariBpb = true; - } - else - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); - useDos33Bpb = true; - minBootNearJump = 0x22; - } - else - { - if(dos32Bpb.hsectors <= partition.Start && - dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); - useDos32Bpb = true; - minBootNearJump = 0x1E; - } - else if(dos30Bpb.sptrk > 0 && dos30Bpb.sptrk < 64 && dos30Bpb.heads > 0 && dos30Bpb.heads < 256) - if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") - { - DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); - useAtariBpb = true; - } - else - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); - useDos3Bpb = true; - minBootNearJump = 0x1C; - } - else - { - if(atariBpb.jump[0] == 0x60 || atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") - { - DicConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); - useAtariBpb = true; - } - else - { - DicConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); - useDos2Bpb = true; - minBootNearJump = 0x16; - } - } - } - } - - BiosParameterBlockEbpb fakeBpb = new BiosParameterBlockEbpb(); - bool isFat12 = false; - bool isFat16 = false; - bool isFat32 = false; - ulong rootDirectorySector = 0; - string extraInfo = null; - string bootChk = null; + bool isFat12 = false; + bool isFat16 = false; + bool isFat32 = false; + ulong rootDirectorySector = 0; + string extraInfo = null; + string bootChk = null; + XmlFsType.Bootable = bootable; // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. uint sectorsPerRealSector; // This is needed because some OSes don't put volume label as first entry in the root directory uint sectorsForRootDirectory = 0; - // DEC Rainbow, lacks a BPB but has a very concrete structure... - if(imagePlugin.Info.Sectors == 800 && imagePlugin.Info.SectorSize == 512 && !useAtariBpb && !useMsxBpb && - !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && !useDos33Bpb && - !userShortExtendedBpb && !useExtendedBpb && - !useHumanBpb && !useShortFat32 && !useLongFat32 && !useApricotBpb) + switch(bpbKind) { - // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) - byte z80Di = bpbSector[0]; - // First FAT1 sector resides at LBA 0x14 - byte[] fat1Sector0 = imagePlugin.ReadSector(0x14); - // First FAT2 sector resides at LBA 0x1A - byte[] fat2Sector0 = imagePlugin.ReadSector(0x1A); - bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; - // Volume is software interleaved 2:1 - MemoryStream rootMs = new MemoryStream(); - foreach(byte[] tmp in from ulong rootSector in new[] {0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20} - select imagePlugin.ReadSector(rootSector)) rootMs.Write(tmp, 0, tmp.Length); - - byte[] rootDir = rootMs.ToArray(); - bool validRootDir = true; - - // Iterate all root directory - for(int e = 0; e < 96 * 32; e += 32) + case BpbKind.DecRainbow: + case BpbKind.Hardcoded: + case BpbKind.Msx: + case BpbKind.Apricot: + isFat12 = true; + break; + case BpbKind.ShortFat32: + case BpbKind.LongFat32: { - for(int c = 0; c < 11; c++) - if(rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05 || - rootDir[c + e] == 0xFF || - rootDir[c + e] == 0x2E) - { - validRootDir = false; - break; - } + isFat32 = true; + Fat32ParameterBlock fat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); + Fat32ParameterBlockShort shortFat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); - if(!validRootDir) break; - } - - if(z80Di == 0xF3 && equalFatIds && (fat1Sector0[0] & 0xF0) == 0xF0 && fat1Sector0[1] == 0xFF && - validRootDir) - { - useDecRainbowBpb = true; - DicConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); - fakeBpb.bps = 512; - fakeBpb.spc = 1; - fakeBpb.rsectors = 20; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 96; - fakeBpb.sectors = 800; - fakeBpb.media = 0xFA; - fakeBpb.sptrk = 10; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 3; - XmlFsType.Bootable = true; - fakeBpb.boot_code = bpbSector; - isFat12 = true; - } - } - - if(!useAtariBpb && !useMsxBpb && !useDos2Bpb && !useDos3Bpb && !useDos32Bpb && - !useDos33Bpb && - !useHumanBpb && !userShortExtendedBpb && !useExtendedBpb && !useShortFat32 && !useLongFat32 && - !useApricotBpb && !useDecRainbowBpb) - { - isFat12 = true; - byte[] fatSector = imagePlugin.ReadSector(1 + partition.Start); - switch(fatSector[0]) - { - case 0xE5: - if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; - fakeBpb.sectors = 2002; - fakeBpb.media = 0xE5; - fakeBpb.sptrk = 26; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - } - - break; - case 0xFD: - if(imagePlugin.Info.Sectors == 4004 && imagePlugin.Info.SectorSize == 128) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 4; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 4004; - fakeBpb.media = 0xFD; - fakeBpb.sptrk = 26; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - } - else if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 4; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 2002; - fakeBpb.media = 0xFD; - fakeBpb.sptrk = 26; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - } - - break; - case 0xFE: - if(imagePlugin.Info.Sectors == 320 && imagePlugin.Info.SectorSize == 512) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); - fakeBpb.bps = 512; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; - fakeBpb.sectors = 320; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - } - else if(imagePlugin.Info.Sectors == 2002 && imagePlugin.Info.SectorSize == 128) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 2002; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 26; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - } - else if(imagePlugin.Info.Sectors == 1232 && imagePlugin.Info.SectorSize == 1024) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 1024; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 192; - fakeBpb.sectors = 1232; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 2; - } - else if(imagePlugin.Info.Sectors == 616 && imagePlugin.Info.SectorSize == 1024) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 1024; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 6192; - fakeBpb.sectors = 616; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - } - else if(imagePlugin.Info.Sectors == 720 && imagePlugin.Info.SectorSize == 128) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 2; - fakeBpb.rsectors = 54; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; - fakeBpb.sectors = 720; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 18; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 4; - } - else if(imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); - fakeBpb.bps = 512; - fakeBpb.spc = 2; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 112; - fakeBpb.sectors = 640; - fakeBpb.media = 0xFF; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - } - - break; - case 0xFF: - if(imagePlugin.Info.Sectors == 640 && imagePlugin.Info.SectorSize == 512) - { - DicConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); - fakeBpb.bps = 512; - fakeBpb.spc = 2; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 112; - fakeBpb.sectors = 640; - fakeBpb.media = 0xFF; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - } - - break; - } - - // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code - XmlFsType.Bootable |= bpbSector[0] == 0xFA || - bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F || - bpbSector[0] == 0xE9 && - BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC; - fakeBpb.boot_code = bpbSector; - } - else if(useShortFat32 || useLongFat32) - { - isFat32 = true; - - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - fat32Bpb.bps *= 4; - fat32Bpb.spc /= 4; - fat32Bpb.big_spfat /= 4; - fat32Bpb.hsectors /= 4; - fat32Bpb.sptrk /= 4; - } - - if(fat32Bpb.version != 0) - { - sb.AppendLine("FAT+"); - XmlFsType.Type = "FAT+"; - } - else - { - sb.AppendLine("Microsoft FAT32"); - XmlFsType.Type = "FAT32"; - } - - if(fat32Bpb.oem_name != null) - if(fat32Bpb.oem_name[5] == 0x49 && fat32Bpb.oem_name[6] == 0x48 && fat32Bpb.oem_name[7] == 0x43) - sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); - else XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); - - if(!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) - sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); - sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); - sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); - XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); - sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); - if(fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28) - { - sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, - shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); - XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; - } - else - { - sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, - fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); - XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; - } - - sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); - sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); - sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); - sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); - sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); - sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); - sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); - sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); - sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); - sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); - sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); - XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; - - if((fat32Bpb.flags & 0xF8) == 0x00) - { - if((fat32Bpb.flags & 0x01) == 0x01) + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - sb.AppendLine("Volume should be checked on next mount."); - XmlFsType.Dirty = true; + fat32Bpb.bps *= 4; + fat32Bpb.spc /= 4; + fat32Bpb.big_spfat /= 4; + fat32Bpb.hsectors /= 4; + fat32Bpb.sptrk /= 4; } - if((fat32Bpb.flags & 0x02) == 0x02) sb.AppendLine("Disk surface should be on next mount."); - } - - if((fat32Bpb.mirror_flags & 0x80) == 0x80) - sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF) - .AppendLine(); - else sb.AppendLine("All copies of FAT are the same."); - - if((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) - sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); - else if((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) - sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); - - if(fat32Bpb.signature == 0x29) - { - XmlFsType.VolumeName = Encoding.ASCII.GetString(fat32Bpb.volume_label); - sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)).AppendLine(); - bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); - } - else bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); - - // Check that jumps to a correct boot code position and has boot signature set. - // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... - XmlFsType.Bootable |= - fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80 || - fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && - BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && - BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC; - - sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; - // First root directory sector - rootDirectorySector = - (ulong)((fat32Bpb.root_cluster - 2) * fat32Bpb.spc + fat32Bpb.big_spfat * fat32Bpb.fats_no + - fat32Bpb.rsectors) * sectorsPerRealSector; - sectorsForRootDirectory = 1; - - if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) - { - byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start); - FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); - - if(fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 && - fsInfo.signature3 == FSINFO_SIGNATURE3) + if(fat32Bpb.version != 0) { - if(fsInfo.free_clusters < 0xFFFFFFFF) - { - sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); - XmlFsType.FreeClusters = fsInfo.free_clusters; - XmlFsType.FreeClustersSpecified = true; - } - - if(fsInfo.last_cluster > 2 && fsInfo.last_cluster < 0xFFFFFFFF) - sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); - } - } - } - else if(useExtendedBpb) fakeBpb = ebpb; - else if(userShortExtendedBpb) - { - fakeBpb.jump = shortEbpb.jump; - fakeBpb.oem_name = shortEbpb.oem_name; - fakeBpb.bps = shortEbpb.bps; - fakeBpb.spc = shortEbpb.spc; - fakeBpb.rsectors = shortEbpb.rsectors; - fakeBpb.fats_no = shortEbpb.fats_no; - fakeBpb.root_ent = shortEbpb.root_ent; - fakeBpb.sectors = shortEbpb.sectors; - fakeBpb.media = shortEbpb.media; - fakeBpb.spfat = shortEbpb.spfat; - fakeBpb.sptrk = shortEbpb.sptrk; - fakeBpb.heads = shortEbpb.heads; - fakeBpb.hsectors = shortEbpb.hsectors; - fakeBpb.big_sectors = shortEbpb.big_sectors; - fakeBpb.drive_no = shortEbpb.drive_no; - fakeBpb.flags = shortEbpb.flags; - fakeBpb.signature = shortEbpb.signature; - fakeBpb.serial_no = shortEbpb.serial_no; - fakeBpb.boot_code = shortEbpb.boot_code; - fakeBpb.boot_signature = shortEbpb.boot_signature; - } - else if(useDos33Bpb) - { - fakeBpb.jump = dos33Bpb.jump; - fakeBpb.oem_name = dos33Bpb.oem_name; - fakeBpb.bps = dos33Bpb.bps; - fakeBpb.spc = dos33Bpb.spc; - fakeBpb.rsectors = dos33Bpb.rsectors; - fakeBpb.fats_no = dos33Bpb.fats_no; - fakeBpb.root_ent = dos33Bpb.root_ent; - fakeBpb.sectors = dos33Bpb.sectors; - fakeBpb.media = dos33Bpb.media; - fakeBpb.spfat = dos33Bpb.spfat; - fakeBpb.sptrk = dos33Bpb.sptrk; - fakeBpb.heads = dos33Bpb.heads; - fakeBpb.hsectors = dos33Bpb.hsectors; - fakeBpb.big_sectors = dos33Bpb.big_sectors; - fakeBpb.boot_code = dos33Bpb.boot_code; - fakeBpb.boot_signature = dos33Bpb.boot_signature; - } - else if(useDos32Bpb) - { - fakeBpb.jump = dos32Bpb.jump; - fakeBpb.oem_name = dos32Bpb.oem_name; - fakeBpb.bps = dos32Bpb.bps; - fakeBpb.spc = dos32Bpb.spc; - fakeBpb.rsectors = dos32Bpb.rsectors; - fakeBpb.fats_no = dos32Bpb.fats_no; - fakeBpb.root_ent = dos32Bpb.root_ent; - fakeBpb.sectors = dos32Bpb.sectors; - fakeBpb.media = dos32Bpb.media; - fakeBpb.spfat = dos32Bpb.spfat; - fakeBpb.sptrk = dos32Bpb.sptrk; - fakeBpb.heads = dos32Bpb.heads; - fakeBpb.hsectors = dos32Bpb.hsectors; - fakeBpb.boot_code = dos32Bpb.boot_code; - fakeBpb.boot_signature = dos32Bpb.boot_signature; - } - else if(useDos3Bpb) - { - fakeBpb.jump = dos30Bpb.jump; - fakeBpb.oem_name = dos30Bpb.oem_name; - fakeBpb.bps = dos30Bpb.bps; - fakeBpb.spc = dos30Bpb.spc; - fakeBpb.rsectors = dos30Bpb.rsectors; - fakeBpb.fats_no = dos30Bpb.fats_no; - fakeBpb.root_ent = dos30Bpb.root_ent; - fakeBpb.sectors = dos30Bpb.sectors; - fakeBpb.media = dos30Bpb.media; - fakeBpb.spfat = dos30Bpb.spfat; - fakeBpb.sptrk = dos30Bpb.sptrk; - fakeBpb.heads = dos30Bpb.heads; - fakeBpb.hsectors = dos30Bpb.hsectors; - fakeBpb.boot_code = dos30Bpb.boot_code; - fakeBpb.boot_signature = dos30Bpb.boot_signature; - } - else if(useDos2Bpb) - { - fakeBpb.jump = dos2Bpb.jump; - fakeBpb.oem_name = dos2Bpb.oem_name; - fakeBpb.bps = dos2Bpb.bps; - fakeBpb.spc = dos2Bpb.spc; - fakeBpb.rsectors = dos2Bpb.rsectors; - fakeBpb.fats_no = dos2Bpb.fats_no; - fakeBpb.root_ent = dos2Bpb.root_ent; - fakeBpb.sectors = dos2Bpb.sectors; - fakeBpb.media = dos2Bpb.media; - fakeBpb.spfat = dos2Bpb.spfat; - fakeBpb.boot_code = dos2Bpb.boot_code; - fakeBpb.boot_signature = dos2Bpb.boot_signature; - } - else if(useMsxBpb) - { - isFat12 = true; - fakeBpb.jump = msxBpb.jump; - fakeBpb.oem_name = msxBpb.oem_name; - fakeBpb.bps = msxBpb.bps; - fakeBpb.spc = msxBpb.spc; - fakeBpb.rsectors = msxBpb.rsectors; - fakeBpb.fats_no = msxBpb.fats_no; - fakeBpb.root_ent = msxBpb.root_ent; - fakeBpb.sectors = msxBpb.sectors; - fakeBpb.media = msxBpb.media; - fakeBpb.spfat = msxBpb.spfat; - fakeBpb.sptrk = msxBpb.sptrk; - fakeBpb.heads = msxBpb.heads; - fakeBpb.hsectors = msxBpb.hsectors; - fakeBpb.boot_code = msxBpb.boot_code; - fakeBpb.boot_signature = msxBpb.boot_signature; - fakeBpb.serial_no = msxBpb.serial_no; - // TODO: Is there any way to check this? - XmlFsType.Bootable = true; - } - else if(useAtariBpb) - { - fakeBpb.jump = atariBpb.jump; - fakeBpb.oem_name = atariBpb.oem_name; - fakeBpb.bps = atariBpb.bps; - fakeBpb.spc = atariBpb.spc; - fakeBpb.rsectors = atariBpb.rsectors; - fakeBpb.fats_no = atariBpb.fats_no; - fakeBpb.root_ent = atariBpb.root_ent; - fakeBpb.sectors = atariBpb.sectors; - fakeBpb.media = atariBpb.media; - fakeBpb.spfat = atariBpb.spfat; - fakeBpb.sptrk = atariBpb.sptrk; - fakeBpb.heads = atariBpb.heads; - fakeBpb.boot_code = atariBpb.boot_code; - - ushort sum = 0; - BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; - for(int i = 0; i < bpbSector.Length; i += 2) sum += BigEndianBitConverter.ToUInt16(bpbSector, i); - - // TODO: Check this - if(sum == 0x1234) - { - XmlFsType.Bootable = true; - StringBuilder atariSb = new StringBuilder(); - atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", - BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); - atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr) - .AppendLine(); - atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf) - .AppendLine(); - if(atariBpb.ldmode == 0) - { - byte[] tmp = new byte[8]; - Array.Copy(atariBpb.fname, 0, tmp, 0, 8); - string fname = Encoding.ASCII.GetString(tmp).Trim(); - tmp = new byte[3]; - Array.Copy(atariBpb.fname, 8, tmp, 0, 3); - string extension = Encoding.ASCII.GetString(tmp).Trim(); - string filename; - - if(string.IsNullOrEmpty(extension)) filename = fname; - else filename = fname + "." + extension; - - atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); + sb.AppendLine("FAT+"); + XmlFsType.Type = "FAT+"; } else - atariSb.AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", + { + sb.AppendLine("Microsoft FAT32"); + XmlFsType.Type = "FAT32"; + } + + if(fat32Bpb.oem_name != null) + if(fat32Bpb.oem_name[5] == 0x49 && fat32Bpb.oem_name[6] == 0x48 && fat32Bpb.oem_name[7] == 0x43) + sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); + else XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); + + if(!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) + sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); + sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); + sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); + XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); + sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); + if(fat32Bpb.big_sectors == 0 && fat32Bpb.signature == 0x28) + { + sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, + shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); + XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; + } + else + { + sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, + fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); + XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; + } + + sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); + sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); + sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); + sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); + sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); + sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); + sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); + sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); + sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); + sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); + sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); + XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; + + if((fat32Bpb.flags & 0xF8) == 0x00) + { + if((fat32Bpb.flags & 0x01) == 0x01) + { + sb.AppendLine("Volume should be checked on next mount."); + XmlFsType.Dirty = true; + } + + if((fat32Bpb.flags & 0x02) == 0x02) sb.AppendLine("Disk surface should be on next mount."); + } + + if((fat32Bpb.mirror_flags & 0x80) == 0x80) + sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF) + .AppendLine(); + else sb.AppendLine("All copies of FAT are the same."); + + if((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) + sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); + else if((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) + sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); + + if(fat32Bpb.signature == 0x29) + { + XmlFsType.VolumeName = Encoding.ASCII.GetString(fat32Bpb.volume_label); + sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)) + .AppendLine(); + bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); + } + else bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); + + // Check that jumps to a correct boot code position and has boot signature set. + // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... + XmlFsType.Bootable = + fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80 || + fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && + BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && + BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC; + + sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; + // First root directory sector + rootDirectorySector = + (ulong)((fat32Bpb.root_cluster - 2) * fat32Bpb.spc + fat32Bpb.big_spfat * fat32Bpb.fats_no + + fat32Bpb.rsectors) * sectorsPerRealSector; + sectorsForRootDirectory = 1; + + if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) + { + byte[] fsinfoSector = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start); + FsInfoSector fsInfo = + Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); + + if(fsInfo.signature1 == FSINFO_SIGNATURE1 && fsInfo.signature2 == FSINFO_SIGNATURE2 && + fsInfo.signature3 == FSINFO_SIGNATURE3) + { + if(fsInfo.free_clusters < 0xFFFFFFFF) + { + sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); + XmlFsType.FreeClusters = fsInfo.free_clusters; + XmlFsType.FreeClustersSpecified = true; + } + + if(fsInfo.last_cluster > 2 && fsInfo.last_cluster < 0xFFFFFFFF) + sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); + } + } + + break; + } + + // Some fields could overflow fake BPB, those will be handled below + case BpbKind.Atari: + { + ushort sum = 0; + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + for(int i = 0; i < bpbSector.Length; i += 2) sum += BigEndianBitConverter.ToUInt16(bpbSector, i); + + // TODO: Check this + if(sum == 0x1234) + { + XmlFsType.Bootable = true; + StringBuilder atariSb = new StringBuilder(); + atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", + BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); + atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr) + .AppendLine(); + atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf) + .AppendLine(); + if(atariBpb.ldmode == 0) + { + byte[] tmp = new byte[8]; + Array.Copy(atariBpb.fname, 0, tmp, 0, 8); + string fname = Encoding.ASCII.GetString(tmp).Trim(); + tmp = new byte[3]; + Array.Copy(atariBpb.fname, 8, tmp, 0, 3); + string extension = Encoding.ASCII.GetString(tmp).Trim(); + string filename; + + if(string.IsNullOrEmpty(extension)) filename = fname; + else filename = fname + "." + extension; + + atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); + } + else + atariSb + .AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps) .AppendLine(); - extraInfo = atariSb.ToString(); - } - } - else if(useApricotBpb) - { - isFat12 = true; - fakeBpb.bps = apricotBpb.mainBPB.bps; - fakeBpb.spc = apricotBpb.mainBPB.spc; - fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; - fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; - fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; - fakeBpb.sectors = apricotBpb.mainBPB.sectors; - fakeBpb.media = apricotBpb.mainBPB.media; - fakeBpb.spfat = apricotBpb.mainBPB.spfat; - fakeBpb.sptrk = apricotBpb.spt; - XmlFsType.Bootable = apricotBpb.bootType > 0; + extraInfo = atariSb.ToString(); + } - if(apricotBpb.bootLocation > 0 && - apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) - fakeBpb.boot_code = imagePlugin.ReadSectors(apricotBpb.bootLocation, - (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / - imagePlugin.Info.SectorSize); - } - // Some fields could overflow fake BPB, those will be handled below - else if(useHumanBpb) - { - isFat16 = true; - fakeBpb.jump = humanBpb.jump; - fakeBpb.oem_name = humanBpb.oem_name; - fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; - fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); - fakeBpb.fats_no = 2; - fakeBpb.root_ent = humanBpb.root_ent; - fakeBpb.media = humanBpb.media; - fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); - fakeBpb.boot_code = humanBpb.boot_code; - XmlFsType.Bootable = true; + break; + } + + case BpbKind.Human: + isFat16 = true; + XmlFsType.Bootable = true; + break; } if(!isFat32) @@ -1242,22 +595,32 @@ namespace DiscImageChef.Filesystems.FAT if(isFat12) { - if(useAtariBpb) sb.AppendLine("Atari FAT12"); - else if(useApricotBpb) sb.AppendLine("Apricot FAT12"); - else sb.AppendLine("Microsoft FAT12"); + switch(bpbKind) + { + case BpbKind.Atari: + sb.AppendLine("Atari FAT12"); + break; + case BpbKind.Apricot: + sb.AppendLine("Apricot FAT12"); + break; + default: + sb.AppendLine("Microsoft FAT12"); + break; + } + XmlFsType.Type = "FAT12"; } else if(isFat16) { - sb.AppendLine(useAtariBpb + sb.AppendLine(bpbKind == BpbKind.Atari ? "Atari FAT16" - : useHumanBpb + : bpbKind == BpbKind.Human ? "Human68k FAT16" : "Microsoft FAT16"); XmlFsType.Type = "FAT16"; } - if(useAtariBpb) + if(bpbKind == BpbKind.Atari) { if(atariBpb.serial_no[0] == 0x49 && atariBpb.serial_no[1] == 0x48 && atariBpb.serial_no[2] == 0x43) sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); @@ -1302,7 +665,7 @@ namespace DiscImageChef.Filesystems.FAT sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine(); - if(!useHumanBpb) + if(bpbKind != BpbKind.Human) if(fakeBpb.sectors == 0) { sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors, @@ -1368,7 +731,7 @@ namespace DiscImageChef.Filesystems.FAT sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine(); } } - else if(useAtariBpb && XmlFsType.VolumeSerial != null) + else if(bpbKind == BpbKind.Atari && XmlFsType.VolumeSerial != null) sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); bootChk = Sha1Context.Data(fakeBpb.boot_code, out _); @@ -1400,7 +763,7 @@ namespace DiscImageChef.Filesystems.FAT byte[] rootDirectory = imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory); - if(useDecRainbowBpb) + if(bpbKind == BpbKind.DecRainbow) { MemoryStream rootMs = new MemoryStream(); foreach(byte[] tmp in from ulong rootSector in new[] {0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20}