From 5fc1f6a1af7df0294069cd432c796e6546a4b38d Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Fri, 31 May 2019 19:31:10 -0500 Subject: [PATCH] Update to version 0.4 of LibHac (#689) * It compiles * Print correct name when loading an exefs * Use DirectorySaveDataFileSystem for savedata * Handle more errors in IFileSystem * Remove structs replaced by LibHac structs * Fix alignment * Fix alignment again * Fix IFile and IFileSystem IPC * Alignment * Use released libhac version --- .../FileSystem/Content/ContentManager.cs | 26 +- .../FileSystem/Content/LocationEntry.cs | 2 +- Ryujinx.HLE/FileSystem/FileSystemProvider.cs | 313 ----------------- Ryujinx.HLE/FileSystem/IFileSystemProvider.cs | 43 --- Ryujinx.HLE/FileSystem/PFsProvider.cs | 152 -------- Ryujinx.HLE/FileSystem/RomFsProvider.cs | 169 --------- Ryujinx.HLE/HOS/Font/SharedFontManager.cs | 26 +- Ryujinx.HLE/HOS/Horizon.cs | 306 +++++++---------- .../HOS/Services/FspSrv/DirectoryEntry.cs | 17 - .../HOS/Services/FspSrv/DirectoryEntryType.cs | 8 - .../HOS/Services/FspSrv/FileTimestamp.cs | 11 - Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs | 52 +-- Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs | 40 +-- .../HOS/Services/FspSrv/IFileSystem.cs | 325 +++++++++++------- .../HOS/Services/FspSrv/IFileSystemProxy.cs | 102 +++--- Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs | 15 +- .../HOS/Services/Lr/ILocationResolver.cs | 4 +- .../HOS/Services/Set/ISystemSettingsServer.cs | 19 +- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx/Configuration.cs | 2 +- 20 files changed, 465 insertions(+), 1169 deletions(-) delete mode 100644 Ryujinx.HLE/FileSystem/FileSystemProvider.cs delete mode 100644 Ryujinx.HLE/FileSystem/IFileSystemProvider.cs delete mode 100644 Ryujinx.HLE/FileSystem/PFsProvider.cs delete mode 100644 Ryujinx.HLE/FileSystem/RomFsProvider.cs delete mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs delete mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs delete mode 100644 Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs index 94f06475..40109f1c 100644 --- a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs +++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs @@ -1,5 +1,5 @@ -using LibHac; -using LibHac.IO; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; @@ -13,6 +13,7 @@ namespace Ryujinx.HLE.FileSystem.Content private Dictionary> _locationEntries; private Dictionary _sharedFontTitleDictionary; + private Dictionary _sharedFontFilenameDictionary; private SortedDictionary<(ulong, ContentType), string> _contentDictionary; @@ -33,6 +34,16 @@ namespace Ryujinx.HLE.FileSystem.Content { "FontNintendoExtended", 0x0100000000000810 } }; + _sharedFontFilenameDictionary = new Dictionary + { + { "FontStandard", "nintendo_udsg-r_std_003.bfttf" }, + { "FontChineseSimplified", "nintendo_udsg-r_org_zh-cn_003.bfttf" }, + { "FontExtendedChineseSimplified", "nintendo_udsg-r_ext_zh-cn_003.bfttf" }, + { "FontKorean", "nintendo_udsg-r_ko_003.bfttf" }, + { "FontChineseTraditional", "nintendo_udjxh-db_zh-tw_003.bfttf" }, + { "FontNintendoExtended", "nintendo_ext_003.bfttf" } + }; + _device = device; } @@ -74,7 +85,7 @@ namespace Ryujinx.HLE.FileSystem.Content using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read)) { - Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage(), false); + Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage()); string switchPath = Path.Combine(contentPathString + ":", ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart('\\')); @@ -102,7 +113,7 @@ namespace Ryujinx.HLE.FileSystem.Content using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { - Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage(), false); + Nca nca = new Nca(_device.System.KeySet, ncaFile.AsStorage()); string switchPath = Path.Combine(contentPathString + ":", filePath.Replace(contentDirectory, string.Empty).TrimStart('\\')); @@ -230,7 +241,7 @@ namespace Ryujinx.HLE.FileSystem.Content { using (FileStream file = new FileStream(installedPath, FileMode.Open, FileAccess.Read)) { - Nca nca = new Nca(_device.System.KeySet, file.AsStorage(), false); + Nca nca = new Nca(_device.System.KeySet, file.AsStorage()); bool contentCheck = nca.Header.ContentType == contentType; return contentCheck; @@ -287,6 +298,11 @@ namespace Ryujinx.HLE.FileSystem.Content return _sharedFontTitleDictionary.TryGetValue(fontName, out titleId); } + public bool TryGetFontFilename(string fontName, out string filename) + { + return _sharedFontFilenameDictionary.TryGetValue(fontName, out filename); + } + private LocationEntry GetLocation(long titleId, ContentType contentType, StorageId storageId) { LinkedList locationList = _locationEntries[storageId]; diff --git a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs index eabcfed8..2ab7ea80 100644 --- a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs +++ b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs @@ -1,4 +1,4 @@ -using LibHac; +using LibHac.Fs.NcaUtils; namespace Ryujinx.HLE.FileSystem.Content { diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs deleted file mode 100644 index f5459eec..00000000 --- a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs +++ /dev/null @@ -1,313 +0,0 @@ -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.FspSrv; -using Ryujinx.HLE.Utilities; -using System; -using System.Collections.Generic; -using System.IO; - -using static Ryujinx.HLE.HOS.ErrorCode; - -namespace Ryujinx.HLE.FileSystem -{ - class FileSystemProvider : IFileSystemProvider - { - private readonly string _basePath; - private readonly string _rootPath; - - public FileSystemProvider(string basePath, string rootPath) - { - _basePath = basePath; - _rootPath = rootPath; - - CheckIfDescendentOfRootPath(basePath); - } - - public long CreateDirectory(string name) - { - CheckIfDescendentOfRootPath(name); - - if (Directory.Exists(name)) - { - return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); - } - - Directory.CreateDirectory(name); - - return 0; - } - - public long CreateFile(string name, long size) - { - CheckIfDescendentOfRootPath(name); - - if (File.Exists(name)) - { - return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); - } - - using (FileStream newFile = File.Create(name)) - { - newFile.SetLength(size); - } - - return 0; - } - - public long DeleteDirectory(string name, bool recursive) - { - CheckIfDescendentOfRootPath(name); - - string dirName = name; - - if (!Directory.Exists(dirName)) - { - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - Directory.Delete(dirName, recursive); - - return 0; - } - - public long DeleteFile(string name) - { - CheckIfDescendentOfRootPath(name); - - if (!File.Exists(name)) - { - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - else - { - File.Delete(name); - } - - return 0; - } - - public DirectoryEntry[] GetDirectories(string path) - { - CheckIfDescendentOfRootPath(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 DirectoryEntry[] GetEntries(string path) - { - CheckIfDescendentOfRootPath(path); - - if (Directory.Exists(path)) - { - 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 entries.ToArray(); - } - - return null; - } - - public DirectoryEntry[] GetFiles(string path) - { - CheckIfDescendentOfRootPath(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) - { - return context.Device.FileSystem.GetDrive().AvailableFreeSpace; - } - - public string GetFullPath(string name) - { - if (name.StartsWith("//")) - { - name = name.Substring(2); - } - else if (name.StartsWith('/')) - { - name = name.Substring(1); - } - else - { - return null; - } - - string fullPath = Path.Combine(_basePath, name); - - CheckIfDescendentOfRootPath(fullPath); - - return fullPath; - } - - public long GetTotalSpace(ServiceCtx context) - { - return context.Device.FileSystem.GetDrive().TotalSize; - } - - public bool DirectoryExists(string name) - { - CheckIfDescendentOfRootPath(name); - - return Directory.Exists(name); - } - - public bool FileExists(string name) - { - CheckIfDescendentOfRootPath(name); - - return File.Exists(name); - } - - public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface) - { - CheckIfDescendentOfRootPath(name); - - if (Directory.Exists(name)) - { - directoryInterface = new IDirectory(name, filterFlags, this); - - return 0; - } - - directoryInterface = null; - - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - public long OpenFile(string name, out IFile fileInterface) - { - CheckIfDescendentOfRootPath(name); - - if (File.Exists(name)) - { - FileStream stream = new FileStream(name, FileMode.Open); - - fileInterface = new IFile(stream, name); - - return 0; - } - - fileInterface = null; - - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - public long RenameDirectory(string oldName, string newName) - { - CheckIfDescendentOfRootPath(oldName); - CheckIfDescendentOfRootPath(newName); - - if (Directory.Exists(oldName)) - { - Directory.Move(oldName, newName); - } - else - { - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - return 0; - } - - public long RenameFile(string oldName, string newName) - { - CheckIfDescendentOfRootPath(oldName); - CheckIfDescendentOfRootPath(newName); - - if (File.Exists(oldName)) - { - File.Move(oldName, newName); - } - else - { - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - return 0; - } - - public void CheckIfDescendentOfRootPath(string path) - { - DirectoryInfo pathInfo = new DirectoryInfo(path); - DirectoryInfo rootInfo = new DirectoryInfo(_rootPath); - - while (pathInfo.Parent != null) - { - if (pathInfo.Parent.FullName == rootInfo.FullName) - { - return; - } - else - { - pathInfo = pathInfo.Parent; - } - } - - throw new InvalidOperationException($"Path {path} is not a child directory of {_rootPath}"); - } - - public FileTimestamp GetFileTimeStampRaw(string name) - { - CheckIfDescendentOfRootPath(name); - - DateTime creationDateTime = DateTime.UnixEpoch; - DateTime modifiedDateTime = DateTime.UnixEpoch; - DateTime lastAccessDateTime = DateTime.UnixEpoch; - - if (File.Exists(name)) - { - creationDateTime = File.GetCreationTime(name); - modifiedDateTime = File.GetLastWriteTime(name); - lastAccessDateTime = File.GetLastAccessTime(name); - } - else if (Directory.Exists(name)) - { - creationDateTime = Directory.GetCreationTime(name); - modifiedDateTime = Directory.GetLastWriteTime(name); - lastAccessDateTime = Directory.GetLastAccessTime(name); - } - - return new FileTimestamp - { - CreationDateTime = creationDateTime, - ModifiedDateTime = modifiedDateTime, - LastAccessDateTime = lastAccessDateTime - }; - } - } -} diff --git a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs deleted file mode 100644 index 82cdebd9..00000000 --- a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.FspSrv; -using System; - -namespace Ryujinx.HLE.FileSystem -{ - interface IFileSystemProvider - { - long CreateFile(string name, long size); - - long CreateDirectory(string name); - - long RenameFile(string oldName, string newName); - - long RenameDirectory(string oldName, string newName); - - DirectoryEntry[] GetEntries(string path); - - DirectoryEntry[] GetDirectories(string path); - - DirectoryEntry[] GetFiles(string path); - - long DeleteFile(string name); - - long DeleteDirectory(string name, bool recursive); - - bool FileExists(string name); - - bool DirectoryExists(string name); - - long OpenFile(string name, out IFile fileInterface); - - long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface); - - string GetFullPath(string name); - - long GetFreeSpace(ServiceCtx context); - - long GetTotalSpace(ServiceCtx context); - - FileTimestamp GetFileTimeStampRaw(string name); - } -} diff --git a/Ryujinx.HLE/FileSystem/PFsProvider.cs b/Ryujinx.HLE/FileSystem/PFsProvider.cs deleted file mode 100644 index 69e7a9b8..00000000 --- a/Ryujinx.HLE/FileSystem/PFsProvider.cs +++ /dev/null @@ -1,152 +0,0 @@ -using LibHac; -using LibHac.IO; -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) - { - _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 == "/"; - } - - 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) - { - name = name.TrimStart('/'); - - if (_pfs.FileExists(name)) - { - Stream stream = _pfs.OpenFile(name).AsStream(); - fileInterface = new IFile(stream, name); - - return 0; - } - - fileInterface = null; - - 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(); - } - - public FileTimestamp GetFileTimeStampRaw(string name) - { - throw new NotImplementedException(); - } - } -} diff --git a/Ryujinx.HLE/FileSystem/RomFsProvider.cs b/Ryujinx.HLE/FileSystem/RomFsProvider.cs deleted file mode 100644 index f64d99c7..00000000 --- a/Ryujinx.HLE/FileSystem/RomFsProvider.cs +++ /dev/null @@ -1,169 +0,0 @@ -using LibHac; -using LibHac.IO; -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 RomFsProvider : IFileSystemProvider - { - private Romfs _romFs; - - public RomFsProvider(LibHac.IO.IStorage storage) - { - _romFs = new Romfs(storage); - } - - 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) - { - List directories = new List(); - - foreach(RomfsDir directory in _romFs.Directories) - { - DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory); - - directories.Add(directoryEntry); - } - - return directories.ToArray(); - } - - public DirectoryEntry[] GetEntries(string path) - { - List entries = new List(); - - foreach (RomfsDir directory in _romFs.Directories) - { - DirectoryEntry directoryEntry = new DirectoryEntry(directory.Name, DirectoryEntryType.Directory); - - entries.Add(directoryEntry); - } - - foreach (RomfsFile file in _romFs.Files) - { - DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength); - - entries.Add(directoryEntry); - } - - return entries.ToArray(); - } - - public DirectoryEntry[] GetFiles(string path) - { - List files = new List(); - - foreach (RomfsFile file in _romFs.Files) - { - DirectoryEntry directoryEntry = new DirectoryEntry(file.Name, DirectoryEntryType.File, file.DataLength); - - files.Add(directoryEntry); - } - - return files.ToArray(); - } - - public long GetFreeSpace(ServiceCtx context) - { - return 0; - } - - public string GetFullPath(string name) - { - return name; - } - - public long GetTotalSpace(ServiceCtx context) - { - return _romFs.Files.Sum(x => x.DataLength); - } - - public bool DirectoryExists(string name) - { - return _romFs.Directories.Exists(x=>x.Name == name); - } - - public bool FileExists(string name) - { - return _romFs.FileExists(name); - } - - public long OpenDirectory(string name, int filterFlags, out IDirectory directoryInterface) - { - RomfsDir directory = _romFs.Directories.Find(x => x.Name == name); - - if (directory != null) - { - directoryInterface = new IDirectory(name, filterFlags, this); - - return 0; - } - - directoryInterface = null; - - return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); - } - - public long OpenFile(string name, out IFile fileInterface) - { - if (_romFs.FileExists(name)) - { - Stream stream = _romFs.OpenFile(name).AsStream(); - - fileInterface = new IFile(stream, name); - - return 0; - } - - fileInterface = null; - - 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(); - } - - public FileTimestamp GetFileTimeStampRaw(string name) - { - throw new NotImplementedException(); - } - } -} diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs index 9eb2c7e5..dfb87f3c 100644 --- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs @@ -1,13 +1,11 @@ -using LibHac; -using LibHac.IO; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.Resource; using Ryujinx.HLE.Utilities; using System.Collections.Generic; using System.IO; -using System.Linq; - using static Ryujinx.HLE.Utilities.FontUtils; namespace Ryujinx.HLE.HOS.Font @@ -53,29 +51,21 @@ namespace Ryujinx.HLE.HOS.Font FontInfo CreateFont(string name) { - if (contentManager.TryGetFontTitle(name, out long fontTitle)) + if (contentManager.TryGetFontTitle(name, out long fontTitle) && + contentManager.TryGetFontFilename(name, out string fontFilename)) { string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, ContentType.Data); string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath); if (!string.IsNullOrWhiteSpace(fontPath)) { - int fileIndex = 0; - - //Use second file in Chinese Font title for standard - if(name == "FontChineseSimplified") - { - fileIndex = 1; - } - byte[] data; - using (FileStream ncaFileStream = new FileStream(fontPath, FileMode.Open, FileAccess.Read)) + using (IStorage ncaFileStream = new LocalStorage(fontPath, FileAccess.Read, FileMode.Open)) { - Nca nca = new Nca(_device.System.KeySet, ncaFileStream.AsStorage(), false); - NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - Romfs romfs = new Romfs(nca.OpenSection(romfsSection.SectionNum, false, _device.System.FsIntegrityCheckLevel, false)); - Stream fontFile = romfs.OpenFile(romfs.Files[fileIndex]).AsStream(); + Nca nca = new Nca(_device.System.KeySet, ncaFileStream); + IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel); + Stream fontFile = romfs.OpenFile(fontFilename, OpenMode.Read).AsStream(); data = DecryptFont(fontFile); } diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index acb6b1d1..0b55ee0a 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -1,5 +1,6 @@ using LibHac; -using LibHac.IO; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Font; @@ -195,58 +196,9 @@ namespace Ryujinx.HLE.HOS Device.FileSystem.LoadRomFs(romFsFile); } - string npdmFileName = Path.Combine(exeFsDir, "main.npdm"); + LocalFileSystem codeFs = new LocalFileSystem(exeFsDir); - Npdm metaData = null; - - if (File.Exists(npdmFileName)) - { - Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); - - using (FileStream input = new FileStream(npdmFileName, FileMode.Open)) - { - metaData = new Npdm(input); - } - } - else - { - Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); - - metaData = GetDefaultNpdm(); - } - - List staticObjects = new List(); - - void LoadNso(string searchPattern) - { - foreach (string file in Directory.GetFiles(exeFsDir, searchPattern)) - { - if (Path.GetExtension(file) != string.Empty) - { - continue; - } - - Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(file)}..."); - - using (FileStream input = new FileStream(file, FileMode.Open)) - { - NxStaticObject staticObject = new NxStaticObject(input); - - staticObjects.Add(staticObject); - } - } - } - - TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); - - LoadNso("rtld"); - LoadNso("main"); - LoadNso("subsdk*"); - LoadNso("sdk"); - - ContentManager.LoadEntries(); - - ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray()); + LoadExeFs(codeFs, out _); } public void LoadXci(string xciFile) @@ -255,7 +207,7 @@ namespace Ryujinx.HLE.HOS Xci xci = new Xci(KeySet, file.AsStorage()); - (Nca mainNca, Nca controlNca) = GetXciGameData(xci); + (Nca mainNca, Nca patchNca, Nca controlNca) = GetXciGameData(xci); if (mainNca == null) { @@ -266,7 +218,7 @@ namespace Ryujinx.HLE.HOS ContentManager.LoadEntries(); - LoadNca(mainNca, controlNca); + LoadNca(mainNca, patchNca, controlNca); } public void LoadKip(string kipFile) @@ -277,9 +229,9 @@ namespace Ryujinx.HLE.HOS } } - private (Nca Main, Nca Control) GetXciGameData(Xci xci) + private (Nca Main, Nca patch, Nca Control) GetXciGameData(Xci xci) { - if (xci.SecurePartition == null) + if (!xci.HasPartition(XciPartitionType.Secure)) { throw new InvalidDataException("Could not find XCI secure partition"); } @@ -288,9 +240,11 @@ namespace Ryujinx.HLE.HOS Nca patchNca = null; Nca controlNca = null; - foreach (PfsFileEntry ticketEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".tik"))) + XciPartition securePartition = xci.OpenPartition(XciPartitionType.Secure); + + foreach (DirectoryEntry ticketEntry in securePartition.EnumerateEntries("*.tik")) { - Ticket ticket = new Ticket(xci.SecurePartition.OpenFile(ticketEntry).AsStream()); + Ticket ticket = new Ticket(securePartition.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { @@ -298,22 +252,24 @@ namespace Ryujinx.HLE.HOS } } - foreach (PfsFileEntry fileEntry in xci.SecurePartition.Files.Where(x => x.Name.EndsWith(".nca"))) + foreach (DirectoryEntry fileEntry in securePartition.EnumerateEntries("*.nca")) { - IStorage ncaStorage = xci.SecurePartition.OpenFile(fileEntry); + IStorage ncaStorage = securePartition.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage(); - Nca nca = new Nca(KeySet, ncaStorage, true); + Nca nca = new Nca(KeySet, ncaStorage); if (nca.Header.ContentType == ContentType.Program) { - if (nca.Sections.Any(x => x?.Type == SectionType.Romfs)) - { - mainNca = nca; - } - else if (nca.Sections.Any(x => x?.Type == SectionType.Bktr)) + int dataIndex = Nca.SectionIndexFromType(NcaSectionType.Data, ContentType.Program); + + if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) { patchNca = nca; } + else + { + mainNca = nca; + } } else if (nca.Header.ContentType == ContentType.Control) { @@ -326,50 +282,43 @@ namespace Ryujinx.HLE.HOS Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } - mainNca.SetBaseNca(patchNca); - if (controlNca != null) { ReadControlData(controlNca); } - if (patchNca != null) - { - patchNca.SetBaseNca(mainNca); - - return (patchNca, controlNca); - } - - return (mainNca, controlNca); + return (mainNca, patchNca, controlNca); } public void ReadControlData(Nca controlNca) { - Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel, true)); + IFileSystem controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel); - IStorage controlFile = controlRomfs.OpenFile("/control.nacp"); + IFile controlFile = controlFs.OpenFile("/control.nacp", OpenMode.Read); ControlData = new Nacp(controlFile.AsStream()); + + TitleName = CurrentTitle = ControlData.Descriptions[(int)State.DesiredTitleLanguage].Title; } public void LoadNca(string ncaFile) { FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); - Nca nca = new Nca(KeySet, file.AsStorage(false), false); + Nca nca = new Nca(KeySet, file.AsStorage(false)); - LoadNca(nca, null); + LoadNca(nca, null, null); } public void LoadNsp(string nspFile) { FileStream file = new FileStream(nspFile, FileMode.Open, FileAccess.Read); - Pfs nsp = new Pfs(file.AsStorage(false)); + PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - foreach (PfsFileEntry ticketEntry in nsp.Files.Where(x => x.Name.EndsWith(".tik"))) + foreach (DirectoryEntry ticketEntry in nsp.EnumerateEntries("*.tik")) { - Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry).AsStream()); + Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!KeySet.TitleKeys.ContainsKey(ticket.RightsId)) { @@ -378,15 +327,27 @@ namespace Ryujinx.HLE.HOS } Nca mainNca = null; + Nca patchNca = null; Nca controlNca = null; - foreach (PfsFileEntry ncaFile in nsp.Files.Where(x => x.Name.EndsWith(".nca"))) + foreach (DirectoryEntry fileEntry in nsp.EnumerateEntries("*.nca")) { - Nca nca = new Nca(KeySet, nsp.OpenFile(ncaFile), true); + IStorage ncaStorage = nsp.OpenFile(fileEntry.FullPath, OpenMode.Read).AsStorage(); + + Nca nca = new Nca(KeySet, ncaStorage); if (nca.Header.ContentType == ContentType.Program) { - mainNca = nca; + int dataIndex = Nca.SectionIndexFromType(NcaSectionType.Data, ContentType.Program); + + if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) + { + patchNca = nca; + } + else + { + mainNca = nca; + } } else if (nca.Header.ContentType == ContentType.Control) { @@ -396,65 +357,16 @@ namespace Ryujinx.HLE.HOS if (mainNca != null) { - LoadNca(mainNca, controlNca); + LoadNca(mainNca, patchNca, controlNca); return; } // This is not a normal NSP, it's actually a ExeFS as a NSP - Npdm metaData = null; - - PfsFileEntry npdmFile = nsp.Files.FirstOrDefault(x => x.Name.Equals("main.npdm")); - - if (npdmFile != null) - { - Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); - - metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream()); - } - else - { - Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); - - metaData = GetDefaultNpdm(); - } - - List staticObjects = new List(); - - void LoadNso(string searchPattern) - { - PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern)); - - if (entry != null) - { - Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}..."); - - NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream()); - - staticObjects.Add(staticObject); - } - } - - TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); - - LoadNso("rtld"); - LoadNso("main"); - LoadNso("subsdk*"); - LoadNso("sdk"); - - ContentManager.LoadEntries(); - - if (staticObjects.Count == 0) - { - Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); - } - else - { - ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray()); - } + LoadExeFs(nsp, out _); } - public void LoadNca(Nca mainNca, Nca controlNca) + public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) { if (mainNca.Header.ContentType != ContentType.Program) { @@ -463,71 +375,64 @@ namespace Ryujinx.HLE.HOS return; } - IStorage romfsStorage = mainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel, false); - IStorage exefsStorage = mainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel, true); + IStorage dataStorage = null; + IFileSystem codeFs = null; - if (exefsStorage == null) + if (patchNca == null) + { + if (mainNca.CanOpenSection(NcaSectionType.Data)) + { + dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel); + } + + if (mainNca.CanOpenSection(NcaSectionType.Code)) + { + codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel); + } + } + else + { + if (patchNca.CanOpenSection(NcaSectionType.Data)) + { + dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel); + } + + if (patchNca.CanOpenSection(NcaSectionType.Code)) + { + codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel); + } + } + + if (codeFs == null) { Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA"); return; } - if (romfsStorage == null) + if (dataStorage == null) { Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); } else { - Device.FileSystem.SetRomFs(romfsStorage.AsStream(false)); + Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read)); } - Pfs exefs = new Pfs(exefsStorage); - - Npdm metaData = null; - - if (exefs.FileExists("main.npdm")) - { - Logger.PrintInfo(LogClass.Loader, "Loading main.npdm..."); - - metaData = new Npdm(exefs.OpenFile("main.npdm").AsStream()); - } - else - { - Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); - - metaData = GetDefaultNpdm(); - } - - List staticObjects = new List(); - - void LoadNso(string filename) - { - foreach (PfsFileEntry file in exefs.Files.Where(x => x.Name.StartsWith(filename))) - { - if (Path.GetExtension(file.Name) != string.Empty) - { - continue; - } - - Logger.PrintInfo(LogClass.Loader, $"Loading {filename}..."); - - NxStaticObject staticObject = new NxStaticObject(exefs.OpenFile(file).AsStream()); - - staticObjects.Add(staticObject); - } - } + LoadExeFs(codeFs, out Npdm metaData); Nacp ReadControlData() { - Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel, true)); + IFileSystem controlRomfs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel); - IStorage controlFile = controlRomfs.OpenFile("/control.nacp"); + IFile controlFile = controlRomfs.OpenFile("/control.nacp", OpenMode.Read); Nacp controlData = new Nacp(controlFile.AsStream()); TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; - TitleID = metaData.Aci0.TitleId.ToString("x16"); + TitleID = metaData.Aci0.TitleId.ToString("x16"); + + CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title; if (string.IsNullOrWhiteSpace(CurrentTitle)) { @@ -545,6 +450,43 @@ namespace Ryujinx.HLE.HOS { TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); } + } + + private void LoadExeFs(IFileSystem codeFs, out Npdm metaData) + { + if (codeFs.FileExists("/main.npdm")) + { + Logger.PrintInfo(LogClass.Loader, "Loading main.npdm..."); + + metaData = new Npdm(codeFs.OpenFile("/main.npdm", OpenMode.Read).AsStream()); + } + else + { + Logger.PrintWarning(LogClass.Loader, "NPDM file not found, using default values!"); + + metaData = GetDefaultNpdm(); + } + + List staticObjects = new List(); + + void LoadNso(string filename) + { + foreach (DirectoryEntry file in codeFs.EnumerateEntries($"{filename}*")) + { + if (Path.GetExtension(file.Name) != string.Empty) + { + continue; + } + + Logger.PrintInfo(LogClass.Loader, $"Loading {file.Name}..."); + + NxStaticObject staticObject = new NxStaticObject(codeFs.OpenFile(file.FullPath, OpenMode.Read).AsStream()); + + staticObjects.Add(staticObject); + } + } + + TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); LoadNso("rtld"); LoadNso("main"); @@ -613,7 +555,7 @@ namespace Ryujinx.HLE.HOS ContentManager.LoadEntries(); - TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); + TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16"); TitleName = metaData.TitleName; ProgramLoader.LoadStaticObjects(this, metaData, new IExecutable[] { staticObject }); diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs deleted file mode 100644 index 6c3dadb1..00000000 --- a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs +++ /dev/null @@ -1,17 +0,0 @@ -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) - { - Path = path; - EntryType = directoryEntryType; - Size = size; - } - } -} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs deleted file mode 100644 index da075a5f..00000000 --- a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.FspSrv -{ - public enum DirectoryEntryType - { - Directory, - File - } -} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs deleted file mode 100644 index 879fb78c..00000000 --- a/Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Ryujinx.HLE.HOS.Services.FspSrv -{ - struct FileTimestamp - { - public DateTime CreationDateTime; - public DateTime ModifiedDateTime; - public DateTime LastAccessDateTime; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs index 5f6235a5..c0ffe767 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs @@ -1,8 +1,6 @@ -using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; -using System.IO; using System.Text; namespace Ryujinx.HLE.HOS.Services.FspSrv @@ -15,17 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public override IReadOnlyDictionary Commands => _commands; - private List _directoryEntries; - - private int _currentItemIndex; + private IEnumerator _enumerator; public event EventHandler Disposed; - public string DirectoryPath { get; private set; } + public string Path { get; } - private IFileSystemProvider _provider; + private LibHac.Fs.IDirectory _provider; - public IDirectory(string directoryPath, int flags, IFileSystemProvider provider) + public IDirectory(LibHac.Fs.IDirectory directory) { _commands = new Dictionary { @@ -33,22 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { 1, GetEntryCount } }; - _provider = provider; - DirectoryPath = directoryPath; + _provider = directory; - _directoryEntries = new List(); + Path = directory.FullPath; - if ((flags & 1) != 0) - { - _directoryEntries.AddRange(provider.GetDirectories(directoryPath)); - } - - if ((flags & 2) != 0) - { - _directoryEntries.AddRange(provider.GetFiles(directoryPath)); - } - - _currentItemIndex = 0; + _enumerator = directory.Read().GetEnumerator(); } // Read() -> (u64 count, buffer entries) @@ -58,41 +43,42 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv long bufferLen = context.Request.ReceiveBuff[0].Size; int maxReadCount = (int)(bufferLen / DirectoryEntrySize); + int readCount = 0; - int count = Math.Min(_directoryEntries.Count - _currentItemIndex, maxReadCount); - - for (int index = 0; index < count; index++) + while (readCount < maxReadCount && _enumerator.MoveNext()) { - long position = bufferPosition + index * DirectoryEntrySize; + long position = bufferPosition + readCount * DirectoryEntrySize; - WriteDirectoryEntry(context, position, _directoryEntries[_currentItemIndex++]); + WriteDirectoryEntry(context, position, _enumerator.Current); + + readCount++; } - context.ResponseData.Write((long)count); + context.ResponseData.Write((long)readCount); return 0; } - private void WriteDirectoryEntry(ServiceCtx context, long position, DirectoryEntry entry) + private void WriteDirectoryEntry(ServiceCtx context, long position, LibHac.Fs.DirectoryEntry entry) { for (int offset = 0; offset < 0x300; offset += 8) { context.Memory.WriteInt64(position + offset, 0); } - byte[] nameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(entry.Path)); + byte[] nameBuffer = Encoding.UTF8.GetBytes(entry.Name); context.Memory.WriteBytes(position, nameBuffer); - context.Memory.WriteInt32(position + 0x300, 0); //Padding? - context.Memory.WriteInt32(position + 0x304, (byte)entry.EntryType); + context.Memory.WriteInt32(position + 0x300, (int)entry.Attributes); + context.Memory.WriteInt32(position + 0x304, (byte)entry.Type); context.Memory.WriteInt64(position + 0x308, entry.Size); } // GetEntryCount() -> u64 public long GetEntryCount(ServiceCtx context) { - context.ResponseData.Write((long)_directoryEntries.Count); + context.ResponseData.Write((long)_provider.GetEntryCount()); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs index c0ff5c64..3cedf4fe 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs @@ -1,7 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; -using System.IO; namespace Ryujinx.HLE.HOS.Services.FspSrv { @@ -11,13 +10,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public override IReadOnlyDictionary Commands => _commands; - private Stream _baseStream; + private LibHac.Fs.IFile _baseFile; public event EventHandler Disposed; - public string HostPath { get; private set; } + public string Path { get; private set; } - public IFile(Stream baseStream, string hostPath) + public IFile(LibHac.Fs.IFile baseFile, string path) { _commands = new Dictionary { @@ -28,24 +27,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { 4, GetSize } }; - _baseStream = baseStream; - HostPath = hostPath; + _baseFile = baseFile; + Path = LibHac.Fs.PathTools.Normalize(path); } - // Read(u32, u64 offset, u64 size) -> (u64 out_size, buffer out_buf) + // Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer out_buf) public long Read(ServiceCtx context) { long position = context.Request.ReceiveBuff[0].Position; - long zero = context.RequestData.ReadInt64(); + int readOption = context.RequestData.ReadInt32(); + context.RequestData.BaseStream.Position += 4; + long offset = context.RequestData.ReadInt64(); long size = context.RequestData.ReadInt64(); byte[] data = new byte[size]; - _baseStream.Seek(offset, SeekOrigin.Begin); - - int readSize = _baseStream.Read(data, 0, (int)size); + int readSize = _baseFile.Read(data, offset); context.Memory.WriteBytes(position, data); @@ -54,19 +53,20 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } - // Write(u32, u64 offset, u64 size, buffer) + // Write(u32 writeOption, u64 offset, u64 size, buffer) public long Write(ServiceCtx context) { long position = context.Request.SendBuff[0].Position; - long zero = context.RequestData.ReadInt64(); + int writeOption = context.RequestData.ReadInt32(); + context.RequestData.BaseStream.Position += 4; + long offset = context.RequestData.ReadInt64(); long size = context.RequestData.ReadInt64(); byte[] data = context.Memory.ReadBytes(position, size); - _baseStream.Seek(offset, SeekOrigin.Begin); - _baseStream.Write(data, 0, (int)size); + _baseFile.Write(data, offset); return 0; } @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // Flush() public long Flush(ServiceCtx context) { - _baseStream.Flush(); + _baseFile.Flush(); return 0; } @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { long size = context.RequestData.ReadInt64(); - _baseStream.SetLength(size); + _baseFile.SetSize(size); return 0; } @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // GetSize() -> u64 fileSize public long GetSize(ServiceCtx context) { - context.ResponseData.Write(_baseStream.Length); + context.ResponseData.Write(_baseFile.GetSize()); return 0; } @@ -104,9 +104,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv protected virtual void Dispose(bool disposing) { - if (disposing && _baseStream != null) + if (disposing && _baseFile != null) { - _baseStream.Dispose(); + _baseFile.Dispose(); Disposed?.Invoke(this, EventArgs.Empty); } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index bcb9dbaf..9e772213 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -1,9 +1,9 @@ -using Ryujinx.HLE.FileSystem; +using LibHac.Fs; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; using System.IO; - +using Ryujinx.Common.Logging; using static Ryujinx.HLE.HOS.ErrorCode; using static Ryujinx.HLE.Utilities.StringUtils; @@ -17,11 +17,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv private HashSet _openPaths; - private string _path; + private LibHac.Fs.IFileSystem _provider; - private IFileSystemProvider _provider; - - public IFileSystem(string path, IFileSystemProvider provider) + public IFileSystem(LibHac.Fs.IFileSystem provider) { _commands = new Dictionary { @@ -44,36 +42,50 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv _openPaths = new HashSet(); - _path = path; _provider = provider; } - // CreateFile(u32 mode, u64 size, buffer, 0x19, 0x301> path) + // CreateFile(u32 createOption, u64 size, buffer, 0x19, 0x301> path) public long CreateFile(ServiceCtx context) { string name = ReadUtf8String(context); - long mode = context.RequestData.ReadInt64(); - int size = context.RequestData.ReadInt32(); + int createOption = context.RequestData.ReadInt32(); + context.RequestData.BaseStream.Position += 4; - string fileName = _provider.GetFullPath(name); + long size = context.RequestData.ReadInt64(); - if (fileName == null) + if (name == null) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (_provider.FileExists(fileName)) + if (_provider.FileExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } - if (IsPathAlreadyInUse(fileName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - return _provider.CreateFile(fileName, size); + try + { + _provider.CreateFile(name, size, (CreateFileOptions)createOption); + } + catch (DirectoryNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; + } + + return 0; } // DeleteFile(buffer, 0x19, 0x301> path) @@ -81,19 +93,32 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - string fileName = _provider.GetFullPath(name); - - if (!_provider.FileExists(fileName)) + if (!_provider.FileExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(fileName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - return _provider.DeleteFile(fileName); + try + { + _provider.DeleteFile(name); + } + catch (FileNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; + } + + return 0; } // CreateDirectory(buffer, 0x19, 0x301> path) @@ -101,24 +126,35 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - string dirName = _provider.GetFullPath(name); - - if (dirName == null) + if (name == null) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (_provider.DirectoryExists(dirName)) + if (_provider.DirectoryExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } - if (IsPathAlreadyInUse(dirName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - _provider.CreateDirectory(dirName); + try + { + _provider.CreateDirectory(name); + } + catch (DirectoryNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; + } return 0; } @@ -126,32 +162,61 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // DeleteDirectory(buffer, 0x19, 0x301> path) public long DeleteDirectory(ServiceCtx context) { - return DeleteDirectory(context, false); + string name = ReadUtf8String(context); + + if (!_provider.DirectoryExists(name)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(name)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + try + { + _provider.DeleteDirectory(name); + } + catch (DirectoryNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; + } + + return 0; } // DeleteDirectoryRecursively(buffer, 0x19, 0x301> path) public long DeleteDirectoryRecursively(ServiceCtx context) - { - return DeleteDirectory(context, true); - } - - private long DeleteDirectory(ServiceCtx context, bool recursive) { string name = ReadUtf8String(context); - string dirName = _provider.GetFullPath(name); - - if (!Directory.Exists(dirName)) + if (!_provider.DirectoryExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(dirName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - _provider.DeleteDirectory(dirName, recursive); + try + { + _provider.DeleteDirectoryRecursively(name); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; + } return 0; } @@ -162,25 +227,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv string oldName = ReadUtf8String(context, 0); string newName = ReadUtf8String(context, 1); - string oldFileName = _provider.GetFullPath(oldName); - string newFileName = _provider.GetFullPath(newName); - - if (_provider.FileExists(oldFileName)) + if (_provider.FileExists(oldName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (_provider.FileExists(newFileName)) + if (_provider.FileExists(newName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } - if (IsPathAlreadyInUse(oldFileName)) + if (IsPathAlreadyInUse(oldName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - return _provider.RenameFile(oldFileName, newFileName); + try + { + _provider.RenameFile(oldName, newName); + } + catch (FileNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {oldName} or {newName}"); + + throw; + } + + return 0; } // RenameDirectory(buffer, 0x19, 0x301> oldPath, buffer, 0x19, 0x301> newPath) @@ -189,25 +266,37 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv string oldName = ReadUtf8String(context, 0); string newName = ReadUtf8String(context, 1); - string oldDirName = _provider.GetFullPath(oldName); - string newDirName = _provider.GetFullPath(newName); - - if (!_provider.DirectoryExists(oldDirName)) + if (!_provider.DirectoryExists(oldName)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (!_provider.DirectoryExists(newDirName)) + if (!_provider.DirectoryExists(newName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); } - if (IsPathAlreadyInUse(oldDirName)) + if (IsPathAlreadyInUse(oldName)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - return _provider.RenameDirectory(oldDirName, newDirName); + try + { + _provider.RenameFile(oldName, newName); + } + catch (DirectoryNotFoundException) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {oldName} or {newName}"); + + throw; + } + + return 0; } // GetEntryType(buffer, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType @@ -215,17 +304,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - string fileName = _provider.GetFullPath(name); + try + { + LibHac.Fs.DirectoryEntryType entryType = _provider.GetEntryType(name); - if (_provider.FileExists(fileName)) - { - context.ResponseData.Write(1); + context.ResponseData.Write((int)entryType); } - else if (_provider.DirectoryExists(fileName)) - { - context.ResponseData.Write(0); - } - else + catch (FileNotFoundException) { context.ResponseData.Write(0); @@ -238,81 +323,96 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // OpenFile(u32 mode, buffer, 0x19, 0x301> path) -> object file public long OpenFile(ServiceCtx context) { - int filterFlags = context.RequestData.ReadInt32(); + int mode = context.RequestData.ReadInt32(); string name = ReadUtf8String(context); - string fileName = _provider.GetFullPath(name); - - if (!_provider.FileExists(fileName)) + if (!_provider.FileExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(fileName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } + IFile fileInterface; - long error = _provider.OpenFile(fileName, out IFile fileInterface); - - if (error == 0) + try { - fileInterface.Disposed += RemoveFileInUse; + LibHac.Fs.IFile file = _provider.OpenFile(name, (OpenMode)mode); - lock (_openPaths) - { - _openPaths.Add(fileName); - } + fileInterface = new IFile(file, name); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); - MakeObject(context, fileInterface); - - return 0; + throw; } - return error; + fileInterface.Disposed += RemoveFileInUse; + + lock (_openPaths) + { + _openPaths.Add(fileInterface.Path); + } + + MakeObject(context, fileInterface); + + return 0; } // OpenDirectory(u32 filter_flags, buffer, 0x19, 0x301> path) -> object directory public long OpenDirectory(ServiceCtx context) { - int filterFlags = context.RequestData.ReadInt32(); + int mode = context.RequestData.ReadInt32(); string name = ReadUtf8String(context); - string dirName = _provider.GetFullPath(name); - - if (!_provider.DirectoryExists(dirName)) + if (!_provider.DirectoryExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(dirName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - long error = _provider.OpenDirectory(dirName, filterFlags, out IDirectory dirInterface); + IDirectory dirInterface; - if (error == 0) + try { - dirInterface.Disposed += RemoveDirectoryInUse; + LibHac.Fs.IDirectory dir = _provider.OpenDirectory(name, (OpenDirectoryMode) mode); - lock (_openPaths) - { - _openPaths.Add(dirName); - } + dirInterface = new IDirectory(dir); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); - MakeObject(context, dirInterface); + throw; } - return error; + dirInterface.Disposed += RemoveDirectoryInUse; + + lock (_openPaths) + { + _openPaths.Add(dirInterface.Path); + } + + MakeObject(context, dirInterface); + + return 0; } // Commit() public long Commit(ServiceCtx context) { + _provider.Commit(); + return 0; } @@ -321,7 +421,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - context.ResponseData.Write(_provider.GetFreeSpace(context)); + context.ResponseData.Write(_provider.GetFreeSpaceSize(name)); return 0; } @@ -331,7 +431,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - context.ResponseData.Write(_provider.GetFreeSpace(context)); + context.ResponseData.Write(_provider.GetTotalSpaceSize(name)); return 0; } @@ -341,28 +441,25 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - string dirName = _provider.GetFullPath(name); - - if (!_provider.DirectoryExists(dirName)) + if (!_provider.DirectoryExists(name)) { return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(dirName)) + if (IsPathAlreadyInUse(name)) { return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); } - foreach (DirectoryEntry entry in _provider.GetEntries(dirName)) + try { - if (_provider.DirectoryExists(entry.Path)) - { - _provider.DeleteDirectory(entry.Path, true); - } - else if (_provider.FileExists(entry.Path)) - { - _provider.DeleteFile(entry.Path); - } + _provider.CleanDirectoryRecursively(name); + } + catch (UnauthorizedAccessException) + { + Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}"); + + throw; } return 0; @@ -373,15 +470,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string name = ReadUtf8String(context); - string path = _provider.GetFullPath(name); - - if (_provider.FileExists(path) || _provider.DirectoryExists(path)) + if (_provider.FileExists(name) || _provider.DirectoryExists(name)) { - FileTimestamp timestamp = _provider.GetFileTimeStampRaw(path); + FileTimeStampRaw timestamp = _provider.GetFileTimeStampRaw(name); - context.ResponseData.Write(new DateTimeOffset(timestamp.CreationDateTime).ToUnixTimeSeconds()); - context.ResponseData.Write(new DateTimeOffset(timestamp.ModifiedDateTime).ToUnixTimeSeconds()); - context.ResponseData.Write(new DateTimeOffset(timestamp.LastAccessDateTime).ToUnixTimeSeconds()); + context.ResponseData.Write(timestamp.Created); + context.ResponseData.Write(timestamp.Modified); + context.ResponseData.Write(timestamp.Accessed); byte[] data = new byte[8]; @@ -412,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { fileInterface.Disposed -= RemoveFileInUse; - _openPaths.Remove(fileInterface.HostPath); + _openPaths.Remove(fileInterface.Path); } } @@ -424,7 +519,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { dirInterface.Disposed -= RemoveDirectoryInUse; - _openPaths.Remove(dirInterface.DirectoryPath); + _openPaths.Remove(dirInterface.Path); } } } diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index 878f2e4e..05abb7f0 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -1,11 +1,11 @@ using LibHac; -using LibHac.IO; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.Utilities; using System.Collections.Generic; using System.IO; -using System.Linq; using static Ryujinx.HLE.FileSystem.VirtualFileSystem; using static Ryujinx.HLE.HOS.ErrorCode; @@ -79,31 +79,31 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // OpenBisFileSystem(nn::fssrv::sf::Partition partitionID, buffer, 0x19, 0x301>) -> object Bis public long OpenBisFileSystem(ServiceCtx context) { - int bisPartitionId = context.RequestData.ReadInt32(); - string partitionString = ReadUtf8String(context); - string bisPartitonPath = string.Empty; + int bisPartitionId = context.RequestData.ReadInt32(); + string partitionString = ReadUtf8String(context); + string bisPartitionPath = string.Empty; switch (bisPartitionId) { case 29: - bisPartitonPath = SafeNandPath; + bisPartitionPath = SafeNandPath; break; case 30: case 31: - bisPartitonPath = SystemNandPath; + bisPartitionPath = SystemNandPath; break; case 32: - bisPartitonPath = UserNandPath; + bisPartitionPath = UserNandPath; break; default: return MakeError(ErrorModule.Fs, FsErr.InvalidInput); } - string fullPath = context.Device.FileSystem.GetFullPartitionPath(bisPartitonPath); + string fullPath = context.Device.FileSystem.GetFullPartitionPath(bisPartitionPath); - FileSystemProvider fileSystemProvider = new FileSystemProvider(fullPath, context.Device.FileSystem.GetBasePath()); + LocalFileSystem fileSystem = new LocalFileSystem(fullPath); - MakeObject(context, new IFileSystem(fullPath, fileSystemProvider)); + MakeObject(context, new IFileSystem(fileSystem)); return 0; } @@ -113,9 +113,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { string sdCardPath = context.Device.FileSystem.GetSdCardPath(); - FileSystemProvider fileSystemProvider = new FileSystemProvider(sdCardPath, context.Device.FileSystem.GetBasePath()); + LocalFileSystem fileSystem = new LocalFileSystem(sdCardPath); - MakeObject(context, new IFileSystem(sdCardPath, fileSystemProvider)); + MakeObject(context, new IFileSystem(fileSystem)); return 0; } @@ -139,7 +139,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // OpenDataStorageByCurrentProcess() -> object dataStorage public long OpenDataStorageByCurrentProcess(ServiceCtx context) { - MakeObject(context, new IStorage(context.Device.FileSystem.RomFs)); + MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage())); return 0; } @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv if (installedStorage == StorageId.None) { - contentType = ContentType.AocData; + contentType = ContentType.PublicData; installedStorage = context.Device.System.ContentManager.GetInstalledStorage(titleId, contentType, storageId); @@ -175,12 +175,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv if (File.Exists(ncaPath)) { - LibHac.IO.IStorage ncaStorage = new FileStream(ncaPath, FileMode.Open, FileAccess.Read).AsStorage(); - Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false); - NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - Stream romfsStream = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false).AsStream(); + LibHac.Fs.IStorage ncaStorage = new LocalStorage(ncaPath, FileAccess.Read, FileMode.Open); + Nca nca = new Nca(context.Device.System.KeySet, ncaStorage); + LibHac.Fs.IStorage romfsStorage = nca.OpenStorage(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel); - MakeObject(context, new IStorage(romfsStream)); + MakeObject(context, new IStorage(romfsStorage)); return 0; } @@ -201,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // OpenPatchDataStorageByCurrentProcess() -> object public long OpenPatchDataStorageByCurrentProcess(ServiceCtx context) { - MakeObject(context, new IStorage(context.Device.FileSystem.RomFs)); + MakeObject(context, new IStorage(context.Device.FileSystem.RomFs.AsStorage())); return 0; } @@ -224,57 +223,44 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv context.RequestData.ReadInt64(), context.RequestData.ReadInt64()); - long saveId = context.RequestData.ReadInt64(); - SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte(); - SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId); - string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context); - FileSystemProvider fileSystemProvider = new FileSystemProvider(savePath, context.Device.FileSystem.GetBasePath()); + long saveId = context.RequestData.ReadInt64(); + SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte(); + SaveInfo saveInfo = new SaveInfo(titleId, saveId, saveDataType, userId, saveSpaceId); + string savePath = context.Device.FileSystem.GetGameSavePath(saveInfo, context); + LocalFileSystem fileSystem = new LocalFileSystem(savePath); - MakeObject(context, new IFileSystem(savePath, fileSystemProvider)); + DirectorySaveDataFileSystem saveFileSystem = new DirectorySaveDataFileSystem(fileSystem); + + MakeObject(context, new IFileSystem(saveFileSystem)); } private long OpenNsp(ServiceCtx context, string pfsPath) { - FileStream pfsFile = new FileStream(pfsPath, FileMode.Open, FileAccess.Read); - Pfs nsp = new Pfs(pfsFile.AsStorage()); + LocalStorage storage = new LocalStorage(pfsPath, FileAccess.Read, FileMode.Open); + PartitionFileSystem nsp = new PartitionFileSystem(storage); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); - - - IFileSystem nspFileSystem = new IFileSystem(pfsPath, new PFsProvider(nsp)); + + IFileSystem nspFileSystem = new IFileSystem(nsp); MakeObject(context, nspFileSystem); return 0; } - private long OpenNcaFs(ServiceCtx context, string ncaPath, LibHac.IO.IStorage ncaStorage) + private long OpenNcaFs(ServiceCtx context, string ncaPath, LibHac.Fs.IStorage ncaStorage) { - Nca nca = new Nca(context.Device.System.KeySet, ncaStorage, false); + Nca nca = new Nca(context.Device.System.KeySet, ncaStorage); - NcaSection romfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); - NcaSection pfsSection = nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0); - - if (romfsSection != null) - { - LibHac.IO.IStorage romfsStorage = nca.OpenSection(romfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false); - IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new RomFsProvider(romfsStorage)); - - MakeObject(context, ncaFileSystem); - } - else if(pfsSection != null) - { - LibHac.IO.IStorage pfsStorage = nca.OpenSection(pfsSection.SectionNum, false, context.Device.System.FsIntegrityCheckLevel, false); - Pfs pfs = new Pfs(pfsStorage); - IFileSystem ncaFileSystem = new IFileSystem(ncaPath, new PFsProvider(pfs)); - - MakeObject(context, ncaFileSystem); - } - else + if (!nca.SectionExists(NcaSectionType.Data)) { return MakeError(ErrorModule.Fs, FsErr.PartitionNotFound); } + LibHac.Fs.IFileSystem fileSystem = nca.OpenFileSystem(NcaSectionType.Data, context.Device.System.FsIntegrityCheckLevel); + + MakeObject(context, new IFileSystem(fileSystem)); + return 0; } @@ -294,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv FileMode.Open, FileAccess.Read); - Pfs nsp = new Pfs(pfsFile.AsStorage()); + PartitionFileSystem nsp = new PartitionFileSystem(pfsFile.AsStorage()); ImportTitleKeysFromNsp(nsp, context.Device.System.KeySet); @@ -302,18 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv if (nsp.FileExists(filename)) { - return OpenNcaFs(context, fullPath, nsp.OpenFile(filename)); + return OpenNcaFs(context, fullPath, nsp.OpenFile(filename, OpenMode.Read).AsStorage()); } } return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - private void ImportTitleKeysFromNsp(Pfs nsp, Keyset keySet) + private void ImportTitleKeysFromNsp(LibHac.Fs.IFileSystem nsp, Keyset keySet) { - foreach (PfsFileEntry ticketEntry in nsp.Files.Where(x => x.Name.EndsWith(".tik"))) + foreach (LibHac.Fs.DirectoryEntry ticketEntry in nsp.EnumerateEntries("*.tik")) { - Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry).AsStream()); + Ticket ticket = new Ticket(nsp.OpenFile(ticketEntry.FullPath, OpenMode.Read).AsStream()); if (!keySet.TitleKeys.ContainsKey(ticket.RightsId)) { diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs index 85ab2cf6..75f2c9ee 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs @@ -1,6 +1,5 @@ using Ryujinx.HLE.HOS.Ipc; using System.Collections.Generic; -using System.IO; namespace Ryujinx.HLE.HOS.Services.FspSrv { @@ -10,9 +9,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv public override IReadOnlyDictionary Commands => _commands; - private Stream _baseStream; + private LibHac.Fs.IStorage _baseStorage; - public IStorage(Stream baseStream) + public IStorage(LibHac.Fs.IStorage baseStorage) { _commands = new Dictionary { @@ -20,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { 4, GetSize } }; - _baseStream = baseStream; + _baseStorage = baseStorage; } // Read(u64 offset, u64 length) -> buffer buffer @@ -41,11 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv byte[] data = new byte[size]; - lock (_baseStream) - { - _baseStream.Seek(offset, SeekOrigin.Begin); - _baseStream.Read(data, 0, data.Length); - } + _baseStorage.Read(data, offset); context.Memory.WriteBytes(buffDesc.Position, data); } @@ -56,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv // GetSize() -> u64 size public long GetSize(ServiceCtx context) { - context.ResponseData.Write(_baseStream.Length); + context.ResponseData.Write(_baseStorage.GetSize()); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs index 6c2f1970..dac776f2 100644 --- a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs @@ -1,4 +1,4 @@ -using LibHac; +using LibHac.Fs.NcaUtils; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem.Content; using Ryujinx.HLE.HOS.Ipc; @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Lr { long titleId = context.RequestData.ReadInt64(); - if (ResolvePath(context, titleId, ContentType.Data) || ResolvePath(context, titleId, ContentType.AocData)) + if (ResolvePath(context, titleId, ContentType.Data) || ResolvePath(context, titleId, ContentType.PublicData)) { return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs index 8eec31cf..4a67638b 100644 --- a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs @@ -1,5 +1,5 @@ -using LibHac; -using LibHac.IO; +using LibHac.Fs; +using LibHac.Fs.NcaUtils; using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; @@ -184,21 +184,20 @@ namespace Ryujinx.HLE.HOS.Services.Set string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath); - using(FileStream firmwareStream = File.Open(firmwareTitlePath, FileMode.Open, FileAccess.Read)) - { - Nca firmwareContent = new Nca(device.System.KeySet, firmwareStream.AsStorage(), false); - IStorage romFsStorage = firmwareContent.OpenSection(0, false, device.System.FsIntegrityCheckLevel, false); + using(IStorage firmwareStorage = new LocalStorage(firmwareTitlePath, FileAccess.Read)) + { + Nca firmwareContent = new Nca(device.System.KeySet, firmwareStorage); - if(romFsStorage == null) + if (!firmwareContent.CanOpenSection(NcaSectionType.Data)) { return null; } - Romfs firmwareRomFs = new Romfs(romFsStorage); + IFileSystem firmwareRomFs = firmwareContent.OpenFileSystem(NcaSectionType.Data, device.System.FsIntegrityCheckLevel); - IStorage firmwareFile = firmwareRomFs.OpenFile("/file"); + IFile firmwareFile = firmwareRomFs.OpenFile("/file", OpenMode.Read); - byte[] data = new byte[firmwareFile.Length]; + byte[] data = new byte[firmwareFile.GetSize()]; firmwareFile.Read(data, 0); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index a653b53f..3c42cf54 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -46,7 +46,7 @@ - + diff --git a/Ryujinx/Configuration.cs b/Ryujinx/Configuration.cs index a5108544..1f670ca5 100644 --- a/Ryujinx/Configuration.cs +++ b/Ryujinx/Configuration.cs @@ -1,4 +1,4 @@ -using LibHac.IO; +using LibHac.Fs; using OpenTK.Input; using Ryujinx.Common; using Ryujinx.Common.Logging;