diff --git a/.gitignore b/.gitignore
index 123f46184d..c0af260973 100644
--- a/.gitignore
+++ b/.gitignore
@@ -161,3 +161,6 @@ $RECYCLE.BIN/
 
 # VS Launch Settings
 launchSettings.json
+
+# NetCore Publishing Profiles
+PublishProfiles/
diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
new file mode 100644
index 0000000000..025eb26191
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
@@ -0,0 +1,300 @@
+using LibHac;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    internal class ContentManager
+    {
+        private Dictionary<StorageId, LinkedList<LocationEntry>> LocationEntries;
+
+        private Dictionary<string, long> SharedFontTitleDictionary;
+
+        private SortedDictionary<(ulong, ContentType), string> ContentDictionary;
+
+        private Switch Device;
+
+        public ContentManager(Switch Device)
+        {
+            ContentDictionary = new SortedDictionary<(ulong, ContentType), string>();
+            LocationEntries   = new Dictionary<StorageId, LinkedList<LocationEntry>>();
+
+            SharedFontTitleDictionary = new Dictionary<string, long>()
+            {
+                { "FontStandard",                  0x0100000000000811 },
+                { "FontChineseSimplified",         0x0100000000000814 },
+                { "FontExtendedChineseSimplified", 0x0100000000000814 },
+                { "FontKorean",                    0x0100000000000812 },
+                { "FontChineseTraditional",        0x0100000000000813 },
+                { "FontNintendoExtended" ,         0x0100000000000810 },
+            };
+
+            this.Device = Device;
+        }
+
+        public void LoadEntries()
+        {
+            ContentDictionary = new SortedDictionary<(ulong, ContentType), string>();
+
+            foreach (StorageId StorageId in Enum.GetValues(typeof(StorageId)))
+            {
+                string ContentDirectory    = null;
+                string ContentPathString   = null;
+                string RegisteredDirectory = null;
+
+                try
+                {
+                    ContentPathString   = LocationHelper.GetContentRoot(StorageId);
+                    ContentDirectory    = LocationHelper.GetRealPath(Device.FileSystem, ContentPathString);
+                    RegisteredDirectory = Path.Combine(ContentDirectory, "registered");
+                }
+                catch (NotSupportedException NEx)
+                {
+                    continue;
+                }
+
+                Directory.CreateDirectory(RegisteredDirectory);
+
+                LinkedList<LocationEntry> LocationList = new LinkedList<LocationEntry>();
+
+                void AddEntry(LocationEntry Entry)
+                {
+                    LocationList.AddLast(Entry);
+                }
+
+                foreach (string DirectoryPath in Directory.EnumerateDirectories(RegisteredDirectory))
+                {
+                    if (Directory.GetFiles(DirectoryPath).Length > 0)
+                    {
+                        string NcaName = new DirectoryInfo(DirectoryPath).Name.Replace(".nca", string.Empty);
+
+                        using (FileStream NcaFile = new FileStream(Directory.GetFiles(DirectoryPath)[0], FileMode.Open, FileAccess.Read))
+                        {
+                            Nca Nca = new Nca(Device.System.KeySet, NcaFile, false);
+
+                            string SwitchPath = Path.Combine(ContentPathString + ":",
+                                                              NcaFile.Name.Replace(ContentDirectory, string.Empty).TrimStart('\\'));
+
+                            // Change path format to switch's
+                            SwitchPath = SwitchPath.Replace('\\', '/');
+
+                            LocationEntry Entry = new LocationEntry(SwitchPath,
+                                                                    0,
+                                                                    (long)Nca.Header.TitleId,
+                                                                    Nca.Header.ContentType);
+
+                            AddEntry(Entry);
+
+                            ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
+
+                            NcaFile.Close();
+                            Nca.Dispose();
+                            NcaFile.Dispose();
+                        }
+                    }
+                }
+
+                foreach (string FilePath in Directory.EnumerateFiles(ContentDirectory))
+                {
+                    if (Path.GetExtension(FilePath) == ".nca")
+                    {
+                        string NcaName = Path.GetFileNameWithoutExtension(FilePath);
+
+                        using (FileStream NcaFile = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
+                        {
+                            Nca Nca = new Nca(Device.System.KeySet, NcaFile, false);
+
+                            string SwitchPath = Path.Combine(ContentPathString + ":",
+                                                              FilePath.Replace(ContentDirectory, string.Empty).TrimStart('\\'));
+
+                            // Change path format to switch's
+                            SwitchPath = SwitchPath.Replace('\\', '/');
+
+                            LocationEntry Entry = new LocationEntry(SwitchPath,
+                                                                    0,
+                                                                    (long)Nca.Header.TitleId,
+                                                                    Nca.Header.ContentType);
+
+                            AddEntry(Entry);
+
+                            ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
+
+                            NcaFile.Close();
+                            Nca.Dispose();
+                            NcaFile.Dispose();
+                        }
+                    }
+                }
+
+                if(LocationEntries.ContainsKey(StorageId) && LocationEntries[StorageId]?.Count == 0)
+                {
+                    LocationEntries.Remove(StorageId);
+                }
+
+                if (!LocationEntries.ContainsKey(StorageId))
+                {
+                    LocationEntries.Add(StorageId, LocationList);
+                }
+            }
+        }
+
+        public void ClearEntry(long TitleId, ContentType ContentType,StorageId StorageId)
+        {
+            RemoveLocationEntry(TitleId, ContentType, StorageId);
+        }
+
+        public void RefreshEntries(StorageId StorageId, int Flag)
+        {
+            LinkedList<LocationEntry> LocationList      = LocationEntries[StorageId];
+            LinkedListNode<LocationEntry> LocationEntry = LocationList.First;
+
+            while (LocationEntry != null)
+            {
+                LinkedListNode<LocationEntry> NextLocationEntry = LocationEntry.Next;
+
+                if (LocationEntry.Value.Flag == Flag)
+                {
+                    LocationList.Remove(LocationEntry.Value);
+                }
+
+                LocationEntry = NextLocationEntry;
+            }
+        }
+
+        public bool HasNca(string NcaId, StorageId StorageId)
+        {
+            if (ContentDictionary.ContainsValue(NcaId))
+            {
+                var         Content     = ContentDictionary.FirstOrDefault(x => x.Value == NcaId);
+                long        TitleId     = (long)Content.Key.Item1;
+                ContentType ContentType = Content.Key.Item2;
+                StorageId   Storage     = GetInstalledStorage(TitleId, ContentType, StorageId);
+
+                return Storage == StorageId;
+            }
+
+            return false;
+        }
+
+        public UInt128 GetInstalledNcaId(long TitleId, ContentType ContentType)
+        {
+            if (ContentDictionary.ContainsKey(((ulong)TitleId,ContentType)))
+            {
+                return new UInt128(ContentDictionary[((ulong)TitleId,ContentType)]);
+            }
+
+            return new UInt128();
+        }
+
+        public StorageId GetInstalledStorage(long TitleId, ContentType ContentType, StorageId StorageId)
+        {
+            LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId);
+
+            return LocationEntry.ContentPath != null ?
+                LocationHelper.GetStorageId(LocationEntry.ContentPath) : StorageId.None;
+        }
+
+        public string GetInstalledContentPath(long TitleId, StorageId StorageId, ContentType ContentType)
+        {
+            LocationEntry LocationEntry = GetLocation(TitleId, ContentType, StorageId);
+
+            if (VerifyContentType(LocationEntry, ContentType))
+            {
+                return LocationEntry.ContentPath;
+            }
+
+            return string.Empty;
+        }
+
+        public void RedirectLocation(LocationEntry NewEntry, StorageId StorageId)
+        {
+            LocationEntry LocationEntry = GetLocation(NewEntry.TitleId, NewEntry.ContentType, StorageId);
+
+            if (LocationEntry.ContentPath != null)
+            {
+                RemoveLocationEntry(NewEntry.TitleId, NewEntry.ContentType, StorageId);
+            }
+
+            AddLocationEntry(NewEntry, StorageId);
+        }
+
+        private bool VerifyContentType(LocationEntry LocationEntry, ContentType ContentType)
+        {
+            StorageId StorageId     = LocationHelper.GetStorageId(LocationEntry.ContentPath);
+            string    InstalledPath = Device.FileSystem.SwitchPathToSystemPath(LocationEntry.ContentPath);
+
+            if (!string.IsNullOrWhiteSpace(InstalledPath))
+            {
+                if (File.Exists(InstalledPath))
+                {
+                    FileStream File         = new FileStream(InstalledPath, FileMode.Open, FileAccess.Read);
+                    Nca        Nca          = new Nca(Device.System.KeySet, File, false);
+                    bool       ContentCheck = Nca.Header.ContentType == ContentType;
+
+                    Nca.Dispose();
+                    File.Dispose();
+
+                    return ContentCheck;
+                }
+            }
+
+            return false;
+        }
+
+        private void AddLocationEntry(LocationEntry Entry, StorageId StorageId)
+        {
+            LinkedList<LocationEntry> LocationList = null;
+
+            if (LocationEntries.ContainsKey(StorageId))
+            {
+                LocationList = LocationEntries[StorageId];
+            }
+
+            if (LocationList != null)
+            {
+                if (LocationList.Contains(Entry))
+                {
+                    LocationList.Remove(Entry);
+                }
+
+                LocationList.AddLast(Entry);
+            }
+        }
+
+        private void RemoveLocationEntry(long TitleId, ContentType ContentType, StorageId StorageId)
+        {
+            LinkedList<LocationEntry> LocationList = null;
+
+            if (LocationEntries.ContainsKey(StorageId))
+            {
+                LocationList = LocationEntries[StorageId];
+            }
+
+            if (LocationList != null)
+            {
+                LocationEntry Entry =
+                    LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType);
+
+                if (Entry.ContentPath != null)
+                {
+                    LocationList.Remove(Entry);
+                }
+            }
+        }
+
+        public bool TryGetFontTitle(string FontName, out long TitleId)
+        {
+            return SharedFontTitleDictionary.TryGetValue(FontName, out TitleId);
+        }
+
+        private LocationEntry GetLocation(long TitleId, ContentType ContentType,StorageId StorageId)
+        {
+            LinkedList<LocationEntry> LocationList = LocationEntries[StorageId];
+
+            return LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType);
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/Content/ContentPath.cs b/Ryujinx.HLE/FileSystem/Content/ContentPath.cs
new file mode 100644
index 0000000000..1e2c8ab32a
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/ContentPath.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    static class ContentPath
+    {
+        public const string SystemContent    = "@SystemContent";
+        public const string UserContent      = "@UserContent";
+        public const string SdCardContent    = "@SdCardContent";
+        public const string SdCard           = "@SdCard";
+        public const string CalibFile        = "@CalibFile";
+        public const string Safe             = "@Safe";
+        public const string User             = "@User";
+        public const string System           = "@System";
+        public const string Host             = "@Host";
+        public const string GamecardApp      = "@GcApp";
+        public const string GamecardContents = "@GcS00000001";
+        public const string GamecardUpdate   = "@upp";
+        public const string RegisteredUpdate = "@RegUpdate";
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs
new file mode 100644
index 0000000000..c7c6133b58
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/LocationEntry.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using LibHac;
+
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    public struct LocationEntry
+    {
+        public string      ContentPath { get; private set; }
+        public int         Flag        { get; private set; }
+        public long        TitleId     { get; private set; }
+        public ContentType ContentType { get; private set; }
+
+        public LocationEntry(string ContentPath, int Flag, long TitleId, ContentType ContentType)
+        {
+            this.ContentPath = ContentPath;
+            this.Flag        = Flag;
+            this.TitleId     = TitleId;
+            this.ContentType = ContentType;
+        }
+
+        public void SetFlag(int Flag)
+        {
+            this.Flag = Flag;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs b/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs
new file mode 100644
index 0000000000..75b5943161
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+
+using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
+
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    internal static class LocationHelper
+    {
+        public static string GetRealPath(VirtualFileSystem FileSystem, string SwitchContentPath)
+        {
+            string BasePath = FileSystem.GetBasePath();
+
+            switch (SwitchContentPath)
+            {
+                case ContentPath.SystemContent:
+                    return Path.Combine(FileSystem.GetBasePath(), SystemNandPath, "Contents");
+                case ContentPath.UserContent:
+                    return Path.Combine(FileSystem.GetBasePath(), UserNandPath, "Contents");
+                case ContentPath.SdCardContent:
+                    return Path.Combine(FileSystem.GetSdCardPath(), "Nintendo", "Contents");
+                case ContentPath.System:
+                    return Path.Combine(BasePath, SystemNandPath);
+                case ContentPath.User:
+                    return Path.Combine(BasePath, UserNandPath);
+                default:
+                    throw new NotSupportedException($"Content Path `{SwitchContentPath}` is not supported.");
+            }
+        }
+
+        public static string GetContentPath(ContentStorageId ContentStorageId)
+        {
+            switch (ContentStorageId)
+            {
+                case ContentStorageId.NandSystem:
+                    return ContentPath.SystemContent;
+                case ContentStorageId.NandUser:
+                    return ContentPath.UserContent;
+                case ContentStorageId.SdCard:
+                    return ContentPath.SdCardContent;
+                default:
+                    throw new NotSupportedException($"Content Storage `{ContentStorageId}` is not supported.");
+            }
+        }
+
+        public static string GetContentRoot(StorageId StorageId)
+        {
+            switch (StorageId)
+            {
+                case StorageId.NandSystem:
+                    return ContentPath.SystemContent;
+                case StorageId.NandUser:
+                    return ContentPath.UserContent;
+                case StorageId.SdCard:
+                    return ContentPath.SdCardContent;
+                default:
+                    throw new NotSupportedException($"Storage Id `{StorageId}` is not supported.");
+            }
+        }
+
+        public static StorageId GetStorageId(string ContentPathString)
+        {
+            string CleanedPath = ContentPathString.Split(':')[0];
+
+            switch (CleanedPath)
+            {
+                case ContentPath.SystemContent:
+                case ContentPath.System:
+                    return StorageId.NandSystem;
+
+                case ContentPath.UserContent:
+                case ContentPath.User:
+                    return StorageId.NandUser;
+
+                case ContentPath.SdCardContent:
+                    return StorageId.SdCard;
+
+                case ContentPath.Host:
+                    return StorageId.Host;
+
+                case ContentPath.GamecardApp:
+                case ContentPath.GamecardContents:
+                case ContentPath.GamecardUpdate:
+                    return StorageId.GameCard;
+
+                default:
+                    return StorageId.None;
+            }
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/Content/StorageId.cs b/Ryujinx.HLE/FileSystem/Content/StorageId.cs
new file mode 100644
index 0000000000..4ff3dd6574
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/StorageId.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    public enum ContentStorageId
+    {
+        NandSystem,
+        NandUser,
+        SdCard
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/Content/TitleType.cs b/Ryujinx.HLE/FileSystem/Content/TitleType.cs
new file mode 100644
index 0000000000..6ad26c9cdd
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/Content/TitleType.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.FileSystem.Content
+{
+    enum TitleType
+    {
+        SystemPrograms     = 0x01,
+        SystemDataArchive  = 0x02,
+        SystemUpdate       = 0x03,
+        FirmwarePackageA   = 0x04,
+        FirmwarePackageB   = 0x05,
+        RegularApplication = 0x80,
+        Update             = 0x81,
+        AddOnContent       = 0x82,
+        DeltaTitle         = 0x83
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/FileSystemProvider.cs b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs
new file mode 100644
index 0000000000..37ccfb10cd
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/FileSystemProvider.cs
@@ -0,0 +1,281 @@
+using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services.FspSrv;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using static Ryujinx.HLE.HOS.ErrorCode;
+
+namespace Ryujinx.HLE.FileSystem
+{
+    class FileSystemProvider : IFileSystemProvider
+    {
+        private readonly string BasePath;
+        private readonly string RootPath;
+
+        public FileSystemProvider(string BasePath, string RootPath)
+        {
+            this.BasePath = BasePath;
+            this.RootPath = RootPath;
+
+            CheckIfDecendentOfRootPath(BasePath);
+        }
+
+        public long CreateDirectory(string Name)
+        {
+            CheckIfDecendentOfRootPath(Name);
+
+            if (Directory.Exists(Name))
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
+            }
+
+            Directory.CreateDirectory(Name);
+
+            return 0;
+        }
+
+        public long CreateFile(string Name, long Size)
+        {
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(Name);
+
+            if (!File.Exists(Name))
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
+            }
+            else
+            {
+                File.Delete(Name);
+            }
+
+            return 0;
+        }
+
+        public DirectoryEntry[] GetDirectories(string Path)
+        {
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(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 null;
+        }
+
+        public DirectoryEntry[] GetFiles(string Path)
+        {
+            CheckIfDecendentOfRootPath(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);
+
+            CheckIfDecendentOfRootPath(FullPath);
+
+            return FullPath;
+        }
+
+        public long GetTotalSpace(ServiceCtx Context)
+        {
+            return Context.Device.FileSystem.GetDrive().TotalSize;
+        }
+
+        public bool DirectoryExists(string Name)
+        {
+            CheckIfDecendentOfRootPath(Name);
+
+            return Directory.Exists(Name);
+        }
+
+        public bool FileExists(string Name)
+        {
+            CheckIfDecendentOfRootPath(Name);
+
+            return File.Exists(Name);
+        }
+
+        public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface)
+        {
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(OldName);
+            CheckIfDecendentOfRootPath(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)
+        {
+            CheckIfDecendentOfRootPath(OldName);
+            CheckIfDecendentOfRootPath(NewName);
+
+            if (File.Exists(OldName))
+            {
+                File.Move(OldName, NewName);
+            }
+            else
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
+            }
+
+            return 0;
+        }
+
+        public void CheckIfDecendentOfRootPath(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}");
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs
new file mode 100644
index 0000000000..88a630a0d8
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/IFileSystemProvider.cs
@@ -0,0 +1,41 @@
+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);
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/PFsProvider.cs b/Ryujinx.HLE/FileSystem/PFsProvider.cs
new file mode 100644
index 0000000000..c901f073ea
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/PFsProvider.cs
@@ -0,0 +1,146 @@
+using LibHac;
+using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services.FspSrv;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using static Ryujinx.HLE.HOS.ErrorCode;
+
+namespace Ryujinx.HLE.FileSystem
+{
+    class PFsProvider : IFileSystemProvider
+    {
+        private Pfs Pfs;
+
+        public PFsProvider(Pfs Pfs)
+        {
+            this.Pfs = Pfs;
+        }
+
+        public long CreateDirectory(string Name)
+        {
+            throw new NotSupportedException();
+        }
+
+        public long CreateFile(string Name, long Size)
+        {
+            throw new NotSupportedException();
+        }
+
+        public long DeleteDirectory(string Name, bool Recursive)
+        {
+            throw new NotSupportedException();
+        }
+
+        public long DeleteFile(string Name)
+        {
+            throw new NotSupportedException();
+        }
+
+        public DirectoryEntry[] GetDirectories(string Path)
+        {
+            return new DirectoryEntry[0];
+        }
+
+        public DirectoryEntry[] GetEntries(string Path)
+        {
+            List<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 == "/" ? true : false;
+        }
+
+        public bool FileExists(string Name)
+        {
+            Name = Name.TrimStart('/');
+
+            return Pfs.FileExists(Name);
+        }
+
+        public long OpenDirectory(string Name, int FilterFlags, out IDirectory DirectoryInterface)
+        {
+            if (Name == "/")
+            {
+                DirectoryInterface = new IDirectory(Name, FilterFlags, this);
+
+                return 0;
+            }
+
+            throw new NotSupportedException();
+        }
+
+        public long OpenFile(string Name, out IFile FileInterface)
+        {
+            Name = Name.TrimStart('/');
+
+            if (Pfs.FileExists(Name))
+            {
+                Stream Stream = Pfs.OpenFile(Name);
+                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();
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/RomFsProvider.cs b/Ryujinx.HLE/FileSystem/RomFsProvider.cs
new file mode 100644
index 0000000000..d966d3d7ce
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/RomFsProvider.cs
@@ -0,0 +1,163 @@
+using LibHac;
+using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.HOS.Services.FspSrv;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using static Ryujinx.HLE.HOS.ErrorCode;
+
+namespace Ryujinx.HLE.FileSystem
+{
+    class RomFsProvider : IFileSystemProvider
+    {
+        private Romfs RomFs;
+
+        public RomFsProvider(Stream StorageStream)
+        {
+            RomFs = new Romfs(StorageStream);
+        }
+
+        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);
+
+                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();
+        }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/SaveSpaceId.cs b/Ryujinx.HLE/FileSystem/SaveSpaceId.cs
index 5a2b32d635..d51922df1a 100644
--- a/Ryujinx.HLE/FileSystem/SaveSpaceId.cs
+++ b/Ryujinx.HLE/FileSystem/SaveSpaceId.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.HLE.FileSystem
 {
-    enum SaveSpaceId : byte
+    enum SaveSpaceId
     {
         NandSystem,
         NandUser,
diff --git a/Ryujinx.HLE/FileSystem/StorageId.cs b/Ryujinx.HLE/FileSystem/StorageId.cs
new file mode 100644
index 0000000000..1ef38e0167
--- /dev/null
+++ b/Ryujinx.HLE/FileSystem/StorageId.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.FileSystem
+{
+    internal enum StorageId
+    {
+        None,
+        Host,
+        GameCard,
+        NandSystem,
+        NandUser,
+        SdCard
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
index e621ec2b11..6bb2847f18 100644
--- a/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
+++ b/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs
@@ -1,3 +1,4 @@
+using Ryujinx.HLE.FileSystem.Content;
 using Ryujinx.HLE.HOS;
 using System;
 using System.IO;
@@ -11,6 +12,7 @@ namespace Ryujinx.HLE.FileSystem
         public const string SdCardPath = "sdmc";
         public const string SystemPath = "system";
 
+        public static string SafeNandPath   = Path.Combine(NandPath, "safe");
         public static string SystemNandPath = Path.Combine(NandPath, "system");
         public static string UserNandPath   = Path.Combine(NandPath, "user");
 
@@ -63,9 +65,15 @@ namespace Ryujinx.HLE.FileSystem
             return MakeDirAndGetFullPath(SaveHelper.GetSavePath(Save, Context));
         }
 
+        public string GetFullPartitionPath(string PartitionPath)
+        {
+            return MakeDirAndGetFullPath(PartitionPath);
+        }
+
         public string SwitchPathToSystemPath(string SwitchPath)
         {
             string[] Parts = SwitchPath.Split(":");
+
             if (Parts.Length != 2)
             {
                 return null;
@@ -76,10 +84,12 @@ namespace Ryujinx.HLE.FileSystem
         public string SystemPathToSwitchPath(string SystemPath)
         {
             string BaseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
+
             if (SystemPath.StartsWith(BaseSystemPath))
             {
-                string RawPath = SystemPath.Replace(BaseSystemPath, "");
-                int FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar);
+                string RawPath              = SystemPath.Replace(BaseSystemPath, "");
+                int    FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar);
+
                 if (FirstSeparatorOffset == -1)
                 {
                     return $"{RawPath}:/";
@@ -87,6 +97,7 @@ namespace Ryujinx.HLE.FileSystem
 
                 string BasePath = RawPath.Substring(0, FirstSeparatorOffset);
                 string FileName = RawPath.Substring(FirstSeparatorOffset + 1);
+
                 return $"{BasePath}:/{FileName}";
             }
             return null;
@@ -94,6 +105,30 @@ namespace Ryujinx.HLE.FileSystem
 
         private string MakeDirAndGetFullPath(string Dir)
         {
+            // Handles Common Switch Content Paths
+            switch (Dir)
+            {
+                case ContentPath.SdCard:
+                case "@Sdcard":
+                    Dir = SdCardPath;
+                    break;
+                case ContentPath.User:
+                    Dir = UserNandPath;
+                    break;
+                case ContentPath.System:
+                    Dir = SystemNandPath;
+                    break;
+                case ContentPath.SdCardContent:
+                    Dir = Path.Combine(SdCardPath, "Nintendo", "Contents");
+                    break;
+                case ContentPath.UserContent:
+                    Dir = Path.Combine(UserNandPath, "Contents");
+                    break;
+                case ContentPath.SystemContent:
+                    Dir = Path.Combine(SystemNandPath, "Contents");
+                    break;
+            }
+
             string FullPath = Path.Combine(GetBasePath(), Dir);
 
             if (!Directory.Exists(FullPath))
diff --git a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
index 0be5e89670..313db34571 100644
--- a/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Font/SharedFontManager.cs
@@ -1,14 +1,19 @@
-using Ryujinx.HLE.Memory;
+using LibHac;
+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
 {
     class SharedFontManager
     {
-        private DeviceMemory Memory;
+        private Switch Device;
 
         private long PhysicalAddress;
 
@@ -32,21 +37,64 @@ namespace Ryujinx.HLE.HOS.Font
         {
             this.PhysicalAddress = PhysicalAddress;
 
-            Memory = Device.Memory;
+            this.Device = Device;
 
             FontsPath = Path.Combine(Device.FileSystem.GetSystemPath(), "fonts");
         }
 
-        public void EnsureInitialized()
+        public void EnsureInitialized(ContentManager ContentManager)
         {
             if (FontData == null)
             {
-                Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize);
+                Device.Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize);
 
                 uint FontOffset = 0;
 
                 FontInfo CreateFont(string Name)
                 {
+                    if (ContentManager.TryGetFontTitle(Name, out long FontTitle))
+                    {
+                        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;
+                            }
+
+                            FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read);
+                            Nca        Nca           = new Nca(Device.System.KeySet, NcaFileStream, false);
+                            NcaSection RomfsSection  = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
+                            Romfs      Romfs         = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel));
+                            Stream     FontFile      = Romfs.OpenFile(Romfs.Files[FileIndex]);
+
+                            byte[] Data = DecryptFont(FontFile);
+
+                            FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
+
+                            WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length);
+
+                            FontOffset += 8;
+
+                            uint Start = FontOffset;
+
+                            for (; FontOffset - Start < Data.Length; FontOffset++)
+                            {
+                                Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
+                            }
+
+                            NcaFileStream.Dispose();
+                            Nca.Dispose();
+
+                            return Info;
+                        }
+                    }
+
                     string FontFilePath = Path.Combine(FontsPath, Name + ".ttf");
 
                     if (File.Exists(FontFilePath))
@@ -63,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Font
 
                         for (; FontOffset - Start < Data.Length; FontOffset++)
                         {
-                            Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
+                            Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]);
                         }
 
                         return Info;
@@ -101,20 +149,20 @@ namespace Ryujinx.HLE.HOS.Font
 
             int EncryptedSize = EndianSwap.Swap32(Size ^ Key);
 
-            Memory.WriteInt32(Position + 0, DecMagic);
-            Memory.WriteInt32(Position + 4, EncryptedSize);
+            Device.Memory.WriteInt32(Position + 0, DecMagic);
+            Device.Memory.WriteInt32(Position + 4, EncryptedSize);
         }
 
         public int GetFontSize(SharedFontType FontType)
         {
-            EnsureInitialized();
+            EnsureInitialized(Device.System.ContentManager);
 
             return FontData[FontType].Size;
         }
 
         public int GetSharedMemoryAddressOffset(SharedFontType FontType)
         {
-            EnsureInitialized();
+            EnsureInitialized(Device.System.ContentManager);
 
             return FontData[FontType].Offset + 8;
         }
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 1b336647fa..d967c8969b 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -1,5 +1,6 @@
 using LibHac;
 using Ryujinx.Common.Logging;
+using Ryujinx.HLE.FileSystem.Content;
 using Ryujinx.HLE.HOS.Font;
 using Ryujinx.HLE.HOS.Kernel;
 using Ryujinx.HLE.HOS.SystemState;
@@ -42,6 +43,8 @@ namespace Ryujinx.HLE.HOS
 
         internal SharedFontManager Font { get; private set; }
 
+        internal ContentManager ContentManager { get; private set; }
+
         internal KEvent VsyncEvent { get; private set; }
 
         internal Keyset KeySet { get; private set; }
@@ -90,6 +93,8 @@ namespace Ryujinx.HLE.HOS
             VsyncEvent = new KEvent(this);
 
             LoadKeySet();
+
+            ContentManager = new ContentManager(Device);
         }
 
         public void LoadCart(string ExeFsDir, string RomFsFile = null)
@@ -156,6 +161,8 @@ namespace Ryujinx.HLE.HOS
             LoadNso("subsdk*");
             LoadNso("sdk");
 
+            ContentManager.LoadEntries();
+
             MainProcess.Run();
         }
 
@@ -174,6 +181,8 @@ namespace Ryujinx.HLE.HOS
                 return;
             }
 
+            ContentManager.LoadEntries();
+
             LoadNca(MainNca, ControlNca);
         }
 
@@ -412,6 +421,8 @@ namespace Ryujinx.HLE.HOS
             LoadNso("subsdk");
             LoadNso("sdk");
 
+            ContentManager.LoadEntries();
+
             MainProcess.Run();
         }
 
@@ -419,13 +430,13 @@ namespace Ryujinx.HLE.HOS
         {
             bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
 
-            string Name = Path.GetFileNameWithoutExtension(FilePath);
+            string Name           = Path.GetFileNameWithoutExtension(FilePath);
             string SwitchFilePath = Device.FileSystem.SystemPathToSwitchPath(FilePath);
 
             if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
             {
                 string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
-                string TempPath = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
+                string TempPath   = Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
 
                 string SwitchDir = Path.GetDirectoryName(TempPath);
 
@@ -449,6 +460,9 @@ namespace Ryujinx.HLE.HOS
             }
 
             MainProcess.SetEmptyArgs();
+
+            ContentManager.LoadEntries();
+
             MainProcess.Run(IsNro);
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs b/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs
new file mode 100644
index 0000000000..f11c78ccaa
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Es/IETicketService.cs
@@ -0,0 +1,23 @@
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Es
+{
+    class IETicketService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private bool IsInitialized;
+
+        public IETicketService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+
+            };
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs
new file mode 100644
index 0000000000..74ebddc215
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntry.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.FspSrv
+{
+    public struct DirectoryEntry
+    {
+        public string Path { get; private set; }
+        public long   Size { get; private set; }
+
+        public DirectoryEntryType EntryType { get; set; }
+
+        public DirectoryEntry(string Path, DirectoryEntryType DirectoryEntryType, long Size = 0)
+        {
+            this.Path = Path;
+            EntryType = DirectoryEntryType;
+            this.Size = Size;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs
new file mode 100644
index 0000000000..da075a5fcd
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/DirectoryEntryType.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.FspSrv
+{
+    public enum DirectoryEntryType
+    {
+        Directory,
+        File
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs
new file mode 100644
index 0000000000..2fd5967341
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/FileSystemType.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Services.FspSrv
+{
+    enum FileSystemType : int
+    {
+        Logo               = 2,
+        ContentControl     = 3,
+        ContentManual      = 4,
+        ContentMeta        = 5,
+        ContentData        = 6,
+        ApplicationPackage = 7
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs b/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs
index 39eadcec62..16ef03be30 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/FsErr.cs
@@ -5,5 +5,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
         public const int PathDoesNotExist  = 1;
         public const int PathAlreadyExists = 2;
         public const int PathAlreadyInUse  = 7;
+        public const int PartitionNotFound = 1001;
+        public const int InvalidInput      = 6001;
     }
 }
\ 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 d6ae084f89..c964eecbac 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IDirectory.cs
@@ -1,3 +1,4 @@
+using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Ipc;
 using System;
 using System.Collections.Generic;
@@ -14,15 +15,17 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
 
         public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private List<string> DirectoryEntries;
+        private List<DirectoryEntry> DirectoryEntries;
 
         private int CurrentItemIndex;
 
         public event EventHandler<EventArgs> Disposed;
 
-        public string HostPath { get; private set; }
+        public string DirectoryPath { get; private set; }
 
-        public IDirectory(string HostPath, int Flags)
+        private IFileSystemProvider Provider;
+
+        public IDirectory(string DirectoryPath, int Flags, IFileSystemProvider Provider)
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -30,23 +33,25 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 { 1, GetEntryCount }
             };
 
-            this.HostPath = HostPath;
+            this.Provider      = Provider;
+            this.DirectoryPath = DirectoryPath;
 
-            DirectoryEntries = new List<string>();
+            DirectoryEntries = new List<DirectoryEntry>();
 
             if ((Flags & 1) != 0)
             {
-                DirectoryEntries.AddRange(Directory.GetDirectories(HostPath));
+                DirectoryEntries.AddRange(Provider.GetDirectories(DirectoryPath));
             }
 
             if ((Flags & 2) != 0)
             {
-                DirectoryEntries.AddRange(Directory.GetFiles(HostPath));
+                DirectoryEntries.AddRange(Provider.GetFiles(DirectoryPath));
             }
 
             CurrentItemIndex = 0;
         }
 
+        // Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
         public long Read(ServiceCtx Context)
         {
             long BufferPosition = Context.Request.ReceiveBuff[0].Position;
@@ -68,31 +73,23 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
-        private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath)
+        private void WriteDirectoryEntry(ServiceCtx Context, long Position, DirectoryEntry Entry)
         {
             for (int Offset = 0; Offset < 0x300; Offset += 8)
             {
                 Context.Memory.WriteInt64(Position + Offset, 0);
             }
 
-            byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath));
+            byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(Entry.Path));
 
             Context.Memory.WriteBytes(Position, NameBuffer);
 
-            int  Type = 0;
-            long Size = 0;
-
-            if (File.Exists(FullPath))
-            {
-                Type = 1;
-                Size = new FileInfo(FullPath).Length;
-            }
-
             Context.Memory.WriteInt32(Position + 0x300, 0); //Padding?
-            Context.Memory.WriteInt32(Position + 0x304, Type);
-            Context.Memory.WriteInt64(Position + 0x308, Size);
+            Context.Memory.WriteInt32(Position + 0x304, (byte)Entry.EntryType);
+            Context.Memory.WriteInt64(Position + 0x308, Entry.Size);
         }
 
+        // GetEntryCount() -> u64
         public long GetEntryCount(ServiceCtx Context)
         {
             Context.ResponseData.Write((long)DirectoryEntries.Count);
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs
index 194d942090..9bf152c4cd 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFile.cs
@@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             this.HostPath   = HostPath;
         }
 
+        // Read(u32, 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;
@@ -53,6 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // Write(u32, u64 offset, u64 size, buffer<u8, 0x45, 0>)
         public long Write(ServiceCtx Context)
         {
             long Position = Context.Request.SendBuff[0].Position;
@@ -69,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // Flush()
         public long Flush(ServiceCtx Context)
         {
             BaseStream.Flush();
@@ -76,6 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // SetSize(u64 size)
         public long SetSize(ServiceCtx Context)
         {
             long Size = Context.RequestData.ReadInt64();
@@ -85,6 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // GetSize() -> u64 fileSize
         public long GetSize(ServiceCtx Context)
         {
             Context.ResponseData.Write(BaseStream.Length);
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs
index bd249e5084..edcdfa58c0 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs
@@ -1,10 +1,11 @@
+using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Ipc;
 using System;
 using System.Collections.Generic;
 using System.IO;
-using System.Text;
 
 using static Ryujinx.HLE.HOS.ErrorCode;
+using static Ryujinx.HLE.Utilities.StringUtils;
 
 namespace Ryujinx.HLE.HOS.Services.FspSrv
 {
@@ -18,7 +19,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
 
         private string Path;
 
-        public IFileSystem(string Path)
+        private IFileSystemProvider Provider;
+
+        public IFileSystem(string Path, IFileSystemProvider Provider)
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -41,9 +44,11 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
 
             OpenPaths = new HashSet<string>();
 
-            this.Path = Path;
+            this.Path     = Path;
+            this.Provider = Provider;
         }
 
+        // CreateFile(u32 mode, u64 size, buffer<bytes<0x301>, 0x19, 0x301> path)
         public long CreateFile(ServiceCtx Context)
         {
             string Name = ReadUtf8String(Context);
@@ -51,14 +56,14 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             long Mode = Context.RequestData.ReadInt64();
             int  Size = Context.RequestData.ReadInt32();
 
-            string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string FileName = Provider.GetFullPath(Name);
 
             if (FileName == null)
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
 
-            if (File.Exists(FileName))
+            if (Provider.FileExists(FileName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
             }
@@ -68,21 +73,17 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            using (FileStream NewFile = File.Create(FileName))
-            {
-                NewFile.SetLength(Size);
-            }
-
-            return 0;
+            return Provider.CreateFile(FileName, Size);
         }
 
+        // DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path)
         public long DeleteFile(ServiceCtx Context)
         {
             string Name = ReadUtf8String(Context);
 
-            string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string FileName = Provider.GetFullPath(Name);
 
-            if (!File.Exists(FileName))
+            if (!Provider.FileExists(FileName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
@@ -92,23 +93,22 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            File.Delete(FileName);
-
-            return 0;
+            return Provider.DeleteFile(FileName);
         }
 
+        // CreateDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
         public long CreateDirectory(ServiceCtx Context)
         {
             string Name = ReadUtf8String(Context);
 
-            string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string DirName = Provider.GetFullPath(Name);
 
             if (DirName == null)
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
 
-            if (Directory.Exists(DirName))
+            if (Provider.DirectoryExists(DirName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
             }
@@ -118,26 +118,28 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            Directory.CreateDirectory(DirName);
+            Provider.CreateDirectory(DirName);
 
             return 0;
         }
 
+        // DeleteDirectory(buffer<bytes<0x301>, 0x19, 0x301> path)
         public long DeleteDirectory(ServiceCtx Context)
         {
             return DeleteDirectory(Context, false);
         }
 
+        // 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 = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string DirName = Provider.GetFullPath(Name);
 
             if (!Directory.Exists(DirName))
             {
@@ -149,25 +151,26 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            Directory.Delete(DirName, Recursive);
+            Provider.DeleteDirectory(DirName, Recursive);
 
             return 0;
         }
 
+        // RenameFile(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
         public long RenameFile(ServiceCtx Context)
         {
             string OldName = ReadUtf8String(Context, 0);
             string NewName = ReadUtf8String(Context, 1);
 
-            string OldFileName = Context.Device.FileSystem.GetFullPath(Path, OldName);
-            string NewFileName = Context.Device.FileSystem.GetFullPath(Path, NewName);
+            string OldFileName = Provider.GetFullPath(OldName);
+            string NewFileName = Provider.GetFullPath(NewName);
 
-            if (!File.Exists(OldFileName))
+            if (Provider.FileExists(OldFileName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
 
-            if (File.Exists(NewFileName))
+            if (Provider.FileExists(NewFileName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
             }
@@ -177,25 +180,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            File.Move(OldFileName, NewFileName);
-
-            return 0;
+            return Provider.RenameFile(OldFileName, NewFileName);
         }
 
+        // RenameDirectory(buffer<bytes<0x301>, 0x19, 0x301> oldPath, buffer<bytes<0x301>, 0x19, 0x301> newPath)
         public long RenameDirectory(ServiceCtx Context)
         {
             string OldName = ReadUtf8String(Context, 0);
             string NewName = ReadUtf8String(Context, 1);
 
-            string OldDirName = Context.Device.FileSystem.GetFullPath(Path, OldName);
-            string NewDirName = Context.Device.FileSystem.GetFullPath(Path, NewName);
+            string OldDirName = Provider.GetFullPath(OldName);
+            string NewDirName = Provider.GetFullPath(NewName);
 
-            if (!Directory.Exists(OldDirName))
+            if (!Provider.DirectoryExists(OldDirName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
 
-            if (Directory.Exists(NewDirName))
+            if (!Provider.DirectoryExists(NewDirName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists);
             }
@@ -205,22 +207,21 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            Directory.Move(OldDirName, NewDirName);
-
-            return 0;
+            return Provider.RenameDirectory(OldDirName, NewDirName);
         }
 
+        // GetEntryType(buffer<bytes<0x301>, 0x19, 0x301> path) -> nn::fssrv::sf::DirectoryEntryType
         public long GetEntryType(ServiceCtx Context)
         {
             string Name = ReadUtf8String(Context);
 
-            string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string FileName = Provider.GetFullPath(Name);
 
-            if (File.Exists(FileName))
+            if (Provider.FileExists(FileName))
             {
                 Context.ResponseData.Write(1);
             }
-            else if (Directory.Exists(FileName))
+            else if (Provider.DirectoryExists(FileName))
             {
                 Context.ResponseData.Write(0);
             }
@@ -234,15 +235,16 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // 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();
 
             string Name = ReadUtf8String(Context);
 
-            string FileName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string FileName = Provider.GetFullPath(Name);
 
-            if (!File.Exists(FileName))
+            if (!Provider.FileExists(FileName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
@@ -252,79 +254,36 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            FileStream Stream = new FileStream(FileName, FileMode.Open);
 
-            IFile FileInterface = new IFile(Stream, FileName);
+            long Error = Provider.OpenFile(FileName, out IFile FileInterface);
 
-            FileInterface.Disposed += RemoveFileInUse;
-
-            lock (OpenPaths)
+            if (Error == 0)
             {
-                OpenPaths.Add(FileName);
+                FileInterface.Disposed += RemoveFileInUse;
+
+                lock (OpenPaths)
+                {
+                    OpenPaths.Add(FileName);
+                }
+
+                MakeObject(Context, FileInterface);
+
+                return 0;
             }
 
-            MakeObject(Context, FileInterface);
-
-            return 0;
+            return Error;
         }
 
+        // 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();
 
             string Name = ReadUtf8String(Context);
 
-            string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
+            string DirName = Provider.GetFullPath(Name);
 
-            if (!Directory.Exists(DirName))
-            {
-                return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
-            }
-
-            IDirectory DirInterface = new IDirectory(DirName, FilterFlags);
-
-            DirInterface.Disposed += RemoveDirectoryInUse;
-
-            lock (OpenPaths)
-            {
-                OpenPaths.Add(DirName);
-            }
-
-            MakeObject(Context, DirInterface);
-
-            return 0;
-        }
-
-        public long Commit(ServiceCtx Context)
-        {
-            return 0;
-        }
-
-        public long GetFreeSpaceSize(ServiceCtx Context)
-        {
-            string Name = ReadUtf8String(Context);
-
-            Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().AvailableFreeSpace);
-
-            return 0;
-        }
-
-        public long GetTotalSpaceSize(ServiceCtx Context)
-        {
-            string Name = ReadUtf8String(Context);
-
-            Context.ResponseData.Write(Context.Device.FileSystem.GetDrive().TotalSize);
-
-            return 0;
-        }
-
-        public long CleanDirectoryRecursively(ServiceCtx Context)
-        {
-            string Name = ReadUtf8String(Context);
-
-            string DirName = Context.Device.FileSystem.GetFullPath(Path, Name);
-
-            if (!Directory.Exists(DirName))
+            if (!Provider.DirectoryExists(DirName))
             {
                 return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
             }
@@ -334,15 +293,75 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
             }
 
-            foreach (string Entry in Directory.EnumerateFileSystemEntries(DirName))
+            long Error = Provider.OpenDirectory(DirName, FilterFlags, out IDirectory DirInterface);
+
+            if (Error == 0)
             {
-                if (Directory.Exists(Entry))
+                DirInterface.Disposed += RemoveDirectoryInUse;
+
+                lock (OpenPaths)
                 {
-                    Directory.Delete(Entry, true);
+                    OpenPaths.Add(DirName);
                 }
-                else if (File.Exists(Entry))
+
+                MakeObject(Context, DirInterface);
+            }
+
+            return Error;
+        }
+
+        // Commit()
+        public long Commit(ServiceCtx Context)
+        {
+            return 0;
+        }
+
+        // GetFreeSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalFreeSpace
+        public long GetFreeSpaceSize(ServiceCtx Context)
+        {
+            string Name = ReadUtf8String(Context);
+
+            Context.ResponseData.Write(Provider.GetFreeSpace(Context));
+
+            return 0;
+        }
+
+        // GetTotalSpaceSize(buffer<bytes<0x301>, 0x19, 0x301> path) -> u64 totalSize
+        public long GetTotalSpaceSize(ServiceCtx Context)
+        {
+            string Name = ReadUtf8String(Context);
+
+            Context.ResponseData.Write(Provider.GetFreeSpace(Context));
+
+            return 0;
+        }
+
+        // CleanDirectoryRecursively(buffer<bytes<0x301>, 0x19, 0x301> path)
+        public long CleanDirectoryRecursively(ServiceCtx Context)
+        {
+            string Name = ReadUtf8String(Context);
+
+            string DirName = Provider.GetFullPath(Name);
+
+            if (!Provider.DirectoryExists(DirName))
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
+            }
+
+            if (IsPathAlreadyInUse(DirName))
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse);
+            }
+
+            foreach (DirectoryEntry Entry in Provider.GetEntries(DirName))
+            {
+                if (Provider.DirectoryExists(Entry.Path))
                 {
-                    File.Delete(Entry);
+                    Provider.DeleteDirectory(Entry.Path, true);
+                }
+                else if (Provider.FileExists(Entry.Path))
+                {
+                   Provider.DeleteFile(Entry.Path);
                 }
             }
 
@@ -377,30 +396,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             {
                 DirInterface.Disposed -= RemoveDirectoryInUse;
 
-                OpenPaths.Remove(DirInterface.HostPath);
-            }
-        }
-
-        private string ReadUtf8String(ServiceCtx Context, int Index = 0)
-        {
-            long Position = Context.Request.PtrBuff[Index].Position;
-            long Size     = Context.Request.PtrBuff[Index].Size;
-
-            using (MemoryStream MS = new MemoryStream())
-            {
-                while (Size-- > 0)
-                {
-                    byte Value = Context.Memory.ReadByte(Position++);
-
-                    if (Value == 0)
-                    {
-                        break;
-                    }
-
-                    MS.WriteByte(Value);
-                }
-
-                return Encoding.UTF8.GetString(MS.ToArray());
+                OpenPaths.Remove(DirInterface.DirectoryPath);
             }
         }
     }
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
index daf5e0b269..0fc1eb80ed 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs
@@ -1,7 +1,14 @@
+using LibHac;
 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;
+using static Ryujinx.HLE.Utilities.StringUtils;
 
 namespace Ryujinx.HLE.HOS.Services.FspSrv
 {
@@ -15,28 +22,104 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 1,    SetCurrentProcess                        },
+                { 1,    Initialize                               },
+                { 8,    OpenFileSystemWithId                     },
+                { 11,   OpenBisFileSystem                        },
                 { 18,   OpenSdCardFileSystem                     },
                 { 51,   OpenSaveDataFileSystem                   },
                 { 52,   OpenSaveDataFileSystemBySystemSaveDataId },
                 { 200,  OpenDataStorageByCurrentProcess          },
+                { 202,  OpenDataStorageByDataId                  },
                 { 203,  OpenPatchDataStorageByCurrentProcess     },
                 { 1005, GetGlobalAccessLogMode                   }
             };
         }
 
-        public long SetCurrentProcess(ServiceCtx Context)
+        // Initialize(u64, pid)
+        public long Initialize(ServiceCtx Context)
         {
             return 0;
         }
 
+        // OpenFileSystemWithId(nn::fssrv::sf::FileSystemType filesystem_type, nn::ApplicationId tid, buffer<bytes<0x301>, 0x19, 0x301> path) 
+        // -> object<nn::fssrv::sf::IFileSystem> contentFs
+        public long OpenFileSystemWithId(ServiceCtx Context)
+        {
+            FileSystemType FileSystemType = (FileSystemType)Context.RequestData.ReadInt32();
+            long           TitleId        = Context.RequestData.ReadInt64();
+            string         SwitchPath     = ReadUtf8String(Context);
+            string         FullPath       = Context.Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
+
+            if (!File.Exists(FullPath))
+            {
+                if (FullPath.Contains("."))
+                {
+                    return OpenFileSystemFromInternalFile(Context, FullPath);
+                }
+
+                return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
+            }
+
+            FileStream FileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read);
+            string     Extension  = Path.GetExtension(FullPath);
+
+            if (Extension == ".nca")
+            {
+                return OpenNcaFs(Context, FullPath, FileStream);
+            }
+            else if (Extension == ".nsp")
+            {
+                return OpenNsp(Context, FullPath);
+            }
+
+            return MakeError(ErrorModule.Fs, FsErr.InvalidInput);
+        }
+
+        // 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;
+
+            switch (BisPartitionId)
+            {
+                case 29:
+                    BisPartitonPath = SafeNandPath;
+                    break;
+                case 30:
+                case 31:
+                    BisPartitonPath = SystemNandPath;
+                    break;
+                case 32:
+                    BisPartitonPath = UserNandPath;
+                    break;
+                default:
+                    return MakeError(ErrorModule.Fs, FsErr.InvalidInput);
+            }
+
+            string FullPath = Context.Device.FileSystem.GetFullPartitionPath(BisPartitonPath);
+
+            FileSystemProvider FileSystemProvider = new FileSystemProvider(FullPath, Context.Device.FileSystem.GetBasePath());
+
+            MakeObject(Context, new IFileSystem(FullPath, FileSystemProvider));
+
+            return 0;
+        }
+
+        // OpenSdCardFileSystem() -> object<nn::fssrv::sf::IFileSystem>
         public long OpenSdCardFileSystem(ServiceCtx Context)
         {
-            MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetSdCardPath()));
+            string SdCardPath = Context.Device.FileSystem.GetSdCardPath();
+
+            FileSystemProvider FileSystemProvider = new FileSystemProvider(SdCardPath, Context.Device.FileSystem.GetBasePath());
+
+            MakeObject(Context, new IFileSystem(SdCardPath, FileSystemProvider));
 
             return 0;
         }
 
+        // OpenSaveDataFileSystem(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> saveDataFs
         public long OpenSaveDataFileSystem(ServiceCtx Context)
         {
             LoadSaveDataFileSystem(Context);
@@ -44,6 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // OpenSaveDataFileSystemBySystemSaveDataId(u8 save_data_space_id, nn::fssrv::sf::SaveStruct saveStruct) -> object<nn::fssrv::sf::IFileSystem> systemSaveDataFs
         public long OpenSaveDataFileSystemBySystemSaveDataId(ServiceCtx Context)
         {
             LoadSaveDataFileSystem(Context);
@@ -51,6 +135,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // OpenDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage> dataStorage
         public long OpenDataStorageByCurrentProcess(ServiceCtx Context)
         {
             MakeObject(Context, new IStorage(Context.Device.FileSystem.RomFs));
@@ -58,6 +143,63 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // OpenDataStorageByDataId(u8 storageId, nn::ApplicationId tid) -> object<nn::fssrv::sf::IStorage> dataStorage
+        public long OpenDataStorageByDataId(ServiceCtx Context)
+        {
+            StorageId StorageId = (StorageId)Context.RequestData.ReadByte();
+            byte[]    Padding   = Context.RequestData.ReadBytes(7);
+            long      TitleId   = Context.RequestData.ReadInt64();
+
+            StorageId InstalledStorage =
+                Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.Data, StorageId);
+
+            if (InstalledStorage == StorageId.None)
+            {
+                InstalledStorage =
+                    Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.AocData, StorageId);
+            }
+
+            if (InstalledStorage != StorageId.None)
+            {
+                string ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData);
+
+                if (string.IsNullOrWhiteSpace(ContentPath))
+                {
+                    ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData);
+                }
+
+                string InstallPath = Context.Device.FileSystem.SwitchPathToSystemPath(ContentPath);
+
+                if (!string.IsNullOrWhiteSpace(InstallPath))
+                {
+                    string NcaPath = InstallPath;
+
+                    if (File.Exists(NcaPath))
+                    {
+                        FileStream NcaStream    = new FileStream(NcaPath, FileMode.Open, FileAccess.Read);
+                        Nca        Nca          = new Nca(Context.Device.System.KeySet, NcaStream, false);
+                        NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
+                        Stream     RomfsStream  = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
+
+                        MakeObject(Context, new IStorage(RomfsStream));
+
+                        return 0;
+                    }
+                    else
+                    { 
+                        throw new FileNotFoundException($"No Nca found in Path `{NcaPath}`.");
+                    }
+                }
+                else
+                { 
+                    throw new DirectoryNotFoundException($"Path for title id {TitleId:x16} on Storage {StorageId} was not found in Path {InstallPath}.");
+                }
+            }
+
+            throw new FileNotFoundException($"System archive with titleid {TitleId:x16} was not found on Storage {StorageId}. Found in {InstalledStorage}.");
+        }
+
+        // OpenPatchDataStorageByCurrentProcess() -> object<nn::fssrv::sf::IStorage>
         public long OpenPatchDataStorageByCurrentProcess(ServiceCtx Context)
         {
             MakeObject(Context, new IStorage(Context.Device.FileSystem.RomFs));
@@ -65,6 +207,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             return 0;
         }
 
+        // GetGlobalAccessLogMode() -> u32 logMode
         public long GetGlobalAccessLogMode(ServiceCtx Context)
         {
             Context.ResponseData.Write(0);
@@ -82,13 +225,102 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
                 Context.RequestData.ReadInt64(), 
                 Context.RequestData.ReadInt64());
 
-            long SaveId = 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());
 
-            SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte();
+            MakeObject(Context, new IFileSystem(SavePath, FileSystemProvider));
+        }
 
-            SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId);
+        private long OpenNsp(ServiceCtx Context, string PfsPath)
+        {
+            FileStream   PfsFile    = new FileStream(PfsPath, FileMode.Open, FileAccess.Read);
+            Pfs          Nsp        = new Pfs(PfsFile);
+            PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
 
-            MakeObject(Context, new IFileSystem(Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context)));
+            if (TicketFile != null)
+            {
+                Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile));
+
+                Context.Device.System.KeySet.TitleKeys[Ticket.RightsId] =
+                    Ticket.GetTitleKey(Context.Device.System.KeySet);
+            }
+
+            IFileSystem NspFileSystem = new IFileSystem(PfsPath, new PFsProvider(Nsp));
+
+            MakeObject(Context, NspFileSystem);
+
+            return 0;
+        }
+
+        private long OpenNcaFs(ServiceCtx Context,string NcaPath, Stream NcaStream)
+        {
+            Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false);
+
+            NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
+            NcaSection PfsSection   = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Pfs0);
+
+            if (RomfsSection != null)
+            {
+                Stream      RomfsStream   = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
+                IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new RomFsProvider(RomfsStream));
+
+                MakeObject(Context, NcaFileSystem);
+            }
+            else if(PfsSection !=null)
+            {
+                Stream      PfsStream     = Nca.OpenSection(PfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
+                Pfs         Pfs           = new Pfs(PfsStream);
+                IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new PFsProvider(Pfs));
+
+                MakeObject(Context, NcaFileSystem);
+            }
+            else
+            {
+                return MakeError(ErrorModule.Fs, FsErr.PartitionNotFound);
+            }
+
+            return 0;
+        }
+
+        private long OpenFileSystemFromInternalFile(ServiceCtx Context, string FullPath)
+        {
+            DirectoryInfo ArchivePath = new DirectoryInfo(FullPath).Parent;
+
+            while (string.IsNullOrWhiteSpace(ArchivePath.Extension))
+            {
+                ArchivePath = ArchivePath.Parent;
+            }
+
+            if (ArchivePath.Extension == ".nsp" && File.Exists(ArchivePath.FullName))
+            {
+                FileStream PfsFile = new FileStream(
+                    ArchivePath.FullName.TrimEnd(Path.DirectorySeparatorChar),
+                    FileMode.Open,
+                    FileAccess.Read);
+
+                Pfs          Nsp        = new Pfs(PfsFile);
+                PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
+
+                if (TicketFile != null)
+                {
+                    Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile));
+
+                    Context.Device.System.KeySet.TitleKeys[Ticket.RightsId] =
+                        Ticket.GetTitleKey(Context.Device.System.KeySet);
+                }
+
+                string Filename = FullPath.Replace(ArchivePath.FullName, string.Empty).TrimStart('\\');
+
+                if (Nsp.FileExists(Filename))
+                {
+                    return OpenNcaFs(Context, FullPath, Nsp.OpenFile(Filename));
+                }
+            }
+
+            return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs
index c3f1f2c4c4..6a78cdfe0e 100644
--- a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs
+++ b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs
@@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
             this.BaseStream = BaseStream;
         }
 
+        // Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
         public long Read(ServiceCtx Context)
         {
             long Offset = Context.RequestData.ReadInt64();
diff --git a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs
new file mode 100644
index 0000000000..dc057bc1ec
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolver.cs
@@ -0,0 +1,265 @@
+using LibHac;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.FileSystem.Content;
+using Ryujinx.HLE.HOS.Ipc;
+using System.Collections.Generic;
+using System.Text;
+
+using static Ryujinx.HLE.HOS.ErrorCode;
+using static Ryujinx.HLE.Utilities.StringUtils;
+
+namespace Ryujinx.HLE.HOS.Services.Lr
+{
+    class ILocationResolver : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private StorageId StorageId;
+
+        public ILocationResolver(StorageId StorageId)
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0,  ResolveProgramPath                      },
+                { 1,  RedirectProgramPath                     },
+                { 2,  ResolveApplicationControlPath           },
+                { 3,  ResolveApplicationHtmlDocumentPath      },
+                { 4,  ResolveDataPath                         },
+                { 5,  RedirectApplicationControlPath          },
+                { 6,  RedirectApplicationHtmlDocumentPath     },
+                { 7,  ResolveApplicationLegalInformationPath  },
+                { 8,  RedirectApplicationLegalInformationPath },
+                { 9,  Refresh                                 },
+                { 10, SetProgramNcaPath2                      },
+                { 11, ClearLocationResolver2                  },
+                { 12, DeleteProgramNcaPath                    },
+                { 13, DeleteControlNcaPath                    },
+                { 14, DeleteDocHtmlNcaPath                    },
+                { 15, DeleteInfoHtmlNcaPath                   }
+            };
+
+            this.StorageId = StorageId;
+        }
+
+        // DeleteInfoHtmlNcaPath()
+        public long DeleteInfoHtmlNcaPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            DeleteContentPath(Context, TitleId, ContentType.Manual);
+
+            return 0;
+        }
+
+        // DeleteDocHtmlNcaPath()
+        public long DeleteDocHtmlNcaPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            DeleteContentPath(Context, TitleId, ContentType.Manual);
+
+            return 0;
+        }
+
+        // DeleteControlNcaPath()
+        public long DeleteControlNcaPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            DeleteContentPath(Context, TitleId, ContentType.Control);
+
+            return 0;
+        }
+
+        // DeleteProgramNcaPath()
+        public long DeleteProgramNcaPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            DeleteContentPath(Context, TitleId, ContentType.Program);
+
+            return 0;
+        }
+
+        // ClearLocationResolver2()
+        public long ClearLocationResolver2(ServiceCtx Context)
+        {
+            Context.Device.System.ContentManager.RefreshEntries(StorageId, 1);
+
+            return 0;
+        }
+
+        // SetProgramNcaPath2()
+        public long SetProgramNcaPath2(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            RedirectPath(Context, TitleId, 1, ContentType.Program);
+
+            return 0;
+        }
+
+        // RedirectApplicationControlPath()
+        public long RedirectApplicationControlPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            RedirectPath(Context, TitleId, 1, ContentType.Control);
+
+            return 0;
+        }
+
+        // RedirectApplicationHtmlDocumentPath()
+        public long RedirectApplicationHtmlDocumentPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            RedirectPath(Context, TitleId, 1, ContentType.Manual);
+
+            return 0;
+        }
+
+        // RedirectApplicationLegalInformationPath()
+        public long RedirectApplicationLegalInformationPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            RedirectPath(Context, TitleId, 1, ContentType.Manual);
+
+            return 0;
+        }
+
+        // ResolveDataPath()
+        public long ResolveDataPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            if (ResolvePath(Context, TitleId, ContentType.Data) || ResolvePath(Context, TitleId, ContentType.AocData))
+            {
+                return 0;
+            }
+            else
+            {
+                return MakeError(ErrorModule.Lr, LrErr.AccessDenied);
+            }
+        }
+
+        // ResolveApplicationHtmlDocumentPath()
+        public long ResolveApplicationHtmlDocumentPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            if (ResolvePath(Context, TitleId, ContentType.Manual))
+            {
+                return 0;
+            }
+            else
+            {
+                return MakeError(ErrorModule.Lr, LrErr.AccessDenied);
+            }
+        }
+
+        // ResolveApplicationLegalInformationPath()
+        public long ResolveApplicationLegalInformationPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            if (ResolvePath(Context, TitleId, ContentType.Manual))
+            {
+                return 0;
+            }
+            else
+            {
+                return MakeError(ErrorModule.Lr, LrErr.AccessDenied);
+            }
+        }
+
+        // ResolveApplicationControlPath()
+        public long ResolveApplicationControlPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            if (ResolvePath(Context, TitleId, ContentType.Control))
+            {
+                return 0;
+            }
+            else
+            {
+                return MakeError(ErrorModule.Lr, LrErr.AccessDenied);
+            }
+        }
+
+        // RedirectProgramPath()
+        public long RedirectProgramPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            RedirectPath(Context, TitleId, 0, ContentType.Program);
+
+            return 0;
+        }
+
+        // Refresh()
+        public long Refresh(ServiceCtx Context)
+        {
+            Context.Device.System.ContentManager.RefreshEntries(StorageId, 1);
+
+            return 0;
+        }
+
+        // ResolveProgramPath()
+        public long ResolveProgramPath(ServiceCtx Context)
+        {
+            long TitleId = Context.RequestData.ReadInt64();
+
+            if (ResolvePath(Context, TitleId, ContentType.Program))
+            {
+                return 0;
+            }
+            else
+            {
+                return MakeError(ErrorModule.Lr, LrErr.ProgramLocationEntryNotFound);
+            }
+        }
+
+        private void RedirectPath(ServiceCtx Context, long TitleId, int Flag, ContentType ContentType)
+        {
+            string        ContentPath = ReadUtf8String(Context);
+            LocationEntry NewLocation = new LocationEntry(ContentPath, Flag, TitleId, ContentType);
+
+            Context.Device.System.ContentManager.RedirectLocation(NewLocation, StorageId);
+        }
+
+        private bool ResolvePath(ServiceCtx Context, long TitleId,ContentType ContentType)
+        {
+            ContentManager ContentManager = Context.Device.System.ContentManager;
+            string         ContentPath    = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Program);
+
+            if (!string.IsNullOrWhiteSpace(ContentPath))
+            {
+                long Position = Context.Request.RecvListBuff[0].Position;
+                long Size     = Context.Request.RecvListBuff[0].Size;
+
+                byte[] ContentPathBuffer = Encoding.UTF8.GetBytes(ContentPath);
+
+                Context.Memory.WriteBytes(Position, ContentPathBuffer);
+            }
+            else
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private void DeleteContentPath(ServiceCtx Context, long TitleId, ContentType ContentType)
+        {
+            ContentManager ContentManager = Context.Device.System.ContentManager;
+            string         ContentPath    = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Manual);
+
+            ContentManager.ClearEntry(TitleId, ContentType.Manual, StorageId);
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs
new file mode 100644
index 0000000000..77710f7602
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Lr/ILocationResolverManager.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.FileSystem;
+
+namespace Ryujinx.HLE.HOS.Services.Lr
+{
+    class ILocationResolverManager : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ILocationResolverManager()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, OpenLocationResolver },
+            };
+        }
+
+        // OpenLocationResolver()
+        private long OpenLocationResolver(ServiceCtx Context)
+        {
+            StorageId StorageId = (StorageId)Context.RequestData.ReadByte();
+
+            MakeObject(Context, new ILocationResolver(StorageId));
+
+            return 0;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs b/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs
new file mode 100644
index 0000000000..5c87b73be6
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Lr/LrErr.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Lr
+{
+    class LrErr
+    {
+        public const int ProgramLocationEntryNotFound = 2;
+        public const int AccessDenied                 = 5;
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs b/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs
new file mode 100644
index 0000000000..29792a1baf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ncm/IContentManager.cs
@@ -0,0 +1,20 @@
+using Ryujinx.HLE.HOS.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Ncm
+{
+    class IContentManager : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IContentManager()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+
+            };
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs b/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs
new file mode 100644
index 0000000000..a19fe079d5
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ncm/IContentStorage.cs
@@ -0,0 +1,20 @@
+using Ryujinx.HLE.HOS.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Ncm
+{
+    class IContentStorage : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IContentStorage()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+
+            };
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
new file mode 100644
index 0000000000..ee438d993a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
@@ -0,0 +1,23 @@
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+    class IApplicationManagerInterface : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private bool IsInitialized;
+
+        public IApplicationManagerInterface()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                
+            };
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs
index d73adc0efd..0495a388a3 100644
--- a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs
@@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Pl
 
         public long GetSharedMemoryNativeHandle(ServiceCtx Context)
         {
-            Context.Device.System.Font.EnsureInitialized();
+            Context.Device.System.Font.EnsureInitialized(Context.Device.System.ContentManager);
 
             if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.FontSharedMem, out int Handle) != KernelResult.Success)
             {
diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
index d91583d62b..2be1db7012 100644
--- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
+++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs
@@ -4,12 +4,14 @@ using Ryujinx.HLE.HOS.Services.Apm;
 using Ryujinx.HLE.HOS.Services.Aud;
 using Ryujinx.HLE.HOS.Services.Bsd;
 using Ryujinx.HLE.HOS.Services.Caps;
+using Ryujinx.HLE.HOS.Services.Es;
 using Ryujinx.HLE.HOS.Services.FspSrv;
 using Ryujinx.HLE.HOS.Services.Hid;
 using Ryujinx.HLE.HOS.Services.Irs;
 using Ryujinx.HLE.HOS.Services.Ldr;
 using Ryujinx.HLE.HOS.Services.Lm;
 using Ryujinx.HLE.HOS.Services.Mm;
+using Ryujinx.HLE.HOS.Services.Ncm;
 using Ryujinx.HLE.HOS.Services.Nfp;
 using Ryujinx.HLE.HOS.Services.Ns;
 using Ryujinx.HLE.HOS.Services.Nv;
@@ -87,6 +89,9 @@ namespace Ryujinx.HLE.HOS.Services
                 case "csrng":
                     return new IRandomInterface();
 
+                case "es":
+                    return new IETicketService();
+
                 case "friend:a":
                     return new Friend.IServiceCreator();
 
@@ -114,12 +119,18 @@ namespace Ryujinx.HLE.HOS.Services
                 case "mm:u":
                     return new IRequest();
 
+                case "ncm":
+                    return new IContentManager();
+
                 case "nfp:user":
                     return new IUserManager();
 
                 case "nifm:u":
                     return new Nifm.IStaticService();
 
+                case "ns:am":
+                    return new IApplicationManagerInterface();
+
                 case "ns:ec":
                     return new IServiceGetterInterface();
 
diff --git a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs
index 070a4d5e4d..416ea1fb7b 100644
--- a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs
@@ -5,6 +5,8 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
+using LibHac;
+using Ryujinx.HLE.FileSystem;
 
 namespace Ryujinx.HLE.HOS.Services.Set
 {
@@ -18,6 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Set
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
+                { 3,  GetFirmwareVersion  },
                 { 4,  GetFirmwareVersion2  },
                 { 23, GetColorSetId        },
                 { 24, SetColorSetId        },
@@ -25,11 +28,27 @@ namespace Ryujinx.HLE.HOS.Services.Set
             };
         }
 
+        // GetFirmwareVersion() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100>
+        public static long GetFirmwareVersion(ServiceCtx Context)
+        {
+            return GetFirmwareVersion2(Context);
+        }
+
+        // GetFirmwareVersion2() -> buffer<nn::settings::system::FirmwareVersion, 0x1a, 0x100>
         public static long GetFirmwareVersion2(ServiceCtx Context)
         {
             long ReplyPos  = Context.Request.RecvListBuff[0].Position;
             long ReplySize = Context.Request.RecvListBuff[0].Size;
 
+            byte[] FirmwareData = GetFirmwareData(Context.Device);
+
+            if (FirmwareData != null)
+            {
+                Context.Memory.WriteBytes(ReplyPos, FirmwareData);
+
+                return 0;
+            }
+
             const byte MajorFWVersion = 0x03;
             const byte MinorFWVersion = 0x00;
             const byte MicroFWVersion = 0x00;
@@ -74,6 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Set
             return 0;
         }
 
+        // GetColorSetId() -> i32
         public static long GetColorSetId(ServiceCtx Context)
         {
             Context.ResponseData.Write((int)Context.Device.System.State.ThemeColor);
@@ -81,6 +101,7 @@ namespace Ryujinx.HLE.HOS.Services.Set
             return 0;
         }
 
+        // GetColorSetId() -> i32
         public static long SetColorSetId(ServiceCtx Context)
         {
             int ColorSetId = Context.RequestData.ReadInt32();
@@ -90,6 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Set
             return 0;
         }
 
+        // GetSettingsItemValue(buffer<nn::settings::SettingsName, 0x19, 0x48>, buffer<nn::settings::SettingsItemKey, 0x19, 0x48>) -> (u64, buffer<unknown, 6, 0>)
         public static long GetSettingsItemValue(ServiceCtx Context)
         {
             long ClassPos  = Context.Request.PtrBuff[0].Position;
@@ -148,5 +170,44 @@ namespace Ryujinx.HLE.HOS.Services.Set
 
             return 0;
         }
+
+        public static byte[] GetFirmwareData(Switch Device)
+        {
+            byte[] Data        = null;
+            long   TitleId     = 0x0100000000000809;
+            string ContentPath = Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId.NandSystem, ContentType.Data);
+
+            if(string.IsNullOrWhiteSpace(ContentPath))
+            {
+                return null;
+            }
+
+            string     FirmwareTitlePath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
+            FileStream FirmwareStream    = File.Open(FirmwareTitlePath, FileMode.Open, FileAccess.Read);
+            Nca        FirmwareContent   = new Nca(Device.System.KeySet, FirmwareStream, false);
+            Stream     RomFsStream       = FirmwareContent.OpenSection(0, false, Device.System.FsIntegrityCheckLevel);
+
+            if(RomFsStream == null)
+            {
+                return null;
+            }
+
+            Romfs FirmwareRomFs = new Romfs(RomFsStream);
+
+            using(MemoryStream MemoryStream = new MemoryStream())
+            {
+                using (Stream FirmwareFile = FirmwareRomFs.OpenFile("/file"))
+                {
+                    FirmwareFile.CopyTo(MemoryStream);
+                }
+
+                Data = MemoryStream.ToArray();
+            }
+
+            FirmwareContent.Dispose();
+            FirmwareStream.Dispose();
+
+            return Data;
+        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
index 3833ce9ec9..aa96a4163f 100644
--- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
+++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
@@ -46,6 +46,8 @@ namespace Ryujinx.HLE.HOS.SystemState
 
         public ColorSet ThemeColor { get; set; }
 
+        public bool InstallContents { get; set; }
+
         private ConcurrentDictionary<string, UserProfile> Profiles;
 
         internal UserProfile LastOpenUser { get; private set; }
diff --git a/Ryujinx.HLE/Utilities/FontUtils.cs b/Ryujinx.HLE/Utilities/FontUtils.cs
new file mode 100644
index 0000000000..efe7560c68
--- /dev/null
+++ b/Ryujinx.HLE/Utilities/FontUtils.cs
@@ -0,0 +1,37 @@
+using System.IO;
+
+namespace Ryujinx.HLE.Utilities
+{
+    public static class FontUtils
+    {
+        private static readonly uint FontKey = 0x06186249;
+
+        public static byte[] DecryptFont(Stream BFTTFStream)
+        {
+            uint KXor(uint In) => In ^ 0x06186249;
+
+            using (BinaryReader Reader = new BinaryReader(BFTTFStream))
+            {
+                using (MemoryStream TTFStream = new MemoryStream())
+                {
+                    using (BinaryWriter Output = new BinaryWriter(TTFStream))
+                    {
+                        if (KXor(Reader.ReadUInt32()) != 0x18029a7f)
+                        {
+                            throw new InvalidDataException("Error: Input file is not in BFTTF format!");
+                        }
+
+                        BFTTFStream.Position += 4;
+
+                        for (int i = 0; i < (BFTTFStream.Length - 8) / 4; i++)
+                        {
+                            Output.Write(KXor(Reader.ReadUInt32()));
+                        }
+
+                        return TTFStream.ToArray();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs
index e8d6550ad1..a10273eef7 100644
--- a/Ryujinx.HLE/Utilities/StringUtils.cs
+++ b/Ryujinx.HLE/Utilities/StringUtils.cs
@@ -1,5 +1,7 @@
-using System;
+using Ryujinx.HLE.HOS;
+using System;
 using System.Globalization;
+using System.IO;
 using System.Linq;
 using System.Text;
 
@@ -47,5 +49,28 @@ namespace Ryujinx.HLE.Utilities
 
             return Output;
         }
+
+        public static string ReadUtf8String(ServiceCtx Context, int Index = 0)
+        {
+            long Position = Context.Request.PtrBuff[Index].Position;
+            long Size     = Context.Request.PtrBuff[Index].Size;
+
+            using (MemoryStream MS = new MemoryStream())
+            {
+                while (Size-- > 0)
+                {
+                    byte Value = Context.Memory.ReadByte(Position++);
+
+                    if (Value == 0)
+                    {
+                        break;
+                    }
+
+                    MS.WriteByte(Value);
+                }
+
+                return Encoding.UTF8.GetString(MS.ToArray());
+            }
+        }
     }
 }