Browse Source

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
Alex Barney 6 years ago
parent
commit
5fc1f6a1af

+ 21 - 5
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<StorageId, LinkedList<LocationEntry>> _locationEntries;
 
         private Dictionary<string, long> _sharedFontTitleDictionary;
+        private Dictionary<string, string> _sharedFontFilenameDictionary;
 
         private SortedDictionary<(ulong, ContentType), string> _contentDictionary;
 
@@ -33,6 +34,16 @@ namespace Ryujinx.HLE.FileSystem.Content
                 { "FontNintendoExtended",          0x0100000000000810 }
             };
 
+            _sharedFontFilenameDictionary = new Dictionary<string, string>
+            {
+                { "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<LocationEntry> locationList = _locationEntries[storageId];

+ 1 - 1
Ryujinx.HLE/FileSystem/Content/LocationEntry.cs

@@ -1,4 +1,4 @@
-using LibHac;
+using LibHac.Fs.NcaUtils;
 
 namespace Ryujinx.HLE.FileSystem.Content
 {

+ 0 - 313
Ryujinx.HLE/FileSystem/FileSystemProvider.cs

@@ -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<DirectoryEntry> entries = new List<DirectoryEntry>();
-
-            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<DirectoryEntry> entries = new List<DirectoryEntry>();
-
-                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<DirectoryEntry> entries = new List<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();
-        }
-
-        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
-            };
-        }
-    }
-}

+ 0 - 43
Ryujinx.HLE/FileSystem/IFileSystemProvider.cs

@@ -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);
-    }
-}

+ 0 - 152
Ryujinx.HLE/FileSystem/PFsProvider.cs

@@ -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<DirectoryEntry> entries = new List<DirectoryEntry>();
-
-            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<DirectoryEntry> entries = new List<DirectoryEntry>();
-
-            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();
-        }
-    }
-}

+ 0 - 169
Ryujinx.HLE/FileSystem/RomFsProvider.cs

@@ -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<DirectoryEntry> directories = new List<DirectoryEntry>();
-
-            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<DirectoryEntry> entries = new List<DirectoryEntry>();
-
-            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<DirectoryEntry> files = new List<DirectoryEntry>();
-
-            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();
-        }
-    }
-}

+ 8 - 18
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);
                             }

+ 112 - 170
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<IExecutable> staticObjects = new List<IExecutable>();
-
-            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,21 +252,23 @@ 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))
+                    int dataIndex = Nca.SectionIndexFromType(NcaSectionType.Data, ContentType.Program);
+
+                    if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
                     {
-                        mainNca = nca;
+                        patchNca = nca;
                     }
-                    else if (nca.Sections.Any(x => x?.Type == SectionType.Bktr))
+                    else
                     {
-                        patchNca = nca;
+                        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,105 +357,112 @@ 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...");
+            LoadExeFs(nsp, out _);
+        }
 
-                metaData = new Npdm(nsp.OpenFile(npdmFile).AsStream());
-            }
-            else
+        public void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
+        {
+            if (mainNca.Header.ContentType != ContentType.Program)
             {
-                Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+                Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
 
-                metaData = GetDefaultNpdm();
+                return;
             }
 
-            List<IExecutable> staticObjects = new List<IExecutable>();
+            IStorage    dataStorage = null;
+            IFileSystem codeFs      = null;
 
-            void LoadNso(string searchPattern)
+            if (patchNca == null)
             {
-                PfsFileEntry entry = nsp.Files.FirstOrDefault(x => x.Name.Equals(searchPattern));
-
-                if (entry != null)
+                if (mainNca.CanOpenSection(NcaSectionType.Data))
                 {
-                    Logger.PrintInfo(LogClass.Loader, $"Loading {entry.Name}...");
-
-                    NxStaticObject staticObject = new NxStaticObject(nsp.OpenFile(entry).AsStream());
+                    dataStorage = mainNca.OpenStorage(NcaSectionType.Data, FsIntegrityCheckLevel);
+                }
 
-                    staticObjects.Add(staticObject);
+                if (mainNca.CanOpenSection(NcaSectionType.Code))
+                {
+                    codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, FsIntegrityCheckLevel);
                 }
             }
+            else
+            {
+                if (patchNca.CanOpenSection(NcaSectionType.Data))
+                {
+                    dataStorage = mainNca.OpenStorageWithPatch(patchNca, NcaSectionType.Data, FsIntegrityCheckLevel);
+                }
 
-            TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
+                if (patchNca.CanOpenSection(NcaSectionType.Code))
+                {
+                    codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, FsIntegrityCheckLevel);
+                }
+            }
 
-            LoadNso("rtld");
-            LoadNso("main");
-            LoadNso("subsdk*");
-            LoadNso("sdk");
+            if (codeFs == null)
+            {
+                Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
 
-            ContentManager.LoadEntries();
+                return;
+            }
 
-            if (staticObjects.Count == 0)
+            if (dataStorage == null)
             {
-                Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file");
+                Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
             }
             else
             {
-                ProgramLoader.LoadStaticObjects(this, metaData, staticObjects.ToArray());
+                Device.FileSystem.SetRomFs(dataStorage.AsStream(FileAccess.Read));
             }
-        }
 
-        public void LoadNca(Nca mainNca, Nca controlNca)
-        {
-            if (mainNca.Header.ContentType != ContentType.Program)
+            LoadExeFs(codeFs, out Npdm metaData);
+
+            Nacp ReadControlData()
             {
-                Logger.PrintError(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
+                IFileSystem controlRomfs = controlNca.OpenFileSystem(NcaSectionType.Data, FsIntegrityCheckLevel);
 
-                return;
-            }
+                IFile controlFile = controlRomfs.OpenFile("/control.nacp", OpenMode.Read);
+
+                Nacp controlData = new Nacp(controlFile.AsStream());
 
-            IStorage romfsStorage = mainNca.OpenSection(ProgramPartitionType.Data, false, FsIntegrityCheckLevel, false);
-            IStorage exefsStorage = mainNca.OpenSection(ProgramPartitionType.Code, false, FsIntegrityCheckLevel, true);
+                TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
+                TitleID = metaData.Aci0.TitleId.ToString("x16");
 
-            if (exefsStorage == null)
-            {
-                Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA");
+                CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
 
-                return;
+                if (string.IsNullOrWhiteSpace(CurrentTitle))
+                {
+                    TitleName = CurrentTitle = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
+                }
+
+                return controlData;
             }
 
-            if (romfsStorage == null)
+            if (controlNca != null)
             {
-                Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA");
+                ReadControlData();
             }
             else
             {
-                Device.FileSystem.SetRomFs(romfsStorage.AsStream(false));
+                TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
             }
+        }
 
-            Pfs exefs = new Pfs(exefsStorage);
-
-            Npdm metaData = null;
-
-            if (exefs.FileExists("main.npdm"))
+        private void LoadExeFs(IFileSystem codeFs, out Npdm metaData)
+        {
+            if (codeFs.FileExists("/main.npdm"))
             {
                 Logger.PrintInfo(LogClass.Loader, "Loading main.npdm...");
 
-                metaData = new Npdm(exefs.OpenFile("main.npdm").AsStream());
+                metaData = new Npdm(codeFs.OpenFile("/main.npdm", OpenMode.Read).AsStream());
             }
             else
             {
-                Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!");
+                Logger.PrintWarning(LogClass.Loader, "NPDM file not found, using default values!");
 
                 metaData = GetDefaultNpdm();
             }
@@ -503,48 +471,22 @@ namespace Ryujinx.HLE.HOS
 
             void LoadNso(string filename)
             {
-                foreach (PfsFileEntry file in exefs.Files.Where(x => x.Name.StartsWith(filename)))
+                foreach (DirectoryEntry file in codeFs.EnumerateEntries($"{filename}*"))
                 {
                     if (Path.GetExtension(file.Name) != string.Empty)
                     {
                         continue;
                     }
 
-                    Logger.PrintInfo(LogClass.Loader, $"Loading {filename}...");
+                    Logger.PrintInfo(LogClass.Loader, $"Loading {file.Name}...");
 
-                    NxStaticObject staticObject = new NxStaticObject(exefs.OpenFile(file).AsStream());
+                    NxStaticObject staticObject = new NxStaticObject(codeFs.OpenFile(file.FullPath, OpenMode.Read).AsStream());
 
                     staticObjects.Add(staticObject);
                 }
             }
 
-            Nacp ReadControlData()
-            {
-                Romfs controlRomfs = new Romfs(controlNca.OpenSection(0, false, FsIntegrityCheckLevel, true));
-
-                IStorage controlFile = controlRomfs.OpenFile("/control.nacp");
-
-                Nacp controlData = new Nacp(controlFile.AsStream());
-
-                TitleName = CurrentTitle = controlData.Descriptions[(int)State.DesiredTitleLanguage].Title;
-                TitleID   = metaData.Aci0.TitleId.ToString("x16");
-
-                if (string.IsNullOrWhiteSpace(CurrentTitle))
-                {
-                    TitleName = CurrentTitle = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
-                }
-
-                return controlData;
-            }
-
-            if (controlNca != null)
-            {
-                ReadControlData();
-            }
-            else
-            {
-                TitleID = CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
-            }
+            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 });

+ 0 - 17
Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs

@@ -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;
-        }
-    }
-}

+ 0 - 8
Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs

@@ -1,8 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.FspSrv
-{
-    public enum DirectoryEntryType
-    {
-        Directory,
-        File
-    }
-}

+ 0 - 11
Ryujinx.HLE/HOS/Services/FspSrv/FileTimestamp.cs

@@ -1,11 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.FspSrv
-{
-    struct FileTimestamp
-    {
-        public DateTime CreationDateTime;
-        public DateTime ModifiedDateTime;
-        public DateTime LastAccessDateTime;
-    }
-}

+ 19 - 33
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<int, ServiceProcessRequest> Commands => _commands;
 
-        private List<DirectoryEntry> _directoryEntries;
-
-        private int _currentItemIndex;
+        private IEnumerator<LibHac.Fs.DirectoryEntry> _enumerator;
 
         public event EventHandler<EventArgs> 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<int, ServiceProcessRequest>
             {
@@ -33,22 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 { 1, GetEntryCount }
             };
 
-            _provider     = provider;
-            DirectoryPath = directoryPath;
+            _provider = directory;
 
-            _directoryEntries = new List<DirectoryEntry>();
+            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<nn::fssrv::sf::IDirectoryEntry, 6, 0> 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, _enumerator.Current);
 
-                WriteDirectoryEntry(context, position, _directoryEntries[_currentItemIndex++]);
+                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;
         }

+ 20 - 20
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<int, ServiceProcessRequest> Commands => _commands;
 
-        private Stream _baseStream;
+        private LibHac.Fs.IFile _baseFile;
 
         public event EventHandler<EventArgs> 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<int, ServiceProcessRequest>
             {
@@ -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<u8, 0x46, 0> out_buf)
+        // Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> 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<u8, 0x45, 0>)
+        // Write(u32 writeOption, u64 offset, u64 size, buffer<u8, 0x45, 0>)
         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);
             }

+ 208 - 113
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<string> _openPaths;
 
-        private string _path;
-
-        private IFileSystemProvider _provider;
+        private LibHac.Fs.IFileSystem _provider;
 
-        public IFileSystem(string path, IFileSystemProvider provider)
+        public IFileSystem(LibHac.Fs.IFileSystem provider)
         {
             _commands = new Dictionary<int, ServiceProcessRequest>
             {
@@ -44,36 +42,50 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
 
             _openPaths = new HashSet<string>();
 
-            _path     = path;
             _provider = provider;
         }
 
-        // CreateFile(u32 mode, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path)
+        // CreateFile(u32 createOption, u64 size, buffer<bytes<0x301>, 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<bytes<0x301>, 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<bytes<0x301>, 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<bytes<0x301>, 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<bytes<0x301>, 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<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 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<bytes<0x301>, 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);
-
-            if (_provider.FileExists(fileName))
-            {
-                context.ResponseData.Write(1);
-            }
-            else if (_provider.DirectoryExists(fileName))
+            try
             {
-                context.ResponseData.Write(0);
+                LibHac.Fs.DirectoryEntryType entryType = _provider.GetEntryType(name);
+
+                context.ResponseData.Write((int)entryType);
             }
-            else
+            catch (FileNotFoundException)
             {
                 context.ResponseData.Write(0);
 
@@ -238,81 +323,96 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
         // OpenFile(u32 mode, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IFile> 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);
+            try
+            {
+                LibHac.Fs.IFile file = _provider.OpenFile(name, (OpenMode)mode);
 
-            if (error == 0)
+                fileInterface = new IFile(file, name);
+            }
+            catch (UnauthorizedAccessException)
             {
-                fileInterface.Disposed += RemoveFileInUse;
+                Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
 
-                lock (_openPaths)
-                {
-                    _openPaths.Add(fileName);
-                }
+                throw;
+            }
 
-                MakeObject(context, fileInterface);
+            fileInterface.Disposed += RemoveFileInUse;
 
-                return 0;
+            lock (_openPaths)
+            {
+                _openPaths.Add(fileInterface.Path);
             }
 
-            return error;
+            MakeObject(context, fileInterface);
+
+            return 0;
         }
 
         // OpenDirectory(u32 filter_flags, buffer<bytes<0x301>, 0x19, 0x301> path) -> object<nn::fssrv::sf::IDirectory> 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;
+
+            try
+            {
+                LibHac.Fs.IDirectory dir = _provider.OpenDirectory(name, (OpenDirectoryMode) mode);
 
-            if (error == 0)
+                dirInterface = new IDirectory(dir);
+            }
+            catch (UnauthorizedAccessException)
             {
-                dirInterface.Disposed += RemoveDirectoryInUse;
+                Logger.PrintError(LogClass.ServiceFs, $"Unable to access {name}");
+
+                throw;
+            }
 
-                lock (_openPaths)
-                {
-                    _openPaths.Add(dirName);
-                }
+            dirInterface.Disposed += RemoveDirectoryInUse;
 
-                MakeObject(context, dirInterface);
+            lock (_openPaths)
+            {
+                _openPaths.Add(dirInterface.Path);
             }
 
-            return error;
+            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);
             }
         }
     }

+ 44 - 58
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<bytes<0x301>, 0x19, 0x301>) -> object<nn::fssrv::sf::IFileSystem> 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<nn::fssrv::sf::IStorage> 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<nn::fssrv::sf::IStorage>
         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);
-
-            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));
+            Nca nca = new Nca(context.Device.System.KeySet, ncaStorage);
 
-                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))
                 {

+ 5 - 10
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<int, ServiceProcessRequest> Commands => _commands;
 
-        private Stream _baseStream;
+        private LibHac.Fs.IStorage _baseStorage;
 
-        public IStorage(Stream baseStream)
+        public IStorage(LibHac.Fs.IStorage baseStorage)
         {
             _commands = new Dictionary<int, ServiceProcessRequest>
             {
@@ -20,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 { 4, GetSize }
             };
 
-            _baseStream = baseStream;
+            _baseStorage = baseStorage;
         }
 
         // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> 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;
         }

+ 2 - 2
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;
             }

+ 9 - 10
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);
 

+ 1 - 1
Ryujinx.HLE/Ryujinx.HLE.csproj

@@ -46,7 +46,7 @@
 
   <ItemGroup>
     <PackageReference Include="Concentus" Version="1.1.7" />
-    <PackageReference Include="LibHac" Version="0.2.0" />
+    <PackageReference Include="LibHac" Version="0.4.0" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
Ryujinx/Configuration.cs

@@ -1,4 +1,4 @@
-using LibHac.IO;
+using LibHac.Fs;
 using OpenTK.Input;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;