From 7b8dd993541025ddaff8ba14fa243c6af6b71233 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 10 Apr 2019 19:15:04 +0100 Subject: [PATCH] Implement ReadDir() in Xbox FAT plugin. --- DiscImageChef.Filesystems/FATX/Dir.cs | 99 ++++++++++++++++++++++++- DiscImageChef.Filesystems/FATX/FATX.cs | 27 ++++--- DiscImageChef.Filesystems/FATX/Super.cs | 4 + 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/DiscImageChef.Filesystems/FATX/Dir.cs b/DiscImageChef.Filesystems/FATX/Dir.cs index a958eab83..28fe2d261 100644 --- a/DiscImageChef.Filesystems/FATX/Dir.cs +++ b/DiscImageChef.Filesystems/FATX/Dir.cs @@ -32,7 +32,9 @@ using System; using System.Collections.Generic; +using System.Linq; using DiscImageChef.CommonTypes.Structs; +using DiscImageChef.Helpers; namespace DiscImageChef.Filesystems.FATX { @@ -43,7 +45,102 @@ namespace DiscImageChef.Filesystems.FATX contents = null; if(!mounted) return Errno.AccessDenied; - throw new NotImplementedException(); + if(string.IsNullOrWhiteSpace(path) || path == "/") + { + contents = rootDirectory.Keys.ToList(); + return Errno.NoError; + } + + string cutPath = path.StartsWith("/") ? path.Substring(0).ToLower(cultureInfo) : path.ToLower(cultureInfo); + + if(directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) + { + contents = currentDirectory.Keys.ToList(); + return Errno.NoError; + } + + string[] pieces = cutPath.Split('/'); + + KeyValuePair entry = + rootDirectory.FirstOrDefault(t => t.Key.ToLower(cultureInfo) == pieces[0]); + + if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile; + + if(!entry.Value.attributes.HasFlag(Attributes.Directory)) return Errno.NotDirectory; + + string currentPath = pieces[0]; + + currentDirectory = rootDirectory; + + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(cultureInfo) == pieces[p]); + + if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile; + + if(!entry.Value.attributes.HasFlag(Attributes.Directory)) return Errno.NotDirectory; + + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; + uint currentCluster = entry.Value.firstCluster; + + if(directoryCache.TryGetValue(currentPath, out currentDirectory)) + { + if(p == pieces.Length - 1) break; + + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(cultureInfo) == pieces[p]); + + if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile; + + if(!entry.Value.attributes.HasFlag(Attributes.Directory)) return Errno.NotDirectory; + + continue; + } + + uint[] clusters = GetClusters(currentCluster); + + if(clusters is null) return Errno.InvalidArgument; + + byte[] directoryBuffer = new byte[bytesPerCluster * clusters.Length]; + + for(int i = 0; i < clusters.Length; i++) + { + byte[] buffer = imagePlugin.ReadSectors(firstClusterSector + (clusters[i] - 1) * sectorsPerCluster, + sectorsPerCluster); + Array.Copy(buffer, 0, directoryBuffer, i * bytesPerCluster, bytesPerCluster); + } + + currentDirectory = new Dictionary(); + + int pos = 0; + while(pos < directoryBuffer.Length) + { + DirectoryEntry dirent = littleEndian + ? Marshal + .ByteArrayToStructureLittleEndian(directoryBuffer, pos, Marshal.SizeOf()) + : Marshal.ByteArrayToStructureBigEndian(directoryBuffer, + pos, + Marshal + .SizeOf< + DirectoryEntry + >()); + + pos += Marshal.SizeOf(); + + if(dirent.filenameSize == UNUSED_DIRENTRY || dirent.filenameSize == FINISHED_DIRENTRY) break; + + if(dirent.filenameSize == DELETED_DIRENTRY || dirent.filenameSize > MAX_FILENAME) continue; + + string filename = Encoding.GetString(dirent.filename, 0, dirent.filenameSize); + + currentDirectory.Add(filename, dirent); + } + + directoryCache.Add(currentPath, currentDirectory); + } + + contents = currentDirectory?.Keys.ToList(); + return Errno.NoError; } } } \ No newline at end of file diff --git a/DiscImageChef.Filesystems/FATX/FATX.cs b/DiscImageChef.Filesystems/FATX/FATX.cs index f01c0057a..f27ca7ba3 100644 --- a/DiscImageChef.Filesystems/FATX/FATX.cs +++ b/DiscImageChef.Filesystems/FATX/FATX.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using DiscImageChef.CommonTypes; using DiscImageChef.CommonTypes.Interfaces; @@ -42,18 +43,20 @@ namespace DiscImageChef.Filesystems.FATX { public partial class XboxFatPlugin : IReadOnlyFilesystem { - uint bytesPerCluster; - ushort[] fat16; - uint[] fat32; - ulong fatStartSector; - ulong firstClusterSector; - IMediaImage imagePlugin; - bool littleEndian; - bool mounted; - Partition partition; - Dictionary rootDirectory; - uint sectorsPerCluster; - FileSystemInfo stat; + uint bytesPerCluster; + CultureInfo cultureInfo; + Dictionary> directoryCache; + ushort[] fat16; + uint[] fat32; + ulong fatStartSector; + ulong firstClusterSector; + IMediaImage imagePlugin; + bool littleEndian; + bool mounted; + Partition partition; + Dictionary rootDirectory; + uint sectorsPerCluster; + FileSystemInfo stat; Superblock superblock; public FileSystemType XmlFsType { get; private set; } diff --git a/DiscImageChef.Filesystems/FATX/Super.cs b/DiscImageChef.Filesystems/FATX/Super.cs index 6d01a1bac..ad6499612 100644 --- a/DiscImageChef.Filesystems/FATX/Super.cs +++ b/DiscImageChef.Filesystems/FATX/Super.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Runtime.InteropServices; using System.Text; using DiscImageChef.CommonTypes; @@ -207,6 +208,9 @@ namespace DiscImageChef.Filesystems.FATX rootDirectory.Add(filename, entry); } + cultureInfo = new CultureInfo("en-US", false); + directoryCache = new Dictionary>(); + return Errno.NoError; }