diff --git a/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs b/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs index cb9c1e4f..1f251b67 100644 --- a/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs @@ -111,13 +111,13 @@ public bool Exists(FileSystemPath path) public Stream CreateFile(FileSystemPath path) { - var zae = ZipArchive.CreateEntry(ToEntryPath(path)); - return zae.Open(); + ZipArchiveEntry entry = ZipArchive.CreateEntry(ToEntryPath(path)); + return entry.Open(); } - public Stream OpenFile(FileSystemPath path, FileAccess access) + public Stream OpenFile(FileSystemPath path, FileMode mode, FileAccess access) { - var entry = ZipArchive.GetEntry(ToEntryPath(path)); + ZipArchiveEntry? entry = ZipArchive.GetEntry(ToEntryPath(path)); return entry?.Open() ?? Stream.Null; } @@ -128,7 +128,7 @@ public void CreateDirectory(FileSystemPath path) public void Delete(FileSystemPath path) { - var entry = ZipArchive.GetEntry(ToEntryPath(path)); + ZipArchiveEntry? entry = ZipArchive.GetEntry(ToEntryPath(path)); entry?.Delete(); } } diff --git a/pkNX.Containers/VFS/FileSystems/IFileSystem.cs b/pkNX.Containers/VFS/FileSystems/IFileSystem.cs index d8a46d35..1298549a 100644 --- a/pkNX.Containers/VFS/FileSystems/IFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/IFileSystem.cs @@ -7,36 +7,109 @@ namespace pkNX.Containers.VFS; public interface IFileSystem : IDisposable { + bool IsReadOnly => false; + IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null); IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null); IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null); + /// + /// Checks if the specified path exists in the filesystem. + /// + /// The path to check. + /// True if the path exists, false otherwise. bool Exists(FileSystemPath path); - Stream CreateFile(FileSystemPath path); - Stream OpenFile(FileSystemPath path, FileAccess access); + + /// + /// Opens a stream to the location of the specified file with the specified mode and access. + /// + /// The path and name of the file to open. + /// A FileMode value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// The access mode to use when opening the file. + /// A stream to the location of the opened file. + Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read); + + /// + /// Creates or overwrites a file in the specified path + /// + /// The path and name of the file to create. + /// A stream to the location of the new file. + public Stream CreateFile(FileSystemPath path) => OpenFile(path, FileMode.Create, FileAccess.Write); + + /// + /// Opens a stream to the location of the specified file using FileMode.OpenOrCreate and FileAccess.Write. + /// + /// The path and name of the file to open. + /// A stream to the location of the opened file. + public Stream OpenWrite(FileSystemPath path) => OpenFile(path, FileMode.OpenOrCreate, FileAccess.Write); + + /// + /// Creates a directory in the specified path. + /// + /// The path and name of the directory to create. void CreateDirectory(FileSystemPath path); + + /// + /// Deletes the specified file or directory. Does not throw an exception if the specified file or directory does not exist. + /// + /// The path and name of the file or directory to delete. void Delete(FileSystemPath path); - bool IsReadOnly => false; + public void Move(FileSystemPath sourcePath, IFileSystem destinationFileSystem, FileSystemPath destinationPath) + { + throw new NotImplementedException(); + } + + public void Copy(FileSystemPath sourcePath, IFileSystem destinationFileSystem, FileSystemPath destinationPath) + { + throw new NotImplementedException(); + } + + public IEnumerable GetEntitiesRecursive(FileSystemPath path, Func? filter = null) + { + if (!path.IsDirectory) + throw new ArgumentException("The specified path is not a directory."); + + foreach (var entity in GetEntityPaths(path, filter)) + { + yield return entity; + + if (!entity.IsDirectory) + continue; + + foreach (var subEntity in GetEntitiesRecursive(entity, filter)) + yield return subEntity; + } + } + + public void CreateDirectoryRecursive(FileSystemPath path) + { + if (!path.IsDirectory) + throw new ArgumentException("The specified path is not a directory."); + + var currentDirectoryPath = FileSystemPath.Root; + foreach (var dirName in path.GetDirectorySegments()) + { + currentDirectoryPath = currentDirectoryPath.AppendDirectory(dirName); + + if (!Exists(currentDirectoryPath)) + CreateDirectory(currentDirectoryPath); + } + } public string ReadAllText(FileSystemPath path) { if (!Exists(path)) return string.Empty; - using var stream = OpenFile(path, FileAccess.Read); + using var stream = OpenFile(path); using var reader = new StreamReader(stream); return reader.ReadToEnd(); } public void WriteAllText(FileSystemPath path, string content) { - if (!Exists(path)) - { - CreateFile(path); - } - - using var stream = OpenFile(path, FileAccess.Write); + using var stream = OpenWrite(path); using var writer = new StreamWriter(stream); writer.Write(content); } @@ -53,16 +126,16 @@ public static RelativeFileSystem AsRelativeFileSystem(this IFileSystem self, Pat public static IEnumerable GetEntities(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetEntityPaths(path, filter).Select(p => IFileSystemEntity.Create(self, p)); + return self.GetEntityPaths(path, filter).Select(p => IFileSystemEntity.Create(self, p)).OrderBy(x => x.Path); } public static IEnumerable GetDirectories(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetDirectoryPaths(path, filter).Select(p => VirtualDirectory.Create(self, p)); + return self.GetDirectoryPaths(path, filter).Select(p => VirtualDirectory.Create(self, p)).OrderBy(x => x.Path); } public static IEnumerable GetFiles(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetFilePaths(path, filter).Select(p => VirtualFile.Create(self, p)); + return self.GetFilePaths(path, filter).Select(p => VirtualFile.Create(self, p)).OrderBy(x => x.Path); } } diff --git a/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs b/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs index e95e8694..028b74a2 100644 --- a/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -9,15 +10,18 @@ namespace pkNX.Containers.VFS; public class LayeredFileSystem : IFileSystem { - public IEnumerable FileSystems { get; } + public IReadOnlyList FileSystems { get; } - public LayeredFileSystem(IEnumerable fileSystems) + public LayeredFileSystem(IReadOnlyList fileSystems) { FileSystems = fileSystems; + + Debug.Assert(FileSystems.Any(), "No filesystems provided."); + Debug.Assert(FileSystems.Any(fs => !fs.IsReadOnly), "Should contain at least one writable filesystem."); } public LayeredFileSystem(params IFileSystem[] fileSystems) : - this(fileSystems.AsEnumerable()) + this(fileSystems.AsReadOnly()) { } public void Dispose() @@ -52,35 +56,157 @@ public IEnumerable GetFilePaths(FileSystemPath path, Func !fs.IsReadOnly); + } + public bool Exists(FileSystemPath path) { return FileSystems.Any(fs => fs.Exists(path)); } - public IFileSystem? GetFirst(FileSystemPath path) + public IFileSystem? GetFirstWhereExists(FileSystemPath path) { return FileSystems.FirstOrDefault(fs => fs.Exists(path)); } - public Stream CreateFile(FileSystemPath path) + public IFileSystem? GetFirstWritableWhereExists(FileSystemPath path) { - IFileSystem fs = GetFirst(path) ?? FileSystems.First(); - return fs.CreateFile(path); + return FileSystems.FirstOrDefault(fs => !fs.IsReadOnly && fs.Exists(path)); } - public Stream OpenFile(FileSystemPath path, FileAccess access) + private bool ValidateOpenMode(FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { - IFileSystem? fs = GetFirst(path); - if (fs == null) - throw new FileNotFoundException($"Unable to find {path.Path}"); - return fs.OpenFile(path, access); + if (!access.HasFlag(FileAccess.Write) && mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append) + { + throw new ArgumentException($"File mode '{mode}' requires files to be accessed with write permission, but the access mode was '{access}'", nameof(access)); + } + + if (access.HasFlag(FileAccess.Read) && mode == FileMode.Append) + { + throw new ArgumentException("File mode 'Append' requires files to be accessed with in read/write permission.", nameof(access)); + } + + return true; + } + + public Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) + { + if (!ValidateOpenMode(mode, access)) + return Stream.Null; + + switch (mode) + { + case FileMode.Open: + { + // just read from top layer + IFileSystem? fs = GetFirstWhereExists(path); + if (fs == null) + throw new FileNotFoundException($"Could not find the file at the specified path: {path}", nameof(path)); + + return fs.OpenFile(path, FileMode.Open, access); + } + case FileMode.OpenOrCreate: + { + // - Specifies that the operating system should open a file if it exists; otherwise, a new file should be created. + // If the file is opened with FileAccess.Read, Read permission is required. + // If the file access is FileAccess.Write, Write permission is required. + // If the file is opened with FileAccess.ReadWrite, both Read and Write permissions are required. + + IFileSystem? fs = GetFirstWhereExists(path); + if (fs != null) + { + // The file exists, now we need to check if we can open it with the requested access + // readonly fs requires special handling for write requests. + // if readonly access is requested, we can open it if the fs is readonly + var readStream = fs.OpenFile(path); + if (!fs.IsReadOnly || access == FileAccess.Read) + return readStream; + + // For write-only access, we can just create a new empty file + IFileSystem writableFs = GetFirstWritable(); + writableFs.CreateDirectoryRecursive(path.ParentPath); + var writeStream = writableFs.CreateFile(path); + + if (access == FileAccess.Write) + return writeStream; + + // For read-write access, we need to first copy the file to a writable fs + readStream.CopyTo(writeStream); + writeStream.Seek(0, SeekOrigin.Begin); + readStream.Dispose(); + return writeStream; + } + + // The file does not exist, create it on the first writable fs + return GetFirstWritable().CreateFile(path); + } + case FileMode.CreateNew: + { + // Specifies that the operating system should create a new file. + // - Requires Write permission. + // - Requires the file does not already exist. + + if (Exists(path)) + throw new IOException($"File {path.Path} already exists."); + + return GetFirstWritable().CreateFile(path); + } + case FileMode.Create: + { + // - This requires Write permission. + // - FileMode.Create is equivalent to requesting that if the file does not exist, use CreateNew; otherwise, use Truncate. + // Specifies that the operating system should create a new file. + // If the file already exists, it will be overwritten. + IFileSystem? fs = GetFirstWhereExists(path); + if (fs != null) + return fs.OpenFile(path, FileMode.Truncate, access); + + return GetFirstWritable().CreateFile(path); + } + + case FileMode.Truncate: + { + // - Requires Write permission. + // - Requires no Read access requested + // - Requires existing file + // When the file is opened, it should be truncated so that its size is zero bytes. + + IFileSystem? fs = GetFirstWhereExists(path); + if (fs == null) + throw new FileNotFoundException($"Could not find the file at the specified path: {path}", nameof(path)); + + return fs.OpenFile(path, FileMode.Truncate, access); + } + case FileMode.Append: + { + // Opens the file if it exists and seeks to the end of the file, or creates a new file. + // This requires R/W permission. + // Trying to seek to a position before the end of the file throws an IOException exception, and any attempt to read fails and throws a NotSupportedException exception. + + IFileSystem? fs = GetFirstWhereExists(path); + if (fs == null) + throw new FileNotFoundException($"Could not find the file at the specified path: {path}", nameof(path)); + + return fs.OpenFile(path, FileMode.Append, access); + } + + default: + throw new ArgumentOutOfRangeException(nameof(mode), mode, null); + } } public void CreateDirectory(FileSystemPath path) { if (Exists(path)) throw new ArgumentException("The specified directory already exists."); - IFileSystem? fs = GetFirst(path.ParentPath); + IFileSystem? fs = GetFirstWhereExists(path.ParentPath); if (fs == null) throw new ArgumentException("The directory-parent does not exist."); fs.CreateDirectory(path); diff --git a/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs b/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs index 3e02660b..82841b45 100644 --- a/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs @@ -88,11 +88,11 @@ public Stream CreateFile(FileSystemPath path) return File.Create(GetPhysicalPath(path)); } - public Stream OpenFile(FileSystemPath path, FileAccess access) + public Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { if (!path.IsFile) throw new ArgumentException("The specified path is not a file.", nameof(path)); - return File.Open(GetPhysicalPath(path), FileMode.Open, access); + return File.Open(GetPhysicalPath(path), mode, access); } public void CreateDirectory(FileSystemPath path) diff --git a/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs b/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs index 270e3b0f..d5f4f28b 100644 --- a/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs @@ -47,25 +47,28 @@ public bool Exists(FileSystemPath path) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Stream OpenFile(FileSystemPath path, FileAccess access) + public Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { if (access != FileAccess.Read) - throw new InvalidOperationException("This is a read-only filesystem."); - return FileSystem.OpenFile(path, access); + throw new UnauthorizedAccessException("This is a read-only filesystem."); + return FileSystem.OpenFile(path, mode, access); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Stream CreateFile(FileSystemPath path) { - throw new InvalidOperationException("This is a read-only filesystem."); + throw new UnauthorizedAccessException("This is a read-only filesystem."); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CreateDirectory(FileSystemPath path) { - throw new InvalidOperationException("This is a read-only filesystem."); + throw new UnauthorizedAccessException("This is a read-only filesystem."); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Delete(FileSystemPath path) { - throw new InvalidOperationException("This is a read-only filesystem."); + throw new UnauthorizedAccessException("This is a read-only filesystem."); } } diff --git a/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs b/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs index bc8ebbf9..afa26a2d 100644 --- a/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs @@ -58,9 +58,9 @@ public bool Exists(FileSystemPath path) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Stream OpenFile(FileSystemPath path, FileAccess access) + public Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { - return FileSystem.OpenFile(ToAbsolutePath(path), access); + return FileSystem.OpenFile(ToAbsolutePath(path), mode, access); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/pkNX.Containers/VFS/Util/FileSystemExtensions.cs b/pkNX.Containers/VFS/Util/FileSystemExtensions.cs deleted file mode 100644 index 4a55361b..00000000 --- a/pkNX.Containers/VFS/Util/FileSystemExtensions.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; - -namespace pkNX.Containers.VFS; - -public static class FileSystemExtensions -{ - public static IEnumerable GetEntityPaths(this VirtualDirectory directory, Func? filter = null) - { - return directory.FileSystem.GetEntityPaths(directory.Path, filter); - } - - public static IEnumerable GetEntitiesRecursive(this IFileSystem fileSystem, FileSystemPath path, Func? filter = null) - { - if (!path.IsDirectory) - throw new ArgumentException("The specified path is not a directory."); - - foreach (var entity in fileSystem.GetEntityPaths(path, filter)) - { - yield return entity; - - if (!entity.IsDirectory) - continue; - - foreach (var subEntity in fileSystem.GetEntitiesRecursive(entity, filter)) - yield return subEntity; - } - } - - public static void CreateDirectoryRecursive(this IFileSystem fileSystem, FileSystemPath path) - { - if (!path.IsDirectory) - throw new ArgumentException("The specified path is not a directory."); - var currentDirectoryPath = FileSystemPath.Root; - foreach (var dirName in path.GetDirectorySegments()) - { - currentDirectoryPath = currentDirectoryPath.AppendDirectory(dirName); - if (!fileSystem.Exists(currentDirectoryPath)) - fileSystem.CreateDirectory(currentDirectoryPath); - } - } - - /*#region Move Extensions - public static void Move(this IFileSystem sourceFileSystem, FileSystemPath sourcePath, IFileSystem destinationFileSystem, FileSystemPath destinationPath) - { - if (!EntityMovers.Registration.TryGetSupported(sourceFileSystem.GetType(), destinationFileSystem.GetType(), out IEntityMover mover)) - throw new ArgumentException("The specified combination of file-systems is not supported."); - mover.Move(sourceFileSystem, sourcePath, destinationFileSystem, destinationPath); - } - - public static void MoveTo(this FileSystemEntity entity, IFileSystem destinationFileSystem, FileSystemPath destinationPath) - { - entity.FileSystem.Move(entity.Path, destinationFileSystem, destinationPath); - } - - public static void MoveTo(this VirtualDirectory source, VirtualDirectory destination) - { - source.FileSystem.Move(source.Path, destination.FileSystem, destination.Path.AppendDirectory(source.Path.EntityName)); - } - - public static void MoveTo(this VirtualFile source, VirtualDirectory destination) - { - source.FileSystem.Move(source.Path, destination.FileSystem, destination.Path.AppendFile(source.Path.EntityName)); - } - #endregion - - #region Copy Extensions - public static void Copy(this IFileSystem sourceFileSystem, FileSystemPath sourcePath, IFileSystem destinationFileSystem, FileSystemPath destinationPath) - { - if (!EntityCopiers.Registration.TryGetSupported(sourceFileSystem.GetType(), destinationFileSystem.GetType(), out IEntityCopier copier)) - throw new ArgumentException("The specified combination of file-systems is not supported."); - copier.Copy(sourceFileSystem, sourcePath, destinationFileSystem, destinationPath); - } - - public static void CopyTo(this FileSystemEntity entity, IFileSystem destinationFileSystem, FileSystemPath destinationPath) - { - entity.FileSystem.Copy(entity.Path, destinationFileSystem, destinationPath); - } - - public static void CopyTo(this VirtualDirectory source, VirtualDirectory destination) - { - source.FileSystem.Copy(source.Path, destination.FileSystem, destination.Path.AppendDirectory(source.Path.EntityName)); - } - - public static void CopyTo(this VirtualFile source, VirtualDirectory destination) - { - source.FileSystem.Copy(source.Path, destination.FileSystem, destination.Path.AppendFile(source.Path.EntityName)); - } - #endregion*/ -} diff --git a/pkNX.Containers/VFS/Util/IFileSystemEntity.cs b/pkNX.Containers/VFS/Util/IFileSystemEntity.cs index 07c646dc..9c9ed6c9 100644 --- a/pkNX.Containers/VFS/Util/IFileSystemEntity.cs +++ b/pkNX.Containers/VFS/Util/IFileSystemEntity.cs @@ -7,6 +7,26 @@ public interface IFileSystemEntity string Name { get; } VirtualDirectory ParentDirectory { get; } + public void CopyTo(IFileSystem destinationFileSystem, FileSystemPath destinationPath) + { + FileSystem.Copy(Path, destinationFileSystem, destinationPath); + } + + public void MoveTo(IFileSystem destinationFileSystem, FileSystemPath destinationPath) + { + FileSystem.Move(Path, destinationFileSystem, destinationPath); + } + + public void Delete() + { + FileSystem.Delete(Path); + } + + public void Exists() + { + FileSystem.Exists(Path); + } + internal static IFileSystemEntity Create(IFileSystem fileSystem, FileSystemPath path) { if (path.IsFile) @@ -16,16 +36,3 @@ internal static IFileSystemEntity Create(IFileSystem fileSystem, FileSystemPath } } -public static class IFileSystemEntityExtensions -{ - public static void Delete(this IFileSystemEntity e) - { - e.FileSystem.Delete(e.Path); - } - - public static void Exists(this IFileSystemEntity e) - { - e.FileSystem.Exists(e.Path); - } -} - diff --git a/pkNX.Containers/VFS/Util/VirtualDirectory.cs b/pkNX.Containers/VFS/Util/VirtualDirectory.cs index 3c13c8db..4f80f5c8 100644 --- a/pkNX.Containers/VFS/Util/VirtualDirectory.cs +++ b/pkNX.Containers/VFS/Util/VirtualDirectory.cs @@ -8,6 +8,15 @@ namespace pkNX.Containers.VFS; public string Name => Path.EntityName; public VirtualDirectory ParentDirectory => Create(FileSystem, Path.ParentPath); + public void CopyTo(VirtualDirectory destination) + { + FileSystem.Copy(Path, destination.FileSystem, destination.Path.AppendDirectory(Name)); + } + + public void MoveTo(VirtualDirectory destination) + { + FileSystem.Move(Path, destination.FileSystem, destination.Path.AppendDirectory(Name)); + } internal static VirtualDirectory Create(IFileSystem fileSystem, FileSystemPath path) { @@ -16,4 +25,9 @@ internal static VirtualDirectory Create(IFileSystem fileSystem, FileSystemPath p return new VirtualDirectory(fileSystem, path); } + + public IEnumerable GetEntityPaths(Func? filter = null) + { + return FileSystem.GetEntityPaths(Path, filter); + } } diff --git a/pkNX.Containers/VFS/Util/VirtualFile.cs b/pkNX.Containers/VFS/Util/VirtualFile.cs index 2b73b6c4..7d0a037e 100644 --- a/pkNX.Containers/VFS/Util/VirtualFile.cs +++ b/pkNX.Containers/VFS/Util/VirtualFile.cs @@ -26,9 +26,29 @@ public string GetExtension() return Name[(lastPeriod + 1)..]; } - public Stream Open(FileAccess access = FileAccess.Read) + public Stream Open(FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { - return FileSystem.OpenFile(Path, access); + return FileSystem.OpenFile(Path, mode, access); + } + + public Stream OpenRead() + { + return FileSystem.OpenFile(Path, FileMode.Open, FileAccess.Read); + } + + public Stream OpenWrite() + { + return FileSystem.OpenWrite(Path); + } + + public void CopyTo(VirtualDirectory destination) + { + FileSystem.Copy(Path, destination.FileSystem, destination.Path.AppendFile(Name)); + } + + public void MoveTo(VirtualDirectory destination) + { + FileSystem.Move(Path, destination.FileSystem, destination.Path.AppendFile(Name)); } public ReadOnlySpan ReadAllBytes() @@ -57,13 +77,13 @@ public string ReadAllText() public void WriteAllBytes(ReadOnlySpan bytes) { - using var stream = Open(FileAccess.Write); + using var stream = OpenWrite(); stream.Write(bytes); } public void WriteAllText(string text) { - using var stream = Open(FileAccess.Write); + using var stream = OpenWrite(); using var writer = new StreamWriter(stream); writer.Write(text); } diff --git a/pkNX.Containers/VFS/VirtualFileSystem.cs b/pkNX.Containers/VFS/VirtualFileSystem.cs index 859718e6..4765d8fa 100644 --- a/pkNX.Containers/VFS/VirtualFileSystem.cs +++ b/pkNX.Containers/VFS/VirtualFileSystem.cs @@ -30,12 +30,15 @@ public MountPoint(FileSystemPath mountPath, IFileSystem fileSystem) public class VirtualFileSystem : IFileSystem { + public static VirtualFileSystem Current { get; private set; } = null!; + public SortedSet Mounts { get; } public bool IsReadOnly => Mounts.All(x => x.FileSystem.IsReadOnly); public VirtualFileSystem(IEnumerable mounts) { Mounts = new SortedSet(mounts); + Current = this; } public VirtualFileSystem(params MountPoint[] mounts) : @@ -84,18 +87,17 @@ public bool Exists(FileSystemPath path) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Stream CreateFile(FileSystemPath path) + public Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read) { var mount = GetMountPoint(path); - return mount.FileSystem.CreateFile(path); + return mount.FileSystem.OpenFile(path, mode, access); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Stream OpenFile(FileSystemPath path, FileAccess access) - { - var mount = GetMountPoint(path); - return mount.FileSystem.OpenFile(path, access); - } + public Stream OpenWrite(FileSystemPath path) => OpenFile(path, FileMode.OpenOrCreate, FileAccess.Write); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Stream CreateFile(FileSystemPath path) => OpenFile(path, FileMode.Create, FileAccess.Write); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CreateDirectory(FileSystemPath path)