Cache root directory for FAT12 and FAT16.

This commit is contained in:
2019-04-26 01:29:29 +01:00
parent 8bb291246b
commit 4d28330dcb
4 changed files with 97 additions and 40 deletions

View File

@@ -39,6 +39,34 @@ namespace DiscImageChef.Filesystems.FAT
const uint FSINFO_SIGNATURE1 = 0x41615252; const uint FSINFO_SIGNATURE1 = 0x41615252;
const uint FSINFO_SIGNATURE2 = 0x61417272; const uint FSINFO_SIGNATURE2 = 0x61417272;
const uint FSINFO_SIGNATURE3 = 0xAA550000; const uint FSINFO_SIGNATURE3 = 0xAA550000;
/// <summary>
/// Directory finishes
/// </summary>
const byte DIRENT_FINISHED = 0x00;
/// <summary>
/// Deleted directory entry
/// </summary>
const byte DIRENT_DELETED = 0xE5;
/// <summary>
/// Minimum allowed value in name/extension
/// </summary>
const byte DIRENT_MIN = 0x20;
/// <summary>
/// Value used instead of <see cref="DIRENT_FINISHED" /> for first name character
/// </summary>
const byte DIRENT_E5 = 0x05;
/// <summary>
/// Entry points to self or parent directory
/// </summary>
const byte DIRENT_SUBDIR = 0x2E;
/// <summary>
/// FASTFAT.SYS indicator that extension is lowercase
/// </summary>
const byte FASTFAT_LOWERCASE_EXTENSION = 0x10;
/// <summary>
/// FASTFAT.SYS indicator that basename is lowercase
/// </summary>
const byte FASTFAT_LOWERCASE_BASENAME = 0x08;
readonly (string hash, string name)[] knownBootHashes = readonly (string hash, string name)[] knownBootHashes =
{ {

View File

@@ -42,17 +42,18 @@ namespace DiscImageChef.Filesystems.FAT
// X68K uses cdate/adate from direntry for extending filename // X68K uses cdate/adate from direntry for extending filename
public partial class FAT : IReadOnlyFilesystem public partial class FAT : IReadOnlyFilesystem
{ {
bool debug; bool debug;
bool fat12; bool fat12;
bool fat16; bool fat16;
bool fat32; bool fat32;
ulong fatFirstSector; ulong fatFirstSector;
ulong firstClusterSector; ulong firstClusterSector;
bool mounted; bool mounted;
uint reservedSectors; uint reservedSectors;
uint sectorsPerCluster; Dictionary<string, DirectoryEntry> rootDirectoryCache;
uint sectorsPerFat; uint sectorsPerCluster;
bool useFirstFat; uint sectorsPerFat;
bool useFirstFat;
public FileSystemType XmlFsType { get; private set; } public FileSystemType XmlFsType { get; private set; }

View File

@@ -775,10 +775,10 @@ namespace DiscImageChef.Filesystems.FAT
for(int i = 0; i < rootDirectory.Length; i += 32) for(int i = 0; i < rootDirectory.Length; i += 32)
{ {
// Not a correct entry // Not a correct entry
if(rootDirectory[i] < 0x20 && rootDirectory[i] != 0x05) continue; if(rootDirectory[i] < DIRENT_MIN && rootDirectory[i] != DIRENT_E5) continue;
// Deleted or subdirectory entry // Deleted or subdirectory entry
if(rootDirectory[i] == 0x2E || rootDirectory[i] == 0xE5) continue; if(rootDirectory[i] == DIRENT_SUBDIR || rootDirectory[i] == DIRENT_DELETED) continue;
// Not a volume label // Not a volume label
if(rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28) continue; if(rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28) continue;
@@ -791,7 +791,7 @@ namespace DiscImageChef.Filesystems.FAT
Array.Copy(entry.extension, 0, fullname, 8, 3); Array.Copy(entry.extension, 0, fullname, 8, 3);
string volname = Encoding.GetString(fullname).Trim(); string volname = Encoding.GetString(fullname).Trim();
if(!string.IsNullOrEmpty(volname)) if(!string.IsNullOrEmpty(volname))
XmlFsType.VolumeName = (entry.caseinfo & 0x0C) > 0 ? volname.ToLower() : volname; XmlFsType.VolumeName = (entry.caseinfo & 0x18) > 0 ? volname.ToLower() : volname;
if(entry.ctime > 0 && entry.cdate > 0) if(entry.ctime > 0 && entry.cdate > 0)
{ {

View File

@@ -32,6 +32,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -307,6 +308,8 @@ namespace DiscImageChef.Filesystems.FAT
firstClusterSector += partition.Start; firstClusterSector += partition.Start;
rootDirectoryCache = new Dictionary<string, DirectoryEntry>();
if(!fat32) if(!fat32)
if(firstClusterSector + partition.Start < partition.End && if(firstClusterSector + partition.Start < partition.End &&
imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc)
@@ -325,40 +328,65 @@ namespace DiscImageChef.Filesystems.FAT
for(int i = 0; i < rootDirectory.Length; i += 32) for(int i = 0; i < rootDirectory.Length; i += 32)
{ {
// Not a correct entry
if(rootDirectory[i] < 0x20 && rootDirectory[i] != 0x05) continue;
// Deleted or subdirectory entry
if(rootDirectory[i] == 0x2E || rootDirectory[i] == 0xE5) continue;
// Not a volume label
if(rootDirectory[i + 0x0B] != 0x08 && rootDirectory[i + 0x0B] != 0x28) continue;
DirectoryEntry entry = DirectoryEntry entry =
Marshal.ByteArrayToStructureLittleEndian<DirectoryEntry>(rootDirectory, i, 32); Marshal.ByteArrayToStructureLittleEndian<DirectoryEntry>(rootDirectory, i, 32);
byte[] fullname = new byte[11]; if(entry.filename[0] == DIRENT_FINISHED) break;
Array.Copy(entry.filename, 0, fullname, 0, 8);
Array.Copy(entry.extension, 0, fullname, 8, 3);
string volname = Encoding.GetString(fullname).Trim();
if(!string.IsNullOrEmpty(volname))
XmlFsType.VolumeName = (entry.caseinfo & 0x0C) > 0 ? volname.ToLower() : volname;
if(entry.ctime > 0 && entry.cdate > 0) // Not a correct entry
if(entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) continue;
// Deleted or subdirectory entry
if(entry.filename[0] == DIRENT_SUBDIR || entry.filename[0] == DIRENT_DELETED) continue;
// TODO: LFN namespace
if(entry.attributes.HasFlag(FatAttributes.LFN)) continue;
string filename;
if(entry.attributes.HasFlag(FatAttributes.VolumeLabel))
{ {
XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); byte[] fullname = new byte[11];
if(entry.ctime_ms > 0) Array.Copy(entry.filename, 0, fullname, 0, 8);
XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); Array.Copy(entry.extension, 0, fullname, 8, 3);
XmlFsType.CreationDateSpecified = true; string volname = Encoding.GetString(fullname).Trim();
if(!string.IsNullOrEmpty(volname))
XmlFsType.VolumeName = (entry.caseinfo & 0x18) > 0 ? volname.ToLower() : volname;
if(entry.ctime > 0 && entry.cdate > 0)
{
XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime);
if(entry.ctime_ms > 0)
XmlFsType.CreationDate =
XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10);
XmlFsType.CreationDateSpecified = true;
}
if(entry.mtime > 0 && entry.mdate > 0)
{
XmlFsType.ModificationDate =
DateHandlers.DosToDateTime(entry.mdate, entry.mtime);
XmlFsType.ModificationDateSpecified = true;
}
continue;
} }
if(entry.mtime > 0 && entry.mdate > 0) if(entry.filename[0] == DIRENT_E5) entry.filename[0] = DIRENT_DELETED;
{
XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime);
XmlFsType.ModificationDateSpecified = true;
}
break; string name = Encoding.GetString(entry.filename).Trim();
string extension = Encoding.GetString(entry.extension).Trim();
if((entry.caseinfo & FASTFAT_LOWERCASE_EXTENSION) > 0)
extension = extension.ToLower(CultureInfo.CurrentCulture);
if((entry.caseinfo & FASTFAT_LOWERCASE_BASENAME) > 0)
name = name.ToLower(CultureInfo.CurrentCulture);
if(extension != "") filename = name + "." + extension;
else filename = name;
rootDirectoryCache.Add(filename, entry);
} }
} }