diff --git a/DiscImageChef.Filesystems/Opera/Dir.cs b/DiscImageChef.Filesystems/Opera/Dir.cs index cef48e5e6..106e1d67c 100644 --- a/DiscImageChef.Filesystems/Opera/Dir.cs +++ b/DiscImageChef.Filesystems/Opera/Dir.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; using DiscImageChef.CommonTypes.Structs; using DiscImageChef.Helpers; @@ -7,7 +9,62 @@ namespace DiscImageChef.Filesystems { public partial class OperaFS { - public Errno ReadDir(string path, out List contents) => throw new NotImplementedException(); + public Errno ReadDir(string path, out List contents) + { + contents = null; + if(!mounted) return Errno.AccessDenied; + + if(string.IsNullOrWhiteSpace(path) || path == "/") + { + contents = rootDirectoryCache.Keys.ToList(); + return Errno.NoError; + } + + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); + + if(directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) + { + contents = currentDirectory.Keys.ToList(); + return Errno.NoError; + } + + string[] pieces = cutPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + + KeyValuePair entry = + rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]); + + if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile; + + if((entry.Value.entry.flags & FLAGS_MASK) != (int)FileFlags.Directory) return Errno.NotDirectory; + + string currentPath = pieces[0]; + + currentDirectory = rootDirectoryCache; + + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]); + + if(string.IsNullOrEmpty(entry.Key)) return Errno.NoSuchFile; + + if((entry.Value.entry.flags & FLAGS_MASK) != (int)FileFlags.Directory) return Errno.NotDirectory; + + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; + + if(directoryCache.TryGetValue(currentPath, out currentDirectory)) continue; + + if(entry.Value.pointers.Length < 1) return Errno.InvalidArgument; + + currentDirectory = DecodeDirectory((int)entry.Value.pointers[0]); + + directoryCache.Add(currentPath, currentDirectory); + } + + contents = currentDirectory?.Keys.ToList(); + return Errno.NoError; + } Dictionary DecodeDirectory(int firstBlock) { diff --git a/DiscImageChef.Filesystems/Opera/Opera.cs b/DiscImageChef.Filesystems/Opera/Opera.cs index 79ff37f15..df6b357c8 100644 --- a/DiscImageChef.Filesystems/Opera/Opera.cs +++ b/DiscImageChef.Filesystems/Opera/Opera.cs @@ -41,11 +41,12 @@ namespace DiscImageChef.Filesystems { public partial class OperaFS : IReadOnlyFilesystem { - IMediaImage image; - bool mounted; - Dictionary rootDirectoryCache; - FileSystemInfo statfs; - uint volumeBlockSizeRatio; + Dictionary> directoryCache; + IMediaImage image; + bool mounted; + Dictionary rootDirectoryCache; + FileSystemInfo statfs; + uint volumeBlockSizeRatio; public FileSystemType XmlFsType { get; private set; } public Encoding Encoding { get; private set; }