From cacac0ea6eecc6c04feecb3608bb24ffc4ceb186 Mon Sep 17 00:00:00 2001 From: emmaus Date: Sun, 4 Nov 2018 20:56:20 +0000 Subject: [PATCH] add pfs support, write directoryentry info for romfs files --- Ryujinx.HLE/FileSystem/FileSystemProvider.cs | 53 +++++-- Ryujinx.HLE/FileSystem/IFileSystemProvider.cs | 6 +- Ryujinx.HLE/FileSystem/PFsProvider.cs | 147 ++++++++++++++++++ ...FileSystemProvider.cs => RomFsProvider.cs} | 36 +++-- .../HOS/Services/FspSrv/DirectoryEntry.cs | 21 +++ .../HOS/Services/FspSrv/DirectoryEntryType.cs | 8 + Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs | 21 +-- .../HOS/Services/FspSrv/IFileSystem.cs | 10 +- .../HOS/Services/FspSrv/IFileSystemProxy.cs | 43 +++-- 9 files changed, 291 insertions(+), 54 deletions(-) create mode 100644 Ryujinx.HLE/FileSystem/PFsProvider.cs rename Ryujinx.HLE/FileSystem/{RomFileSystemProvider.cs => RomFsProvider.cs} (72%) create mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs create mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs index 653d89d6da..1abec88b6a 100644 --- a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.FspSrv; using System; +using System.Collections.Generic; using System.IO; using static Ryujinx.HLE.HOS.ErrorCode; @@ -76,26 +77,60 @@ namespace Ryujinx.HLE.FileSystem return 0; } - public string[] GetDirectories(string Path) + public DirectoryEntry[] GetDirectories(string Path) { - return Directory.GetDirectories(Path); + List Entries = new List(); + + foreach(string Directory in Directory.EnumerateDirectories(Path)) + { + DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory, DirectoryEntryType.Directory); + + Entries.Add(DirectoryEntry); + } + + return Entries.ToArray(); } - public string[] GetEntries(string Path) + public DirectoryEntry[] GetEntries(string Path) { - string DirName = GetFullPath(Path); - - if (Directory.Exists(DirName)) + if (Directory.Exists(Path)) { - return Directory.GetFileSystemEntries(DirName); + List Entries = new List(); + + foreach (string Directory in Directory.EnumerateDirectories(Path)) + { + DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory, DirectoryEntryType.Directory); + + Entries.Add(DirectoryEntry); + } + + foreach (string File in Directory.EnumerateFiles(Path)) + { + FileInfo FileInfo = new FileInfo(File); + + DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length); + + Entries.Add(DirectoryEntry); + } } return null; } - public string[] GetFiles(string Path) + public DirectoryEntry[] GetFiles(string Path) { - return Directory.GetFiles(Path); + List Entries = new List(); + + foreach (string File in Directory.EnumerateFiles(Path)) + { + FileInfo FileInfo = new FileInfo(File); + + DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length); + + Entries.Add(DirectoryEntry); + } + + return Entries.ToArray(); } public long GetFreeSpace(ServiceCtx Context) diff --git a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs index d8cb741e09..70b6c0c835 100644 --- a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs @@ -13,11 +13,11 @@ namespace Ryujinx.HLE.FileSystem long RenameDirectory(string OldName, string NewName); - string[] GetEntries(string Path); + DirectoryEntry[] GetEntries(string Path); - string[] GetDirectories(string Path); + DirectoryEntry[] GetDirectories(string Path); - string[] GetFiles(string Path); + DirectoryEntry[] GetFiles(string Path); long DeleteFile(string Name); diff --git a/Ryujinx.HLE/FileSystem/PFsProvider.cs b/Ryujinx.HLE/FileSystem/PFsProvider.cs new file mode 100644 index 0000000000..c4be3341aa --- /dev/null +++ b/Ryujinx.HLE/FileSystem/PFsProvider.cs @@ -0,0 +1,147 @@ +using LibHac; +using Ryujinx.HLE.HOS; +using Ryujinx.HLE.HOS.Services.FspSrv; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.FileSystem +{ + class PFsProvider : IFileSystemProvider + { + private Pfs Pfs; + + public PFsProvider(Pfs Pfs) + { + this.Pfs = Pfs; + } + + public long CreateDirectory(string Name) + { + throw new NotSupportedException(); + } + + public long CreateFile(string Name, long Size) + { + throw new NotSupportedException(); + } + + public long DeleteDirectory(string Name, bool Recursive) + { + throw new NotSupportedException(); + } + + public long DeleteFile(string Name) + { + throw new NotSupportedException(); + } + + public DirectoryEntry[] GetDirectories(string Path) + { + return new DirectoryEntry[0]; + } + + public DirectoryEntry[] GetEntries(string Path) + { + List Entries = new List(); + + foreach (PfsFileEntry File in Pfs.Files) + { + DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.Size); + + Entries.Add(DirectoryEntry); + } + + return Entries.ToArray(); + } + + public DirectoryEntry[] GetFiles(string Path) + { + List Entries = new List(); + + foreach (PfsFileEntry File in Pfs.Files) + { + DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.Size); + + Entries.Add(DirectoryEntry); + } + + return Entries.ToArray(); + } + + public long GetFreeSpace(ServiceCtx Context) + { + return 0; + } + + public string GetFullPath(string Name) + { + return Name; + } + + public long GetTotalSpace(ServiceCtx Context) + { + return Pfs.Files.Sum(x => x.Size); + } + + public bool DirectoryExists(string Name) + { + return Name == "/" ? true : false; + } + + public bool FileExists(string Name) + { + Name = Name.TrimStart('/'); + + return Pfs.FileExists(Name); + } + + public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface) + { + if (Name == "/") + { + DirectoryInterface = new IDirectory(Name, FilterFlags, this); + + return 0; + } + + throw new NotSupportedException(); + } + + public long OpenFile(string Name, out IFile FileInterface) + { + FileInterface = null; + + Name = Name.TrimStart('/'); + + if (Pfs.FileExists(Name)) + { + Stream Stream = Pfs.OpenFile(Name); + + FileInterface = new IFile(Stream, Name); + + return 0; + } + + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + public long RenameDirectory(string OldName, string NewName) + { + throw new NotSupportedException(); + } + + public long RenameFile(string OldName, string NewName) + { + throw new NotSupportedException(); + } + + public void CheckIfOutsideBasePath(string Path) + { + throw new NotSupportedException(); + } + } +} diff --git a/Ryujinx.HLE/FileSystem/RomFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/RomFsProvider.cs similarity index 72% rename from Ryujinx.HLE/FileSystem/RomFileSystemProvider.cs rename to Ryujinx.HLE/FileSystem/RomFsProvider.cs index 549152ce3b..8beb8bde53 100644 --- a/Ryujinx.HLE/FileSystem/RomFileSystemProvider.cs +++ b/Ryujinx.HLE/FileSystem/RomFsProvider.cs @@ -10,11 +10,11 @@ using static Ryujinx.HLE.HOS.ErrorCode; namespace Ryujinx.HLE.FileSystem { - class RomFileSystemProvider : IFileSystemProvider + class RomFsProvider : IFileSystemProvider { private Romfs RomFs; - public RomFileSystemProvider(Stream StorageStream) + public RomFsProvider(Stream StorageStream) { RomFs = new Romfs(StorageStream); } @@ -39,42 +39,50 @@ namespace Ryujinx.HLE.FileSystem throw new NotSupportedException(); } - public string[] GetDirectories(string Path) + public DirectoryEntry[] GetDirectories(string Path) { - List Directories = new List(); + List Directories = new List(); foreach(RomfsDir Directory in RomFs.Directories) { - Directories.Add(Directory.Name); + DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory.Name, DirectoryEntryType.Directory); + + Directories.Add(DirectoryEntry); } return Directories.ToArray(); } - public string[] GetEntries(string Path) + public DirectoryEntry[] GetEntries(string Path) { - List Entries = new List(); + List Entries = new List(); foreach (RomfsDir Directory in RomFs.Directories) { - Entries.Add(Directory.Name); + DirectoryEntry DirectoryEntry = new DirectoryEntry(Directory.Name, DirectoryEntryType.Directory); + + Entries.Add(DirectoryEntry); } foreach (RomfsFile File in RomFs.Files) { - Entries.Add(File.Name); + DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.DataLength); + + Entries.Add(DirectoryEntry); } return Entries.ToArray(); } - public string[] GetFiles(string Path) + public DirectoryEntry[] GetFiles(string Path) { - List Files = new List(); + List Files = new List(); foreach (RomfsFile File in RomFs.Files) { - Files.Add(File.Name); + DirectoryEntry DirectoryEntry = new DirectoryEntry(File.Name, DirectoryEntryType.File, File.DataLength); + + Files.Add(DirectoryEntry); } return Files.ToArray(); @@ -114,6 +122,8 @@ namespace Ryujinx.HLE.FileSystem if (Directory != null) { DirectoryInterface = new IDirectory(Name, FilterFlags, this); + + return 0; } return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); @@ -123,7 +133,7 @@ namespace Ryujinx.HLE.FileSystem { FileInterface = null; - if (File.Exists(Name)) + if (RomFs.FileExists(Name)) { Stream Stream = RomFs.OpenFile(Name); diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs new file mode 100644 index 0000000000..c7a33cc20c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + public struct DirectoryEntry + { + public string Path { get; private set; } + public long Size { get; private set; } + + public DirectoryEntryType EntryType { get; set; } + + public DirectoryEntry(string Path, DirectoryEntryType DirectoryEntryType, long Size = 0) + { + this.Path = Path; + this.EntryType = DirectoryEntryType; + this.Size = Size; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs new file mode 100644 index 0000000000..a46d8ff4ee --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.FspSrv +{ + public enum DirectoryEntryType : byte + { + Directory, + File + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs index 87a6b36b1f..ca5944610b 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public override IReadOnlyDictionary Commands => m_Commands; - private List DirectoryEntries; + private List DirectoryEntries; private int CurrentItemIndex; @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv this.DirectoryPath = DirectoryPath; - DirectoryEntries = new List(); + DirectoryEntries = new List(); if ((Flags & 1) != 0) { @@ -73,29 +73,20 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } - private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath) + private void WriteDirectoryEntry(ServiceCtx Context, long Position, DirectoryEntry Entry) { for (int Offset = 0; Offset < 0x300; Offset += 8) { Context.Memory.WriteInt64(Position + Offset, 0); } - byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath)); + byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(Entry.Path)); Context.Memory.WriteBytes(Position, NameBuffer); - int Type = 0; - long Size = 0; - - if (File.Exists(FullPath)) - { - Type = 1; - Size = new FileInfo(FullPath).Length; - } - Context.Memory.WriteInt32(Position + 0x300, 0); //Padding? - Context.Memory.WriteInt32(Position + 0x304, Type); - Context.Memory.WriteInt64(Position + 0x308, Size); + Context.Memory.WriteInt32(Position + 0x304, (byte)Entry.EntryType); + Context.Memory.WriteInt64(Position + 0x308, Entry.Size); } public long GetEntryCount(ServiceCtx Context) diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index 91faee9458..2097f4e5a4 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -340,15 +340,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - foreach (string Entry in Provider.GetEntries(DirName)) + foreach (DirectoryEntry Entry in Provider.GetEntries(DirName)) { - if (Provider.DirectoryExists(Entry)) + if (Provider.DirectoryExists(Entry.Path)) { - Provider.DeleteDirectory(Entry, true); + Provider.DeleteDirectory(Entry.Path, true); } - else if (Provider.FileExists(Entry)) + else if (Provider.FileExists(Entry.Path)) { - Provider.DeleteFile(Entry); + Provider.DeleteFile(Entry.Path); } } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index 2db56c1ff3..5370183b44 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -79,23 +79,48 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv long TitleId = Context.RequestData.ReadInt64(); - string Path = ReadUtf8String(Context); + string SwitchPath = ReadUtf8String(Context); - string FullPath = Context.Device.FileSystem.SwitchPathToSystemPath(Path); + string FullPath = Context.Device.FileSystem.SwitchPathToSystemPath(SwitchPath); FileStream FileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read); - Nca Nca = new Nca(Context.Device.System.KeySet, FileStream, false); + string Extension = Path.GetExtension(FullPath); - NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - - if (RomfsSection != null) + if (Extension == ".nca") { - Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); + Nca Nca = new Nca(Context.Device.System.KeySet, FileStream, false); - IFileSystem NcaFileSystem = new IFileSystem(Path, new RomFileSystemProvider(RomfsStream)); + NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - MakeObject(Context, NcaFileSystem); + if (RomfsSection != null) + { + Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel); + + IFileSystem NcaFileSystem = new IFileSystem(SwitchPath, new RomFsProvider(RomfsStream)); + + MakeObject(Context, NcaFileSystem); + + return 0; + } + } + else if (Extension == ".nsp") + { + Pfs Nsp = new Pfs(FileStream); + + PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik")); + + if (TicketFile != null) + { + Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile)); + + Context.Device.System.KeySet.TitleKeys[Ticket.RightsId] = + Ticket.GetTitleKey(Context.Device.System.KeySet); + } + + IFileSystem NspFileSystem = new IFileSystem(SwitchPath, new PFsProvider(Nsp)); + + MakeObject(Context, NspFileSystem); return 0; }