diff --git a/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs b/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs index ac0f62d3..cb9c1e4f 100644 --- a/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/Archives/ZipArchiveFileSystem.cs @@ -50,40 +50,50 @@ protected string ToEntryPath(FileSystemPath path) return ZipArchive.GetEntry(ToEntryPath(path)); } - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { - return GetZipEntries() - .Select(ToPath) - .Where(path.IsParentOf) - .Select(entryPath => entryPath.ParentPath == path + var entries = GetZipEntries().Select(ToPath).Where(path.IsParentOf); + + if (filter != null) + entries = entries.Where(filter); + + return entries.Select(entryPath => entryPath.ParentPath == path ? entryPath : path.AppendDirectory(entryPath.MakeRelativeTo(path).GetDirectorySegments().First())) .Distinct(); } - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { if (!path.IsDirectory) throw new ArgumentException("This FileSystemPath is not a directory.", nameof(path)); - return GetZipEntries() + var entries = GetZipEntries() .Select(ToPath) - .Where(p => path.IsParentOf(p) && p.IsDirectory) - .Select(entryPath => entryPath.ParentPath == path - ? entryPath - : path.AppendDirectory(entryPath.MakeRelativeTo(path).GetDirectorySegments().First())) + .Where(p => path.IsParentOf(p) && p.IsDirectory); + + if (filter != null) + entries = entries.Where(filter); + + return entries.Select(entryPath => + entryPath.ParentPath == path ? entryPath : + path.AppendDirectory(entryPath.MakeRelativeTo(path).GetDirectorySegments().First())) .Distinct(); } - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { if (!path.IsDirectory) throw new ArgumentException("The specified path is not a directory.", nameof(path)); - return GetZipEntries() + var entries = GetZipEntries() .Select(ToPath) - .Where(p => path.IsParentOf(p) && p.IsFile) - .Select(entryPath => entryPath.ParentPath == path + .Where(p => path.IsParentOf(p) && p.IsFile); + + if (filter != null) + entries = entries.Where(filter); + + return entries.Select(entryPath => entryPath.ParentPath == path ? entryPath : path.AppendDirectory(entryPath.MakeRelativeTo(path).GetDirectorySegments().First())) .Distinct(); diff --git a/pkNX.Containers/VFS/FileSystems/IFileSystem.cs b/pkNX.Containers/VFS/FileSystems/IFileSystem.cs index 33050b3d..d8a46d35 100644 --- a/pkNX.Containers/VFS/FileSystems/IFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/IFileSystem.cs @@ -7,9 +7,9 @@ namespace pkNX.Containers.VFS; public interface IFileSystem : IDisposable { - IEnumerable GetEntityPaths(FileSystemPath path); - IEnumerable GetDirectoryPaths(FileSystemPath path); - IEnumerable GetFilePaths(FileSystemPath path); + IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null); + IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null); + IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null); bool Exists(FileSystemPath path); Stream CreateFile(FileSystemPath path); @@ -51,18 +51,18 @@ public static RelativeFileSystem AsRelativeFileSystem(this IFileSystem self, Pat return new(self, toAbsolutePath, toRelativePath); } - public static IEnumerable GetEntities(this IFileSystem self, FileSystemPath path) + public static IEnumerable GetEntities(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetEntityPaths(path).Select(p => IFileSystemEntity.Create(self, p)); + return self.GetEntityPaths(path, filter).Select(p => IFileSystemEntity.Create(self, p)); } - public static IEnumerable GetDirectories(this IFileSystem self, FileSystemPath path) + public static IEnumerable GetDirectories(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetDirectoryPaths(path).Select(p => VirtualDirectory.Create(self, p)); + return self.GetDirectoryPaths(path, filter).Select(p => VirtualDirectory.Create(self, p)); } - public static IEnumerable GetFiles(this IFileSystem self, FileSystemPath path) + public static IEnumerable GetFiles(this IFileSystem self, FileSystemPath path, Func? filter = null) { - return self.GetFilePaths(path).Select(p => VirtualFile.Create(self, p)); + return self.GetFilePaths(path, filter).Select(p => VirtualFile.Create(self, p)); } } diff --git a/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs b/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs index c6e40fc3..e95e8694 100644 --- a/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/LayeredFileSystem.cs @@ -28,27 +28,27 @@ public void Dispose() GC.SuppressFinalize(this); } - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { var entities = new HashSet(); foreach (var fs in FileSystems.Where(fs => fs.Exists(path))) - entities.UnionWith(fs.GetEntityPaths(path)); + entities.UnionWith(fs.GetEntityPaths(path, filter)); return entities; } - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { var directories = new HashSet(); foreach (var fs in FileSystems.Where(fs => fs.Exists(path))) - directories.UnionWith(fs.GetDirectoryPaths(path)); + directories.UnionWith(fs.GetDirectoryPaths(path, filter)); return directories; } - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { var files = new HashSet(); foreach (var fs in FileSystems.Where(fs => fs.Exists(path))) - files.UnionWith(fs.GetFilePaths(path)); + files.UnionWith(fs.GetFilePaths(path, filter)); return files; } diff --git a/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs b/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs index d622b361..3e02660b 100644 --- a/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/PhysicalFileSystem.cs @@ -42,27 +42,37 @@ public FileSystemPath GetVirtualDirectoryPath(string physicalPath) return FileSystemPath.Parse(virtualPath); } - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { - return GetDirectoryPaths(path).Concat(GetFilePaths(path)); + return GetDirectoryPaths(path, filter).Concat(GetFilePaths(path, filter)); } - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { if (!path.IsDirectory) throw new ArgumentException("This FileSystemPath is not a directory.", nameof(path)); var physicalPaths = Directory.GetDirectories(GetPhysicalPath(path)); - return physicalPaths.Select(GetVirtualDirectoryPath); + var virtualPaths = physicalPaths.Select(GetVirtualDirectoryPath); + + if (filter == null) + return virtualPaths; + + return virtualPaths.Where(filter); } - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { if (!path.IsDirectory) throw new ArgumentException("The specified path is not a directory.", nameof(path)); var physicalPaths = Directory.GetFiles(GetPhysicalPath(path)); - return physicalPaths.Select(GetVirtualFilePath); + var virtualPaths = physicalPaths.Select(GetVirtualFilePath); + + if (filter == null) + return virtualPaths; + + return virtualPaths.Where(filter); } public bool Exists(FileSystemPath path) diff --git a/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs b/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs index 80f6dbf1..270e3b0f 100644 --- a/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/ReadOnlyFileSystem.cs @@ -23,21 +23,21 @@ public void Dispose() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetEntityPaths(path); + return FileSystem.GetEntityPaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetDirectoryPaths(path); + return FileSystem.GetDirectoryPaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetFilePaths(path); + return FileSystem.GetFilePaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs b/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs index c02fc677..bc8ebbf9 100644 --- a/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs +++ b/pkNX.Containers/VFS/FileSystems/RelativeFileSystem.cs @@ -31,23 +31,23 @@ public void Dispose() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetEntityPaths(ToAbsolutePath(path)) + return FileSystem.GetEntityPaths(ToAbsolutePath(path), filter) .Select(p => ToRelativePath(p)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetDirectoryPaths(ToAbsolutePath(path)) + return FileSystem.GetDirectoryPaths(ToAbsolutePath(path), filter) .Select(p => ToRelativePath(p)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { - return FileSystem.GetFilePaths(ToAbsolutePath(path)) + return FileSystem.GetFilePaths(ToAbsolutePath(path), filter) .Select(p => ToRelativePath(p)); } diff --git a/pkNX.Containers/VFS/Util/FileSystemExtensions.cs b/pkNX.Containers/VFS/Util/FileSystemExtensions.cs index e2267be9..4a55361b 100644 --- a/pkNX.Containers/VFS/Util/FileSystemExtensions.cs +++ b/pkNX.Containers/VFS/Util/FileSystemExtensions.cs @@ -7,24 +7,24 @@ namespace pkNX.Containers.VFS; public static class FileSystemExtensions { - public static IEnumerable GetEntityPaths(this VirtualDirectory directory) + public static IEnumerable GetEntityPaths(this VirtualDirectory directory, Func? filter = null) { - return directory.FileSystem.GetEntityPaths(directory.Path); + return directory.FileSystem.GetEntityPaths(directory.Path, filter); } - public static IEnumerable GetEntitiesRecursive(this IFileSystem fileSystem, FileSystemPath path) + 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)) + foreach (var entity in fileSystem.GetEntityPaths(path, filter)) { yield return entity; if (!entity.IsDirectory) continue; - foreach (var subEntity in fileSystem.GetEntitiesRecursive(entity)) + foreach (var subEntity in fileSystem.GetEntitiesRecursive(entity, filter)) yield return subEntity; } } diff --git a/pkNX.Containers/VFS/Util/VirtualFile.cs b/pkNX.Containers/VFS/Util/VirtualFile.cs index b24c9f3e..2b73b6c4 100644 --- a/pkNX.Containers/VFS/Util/VirtualFile.cs +++ b/pkNX.Containers/VFS/Util/VirtualFile.cs @@ -8,15 +8,70 @@ namespace pkNX.Containers.VFS; public string Name => Path.EntityName; public VirtualDirectory ParentDirectory => VirtualDirectory.Create(FileSystem, Path.ParentPath); - public Stream Open(FileAccess access) + public (string, string) GetFileNameAndExtension() + { + int lastPeriod = Name.LastIndexOf('.'); + return (Name[..lastPeriod], Name[(lastPeriod + 1)..]); + } + + public string GetFileNameWithoutExtension() + { + int lastPeriod = Name.LastIndexOf('.'); + return Name[..lastPeriod]; + } + + public string GetExtension() + { + int lastPeriod = Name.LastIndexOf('.'); + return Name[(lastPeriod + 1)..]; + } + + public Stream Open(FileAccess access = FileAccess.Read) { return FileSystem.OpenFile(Path, access); } + public ReadOnlySpan ReadAllBytes() + { + using var stream = Open(); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } + + public void ReadAllBytes(Span bytes) + { + using var stream = Open(); + int bytesRead = stream.Read(bytes); + + if (bytesRead != bytes.Length) + throw new IOException("Could not read all bytes."); + } + + public string ReadAllText() + { + using var stream = Open(); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + public void WriteAllBytes(ReadOnlySpan bytes) + { + using var stream = Open(FileAccess.Write); + stream.Write(bytes); + } + + public void WriteAllText(string text) + { + using var stream = Open(FileAccess.Write); + using var writer = new StreamWriter(stream); + writer.Write(text); + } + internal static VirtualFile Create(IFileSystem fileSystem, FileSystemPath path) { if (!path.IsFile) - throw new ArgumentException("The specified path is no file.", nameof(path)); + throw new ArgumentException("The specified path is not a file.", nameof(path)); return new VirtualFile(fileSystem, path); } diff --git a/pkNX.Containers/VFS/VirtualFileSystem.cs b/pkNX.Containers/VFS/VirtualFileSystem.cs index 69637a01..859718e6 100644 --- a/pkNX.Containers/VFS/VirtualFileSystem.cs +++ b/pkNX.Containers/VFS/VirtualFileSystem.cs @@ -56,24 +56,24 @@ protected MountPoint GetMountPoint(FileSystemPath path) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetEntityPaths(FileSystemPath path) + public IEnumerable GetEntityPaths(FileSystemPath path, Func? filter = null) { var mount = GetMountPoint(path); - return mount.FileSystem.GetEntityPaths(path); + return mount.FileSystem.GetEntityPaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetDirectoryPaths(FileSystemPath path) + public IEnumerable GetDirectoryPaths(FileSystemPath path, Func? filter = null) { var mount = GetMountPoint(path); - return mount.FileSystem.GetDirectoryPaths(path); + return mount.FileSystem.GetDirectoryPaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IEnumerable GetFilePaths(FileSystemPath path) + public IEnumerable GetFilePaths(FileSystemPath path, Func? filter = null) { var mount = GetMountPoint(path); - return mount.FileSystem.GetFilePaths(path); + return mount.FileSystem.GetFilePaths(path, filter); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/pkNX.Game/GameManagerPLA.cs b/pkNX.Game/GameManagerPLA.cs index f390f491..46f96121 100644 --- a/pkNX.Game/GameManagerPLA.cs +++ b/pkNX.Game/GameManagerPLA.cs @@ -33,13 +33,11 @@ protected override void SetMitm() var redirect = Path.Combine(basePath, tid); FileMitm.SetRedirect(basePath, redirect); - // VFS test var cleanRomFS = new PhysicalFileSystem(basePath + "/romfs/").AsReadOnlyFileSystem(); var moddedRomFS = new PhysicalFileSystem(redirect + "/romfs/"); var layeredFS = new LayeredFileSystem(moddedRomFS, cleanRomFS); VFS = new VirtualFileSystem(new MountPoint("/romfs/", layeredFS)); - using var file = VFS.OpenFile("/romfs/bin/pokemon/data/poke_ai.bin", FileAccess.Read); } public override void Initialize()