diff --git a/DiscImageChef.Filesystems/FAT.cs b/DiscImageChef.Filesystems/FAT.cs index c17dd0900..45c71c0ac 100644 --- a/DiscImageChef.Filesystems/FAT.cs +++ b/DiscImageChef.Filesystems/FAT.cs @@ -138,6 +138,27 @@ namespace DiscImageChef.Filesystems DicConsole.DebugWriteLine("FAT plugin", "huge_sectors = {0}", huge_sectors); DicConsole.DebugWriteLine("FAT plugin", "fat_id = 0x{0:X2}", fat_id); + ushort apricot_bps = BitConverter.ToUInt16(bpb_sector, 0x50); + byte apricot_spc = bpb_sector[0x52]; + ushort apricot_reserved_secs = BitConverter.ToUInt16(bpb_sector, 0x53); + byte apricot_fats_no = bpb_sector[0x55]; + ushort apricot_root_entries = BitConverter.ToUInt16(bpb_sector, 0x56); + ushort apricot_sectors = BitConverter.ToUInt16(bpb_sector, 0x58); + byte apricot_media_descriptor = bpb_sector[0x5A]; + ushort apricot_fat_sectors = BitConverter.ToUInt16(bpb_sector, 0x5B); + bool apricot_correct_spc = apricot_spc == 1 || apricot_spc == 2 || apricot_spc == 4 || apricot_spc == 8 || apricot_spc == 16 || apricot_spc == 32 || apricot_spc == 64; + int bits_in_apricot_bps = Helpers.CountBits.Count(apricot_bps); + + DicConsole.DebugWriteLine("FAT plugin", "apricot_bps = {0}", apricot_bps); + DicConsole.DebugWriteLine("FAT plugin", "apricot_spc = {0}", apricot_spc); + DicConsole.DebugWriteLine("FAT plugin", "apricot_correct_spc = {0}", apricot_correct_spc); + DicConsole.DebugWriteLine("FAT plugin", "apricot_reserved_secs = {0}", apricot_reserved_secs); + DicConsole.DebugWriteLine("FAT plugin", "apricot_fats_no = {0}", apricot_fats_no); + DicConsole.DebugWriteLine("FAT plugin", "apricot_root_entries = {0}", apricot_root_entries); + DicConsole.DebugWriteLine("FAT plugin", "apricot_sectors = {0}", apricot_sectors); + DicConsole.DebugWriteLine("FAT plugin", "apricot_media_descriptor = 0x{0:X2}", apricot_media_descriptor); + DicConsole.DebugWriteLine("FAT plugin", "apricot_fat_sectors = {0}", apricot_fat_sectors); + // This is to support FAT partitions on hybrid ISO/USB images if(imagePlugin.ImageInfo.xmlMediaType == ImagePlugins.XmlMediaType.OpticalDisc) { @@ -186,6 +207,11 @@ namespace DiscImageChef.Filesystems if(bits_in_bps == 1 && correct_spc && reserved_secs < (partition.End - partition.Start) && fats_no <= 2 && root_entries > 0 && fat_sectors > 0) return sectors == 0 ? big_sectors <= (partition.End - partition.Start) + 1 : sectors <= (partition.End - partition.Start) + 1; + // Apricot BPB + if(bits_in_apricot_bps == 1 && apricot_correct_spc && apricot_reserved_secs < (partition.End - partition.Start) && apricot_fats_no <= 2 && apricot_root_entries > 0 && + apricot_fat_sectors > 0 && apricot_sectors <= (partition.End - partition.Start) + 1) + return true; + // All FAT12 without BPB can only be used on floppies, without partitions. if(partition.Start != 0) return false; @@ -269,6 +295,7 @@ namespace DiscImageChef.Filesystems bool useShortFAT32 = false; bool useLongFAT32 = false; bool andos_oem_correct = false; + bool useApricotBPB = false; AtariParameterBlock atariBPB = new AtariParameterBlock(); MSXParameterBlock msxBPB = new MSXParameterBlock(); @@ -280,6 +307,7 @@ namespace DiscImageChef.Filesystems BIOSParameterBlockEBPB EBPB = new BIOSParameterBlockEBPB(); FAT32ParameterBlockShort shortFat32BPB = new FAT32ParameterBlockShort(); FAT32ParameterBlock Fat32BPB = new FAT32ParameterBlock(); + ApricotParameterBlock ApricotBPB = new ApricotParameterBlock(); byte[] bpb_sector = imagePlugin.ReadSectors(partition.Start, 2); @@ -298,6 +326,7 @@ namespace DiscImageChef.Filesystems EBPB = (BIOSParameterBlockEBPB)Marshal.PtrToStructure(bpbPtr, typeof(BIOSParameterBlockEBPB)); shortFat32BPB = (FAT32ParameterBlockShort)Marshal.PtrToStructure(bpbPtr, typeof(FAT32ParameterBlockShort)); Fat32BPB = (FAT32ParameterBlock)Marshal.PtrToStructure(bpbPtr, typeof(FAT32ParameterBlock)); + ApricotBPB = (ApricotParameterBlock)Marshal.PtrToStructure(bpbPtr, typeof(ApricotParameterBlock)); Marshal.FreeHGlobal(bpbPtr); @@ -311,6 +340,7 @@ namespace DiscImageChef.Filesystems int bits_in_bps_dos40 = Helpers.CountBits.Count(EBPB.bps); int bits_in_bps_fat32_short = Helpers.CountBits.Count(shortFat32BPB.bps); int bits_in_bps_fat32 = Helpers.CountBits.Count(Fat32BPB.bps); + int bits_in_bps_apricot = Helpers.CountBits.Count(ApricotBPB.bps); bool correct_spc_atari = atariBPB.spc == 1 || atariBPB.spc == 2 || atariBPB.spc == 4 || atariBPB.spc == 8 || atariBPB.spc == 16 || atariBPB.spc == 32 || atariBPB.spc == 64; bool correct_spc_msx = msxBPB.spc == 1 || msxBPB.spc == 2 || msxBPB.spc == 4 || msxBPB.spc == 8 || msxBPB.spc == 16 || msxBPB.spc == 32 || msxBPB.spc == 64; @@ -322,6 +352,7 @@ namespace DiscImageChef.Filesystems bool correct_spc_dos40 = EBPB.spc == 1 || EBPB.spc == 2 || EBPB.spc == 4 || EBPB.spc == 8 || EBPB.spc == 16 || EBPB.spc == 32 || EBPB.spc == 64; bool correct_spc_fat32_short = shortFat32BPB.spc == 1 || shortFat32BPB.spc == 2 || shortFat32BPB.spc == 4 || shortFat32BPB.spc == 8 || shortFat32BPB.spc == 16 || shortFat32BPB.spc == 32 || shortFat32BPB.spc == 64; bool correct_spc_fat32 = Fat32BPB.spc == 1 || Fat32BPB.spc == 2 || Fat32BPB.spc == 4 || Fat32BPB.spc == 8 || Fat32BPB.spc == 16 || Fat32BPB.spc == 32 || Fat32BPB.spc == 64; + bool correct_spc_apricot = ApricotBPB.spc == 1 || ApricotBPB.spc == 2 || ApricotBPB.spc == 4 || ApricotBPB.spc == 8 || ApricotBPB.spc == 16 || ApricotBPB.spc == 32 || ApricotBPB.spc == 64; // This is to support FAT partitions on hybrid ISO/USB images if(imagePlugin.ImageInfo.xmlMediaType == ImagePlugins.XmlMediaType.OpticalDisc) @@ -342,6 +373,7 @@ namespace DiscImageChef.Filesystems shortFat32BPB.huge_sectors /= 4; Fat32BPB.sectors /= 4; Fat32BPB.big_sectors /= 4; + ApricotBPB.sectors /= 4; } andos_oem_correct = dos33BPB.oem_name[0] < 0x20 && dos33BPB.oem_name[1] >= 0x20 && dos33BPB.oem_name[2] >= 0x20 && dos33BPB.oem_name[3] >= 0x20 && @@ -362,6 +394,11 @@ namespace DiscImageChef.Filesystems DicConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); useMSXBPB = true; } + else if(bits_in_bps_apricot == 1 && correct_spc_apricot && ApricotBPB.fats_no <= 2 && ApricotBPB.root_ent > 0 && ApricotBPB.sectors <= (partition.End - partition.Start) + 1 && ApricotBPB.spfat > 0) + { + DicConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); + useApricotBPB = true; + } else if(bits_in_bps_dos40 == 1 && correct_spc_dos40 && EBPB.fats_no <= 2 && EBPB.root_ent > 0 && EBPB.spfat > 0 && (EBPB.signature == 0x28 || EBPB.signature == 0x29 || andos_oem_correct)) { if(EBPB.sectors == 0) @@ -468,7 +505,7 @@ namespace DiscImageChef.Filesystems // This is needed because some OSes don't put volume label as first entry in the root directory uint sectors_for_root_directory = 0; - if(!useAtariBPB && !useMSXBPB && !useDOS2BPB && !useDOS3BPB && !useDOS32BPB && !useDOS33BPB && !useShortEBPB && !useEBPB && !useShortFAT32 && !useLongFAT32) + if(!useAtariBPB && !useMSXBPB && !useDOS2BPB && !useDOS3BPB && !useDOS32BPB && !useDOS33BPB && !useShortEBPB && !useEBPB && !useShortFAT32 && !useLongFAT32 && !useApricotBPB) { isFAT12 = true; fat_sector = imagePlugin.ReadSector(1 + partition.Start); @@ -916,6 +953,19 @@ namespace DiscImageChef.Filesystems extraInfo = atariSb.ToString(); } } + else if(useApricotBPB) + { + isFAT12 = true; + fakeBPB.bps = ApricotBPB.bps; + fakeBPB.spc = ApricotBPB.spc; + fakeBPB.rsectors = ApricotBPB.rsectors; + fakeBPB.fats_no = ApricotBPB.fats_no; + fakeBPB.root_ent = ApricotBPB.root_ent; + fakeBPB.sectors = ApricotBPB.sectors; + fakeBPB.media = ApricotBPB.media; + fakeBPB.spfat = ApricotBPB.spfat; + fakeBPB.boot_code = ApricotBPB.boot_code; + } if(!isFAT32) { @@ -956,6 +1006,8 @@ namespace DiscImageChef.Filesystems { if(useAtariBPB) sb.AppendLine("Atari FAT12"); + else if(useApricotBPB) + sb.AppendLine("Apricot FAT12"); else sb.AppendLine("Microsoft FAT12"); xmlFSType.Type = "FAT12"; @@ -1675,6 +1727,38 @@ namespace DiscImageChef.Filesystems public ushort boot_signature; } + /// Apricot BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ApricotParameterBlock + { + /// Boot specific information + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] + public byte[] boot_code; + /// Bytes per sector + public ushort bps; + /// Sectors per cluster + public byte spc; + /// Reserved sectors between BPB and FAT + public ushort rsectors; + /// Number of FATs + public byte fats_no; + /// Number of entries on root directory + public ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public byte media; + /// Sectors per FAT + public ushort spfat; + /// Disk type + public byte diskType; + /// Reserved + public ushort reserved; + /// Unknown. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 416)] + public byte[] unknown; + } + public const uint fsinfo_signature1 = 0x41615252; public const uint fsinfo_signature2 = 0x61417272; public const uint fsinfo_signature3 = 0xAA550000;