mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
Implement LFN namespace in FAT.
This commit is contained in:
@@ -77,6 +77,9 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
const ushort FAT12_END_MASK = 0xFF8;
|
const ushort FAT12_END_MASK = 0xFF8;
|
||||||
const ushort FAT12_FORMATTED = 0xFF6;
|
const ushort FAT12_FORMATTED = 0xFF6;
|
||||||
const ushort FAT12_BAD = 0xFF7;
|
const ushort FAT12_BAD = 0xFF7;
|
||||||
|
const byte LFN_ERASED = 0x80;
|
||||||
|
const byte LFN_LAST = 0x40;
|
||||||
|
const byte LFN_MASK = 0x1F;
|
||||||
|
|
||||||
readonly (string hash, string name)[] knownBootHashes =
|
readonly (string hash, string name)[] knownBootHashes =
|
||||||
{
|
{
|
||||||
@@ -209,7 +212,8 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
enum Namespace
|
enum Namespace
|
||||||
{
|
{
|
||||||
Dos,
|
Dos,
|
||||||
Nt
|
Nt,
|
||||||
|
Lfn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using DiscImageChef.CommonTypes.Structs;
|
using DiscImageChef.CommonTypes.Structs;
|
||||||
using DiscImageChef.Helpers;
|
using DiscImageChef.Helpers;
|
||||||
|
|
||||||
@@ -118,18 +119,47 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentDirectory = new Dictionary<string, DirectoryEntry>();
|
currentDirectory = new Dictionary<string, DirectoryEntry>();
|
||||||
|
byte[] lastLfnName = null;
|
||||||
|
byte lastLfnChecksum = 0;
|
||||||
|
|
||||||
int pos = 0;
|
for(int pos = 0; pos < directoryBuffer.Length; pos += Marshal.SizeOf<DirectoryEntry>())
|
||||||
while(pos < directoryBuffer.Length)
|
|
||||||
{
|
{
|
||||||
DirectoryEntry dirent =
|
DirectoryEntry dirent =
|
||||||
Marshal.ByteArrayToStructureLittleEndian<DirectoryEntry>(directoryBuffer, pos,
|
Marshal.ByteArrayToStructureLittleEndian<DirectoryEntry>(directoryBuffer, pos,
|
||||||
Marshal.SizeOf<DirectoryEntry>());
|
Marshal.SizeOf<DirectoryEntry>());
|
||||||
|
|
||||||
pos += Marshal.SizeOf<DirectoryEntry>();
|
|
||||||
|
|
||||||
if(dirent.filename[0] == DIRENT_FINISHED) break;
|
if(dirent.filename[0] == DIRENT_FINISHED) break;
|
||||||
|
|
||||||
|
if(dirent.attributes.HasFlag(FatAttributes.LFN))
|
||||||
|
{
|
||||||
|
if(@namespace != Namespace.Lfn) continue;
|
||||||
|
|
||||||
|
LfnEntry lfnEntry =
|
||||||
|
Marshal.ByteArrayToStructureLittleEndian<LfnEntry>(directoryBuffer, pos,
|
||||||
|
Marshal.SizeOf<LfnEntry>());
|
||||||
|
|
||||||
|
int lfnSequence = lfnEntry.sequence & LFN_MASK;
|
||||||
|
|
||||||
|
if((lfnEntry.sequence & LFN_ERASED) > 0) continue;
|
||||||
|
|
||||||
|
if((lfnEntry.sequence & LFN_LAST) > 0)
|
||||||
|
{
|
||||||
|
lastLfnName = new byte[lfnSequence * 26];
|
||||||
|
lastLfnChecksum = lfnEntry.checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastLfnName is null) continue;
|
||||||
|
if(lfnEntry.checksum != lastLfnChecksum) continue;
|
||||||
|
|
||||||
|
lfnSequence--;
|
||||||
|
|
||||||
|
Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10);
|
||||||
|
Array.Copy(lfnEntry.name2, 0, lastLfnName, lfnSequence * 26 + 10, 12);
|
||||||
|
Array.Copy(lfnEntry.name3, 0, lastLfnName, lfnSequence * 26 + 22, 4);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Not a correct entry
|
// Not a correct entry
|
||||||
if(dirent.filename[0] < DIRENT_MIN && dirent.filename[0] != DIRENT_E5) continue;
|
if(dirent.filename[0] < DIRENT_MIN && dirent.filename[0] != DIRENT_E5) continue;
|
||||||
|
|
||||||
@@ -142,13 +172,25 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
// Deleted
|
// Deleted
|
||||||
if(dirent.filename[0] == DIRENT_DELETED) continue;
|
if(dirent.filename[0] == DIRENT_DELETED) continue;
|
||||||
|
|
||||||
// TODO: LFN namespace
|
|
||||||
if(dirent.attributes.HasFlag(FatAttributes.LFN)) continue;
|
|
||||||
|
|
||||||
string filename;
|
string filename;
|
||||||
|
|
||||||
if(dirent.attributes.HasFlag(FatAttributes.VolumeLabel)) continue;
|
if(dirent.attributes.HasFlag(FatAttributes.VolumeLabel)) continue;
|
||||||
|
|
||||||
|
if(@namespace == Namespace.Lfn && lastLfnName != null)
|
||||||
|
{
|
||||||
|
byte calculatedLfnChecksum = LfnChecksum(dirent.filename, dirent.extension);
|
||||||
|
|
||||||
|
if(calculatedLfnChecksum == lastLfnChecksum)
|
||||||
|
{
|
||||||
|
filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true);
|
||||||
|
|
||||||
|
currentDirectory[filename] = dirent;
|
||||||
|
lastLfnName = null;
|
||||||
|
lastLfnChecksum = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(dirent.filename[0] == DIRENT_E5) dirent.filename[0] = DIRENT_DELETED;
|
if(dirent.filename[0] == DIRENT_E5) dirent.filename[0] = DIRENT_DELETED;
|
||||||
|
|
||||||
string name = Encoding.GetString(dirent.filename).TrimEnd();
|
string name = Encoding.GetString(dirent.filename).TrimEnd();
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
public Dictionary<string, string> Namespaces =>
|
public Dictionary<string, string> Namespaces =>
|
||||||
new Dictionary<string, string>
|
new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{"dos", "DOS (8.3 all uppercase)"}, {"nt", "Windows NT (8.3 mixed case, default)"}
|
{"dos", "DOS (8.3 all uppercase)"},
|
||||||
|
{"nt", "Windows NT (8.3 mixed case)"},
|
||||||
|
{"lfn", "Long file names (default)"}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Dictionary<string, string> GetDefaultOptions() =>
|
static Dictionary<string, string> GetDefaultOptions() =>
|
||||||
|
|||||||
@@ -233,5 +233,15 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
entry = dirent.Value;
|
entry = dirent.Value;
|
||||||
return Errno.NoError;
|
return Errno.NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte LfnChecksum(byte[] name, byte[] extension)
|
||||||
|
{
|
||||||
|
byte sum = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < 8; i++) sum = (byte)(((sum & 1) << 7) + (sum >> 1) + name[i]);
|
||||||
|
for(int i = 0; i < 3; i++) sum = (byte)(((sum & 1) << 7) + (sum >> 1) + extension[i]);
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -866,5 +866,21 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
public readonly ushort start_cluster;
|
public readonly ushort start_cluster;
|
||||||
public readonly uint size;
|
public readonly uint size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
struct LfnEntry
|
||||||
|
{
|
||||||
|
public readonly byte sequence;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
|
||||||
|
public readonly byte[] name1;
|
||||||
|
public readonly FatAttributes attributes;
|
||||||
|
public readonly byte type;
|
||||||
|
public readonly byte checksum;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
|
||||||
|
public readonly byte[] name2;
|
||||||
|
public readonly ushort start_cluster;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
||||||
|
public readonly byte[] name3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
|
if(options.TryGetValue("debug", out string debugString)) bool.TryParse(debugString, out debug);
|
||||||
|
|
||||||
// Default namespace
|
// Default namespace
|
||||||
if(@namespace is null) @namespace = "nt";
|
if(@namespace is null) @namespace = "lfn";
|
||||||
|
|
||||||
switch(@namespace.ToLowerInvariant())
|
switch(@namespace.ToLowerInvariant())
|
||||||
{
|
{
|
||||||
@@ -76,6 +76,9 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
case "nt":
|
case "nt":
|
||||||
this.@namespace = Namespace.Nt;
|
this.@namespace = Namespace.Nt;
|
||||||
break;
|
break;
|
||||||
|
case "lfn":
|
||||||
|
this.@namespace = Namespace.Lfn;
|
||||||
|
break;
|
||||||
default: return Errno.InvalidArgument;
|
default: return Errno.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,6 +399,9 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
|
|
||||||
if(rootDirectory is null) return Errno.InvalidArgument;
|
if(rootDirectory is null) return Errno.InvalidArgument;
|
||||||
|
|
||||||
|
byte[] lastLfnName = null;
|
||||||
|
byte lastLfnChecksum = 0;
|
||||||
|
|
||||||
for(int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf<DirectoryEntry>())
|
for(int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf<DirectoryEntry>())
|
||||||
{
|
{
|
||||||
DirectoryEntry entry =
|
DirectoryEntry entry =
|
||||||
@@ -404,6 +410,36 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
|
|
||||||
if(entry.filename[0] == DIRENT_FINISHED) break;
|
if(entry.filename[0] == DIRENT_FINISHED) break;
|
||||||
|
|
||||||
|
if(entry.attributes.HasFlag(FatAttributes.LFN))
|
||||||
|
{
|
||||||
|
if(this.@namespace != Namespace.Lfn) continue;
|
||||||
|
|
||||||
|
LfnEntry lfnEntry =
|
||||||
|
Marshal.ByteArrayToStructureLittleEndian<LfnEntry>(rootDirectory, i,
|
||||||
|
Marshal.SizeOf<LfnEntry>());
|
||||||
|
|
||||||
|
int lfnSequence = lfnEntry.sequence & LFN_MASK;
|
||||||
|
|
||||||
|
if((lfnEntry.sequence & LFN_ERASED) > 0) continue;
|
||||||
|
|
||||||
|
if((lfnEntry.sequence & LFN_LAST) > 0)
|
||||||
|
{
|
||||||
|
lastLfnName = new byte[lfnSequence * 26];
|
||||||
|
lastLfnChecksum = lfnEntry.checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lastLfnName is null) continue;
|
||||||
|
if(lfnEntry.checksum != lastLfnChecksum) continue;
|
||||||
|
|
||||||
|
lfnSequence--;
|
||||||
|
|
||||||
|
Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10);
|
||||||
|
Array.Copy(lfnEntry.name2, 0, lastLfnName, lfnSequence * 26 + 10, 12);
|
||||||
|
Array.Copy(lfnEntry.name3, 0, lastLfnName, lfnSequence * 26 + 22, 4);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Not a correct entry
|
// Not a correct entry
|
||||||
if(entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) continue;
|
if(entry.filename[0] < DIRENT_MIN && entry.filename[0] != DIRENT_E5) continue;
|
||||||
|
|
||||||
@@ -416,9 +452,6 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
// Deleted
|
// Deleted
|
||||||
if(entry.filename[0] == DIRENT_DELETED) continue;
|
if(entry.filename[0] == DIRENT_DELETED) continue;
|
||||||
|
|
||||||
// TODO: LFN namespace
|
|
||||||
if(entry.attributes.HasFlag(FatAttributes.LFN)) continue;
|
|
||||||
|
|
||||||
string filename;
|
string filename;
|
||||||
|
|
||||||
if(entry.attributes.HasFlag(FatAttributes.VolumeLabel))
|
if(entry.attributes.HasFlag(FatAttributes.VolumeLabel))
|
||||||
@@ -449,6 +482,21 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.@namespace == Namespace.Lfn && lastLfnName != null)
|
||||||
|
{
|
||||||
|
byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension);
|
||||||
|
|
||||||
|
if(calculatedLfnChecksum == lastLfnChecksum)
|
||||||
|
{
|
||||||
|
filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true);
|
||||||
|
|
||||||
|
rootDirectoryCache[filename] = entry;
|
||||||
|
lastLfnName = null;
|
||||||
|
lastLfnChecksum = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(entry.filename[0] == DIRENT_E5) entry.filename[0] = DIRENT_DELETED;
|
if(entry.filename[0] == DIRENT_E5) entry.filename[0] = DIRENT_DELETED;
|
||||||
|
|
||||||
string name = Encoding.GetString(entry.filename).TrimEnd();
|
string name = Encoding.GetString(entry.filename).TrimEnd();
|
||||||
@@ -466,7 +514,9 @@ namespace DiscImageChef.Filesystems.FAT
|
|||||||
if(extension != "") filename = name + "." + extension;
|
if(extension != "") filename = name + "." + extension;
|
||||||
else filename = name;
|
else filename = name;
|
||||||
|
|
||||||
rootDirectoryCache.Add(filename, entry);
|
rootDirectoryCache[filename] = entry;
|
||||||
|
lastLfnName = null;
|
||||||
|
lastLfnChecksum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim();
|
XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim();
|
||||||
|
|||||||
Reference in New Issue
Block a user