mirror of
https://github.com/kwsch/pkNX.git
synced 2026-09-30 13:16:58 -05:00
Made VFS fully functional
Still need to start using it, but want to do it a bit slow to test it out. Also should really build some unit tests for this one..
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,36 +7,109 @@ namespace pkNX.Containers.VFS;
|
||||
|
||||
public interface IFileSystem : IDisposable
|
||||
{
|
||||
bool IsReadOnly => false;
|
||||
|
||||
IEnumerable<FileSystemPath> GetEntityPaths(FileSystemPath path, Func<FileSystemPath, bool>? filter = null);
|
||||
IEnumerable<FileSystemPath> GetDirectoryPaths(FileSystemPath path, Func<FileSystemPath, bool>? filter = null);
|
||||
IEnumerable<FileSystemPath> GetFilePaths(FileSystemPath path, Func<FileSystemPath, bool>? filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified path exists in the filesystem.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to check.</param>
|
||||
/// <returns>True if the path exists, false otherwise.</returns>
|
||||
bool Exists(FileSystemPath path);
|
||||
Stream CreateFile(FileSystemPath path);
|
||||
Stream OpenFile(FileSystemPath path, FileAccess access);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream to the location of the specified file with the specified mode and access.
|
||||
/// </summary>
|
||||
/// <param name="path">The path and name of the file to open.</param>
|
||||
/// <param name="mode">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.</param>
|
||||
/// <param name="access">The access mode to use when opening the file.</param>
|
||||
/// <returns>A stream to the location of the opened file.</returns>
|
||||
Stream OpenFile(FileSystemPath path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read);
|
||||
|
||||
/// <summary>
|
||||
/// Creates or overwrites a file in the specified path
|
||||
/// </summary>
|
||||
/// <param name="path">The path and name of the file to create.</param>
|
||||
/// <returns>A stream to the location of the new file.</returns>
|
||||
public Stream CreateFile(FileSystemPath path) => OpenFile(path, FileMode.Create, FileAccess.Write);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a stream to the location of the specified file using FileMode.OpenOrCreate and FileAccess.Write.
|
||||
/// </summary>
|
||||
/// <param name="path">The path and name of the file to open.</param>
|
||||
/// <returns>A stream to the location of the opened file.</returns>
|
||||
public Stream OpenWrite(FileSystemPath path) => OpenFile(path, FileMode.OpenOrCreate, FileAccess.Write);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a directory in the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path and name of the directory to create.</param>
|
||||
void CreateDirectory(FileSystemPath path);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file or directory. Does not throw an exception if the specified file or directory does not exist.
|
||||
/// </summary>
|
||||
/// <param name="path">The path and name of the file or directory to delete.</param>
|
||||
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<FileSystemPath> GetEntitiesRecursive(FileSystemPath path, Func<FileSystemPath, bool>? 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<IFileSystemEntity> GetEntities(this IFileSystem self, FileSystemPath path, Func<FileSystemPath, bool>? 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<VirtualDirectory> GetDirectories(this IFileSystem self, FileSystemPath path, Func<FileSystemPath, bool>? 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<VirtualFile> GetFiles(this IFileSystem self, FileSystemPath path, Func<FileSystemPath, bool>? 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IFileSystem> FileSystems { get; }
|
||||
public IReadOnlyList<IFileSystem> FileSystems { get; }
|
||||
|
||||
public LayeredFileSystem(IEnumerable<IFileSystem> fileSystems)
|
||||
public LayeredFileSystem(IReadOnlyList<IFileSystem> 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<FileSystemPath> GetFilePaths(FileSystemPath path, Func<FileSy
|
||||
return files;
|
||||
}
|
||||
|
||||
public IFileSystem GetFirst()
|
||||
{
|
||||
return FileSystems.First();
|
||||
}
|
||||
|
||||
public IFileSystem GetFirstWritable()
|
||||
{
|
||||
return FileSystems.First(fs => !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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<FileSystemPath> GetEntityPaths(this VirtualDirectory directory, Func<FileSystemPath, bool>? filter = null)
|
||||
{
|
||||
return directory.FileSystem.GetEntityPaths(directory.Path, filter);
|
||||
}
|
||||
|
||||
public static IEnumerable<FileSystemPath> GetEntitiesRecursive(this IFileSystem fileSystem, FileSystemPath path, Func<FileSystemPath, bool>? 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*/
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<FileSystemPath> GetEntityPaths(Func<FileSystemPath, bool>? filter = null)
|
||||
{
|
||||
return FileSystem.GetEntityPaths(Path, filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<byte> ReadAllBytes()
|
||||
@@ -57,13 +77,13 @@ public string ReadAllText()
|
||||
|
||||
public void WriteAllBytes(ReadOnlySpan<byte> 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);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,15 @@ public MountPoint(FileSystemPath mountPath, IFileSystem fileSystem)
|
||||
|
||||
public class VirtualFileSystem : IFileSystem
|
||||
{
|
||||
public static VirtualFileSystem Current { get; private set; } = null!;
|
||||
|
||||
public SortedSet<MountPoint> Mounts { get; }
|
||||
public bool IsReadOnly => Mounts.All(x => x.FileSystem.IsReadOnly);
|
||||
|
||||
public VirtualFileSystem(IEnumerable<MountPoint> mounts)
|
||||
{
|
||||
Mounts = new SortedSet<MountPoint>(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)
|
||||
|
||||
Reference in New Issue
Block a user