diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs
index 053d5b521..4d751e2a9 100644
--- a/src/Ryujinx.Ava/AppHost.cs
+++ b/src/Ryujinx.Ava/AppHost.cs
@@ -54,6 +54,8 @@ using System.Threading.Tasks;
 using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
 using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
 using Image = SixLabors.ImageSharp.Image;
+using InputManager = Ryujinx.Input.HLE.InputManager;
+using IRenderer = Ryujinx.Graphics.GAL.IRenderer;
 using Key = Ryujinx.Input.Key;
 using MouseButton = Ryujinx.Input.MouseButton;
 using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
@@ -121,14 +123,12 @@ namespace Ryujinx.Ava
         public int Width { get; private set; }
         public int Height { get; private set; }
         public string ApplicationPath { get; private set; }
-        public ulong ApplicationId { get; private set; }
         public bool ScreenshotRequested { get; set; }
 
         public AppHost(
             RendererHost renderer,
             InputManager inputManager,
             string applicationPath,
-            ulong applicationId,
             VirtualFileSystem virtualFileSystem,
             ContentManager contentManager,
             AccountManager accountManager,
@@ -152,7 +152,6 @@ namespace Ryujinx.Ava
             NpadManager = _inputManager.CreateNpadManager();
             TouchScreenManager = _inputManager.CreateTouchScreenManager();
             ApplicationPath = applicationPath;
-            ApplicationId = applicationId;
             VirtualFileSystem = virtualFileSystem;
             ContentManager = contentManager;
 
@@ -642,7 +641,7 @@ namespace Ryujinx.Ava
                         {
                             Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
 
-                            if (!Device.LoadXci(ApplicationPath, ApplicationId))
+                            if (!Device.LoadXci(ApplicationPath))
                             {
                                 Device.Dispose();
 
@@ -669,7 +668,7 @@ namespace Ryujinx.Ava
                         {
                             Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
 
-                            if (!Device.LoadNsp(ApplicationPath, ApplicationId))
+                            if (!Device.LoadNsp(ApplicationPath))
                             {
                                 Device.Dispose();
 
diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json
index be3e35a9c..bc2bbfe82 100644
--- a/src/Ryujinx.Ava/Assets/Locales/en_US.json
+++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json
@@ -539,8 +539,6 @@
   "OpenSetupGuideMessage": "Open the Setup Guide",
   "NoUpdate": "No Update",
   "TitleUpdateVersionLabel": "Version {0}",
-  "TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
-  "TitleBundledDlcLabel": "Bundled:",
   "RyujinxInfo": "Ryujinx - Info",
   "RyujinxConfirm": "Ryujinx - Confirmation",
   "FileDialogAllTypes": "All types",
diff --git a/src/Ryujinx.Ava/Common/ApplicationHelper.cs b/src/Ryujinx.Ava/Common/ApplicationHelper.cs
index dd4643297..91ca8f4d5 100644
--- a/src/Ryujinx.Ava/Common/ApplicationHelper.cs
+++ b/src/Ryujinx.Ava/Common/ApplicationHelper.cs
@@ -18,8 +18,7 @@ using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
-using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common.Helper;
 using System;
 using System.Buffers;
@@ -227,11 +226,7 @@ namespace Ryujinx.Ava.Common
                     return;
                 }
 
-                IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                    ? IntegrityCheckLevel.ErrorOnInvalid
-                    : IntegrityCheckLevel.None;
-
-                (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
+                (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
                 if (updatePatchNca != null)
                 {
                     patchNca = updatePatchNca;
diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs
index 69465c7c7..0f0071065 100644
--- a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs
@@ -1,6 +1,7 @@
 using Avalonia.Controls;
 using Avalonia.Interactivity;
 using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
 using LibHac.Fs;
 using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Ava.Common;
@@ -14,6 +15,7 @@ using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common.Helper;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using Path = System.IO.Path;
 
@@ -39,7 +41,7 @@ namespace Ryujinx.Ava.UI.Controls
             {
                 viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite;
 
-                ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.IdString, appMetadata =>
+                ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata =>
                 {
                     appMetadata.Favorite = viewModel.SelectedApplication.Favorite;
                 });
@@ -74,9 +76,19 @@ namespace Ryujinx.Ava.UI.Controls
         {
             if (viewModel?.SelectedApplication != null)
             {
-                var saveDataFilter = SaveDataFilter.Make(viewModel.SelectedApplication.Id, saveDataType, userId, saveDataId: default, index: default);
+                if (!ulong.TryParse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
+                {
+                    Dispatcher.UIThread.InvokeAsync(async () =>
+                    {
+                        await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
+                    });
 
-                ApplicationHelper.OpenSaveDir(in saveDataFilter, viewModel.SelectedApplication.Id, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.Name);
+                    return;
+                }
+
+                var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
+
+                ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -86,7 +98,7 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
+                await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -96,7 +108,7 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
+                await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -108,8 +120,8 @@ namespace Ryujinx.Ava.UI.Controls
             {
                 await new CheatWindow(
                     viewModel.VirtualFileSystem,
-                    viewModel.SelectedApplication.IdString,
-                    viewModel.SelectedApplication.Name,
+                    viewModel.SelectedApplication.TitleId,
+                    viewModel.SelectedApplication.TitleName,
                     viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
             }
         }
@@ -121,7 +133,7 @@ namespace Ryujinx.Ava.UI.Controls
             if (viewModel?.SelectedApplication != null)
             {
                 string modsBasePath = ModLoader.GetModsBasePath();
-                string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.IdString);
+                string titleModsPath = ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId);
 
                 OpenHelper.OpenFolder(titleModsPath);
             }
@@ -134,7 +146,7 @@ namespace Ryujinx.Ava.UI.Controls
             if (viewModel?.SelectedApplication != null)
             {
                 string sdModsBasePath = ModLoader.GetSdModsBasePath();
-                string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.IdString);
+                string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
 
                 OpenHelper.OpenFolder(titleModsPath);
             }
@@ -148,15 +160,15 @@ namespace Ryujinx.Ava.UI.Controls
             {
                 UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
                     LocaleManager.Instance[LocaleKeys.DialogWarning],
-                    LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.Name),
+                    LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName),
                     LocaleManager.Instance[LocaleKeys.InputDialogYes],
                     LocaleManager.Instance[LocaleKeys.InputDialogNo],
                     LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
 
                 if (result == UserResult.Yes)
                 {
-                    DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
-                    DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
+                    DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0"));
+                    DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1"));
 
                     List<FileInfo> cacheFiles = new();
 
@@ -196,14 +208,14 @@ namespace Ryujinx.Ava.UI.Controls
             {
                 UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
                     LocaleManager.Instance[LocaleKeys.DialogWarning],
-                    LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.Name),
+                    LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName),
                     LocaleManager.Instance[LocaleKeys.InputDialogYes],
                     LocaleManager.Instance[LocaleKeys.InputDialogNo],
                     LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
 
                 if (result == UserResult.Yes)
                 {
-                    DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader"));
+                    DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"));
 
                     List<DirectoryInfo> oldCacheDirectories = new();
                     List<FileInfo> newCacheFiles = new();
@@ -251,7 +263,7 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu");
+                string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu");
                 string mainDir = Path.Combine(ptcDir, "0");
                 string backupDir = Path.Combine(ptcDir, "1");
 
@@ -272,7 +284,7 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "shader");
+                string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader");
 
                 if (!Directory.Exists(shaderCacheDir))
                 {
@@ -293,7 +305,7 @@ namespace Ryujinx.Ava.UI.Controls
                     viewModel.StorageProvider,
                     NcaSectionType.Code,
                     viewModel.SelectedApplication.Path,
-                    viewModel.SelectedApplication.Name);
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -307,7 +319,7 @@ namespace Ryujinx.Ava.UI.Controls
                     viewModel.StorageProvider,
                     NcaSectionType.Data,
                     viewModel.SelectedApplication.Path,
-                    viewModel.SelectedApplication.Name);
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -321,7 +333,7 @@ namespace Ryujinx.Ava.UI.Controls
                     viewModel.StorageProvider,
                     NcaSectionType.Logo,
                     viewModel.SelectedApplication.Path,
-                    viewModel.SelectedApplication.Name);
+                    viewModel.SelectedApplication.TitleName);
             }
         }
 
@@ -332,7 +344,7 @@ namespace Ryujinx.Ava.UI.Controls
             if (viewModel?.SelectedApplication != null)
             {
                 ApplicationData selectedApplication = viewModel.SelectedApplication;
-                ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.Name, selectedApplication.IdString, selectedApplication.Icon);
+                ShortcutHelper.CreateAppShortcut(selectedApplication.Path, selectedApplication.TitleName, selectedApplication.TitleId, selectedApplication.Icon);
             }
         }
 
@@ -342,7 +354,7 @@ namespace Ryujinx.Ava.UI.Controls
 
             if (viewModel?.SelectedApplication != null)
             {
-                await viewModel.LoadApplication(viewModel.SelectedApplication);
+                await viewModel.LoadApplication(viewModel.SelectedApplication.Path);
             }
         }
     }
diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml
index 5919652e2..bbdb4c4a7 100644
--- a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml
+++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml
@@ -82,7 +82,7 @@
                                     <TextBlock
                                         HorizontalAlignment="Center"
                                         VerticalAlignment="Center"
-                                        Text="{Binding Name}"
+                                        Text="{Binding TitleName}"
                                         TextAlignment="Center"
                                         TextWrapping="Wrap" />
                                 </Panel>
diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
index 24ec2b357..9004f7518 100644
--- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
+++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
@@ -85,7 +85,7 @@
                                         <TextBlock
                                             HorizontalAlignment="Stretch"
                                             FontWeight="Bold"
-                                            Text="{Binding Name}"
+                                            Text="{Binding TitleName}"
                                             TextAlignment="Left"
                                             TextWrapping="Wrap" />
                                         <TextBlock
@@ -109,7 +109,7 @@
                                     Spacing="5">
                                     <TextBlock
                                         HorizontalAlignment="Stretch"
-                                        Text="{Binding Id, StringFormat=X16}"
+                                        Text="{Binding TitleId}"
                                         TextAlignment="Left"
                                         TextWrapping="Wrap" />
                                     <TextBlock
diff --git a/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs b/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs
index e39ffead1..fedb3527b 100644
--- a/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs
+++ b/src/Ryujinx.Ava/UI/Models/DownloadableContentModel.cs
@@ -1,5 +1,4 @@
-using Ryujinx.Ava.Common.Locale;
-using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.Ava.UI.ViewModels;
 using System.IO;
 
 namespace Ryujinx.Ava.UI.Models
@@ -25,9 +24,6 @@ namespace Ryujinx.Ava.UI.Models
 
         public string FileName => Path.GetFileName(ContainerPath);
 
-        public string Label =>
-            Path.GetExtension(FileName)?.ToLower() == ".xci" ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {FileName}" : FileName;
-
         public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
         {
             TitleId = titleId;
diff --git a/src/Ryujinx.Ava/UI/Models/SaveModel.cs b/src/Ryujinx.Ava/UI/Models/SaveModel.cs
index 2e3ed3bae..7b476932b 100644
--- a/src/Ryujinx.Ava/UI/Models/SaveModel.cs
+++ b/src/Ryujinx.Ava/UI/Models/SaveModel.cs
@@ -46,14 +46,14 @@ namespace Ryujinx.Ava.UI.Models
             TitleId = info.ProgramId;
             UserId = info.UserId;
 
-            var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.ToUpper() == TitleIdString);
+            var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.TitleId.ToUpper() == TitleIdString);
 
             InGameList = appData != null;
 
             if (InGameList)
             {
                 Icon = appData.Icon;
-                Title = appData.Name;
+                Title = appData.TitleName;
             }
             else
             {
diff --git a/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs b/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs
index fae2a08d0..3b44e8ee6 100644
--- a/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs
+++ b/src/Ryujinx.Ava/UI/Models/TitleUpdateModel.cs
@@ -8,10 +8,7 @@ namespace Ryujinx.Ava.UI.Models
         public ApplicationControlProperty Control { get; }
         public string Path { get; }
 
-        public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(
-            System.IO.Path.GetExtension(Path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
-            Control.DisplayVersionString.ToString()
-        );
+        public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleUpdateVersionLabel, Control.DisplayVersionString.ToString());
 
         public TitleUpdateModel(ApplicationControlProperty control, string path)
         {
diff --git a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
index 9f3a0045d..cdecae77d 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs
@@ -17,12 +17,11 @@ using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
-using Ryujinx.Ui.App.Common;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Threading.Tasks;
 using Application = Avalonia.Application;
 using Path = System.IO.Path;
 
@@ -39,7 +38,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
 
         private string _search;
-        private readonly ApplicationData _applicationData;
+        private readonly ulong _titleId;
 
         private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
 
@@ -93,25 +92,18 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public IStorageProvider StorageProvider;
 
-        public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
             _virtualFileSystem = virtualFileSystem;
 
-            _applicationData = applicationData;
+            _titleId = titleId;
 
             if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
             {
                 StorageProvider = desktop.MainWindow.StorageProvider;
             }
 
-            _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json");
-
-            if (!File.Exists(_downloadableContentJsonPath))
-            {
-                _downloadableContentContainerList = new List<DownloadableContentContainer>();
-
-                Save();
-            }
+            _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
 
             try
             {
@@ -128,9 +120,6 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         private void LoadDownloadableContents()
         {
-            // NOTE: Try to load downloadable contents from PFS first.
-            AddDownloadableContent(_applicationData.Path);
-
             foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
             {
                 if (File.Exists(downloadableContentContainer.ContainerPath))
@@ -138,11 +127,7 @@ namespace Ryujinx.Ava.UI.ViewModels
                     using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
 
                     PartitionFileSystem partitionFileSystem = new();
-
-                    if (partitionFileSystem.Initialize(containerFile.AsStorage()).IsFailure())
-                    {
-                        continue;
-                    }
+                    partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
 
                     _virtualFileSystem.ImportTickets(partitionFileSystem);
 
@@ -235,34 +220,22 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             foreach (var file in result)
             {
-                if (!AddDownloadableContent(file.Path.LocalPath))
-                {
-                    await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
-                }
+                await AddDownloadableContent(file.Path.LocalPath);
             }
         }
 
-        private bool AddDownloadableContent(string path)
+        private async Task AddDownloadableContent(string path)
         {
             if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
             {
-                return true;
+                return;
             }
 
             using FileStream containerFile = File.OpenRead(path);
 
-            IFileSystem partitionFileSystem;
-
-            if (Path.GetExtension(path).ToLower() == ".xci")
-            {
-                partitionFileSystem = new Xci(_virtualFileSystem.KeySet, containerFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
-            }
-            else
-            {
-                var pfsTemp = new PartitionFileSystem();
-                pfsTemp.Initialize(containerFile.AsStorage()).ThrowIfFailure();
-                partitionFileSystem = pfsTemp;
-            }
+            PartitionFileSystem partitionFileSystem = new();
+            partitionFileSystem.Initialize(containerFile.AsStorage()).ThrowIfFailure();
+            bool containsDownloadableContent = false;
 
             _virtualFileSystem.ImportTickets(partitionFileSystem);
 
@@ -280,7 +253,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 
                 if (nca.Header.ContentType == NcaContentType.PublicData)
                 {
-                    if (nca.GetProgramIdBase() != _applicationData.IdBase)
+                    if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
                     {
                         break;
                     }
@@ -292,11 +265,14 @@ namespace Ryujinx.Ava.UI.ViewModels
                     OnPropertyChanged(nameof(UpdateCount));
                     Sort();
 
-                    return true;
+                    containsDownloadableContent = true;
                 }
             }
 
-            return false;
+            if (!containsDownloadableContent)
+            {
+                await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
+            }
         }
 
         public void Remove(DownloadableContentModel model)
diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
index 692df483d..80df5d398 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
@@ -95,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         private bool _canUpdate = true;
         private Cursor _cursor;
         private string _title;
-        private ApplicationData _currentApplicationData;
+        private string _currentEmulatedGamePath;
         private readonly AutoResetEvent _rendererWaitEvent;
         private WindowState _windowState;
         private double _windowWidth;
@@ -106,6 +106,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         public ApplicationData ListSelectedApplication;
         public ApplicationData GridSelectedApplication;
 
+        private string TitleName { get; set; }
         internal AppHost AppHost { get; set; }
 
         public MainWindowViewModel()
@@ -929,8 +930,8 @@ namespace Ryujinx.Ava.UI.ViewModels
             return SortMode switch
             {
 #pragma warning disable IDE0055 // Disable formatting
-                ApplicationSort.Title           => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Name)
-                                                               : SortExpressionComparer<ApplicationData>.Descending(app => app.Name),
+                ApplicationSort.Title           => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
+                                                               : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
                 ApplicationSort.Developer       => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
                                                                : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
                 ApplicationSort.LastPlayed      => new LastPlayedSortComparer(IsAscending),
@@ -967,7 +968,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         {
             if (arg is ApplicationData app)
             {
-                return string.IsNullOrWhiteSpace(_searchText) || app.Name.ToLower().Contains(_searchText.ToLower());
+                return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
             }
 
             return false;
@@ -1096,7 +1097,7 @@ namespace Ryujinx.Ava.UI.ViewModels
                                 IsLoadingIndeterminate = false;
                                 break;
                             case LoadState.Loaded:
-                                LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
+                                LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
                                 IsLoadingIndeterminate = true;
                                 CacheLoadStatus = "";
                                 break;
@@ -1116,7 +1117,7 @@ namespace Ryujinx.Ava.UI.ViewModels
                                 IsLoadingIndeterminate = false;
                                 break;
                             case ShaderCacheLoadingState.Loaded:
-                                LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
+                                LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
                                 IsLoadingIndeterminate = true;
                                 CacheLoadStatus = "";
                                 break;
@@ -1167,13 +1168,13 @@ namespace Ryujinx.Ava.UI.ViewModels
             {
                 UserChannelPersistence.ShouldRestart = false;
 
-                await LoadApplication(_currentApplicationData);
+                await LoadApplication(_currentEmulatedGamePath);
             }
             else
             {
                 // Otherwise, clear state.
                 UserChannelPersistence = new UserChannelPersistence();
-                _currentApplicationData = null;
+                _currentEmulatedGamePath = null;
             }
         }
 
@@ -1450,12 +1451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             if (result.Count > 0)
             {
-                ApplicationData applicationData = new()
-                {
-                    Path = result[0].Path.LocalPath,
-                };
-
-                await LoadApplication(applicationData);
+                await LoadApplication(result[0].Path.LocalPath);
             }
         }
 
@@ -1469,17 +1465,11 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             if (result.Count > 0)
             {
-                ApplicationData applicationData = new()
-                {
-                    Name = Path.GetFileNameWithoutExtension(result[0].Path.LocalPath),
-                    Path = result[0].Path.LocalPath,
-                };
-
-                await LoadApplication(applicationData);
+                await LoadApplication(result[0].Path.LocalPath);
             }
         }
 
-        public async Task LoadApplication(ApplicationData application, bool startFullscreen = false)
+        public async Task LoadApplication(string path, bool startFullscreen = false, string titleName = "")
         {
             if (AppHost != null)
             {
@@ -1499,7 +1489,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             Logger.RestartTime();
 
-            SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
+            SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path, ConfigurationState.Instance.System.Language);
 
             PrepareLoadScreen();
 
@@ -1508,8 +1498,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             AppHost = new AppHost(
                 RendererHostControl,
                 InputManager,
-                application.Path,
-                application.Id,
+                path,
                 VirtualFileSystem,
                 ContentManager,
                 AccountManager,
@@ -1527,17 +1516,17 @@ namespace Ryujinx.Ava.UI.ViewModels
 
             CanUpdate = false;
 
-            LoadHeading = application.Name;
+            LoadHeading = TitleName = titleName;
 
-            if (string.IsNullOrWhiteSpace(application.Name))
+            if (string.IsNullOrWhiteSpace(titleName))
             {
                 LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Processes.ActiveApplication.Name);
-                application.Name = AppHost.Device.Processes.ActiveApplication.Name;
+                TitleName = AppHost.Device.Processes.ActiveApplication.Name;
             }
 
             SwitchToRenderer(startFullscreen);
 
-            _currentApplicationData = application;
+            _currentEmulatedGamePath = path;
 
             Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
             gameThread.Start();
diff --git a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
index 7bb96131d..5090a8c70 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/TitleUpdateViewModel.cs
@@ -1,3 +1,4 @@
+using Avalonia;
 using Avalonia.Collections;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Platform.Storage;
@@ -7,7 +8,6 @@ using LibHac.Fs;
 using LibHac.Fs.Fsa;
 using LibHac.FsSystem;
 using LibHac.Ns;
-using LibHac.Tools.Fs;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Ava.Common.Locale;
@@ -17,16 +17,12 @@ using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
 using Ryujinx.Ui.App.Common;
-using Ryujinx.Ui.Common.Configuration;
 using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
-using Application = Avalonia.Application;
-using ContentType = LibHac.Ncm.ContentType;
 using Path = System.IO.Path;
 using SpanHelpers = LibHac.Common.SpanHelpers;
 
@@ -37,7 +33,7 @@ namespace Ryujinx.Ava.UI.ViewModels
         public TitleUpdateMetadata TitleUpdateWindowData;
         public readonly string TitleUpdateJsonPath;
         private VirtualFileSystem VirtualFileSystem { get; }
-        private ApplicationData ApplicationData { get; }
+        private ulong TitleId { get; }
 
         private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
         private AvaloniaList<object> _views = new();
@@ -77,18 +73,18 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         public IStorageProvider StorageProvider;
 
-        public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
             VirtualFileSystem = virtualFileSystem;
 
-            ApplicationData = applicationData;
+            TitleId = titleId;
 
             if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
             {
                 StorageProvider = desktop.MainWindow.StorageProvider;
             }
 
-            TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json");
+            TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
 
             try
             {
@@ -96,7 +92,7 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
             catch
             {
-                Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}");
+                Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
 
                 TitleUpdateWindowData = new TitleUpdateMetadata
                 {
@@ -112,9 +108,6 @@ namespace Ryujinx.Ava.UI.ViewModels
 
         private void LoadUpdates()
         {
-            // Try to load updates from PFS first
-            AddUpdate(ApplicationData.Path, true);
-
             foreach (string path in TitleUpdateWindowData.Paths)
             {
                 AddUpdate(path);
@@ -169,41 +162,17 @@ namespace Ryujinx.Ava.UI.ViewModels
             }
         }
 
-        private void AddUpdate(string path, bool ignoreNotFound = false)
+        private void AddUpdate(string path)
         {
             if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
             {
-                IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                    ? IntegrityCheckLevel.ErrorOnInvalid
-                    : IntegrityCheckLevel.None;
-
                 using FileStream file = new(path, FileMode.Open, FileAccess.Read);
 
-                IFileSystem pfs;
-
                 try
                 {
-                    if (Path.GetExtension(path).ToLower() == ".xci")
-                    {
-                        pfs = new Xci(VirtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
-                    }
-                    else
-                    {
-                        var pfsTemp = new PartitionFileSystem();
-                        pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
-                        pfs = pfsTemp;
-                    }
-
-                    Dictionary<ulong, ContentCollection> updates = pfs.GetUpdateData(VirtualFileSystem, checkLevel);
-
-                    Nca patchNca = null;
-                    Nca controlNca = null;
-
-                    if (updates.TryGetValue(ApplicationData.Id, out ContentCollection content))
-                    {
-                        patchNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Program);
-                        controlNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Control);
-                    }
+                    var pfs = new PartitionFileSystem();
+                    pfs.Initialize(file.AsStorage()).ThrowIfFailure();
+                    (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, pfs, TitleId.ToString("x16"), 0);
 
                     if (controlNca != null && patchNca != null)
                     {
@@ -218,10 +187,7 @@ namespace Ryujinx.Ava.UI.ViewModels
                     }
                     else
                     {
-                        if (!ignoreNotFound)
-                        {
-                            Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
-                        }
+                        Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
                     }
                 }
                 catch (Exception ex)
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
index af3c5deb6..4f2d262da 100644
--- a/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs
@@ -10,7 +10,6 @@ using Ryujinx.Ava.UI.Windows;
 using Ryujinx.Common;
 using Ryujinx.Common.Utilities;
 using Ryujinx.Modules;
-using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Helper;
@@ -132,14 +131,7 @@ namespace Ryujinx.Ava.UI.Views.Main
 
             if (!string.IsNullOrEmpty(contentPath))
             {
-                ApplicationData applicationData = new()
-                {
-                    Name = "miiEdit",
-                    Id = 0x0100000000001009,
-                    Path = contentPath,
-                };
-
-                await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen);
+                await ViewModel.LoadApplication(contentPath, false, "Mii Applet");
             }
         }
 
diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
index 7a716fb2a..cc21b5c60 100644
--- a/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
+++ b/src/Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml
@@ -104,7 +104,7 @@
                                 Content="{locale:Locale GameListHeaderApplication}"
                                 GroupName="Sort"
                                 IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
-                                Tag="Application" />
+                                Tag="Title" />
                             <RadioButton
                                 Checked="Sort_Checked"
                                 Content="{locale:Locale GameListHeaderDeveloper}"
diff --git a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs
index 76f1a991c..fde249a0c 100644
--- a/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Windows/CheatWindow.axaml.cs
@@ -1,11 +1,9 @@
 using Avalonia.Collections;
-using LibHac.Tools.FsSystem;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.UI.Models;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
 using Ryujinx.Ui.App.Common;
-using Ryujinx.Ui.Common.Configuration;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -36,12 +34,9 @@ namespace Ryujinx.Ava.UI.Windows
         public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
         {
             LoadedCheats = new AvaloniaList<CheatsList>();
-            IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
 
             Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
-            BuildId = ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath);
+            BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
 
             InitializeComponent();
 
diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml
index 98aac09ce..99cf28e77 100644
--- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml
+++ b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml
@@ -97,7 +97,7 @@
                                         MaxLines="2"
                                         TextWrapping="Wrap"
                                         TextTrimming="CharacterEllipsis"
-                                        Text="{Binding Label}" />
+                                        Text="{Binding FileName}" />
                                     <TextBlock
                                         Grid.Column="1"
                                         Margin="10 0"
diff --git a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs
index c871ae198..dfe8807be 100644
--- a/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs
@@ -7,9 +7,9 @@ using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Models;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common.Helper;
 using System.Threading.Tasks;
+using Button = Avalonia.Controls.Button;
 
 namespace Ryujinx.Ava.UI.Windows
 {
@@ -24,22 +24,22 @@ namespace Ryujinx.Ava.UI.Windows
             InitializeComponent();
         }
 
-        public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
-            DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, applicationData);
+            DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId);
 
             InitializeComponent();
         }
 
-        public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
         {
             ContentDialog contentDialog = new()
             {
                 PrimaryButtonText = "",
                 SecondaryButtonText = "",
                 CloseButtonText = "",
-                Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
-                Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdString),
+                Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId),
+                Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")),
             };
 
             Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs
index 352ac4e54..c78f4160d 100644
--- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs
@@ -4,7 +4,6 @@ using Avalonia.Controls.Primitives;
 using Avalonia.Interactivity;
 using Avalonia.Threading;
 using FluentAvalonia.UI.Controls;
-using LibHac.Tools.FsSystem;
 using Ryujinx.Ava.Common;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.Input;
@@ -24,6 +23,7 @@ using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Helper;
 using System;
+using System.IO;
 using System.Runtime.Versioning;
 using System.Threading;
 using System.Threading.Tasks;
@@ -139,7 +139,9 @@ namespace Ryujinx.Ava.UI.Windows
             {
                 ViewModel.SelectedIcon = args.Application.Icon;
 
-                ViewModel.LoadApplication(args.Application).Wait();
+                string path = new FileInfo(args.Application.Path).FullName;
+
+                ViewModel.LoadApplication(path).Wait();
             }
 
             args.Handled = true;
@@ -188,11 +190,7 @@ namespace Ryujinx.Ava.UI.Windows
             LibHacHorizonManager.InitializeBcatServer();
             LibHacHorizonManager.InitializeSystemClients();
 
-            IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
-
-            ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem, checkLevel);
+            ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem);
 
             // Save data created before we supported extra data in directory save data will not work properly if
             // given empty extra data. Luckily some of that extra data can be created using the data from the
@@ -299,12 +297,7 @@ namespace Ryujinx.Ava.UI.Windows
                 {
                     _deferLoad = false;
 
-                    ApplicationData applicationData = new()
-                    {
-                        Path = _launchPath,
-                    };
-
-                    ViewModel.LoadApplication(applicationData, _startFullscreen).Wait();
+                    ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
                 }
             }
             else
diff --git a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs
index 8ecf165ce..7ece63355 100644
--- a/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs
+++ b/src/Ryujinx.Ava/UI/Windows/TitleUpdateWindow.axaml.cs
@@ -7,15 +7,15 @@ using Ryujinx.Ava.UI.Helpers;
 using Ryujinx.Ava.UI.Models;
 using Ryujinx.Ava.UI.ViewModels;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common.Helper;
 using System.Threading.Tasks;
+using Button = Avalonia.Controls.Button;
 
 namespace Ryujinx.Ava.UI.Windows
 {
     public partial class TitleUpdateWindow : UserControl
     {
-        public readonly TitleUpdateViewModel ViewModel;
+        public TitleUpdateViewModel ViewModel;
 
         public TitleUpdateWindow()
         {
@@ -24,22 +24,22 @@ namespace Ryujinx.Ava.UI.Windows
             InitializeComponent();
         }
 
-        public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
         {
-            DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationData);
+            DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId);
 
             InitializeComponent();
         }
 
-        public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
+        public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
         {
             ContentDialog contentDialog = new()
             {
                 PrimaryButtonText = "",
                 SecondaryButtonText = "",
                 CloseButtonText = "",
-                Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
-                Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdString),
+                Content = new TitleUpdateWindow(virtualFileSystem, titleId),
+                Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")),
             };
 
             Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
diff --git a/src/Ryujinx.HLE/FileSystem/ContentCollection.cs b/src/Ryujinx.HLE/FileSystem/ContentCollection.cs
deleted file mode 100644
index 1c19887be..000000000
--- a/src/Ryujinx.HLE/FileSystem/ContentCollection.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using LibHac.Common.Keys;
-using LibHac.Fs.Fsa;
-using LibHac.Ncm;
-using LibHac.Tools.FsSystem.NcaUtils;
-using LibHac.Tools.Ncm;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
-using System;
-
-namespace Ryujinx.HLE.FileSystem
-{
-    /// <summary>
-    /// Thin wrapper around <see cref="Cnmt"/>
-    /// </summary>
-    public class ContentCollection
-    {
-        private readonly IFileSystem _pfs;
-        private readonly Cnmt _cnmt;
-
-        public ulong Id => _cnmt.TitleId;
-        public TitleVersion Version => _cnmt.TitleVersion;
-        public ContentMetaType Type => _cnmt.Type;
-        public ulong ApplicationId => _cnmt.ApplicationTitleId;
-        public ulong PatchId => _cnmt.PatchTitleId;
-        public TitleVersion RequiredSystemVersion => _cnmt.MinimumSystemVersion;
-        public TitleVersion RequiredApplicationVersion => _cnmt.MinimumApplicationVersion;
-        public byte[] Digest => _cnmt.Hash;
-
-        public ulong ProgramBaseId => Id & ~0x1FFFUL;
-        public bool IsSystemTitle => _cnmt.Type < ContentMetaType.Application;
-
-        public ContentCollection(IFileSystem pfs, Cnmt cnmt)
-        {
-            _pfs = pfs;
-            _cnmt = cnmt;
-        }
-
-        public Nca GetNcaByType(KeySet keySet, ContentType type, int programIndex = 0)
-        {
-            // TODO: Replace this with a check for IdOffset as soon as LibHac supports it:
-            // && entry.IdOffset == programIndex
-
-            foreach (var entry in _cnmt.ContentEntries)
-            {
-                if (entry.Type != type)
-                {
-                    continue;
-                }
-
-                string ncaId = BitConverter.ToString(entry.NcaId).Replace("-", null).ToLower();
-                Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca");
-
-                if (nca.GetProgramIndex() == programIndex)
-                {
-                    return nca;
-                }
-            }
-
-            return null;
-        }
-    }
-}
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
index 6863d1a7c..4568b44da 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
@@ -2,31 +2,21 @@
 using LibHac.Common;
 using LibHac.Fs;
 using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
 using LibHac.Loader;
 using LibHac.Ncm;
 using LibHac.Ns;
-using LibHac.Tools.Fs;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
-using LibHac.Tools.Ncm;
-using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
 using System.IO;
 using System.Linq;
 using ApplicationId = LibHac.Ncm.ApplicationId;
-using ContentType = LibHac.Ncm.ContentType;
-using Path = System.IO.Path;
 
 namespace Ryujinx.HLE.Loaders.Processes.Extensions
 {
-    public static class NcaExtensions
+    static class NcaExtensions
     {
-        private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
         public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
         {
             // Extract RomFs and ExeFs from NCA.
@@ -57,7 +47,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
                 nacpData = controlNca.GetNacp(device);
             }
 
-            /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update.
+            /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update.
 
             // Load program 0 control NCA as we are going to need it for display version.
             (_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
@@ -96,11 +86,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
             return processResult;
         }
 
-        public static ulong GetProgramIdBase(this Nca nca)
-        {
-            return nca.Header.TitleId & ~0x1FFFUL;
-        }
-
         public static int GetProgramIndex(this Nca nca)
         {
             return (int)(nca.Header.TitleId & 0xF);
@@ -111,11 +96,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
             return nca.Header.ContentType == NcaContentType.Program;
         }
 
-        public static bool IsMain(this Nca nca)
-        {
-            return nca.IsProgram() && !nca.IsPatch();
-        }
-
         public static bool IsPatch(this Nca nca)
         {
             int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
@@ -128,56 +108,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
             return nca.Header.ContentType == NcaContentType.Control;
         }
 
-        public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath)
-        {
-            updatePath = "(unknown)";
-
-            // Load Update NCAs.
-            Nca updatePatchNca = null;
-            Nca updateControlNca = null;
-
-            // Clear the program index part.
-            ulong titleIdBase = mainNca.GetProgramIdBase();
-
-            // Load update information if exists.
-            string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
-            if (File.Exists(titleUpdateMetadataPath))
-            {
-                updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
-                if (File.Exists(updatePath))
-                {
-                    var updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
-
-                    IFileSystem updatePartitionFileSystem;
-
-                    if (Path.GetExtension(updatePath).ToLower() == ".xci")
-                    {
-                        updatePartitionFileSystem = new Xci(fileSystem.KeySet, updateFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
-                    }
-                    else
-                    {
-                        PartitionFileSystem pfsTemp = new();
-                        pfsTemp.Initialize(updateFile.AsStorage()).ThrowIfFailure();
-                        updatePartitionFileSystem = pfsTemp;
-                    }
-
-                    foreach ((ulong updateTitleId, ContentCollection content) in updatePartitionFileSystem.GetUpdateData(fileSystem, checkLevel))
-                    {
-                        if ((updateTitleId & ~0x1FFFUL) != titleIdBase)
-                        {
-                            continue;
-                        }
-
-                        updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex);
-                        updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
-                        break;
-                    }
-                }
-            }
-
-            return (updatePatchNca, updateControlNca);
-        }
-
         public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null)
         {
             IFileSystem exeFs = null;
@@ -242,31 +172,5 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
 
             return nacpData;
         }
-
-        public static Cnmt GetCnmt(this Nca cnmtNca, IntegrityCheckLevel checkLevel, ContentMetaType metaType)
-        {
-            string path = $"/{metaType}_{cnmtNca.Header.TitleId:x16}.cnmt";
-            using var cnmtFile = new UniqueRef<IFile>();
-
-            try
-            {
-                Result result = cnmtNca.OpenFileSystem(0, checkLevel)
-                                       .OpenFile(ref cnmtFile.Ref, path.ToU8Span(), OpenMode.Read);
-
-                if (result.IsSuccess())
-                {
-                    return new Cnmt(cnmtFile.Release().AsStream());
-                }
-            }
-            catch (HorizonResultException ex)
-            {
-                if (!ResultFs.PathNotFound.Includes(ex.ResultValue))
-                {
-                    Logger.Warning?.Print(LogClass.Application, $"Failed get cnmt for '{cnmtNca.Header.TitleId:x16}' from nca: {ex.Message}");
-                }
-            }
-
-            return null;
-        }
     }
 }
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
index 5f45cd459..50f7d5853 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
@@ -1,87 +1,26 @@
 using LibHac.Common;
-using LibHac.Common.Keys;
 using LibHac.Fs;
 using LibHac.Fs.Fsa;
 using LibHac.FsSystem;
-using LibHac.Ncm;
 using LibHac.Tools.Fs;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
-using LibHac.Tools.Ncm;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.FileSystem;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
-using ContentType = LibHac.Ncm.ContentType;
 
 namespace Ryujinx.HLE.Loaders.Processes.Extensions
 {
     public static class PartitionFileSystemExtensions
     {
         private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
+        private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
 
-        public static Dictionary<ulong, ContentCollection> GetApplicationData(this IFileSystem partitionFileSystem,
-            VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
-        {
-            fileSystem.ImportTickets(partitionFileSystem);
-
-            var programs = new Dictionary<ulong, ContentCollection>();
-
-            foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
-            {
-                Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Application);
-
-                if (cnmt == null)
-                {
-                    continue;
-                }
-
-                ContentCollection content = new(partitionFileSystem, cnmt);
-
-                if (content.Type != ContentMetaType.Application)
-                {
-                    continue;
-                }
-
-                programs.TryAdd(content.ApplicationId, content);
-            }
-
-            return programs;
-        }
-
-        public static Dictionary<ulong, ContentCollection> GetUpdateData(this IFileSystem partitionFileSystem,
-            VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
-        {
-            fileSystem.ImportTickets(partitionFileSystem);
-
-            var programs = new Dictionary<ulong, ContentCollection>();
-
-            foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
-            {
-                Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Patch);
-
-                if (cnmt == null)
-                {
-                    continue;
-                }
-
-                ContentCollection content = new(partitionFileSystem, cnmt);
-
-                if (content.Type != ContentMetaType.Patch)
-                {
-                    continue;
-                }
-
-                programs.TryAdd(content.ApplicationId, content);
-            }
-
-            return programs;
-        }
-
-        internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, ulong titleId, out string errorMessage)
+        internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, out string errorMessage)
             where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
             where TFormat : IPartitionFileSystemFormat
             where THeader : unmanaged, IPartitionFileSystemHeader
@@ -96,21 +35,30 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
 
             try
             {
-                Dictionary<ulong, ContentCollection> applications = partitionFileSystem.GetApplicationData(device.FileSystem, device.System.FsIntegrityCheckLevel);
+                device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem);
 
-                if (titleId == 0)
+                // TODO: To support multi-games container, this should use CNMT NCA instead.
+                foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
                 {
-                    foreach ((ulong _, ContentCollection content) in applications)
+                    Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
+
+                    if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
                     {
-                        mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
-                        controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
-                        break;
+                        continue;
+                    }
+
+                    if (nca.IsPatch())
+                    {
+                        patchNca = nca;
+                    }
+                    else if (nca.IsProgram())
+                    {
+                        mainNca = nca;
+                    }
+                    else if (nca.IsControl())
+                    {
+                        controlNca = nca;
                     }
-                }
-                else if (applications.TryGetValue(titleId, out ContentCollection content))
-                {
-                    mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
-                    controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
                 }
 
                 ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
@@ -131,7 +79,54 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
                     return (false, ProcessResult.Failed);
                 }
 
-                (Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
+                // Load Update NCAs.
+                Nca updatePatchNca = null;
+                Nca updateControlNca = null;
+
+                if (ulong.TryParse(mainNca.Header.TitleId.ToString("x16"), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
+                {
+                    // Clear the program index part.
+                    titleIdBase &= ~0xFUL;
+
+                    // Load update information if exists.
+                    string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
+                    if (File.Exists(titleUpdateMetadataPath))
+                    {
+                        string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
+                        if (File.Exists(updatePath))
+                        {
+                            PartitionFileSystem updatePartitionFileSystem = new();
+                            updatePartitionFileSystem.Initialize(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()).ThrowIfFailure();
+
+                            device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem);
+
+                            // TODO: This should use CNMT NCA instead.
+                            foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca"))
+                            {
+                                Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath);
+
+                                if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
+                                {
+                                    continue;
+                                }
+
+                                if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16"))
+                                {
+                                    break;
+                                }
+
+                                if (nca.IsProgram())
+                                {
+                                    updatePatchNca = nca;
+                                }
+                                else if (nca.IsControl())
+                                {
+                                    updateControlNca = nca;
+                                }
+                            }
+                        }
+                    }
+                }
 
                 if (updatePatchNca != null)
                 {
@@ -173,18 +168,18 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
                 return (true, mainNca.Load(device, patchNca, controlNca));
             }
 
-            errorMessage = $"Unable to load: Could not find Main NCA for title \"{titleId:X16}\"";
+            errorMessage = "Unable to load: Could not find Main NCA";
 
             return (false, ProcessResult.Failed);
         }
 
-        public static Nca GetNca(this IFileSystem fileSystem, KeySet keySet, string path)
+        public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path)
         {
             using var ncaFile = new UniqueRef<IFile>();
 
             fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-            return new Nca(keySet, ncaFile.Release().AsStorage());
+            return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage());
         }
     }
 }
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
index 6b4a64be8..220b868db 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.Loaders.Processes
             _processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
         }
 
-        public bool LoadXci(string path, ulong titleId)
+        public bool LoadXci(string path)
         {
             FileStream stream = new(path, FileMode.Open, FileAccess.Read);
             Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
@@ -44,7 +44,7 @@ namespace Ryujinx.HLE.Loaders.Processes
                 return false;
             }
 
-            (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, titleId, out string errorMessage);
+            (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage);
 
             if (!success)
             {
@@ -66,13 +66,13 @@ namespace Ryujinx.HLE.Loaders.Processes
             return false;
         }
 
-        public bool LoadNsp(string path, ulong titleId)
+        public bool LoadNsp(string path)
         {
             FileStream file = new(path, FileMode.Open, FileAccess.Read);
             PartitionFileSystem partitionFileSystem = new();
             partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
 
-            (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, titleId, out string errorMessage);
+            (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage);
 
             if (processResult.ProcessId == 0)
             {
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
index 110bb0928..c229b1742 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
@@ -42,14 +42,15 @@ namespace Ryujinx.HLE.Loaders.Processes
 
             foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
             {
-                Nca nca = partitionFileSystem.GetNca(device.FileSystem.KeySet, fileEntry.FullPath);
+                Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
 
-                if (!nca.IsProgram())
+                if (!nca.IsProgram() && nca.IsPatch())
                 {
                     continue;
                 }
 
-                ulong currentMainProgramId = nca.GetProgramIdBase();
+                ulong currentProgramId = nca.Header.TitleId;
+                ulong currentMainProgramId = currentProgramId & ~0xFFFul;
 
                 if (applicationId == 0 && currentMainProgramId != 0)
                 {
@@ -66,7 +67,7 @@ namespace Ryujinx.HLE.Loaders.Processes
                     break;
                 }
 
-                hasIndex[nca.GetProgramIndex()] = true;
+                hasIndex[(int)(currentProgramId & 0xF)] = true;
             }
 
             if (programCount == 0)
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index 3516049c9..ae063a47d 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -72,9 +72,9 @@ namespace Ryujinx.HLE
             return Processes.LoadUnpackedNca(exeFsDir, romFsFile);
         }
 
-        public bool LoadXci(string xciFile, ulong titleId = 0)
+        public bool LoadXci(string xciFile)
         {
-            return Processes.LoadXci(xciFile, titleId);
+            return Processes.LoadXci(xciFile);
         }
 
         public bool LoadNca(string ncaFile)
@@ -82,9 +82,9 @@ namespace Ryujinx.HLE
             return Processes.LoadNca(ncaFile);
         }
 
-        public bool LoadNsp(string nspFile, ulong titleId = 0)
+        public bool LoadNsp(string nspFile)
         {
-            return Processes.LoadNsp(nspFile, titleId);
+            return Processes.LoadNsp(nspFile);
         }
 
         public bool LoadProgram(string fileName)
diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs
index 7495ccb56..65ab01eeb 100644
--- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs
+++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs
@@ -9,11 +9,9 @@ using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
 using Ryujinx.Ui.Common.Helper;
 using System;
 using System.IO;
-using System.Text.Json.Serialization;
 
 namespace Ryujinx.Ui.App.Common
 {
@@ -21,10 +19,10 @@ namespace Ryujinx.Ui.App.Common
     {
         public bool Favorite { get; set; }
         public byte[] Icon { get; set; }
-        public string Name { get; set; } = "Unknown";
-        public ulong Id { get; set; }
-        public string Developer { get; set; } = "Unknown";
-        public string Version { get; set; } = "0";
+        public string TitleName { get; set; }
+        public string TitleId { get; set; }
+        public string Developer { get; set; }
+        public string Version { get; set; }
         public TimeSpan TimePlayed { get; set; }
         public DateTime? LastPlayed { get; set; }
         public string FileExtension { get; set; }
@@ -38,11 +36,7 @@ namespace Ryujinx.Ui.App.Common
 
         public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
 
-        [JsonIgnore] public string IdString => Id.ToString("x16");
-
-        [JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
-
-        public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
+        public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
         {
             using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
 
@@ -111,7 +105,7 @@ namespace Ryujinx.Ui.App.Common
                 return string.Empty;
             }
 
-            (Nca updatePatchNca, _) = mainNca.GetUpdateData(virtualFileSystem, checkLevel, 0, out string _);
+            (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
 
             if (updatePatchNca != null)
             {
diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
index 976129717..46f29851c 100644
--- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
+++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
@@ -14,18 +14,17 @@ using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Loaders.Npdm;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Configuration.System;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Text.Json;
 using System.Threading;
-using ContentType = LibHac.Ncm.ContentType;
 using Path = System.IO.Path;
 using TimeSpan = System.TimeSpan;
 
@@ -43,16 +42,15 @@ namespace Ryujinx.Ui.App.Common
         private readonly byte[] _nsoIcon;
 
         private readonly VirtualFileSystem _virtualFileSystem;
-        private readonly IntegrityCheckLevel _checkLevel;
         private Language _desiredTitleLanguage;
         private CancellationTokenSource _cancellationToken;
 
         private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
+        private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
 
-        public ApplicationLibrary(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel)
+        public ApplicationLibrary(VirtualFileSystem virtualFileSystem)
         {
             _virtualFileSystem = virtualFileSystem;
-            _checkLevel = checkLevel;
 
             _nspIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSP.png");
             _xciIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_XCI.png");
@@ -71,390 +69,6 @@ namespace Ryujinx.Ui.App.Common
             return resourceByteArray;
         }
 
-        private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath)
-        {
-            ApplicationData data = new()
-            {
-                Icon = _nspIcon,
-            };
-
-            using UniqueRef<IFile> npdmFile = new();
-
-            try
-            {
-                Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
-
-                if (ResultFs.PathNotFound.Includes(result))
-                {
-                    Npdm npdm = new(npdmFile.Get.AsStream());
-
-                    data.Name = npdm.TitleName;
-                    data.Id = npdm.Aci0.TitleId;
-                }
-
-                return data;
-            }
-            catch (Exception exception)
-            {
-                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
-
-                return null;
-            }
-        }
-
-        private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath)
-        {
-            bool isExeFs = false;
-
-            // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application.
-            bool hasMainNca = false;
-
-            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
-            {
-                if (Path.GetExtension(fileEntry.FullPath)?.ToLower() == ".nca")
-                {
-                    using UniqueRef<IFile> ncaFile = new();
-
-                    pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                    Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
-                    int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
-
-                    // Some main NCAs don't have a data partition, so check if the partition exists before opening it
-                    if (nca.Header.ContentType == NcaContentType.Program &&
-                        !(nca.SectionExists(NcaSectionType.Data) &&
-                          nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
-                    {
-                        hasMainNca = true;
-
-                        break;
-                    }
-                }
-                else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
-                {
-                    isExeFs = true;
-                }
-            }
-
-            if (hasMainNca)
-            {
-                List<ApplicationData> applications = GetApplicationsFromPfs(pfs, filePath);
-
-                switch (applications.Count)
-                {
-                    case 1:
-                        return applications[0];
-                    case >= 1:
-                        Logger.Warning?.Print(LogClass.Application, $"File '{filePath}' contains more applications than expected: {applications.Count}");
-                        return applications[0];
-                    default:
-                        return null;
-                }
-            }
-
-            if (isExeFs)
-            {
-                return GetApplicationFromExeFs(pfs, filePath);
-            }
-
-            return null;
-        }
-
-        private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath)
-        {
-            var applications = new List<ApplicationData>();
-            string extension = Path.GetExtension(filePath).ToLower();
-
-            foreach ((ulong titleId, ContentCollection content) in pfs.GetApplicationData(_virtualFileSystem, _checkLevel))
-            {
-                ApplicationData applicationData = new()
-                {
-                    Id = titleId,
-                };
-
-                try
-                {
-                    Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
-                    Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
-
-                    BlitStruct<ApplicationControlProperty> controlHolder = new(1);
-
-                    IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
-
-                    // Check if there is an update available.
-                    if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
-                    {
-                        // Replace the original ControlFs by the updated one.
-                        controlFs = updatedControlFs;
-                    }
-
-                    ReadControlData(controlFs, controlHolder.ByteSpan);
-
-                    GetApplicationInformation(ref controlHolder.Value, ref applicationData);
-
-                    // Read the icon from the ControlFS and store it as a byte array
-                    try
-                    {
-                        using UniqueRef<IFile> icon = new();
-
-                        controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                        using MemoryStream stream = new();
-
-                        icon.Get.AsStream().CopyTo(stream);
-                        applicationData.Icon = stream.ToArray();
-                    }
-                    catch (HorizonResultException)
-                    {
-                        foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
-                        {
-                            if (entry.Name == "control.nacp")
-                            {
-                                continue;
-                            }
-
-                            using var icon = new UniqueRef<IFile>();
-
-                            controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                            using MemoryStream stream = new();
-
-                            icon.Get.AsStream().CopyTo(stream);
-                            applicationData.Icon = stream.ToArray();
-
-                            if (applicationData.Icon != null)
-                            {
-                                break;
-                            }
-                        }
-
-                        applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
-                    }
-
-                    applicationData.ControlHolder = controlHolder;
-
-                    applications.Add(applicationData);
-                }
-                catch (MissingKeyException exception)
-                {
-                    applicationData.Icon = extension == ".xci" ? _xciIcon : _nspIcon;
-
-                    Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
-                }
-                catch (InvalidDataException)
-                {
-                    applicationData.Icon = extension == ".xci" ? _xciIcon : _nspIcon;
-
-                    Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}");
-                }
-                catch (Exception exception)
-                {
-                    Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}");
-                }
-            }
-
-            return applications;
-        }
-
-        private bool TryGetApplicationsFromFile(string applicationPath, out List<ApplicationData> applications)
-        {
-            applications = new List<ApplicationData>();
-
-            long fileSizeBytes = new FileInfo(applicationPath).Length;
-
-            double fileSize = fileSizeBytes * 0.000000000931;
-
-            BlitStruct<ApplicationControlProperty> controlHolder = new(1);
-
-            try
-            {
-                string extension = Path.GetExtension(applicationPath).ToLower();
-
-                using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read);
-
-                switch (extension)
-                {
-                    case ".xci":
-                        {
-                            Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
-
-                            applications = GetApplicationsFromPfs(xci.OpenPartition(XciPartitionType.Secure), applicationPath);
-
-                            if (applications.Count == 0)
-                            {
-                                return false;
-                            }
-
-                            break;
-                        }
-                    case ".nsp":
-                    case ".pfs0":
-                        var pfs = new PartitionFileSystem();
-                        pfs.Initialize(file.AsStorage()).ThrowIfFailure();
-
-                        ApplicationData result = GetApplicationFromNsp(pfs, applicationPath);
-
-                        if (result == null)
-                        {
-                            return false;
-                        }
-
-                        applications.Add(result);
-
-                        break;
-                    case ".nro":
-                        {
-                            BinaryReader reader = new(file);
-                            ApplicationData application = new();
-
-                            byte[] Read(long position, int size)
-                            {
-                                file.Seek(position, SeekOrigin.Begin);
-
-                                return reader.ReadBytes(size);
-                            }
-
-                            try
-                            {
-                                file.Seek(24, SeekOrigin.Begin);
-
-                                int assetOffset = reader.ReadInt32();
-
-                                if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
-                                {
-                                    byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
-
-                                    long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
-                                    long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
-
-                                    ulong nacpOffset = reader.ReadUInt64();
-                                    ulong nacpSize = reader.ReadUInt64();
-
-                                    // Reads and stores game icon as byte array
-                                    if (iconSize > 0)
-                                    {
-                                        application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
-                                    }
-                                    else
-                                    {
-                                        application.Icon = _nroIcon;
-                                    }
-
-                                    // Read the NACP data
-                                    Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
-
-                                    GetApplicationInformation(ref controlHolder.Value, ref application);
-                                }
-                                else
-                                {
-                                    application.Icon = _nroIcon;
-                                    application.Name = Path.GetFileNameWithoutExtension(applicationPath);
-                                }
-
-                                application.ControlHolder = controlHolder;
-                                applications.Add(application);
-                            }
-                            catch
-                            {
-                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
-
-                                return false;
-                            }
-
-                            break;
-                        }
-                    case ".nca":
-                        {
-                            try
-                            {
-                                ApplicationData application = new();
-
-                                Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
-
-                                if (!nca.IsProgram() || nca.IsPatch())
-                                {
-                                    return false;
-                                }
-
-                                application.Icon = _ncaIcon;
-                                application.Name = Path.GetFileNameWithoutExtension(applicationPath);
-                                application.ControlHolder = controlHolder;
-
-                                applications.Add(application);
-                            }
-                            catch (InvalidDataException)
-                            {
-                                Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
-                            }
-                            catch
-                            {
-                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
-
-                                return false;
-                            }
-
-                            break;
-                        }
-                    // If its an NSO we just set defaults
-                    case ".nso":
-                        {
-                            ApplicationData application = new()
-                            {
-                                Icon = _nsoIcon,
-                                Name = Path.GetFileNameWithoutExtension(applicationPath),
-                            };
-
-                            applications.Add(application);
-                            break;
-                        }
-                }
-            }
-            catch (IOException exception)
-            {
-                Logger.Warning?.Print(LogClass.Application, exception.Message);
-
-                return false;
-            }
-
-            foreach (var data in applications)
-            {
-                ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
-                {
-                    appMetadata.Title = data.Name;
-
-                    // Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
-                    if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
-                    {
-                        appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
-                        appMetadata.TimePlayedOld = default;
-                    }
-
-                    // Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
-                    if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
-                    {
-                        // Migrate from string-based last_played to DateTime-based last_played_utc.
-                        if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
-                        {
-                            appMetadata.LastPlayed = lastPlayedOldParsed;
-
-                            // Migration successful: deleting last_played from the metadata file.
-                            appMetadata.LastPlayedOld = default;
-                        }
-
-                    }
-                });
-
-                data.Favorite = appMetadata.Favorite;
-                data.TimePlayed = appMetadata.TimePlayed;
-                data.LastPlayed = appMetadata.LastPlayed;
-                data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
-                data.FileSize = new FileInfo(applicationPath).Length;
-                data.Path = applicationPath;
-            }
-
-            return true;
-        }
-
         public void CancelLoading()
         {
             _cancellationToken?.Cancel();
@@ -478,7 +92,7 @@ namespace Ryujinx.Ui.App.Common
             _cancellationToken = new CancellationTokenSource();
 
             // Builds the applications list with paths to found applications
-            List<string> applicationPaths = new();
+            List<string> applications = new();
 
             try
             {
@@ -522,7 +136,7 @@ namespace Ryujinx.Ui.App.Common
                             if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso")
                             {
                                 var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName;
-                                applicationPaths.Add(fullPath);
+                                applications.Add(fullPath);
                                 numApplicationsFound++;
                             }
                         }
@@ -534,34 +148,327 @@ namespace Ryujinx.Ui.App.Common
                 }
 
                 // Loops through applications list, creating a struct and then firing an event containing the struct for each application
-                foreach (string applicationPath in applicationPaths)
+                foreach (string applicationPath in applications)
                 {
                     if (_cancellationToken.Token.IsCancellationRequested)
                     {
                         return;
                     }
 
-                    if (TryGetApplicationsFromFile(applicationPath, out List<ApplicationData> applications))
+                    long fileSize = new FileInfo(applicationPath).Length;
+                    string titleName = "Unknown";
+                    string titleId = "0000000000000000";
+                    string developer = "Unknown";
+                    string version = "0";
+                    byte[] applicationIcon = null;
+
+                    BlitStruct<ApplicationControlProperty> controlHolder = new(1);
+
+                    try
                     {
-                        foreach (var application in applications)
+                        string extension = Path.GetExtension(applicationPath).ToLower();
+
+                        using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read);
+
+                        if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
                         {
-                            OnApplicationAdded(new ApplicationAddedEventArgs
+                            try
                             {
-                                AppData = application,
-                            });
-                        }
+                                IFileSystem pfs;
 
-                        if (applications.Count > 1)
+                                bool isExeFs = false;
+
+                                if (extension == ".xci")
+                                {
+                                    Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
+
+                                    pfs = xci.OpenPartition(XciPartitionType.Secure);
+                                }
+                                else
+                                {
+                                    var pfsTemp = new PartitionFileSystem();
+                                    pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
+                                    pfs = pfsTemp;
+
+                                    // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application.
+                                    bool hasMainNca = false;
+
+                                    foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*"))
+                                    {
+                                        if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca")
+                                        {
+                                            using UniqueRef<IFile> ncaFile = new();
+
+                                            pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                                            Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
+                                            int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+
+                                            // Some main NCAs don't have a data partition, so check if the partition exists before opening it
+                                            if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
+                                            {
+                                                hasMainNca = true;
+
+                                                break;
+                                            }
+                                        }
+                                        else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
+                                        {
+                                            isExeFs = true;
+                                        }
+                                    }
+
+                                    if (!hasMainNca && !isExeFs)
+                                    {
+                                        numApplicationsFound--;
+
+                                        continue;
+                                    }
+                                }
+
+                                if (isExeFs)
+                                {
+                                    applicationIcon = _nspIcon;
+
+                                    using UniqueRef<IFile> npdmFile = new();
+
+                                    Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
+
+                                    if (ResultFs.PathNotFound.Includes(result))
+                                    {
+                                        Npdm npdm = new(npdmFile.Get.AsStream());
+
+                                        titleName = npdm.TitleName;
+                                        titleId = npdm.Aci0.TitleId.ToString("x16");
+                                    }
+                                }
+                                else
+                                {
+                                    GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId);
+
+                                    // Check if there is an update available.
+                                    if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs))
+                                    {
+                                        // Replace the original ControlFs by the updated one.
+                                        controlFs = updatedControlFs;
+                                    }
+
+                                    ReadControlData(controlFs, controlHolder.ByteSpan);
+
+                                    GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version);
+
+                                    // Read the icon from the ControlFS and store it as a byte array
+                                    try
+                                    {
+                                        using UniqueRef<IFile> icon = new();
+
+                                        controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                                        using MemoryStream stream = new();
+
+                                        icon.Get.AsStream().CopyTo(stream);
+                                        applicationIcon = stream.ToArray();
+                                    }
+                                    catch (HorizonResultException)
+                                    {
+                                        foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
+                                        {
+                                            if (entry.Name == "control.nacp")
+                                            {
+                                                continue;
+                                            }
+
+                                            using var icon = new UniqueRef<IFile>();
+
+                                            controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                                            using MemoryStream stream = new();
+
+                                            icon.Get.AsStream().CopyTo(stream);
+                                            applicationIcon = stream.ToArray();
+
+                                            if (applicationIcon != null)
+                                            {
+                                                break;
+                                            }
+                                        }
+
+                                        applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon;
+                                    }
+                                }
+                            }
+                            catch (MissingKeyException exception)
+                            {
+                                applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+
+                                Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
+                            }
+                            catch (InvalidDataException)
+                            {
+                                applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon;
+
+                                Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
+                            }
+                            catch (Exception exception)
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
+
+                                numApplicationsFound--;
+
+                                continue;
+                            }
+                        }
+                        else if (extension == ".nro")
                         {
-                            numApplicationsFound += applications.Count - 1;
+                            BinaryReader reader = new(file);
+
+                            byte[] Read(long position, int size)
+                            {
+                                file.Seek(position, SeekOrigin.Begin);
+
+                                return reader.ReadBytes(size);
+                            }
+
+                            try
+                            {
+                                file.Seek(24, SeekOrigin.Begin);
+
+                                int assetOffset = reader.ReadInt32();
+
+                                if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
+                                {
+                                    byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
+
+                                    long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
+                                    long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
+
+                                    ulong nacpOffset = reader.ReadUInt64();
+                                    ulong nacpSize = reader.ReadUInt64();
+
+                                    // Reads and stores game icon as byte array
+                                    if (iconSize > 0)
+                                    {
+                                        applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
+                                    }
+                                    else
+                                    {
+                                        applicationIcon = _nroIcon;
+                                    }
+
+                                    // Read the NACP data
+                                    Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
+
+                                    GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version);
+                                }
+                                else
+                                {
+                                    applicationIcon = _nroIcon;
+                                    titleName = Path.GetFileNameWithoutExtension(applicationPath);
+                                }
+                            }
+                            catch
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+
+                                numApplicationsFound--;
+
+                                continue;
+                            }
+                        }
+                        else if (extension == ".nca")
+                        {
+                            try
+                            {
+                                Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
+                                int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+
+                                if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()))
+                                {
+                                    numApplicationsFound--;
+
+                                    continue;
+                                }
+                            }
+                            catch (InvalidDataException)
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
+                            }
+                            catch
+                            {
+                                Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+
+                                numApplicationsFound--;
+
+                                continue;
+                            }
+
+                            applicationIcon = _ncaIcon;
+                            titleName = Path.GetFileNameWithoutExtension(applicationPath);
+                        }
+                        // If its an NSO we just set defaults
+                        else if (extension == ".nso")
+                        {
+                            applicationIcon = _nsoIcon;
+                            titleName = Path.GetFileNameWithoutExtension(applicationPath);
+                        }
+                    }
+                    catch (IOException exception)
+                    {
+                        Logger.Warning?.Print(LogClass.Application, exception.Message);
+
+                        numApplicationsFound--;
+
+                        continue;
+                    }
+
+                    ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata =>
+                    {
+                        appMetadata.Title = titleName;
+
+                        // Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
+                        if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
+                        {
+                            appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
+                            appMetadata.TimePlayedOld = default;
                         }
 
-                        numApplicationsLoaded += applications.Count;
-                    }
-                    else
+                        // Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
+                        if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
+                        {
+                            // Migrate from string-based last_played to DateTime-based last_played_utc.
+                            if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
+                            {
+                                appMetadata.LastPlayed = lastPlayedOldParsed;
+
+                                // Migration successful: deleting last_played from the metadata file.
+                                appMetadata.LastPlayedOld = default;
+                            }
+
+                        }
+                    });
+
+                    ApplicationData data = new()
                     {
-                        numApplicationsFound--;
-                    }
+                        Favorite = appMetadata.Favorite,
+                        Icon = applicationIcon,
+                        TitleName = titleName,
+                        TitleId = titleId,
+                        Developer = developer,
+                        Version = version,
+                        TimePlayed = appMetadata.TimePlayed,
+                        LastPlayed = appMetadata.LastPlayed,
+                        FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper(),
+                        FileSize = fileSize,
+                        Path = applicationPath,
+                        ControlHolder = controlHolder,
+                    };
+
+                    numApplicationsLoaded++;
+
+                    OnApplicationAdded(new ApplicationAddedEventArgs
+                    {
+                        AppData = data,
+                    });
 
                     OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs
                     {
@@ -593,6 +500,15 @@ namespace Ryujinx.Ui.App.Common
             ApplicationCountUpdated?.Invoke(null, e);
         }
 
+        private void GetControlFsAndTitleId(IFileSystem pfs, out IFileSystem controlFs, out string titleId)
+        {
+            (_, _, Nca controlNca) = GetGameData(_virtualFileSystem, pfs, 0);
+
+            // Return the ControlFS
+            controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
+            titleId = controlNca?.Header.TitleId.ToString("x16");
+        }
+
         public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
         {
             string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
@@ -630,29 +546,10 @@ namespace Ryujinx.Ui.App.Common
             return appMetadata;
         }
 
-        public byte[] GetApplicationIcon(string applicationPath, Language desiredTitleLanguage, ulong titleId)
+        public byte[] GetApplicationIcon(string applicationPath, Language desiredTitleLanguage)
         {
             byte[] applicationIcon = null;
 
-            if (titleId == 0)
-            {
-                if (Directory.Exists(applicationPath))
-                {
-                    return _ncaIcon;
-                }
-
-                return Path.GetExtension(applicationPath).ToLower() switch
-                {
-                    ".nsp" => _nspIcon,
-                    ".pfs0" => _nspIcon,
-                    ".xci" => _xciIcon,
-                    ".nso" => _nsoIcon,
-                    ".nro" => _nroIcon,
-                    ".nca" => _ncaIcon,
-                    _ => _ncaIcon,
-                };
-            }
-
             try
             {
                 // Look for icon only if applicationPath is not a directory
@@ -698,16 +595,7 @@ namespace Ryujinx.Ui.App.Common
                             else
                             {
                                 // Store the ControlFS in variable called controlFs
-                                Dictionary<ulong, ContentCollection> programs = pfs.GetApplicationData(_virtualFileSystem, _checkLevel);
-                                IFileSystem controlFs = null;
-
-                                if (programs.ContainsKey(titleId))
-                                {
-                                    if (programs[titleId].GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control) is { } controlNca)
-                                    {
-                                        controlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
-                                    }
-                                }
+                                GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _);
 
                                 // Read the icon from the ControlFS and store it as a byte array
                                 try
@@ -734,11 +622,16 @@ namespace Ryujinx.Ui.App.Common
 
                                         controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
-                                        using MemoryStream stream = new();
-                                        icon.Get.AsStream().CopyTo(stream);
-                                        applicationIcon = stream.ToArray();
+                                        using (MemoryStream stream = new())
+                                        {
+                                            icon.Get.AsStream().CopyTo(stream);
+                                            applicationIcon = stream.ToArray();
+                                        }
 
-                                        break;
+                                        if (applicationIcon != null)
+                                        {
+                                            break;
+                                        }
                                     }
 
                                     applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon;
@@ -821,41 +714,41 @@ namespace Ryujinx.Ui.App.Common
             return applicationIcon ?? _ncaIcon;
         }
 
-        private void GetApplicationInformation(ref ApplicationControlProperty controlData, ref ApplicationData data)
+        private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version)
         {
             _ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
 
             if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
             {
-                data.Name = controlData.Title[(int)desiredTitleLanguage].NameString.ToString();
-                data.Developer = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString();
+                titleName = controlData.Title[(int)desiredTitleLanguage].NameString.ToString();
+                publisher = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString();
             }
             else
             {
-                data.Name = null;
-                data.Developer = null;
+                titleName = null;
+                publisher = null;
             }
 
-            if (string.IsNullOrWhiteSpace(data.Name))
+            if (string.IsNullOrWhiteSpace(titleName))
             {
                 foreach (ref readonly var controlTitle in controlData.Title.ItemsRo)
                 {
                     if (!controlTitle.NameString.IsEmpty())
                     {
-                        data.Name = controlTitle.NameString.ToString();
+                        titleName = controlTitle.NameString.ToString();
 
                         break;
                     }
                 }
             }
 
-            if (string.IsNullOrWhiteSpace(data.Developer))
+            if (string.IsNullOrWhiteSpace(publisher))
             {
                 foreach (ref readonly var controlTitle in controlData.Title.ItemsRo)
                 {
                     if (!controlTitle.PublisherString.IsEmpty())
                     {
-                        data.Developer = controlTitle.PublisherString.ToString();
+                        publisher = controlTitle.PublisherString.ToString();
 
                         break;
                     }
@@ -864,21 +757,25 @@ namespace Ryujinx.Ui.App.Common
 
             if (controlData.PresenceGroupId != 0)
             {
-                data.Id = controlData.PresenceGroupId;
+                titleId = controlData.PresenceGroupId.ToString("x16");
             }
             else if (controlData.SaveDataOwnerId != 0)
             {
-                data.Id = controlData.SaveDataOwnerId;
+                titleId = controlData.SaveDataOwnerId.ToString();
             }
             else if (controlData.AddOnContentBaseId != 0)
             {
-                data.Id = (controlData.AddOnContentBaseId - 0x1000);
+                titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16");
+            }
+            else
+            {
+                titleId = "0000000000000000";
             }
 
-            data.Version = controlData.DisplayVersionString.ToString();
+            version = controlData.DisplayVersionString.ToString();
         }
 
-        private bool IsUpdateApplied(Nca mainNca, out IFileSystem updatedControlFs)
+        private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs)
         {
             updatedControlFs = null;
 
@@ -886,11 +783,11 @@ namespace Ryujinx.Ui.App.Common
 
             try
             {
-                (Nca patchNca, Nca controlNca) = mainNca.GetUpdateData(_virtualFileSystem, _checkLevel, 0, out updatePath);
+                (Nca patchNca, Nca controlNca) = GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath);
 
                 if (patchNca != null && controlNca != null)
                 {
-                    updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
+                    updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None);
 
                     return true;
                 }
@@ -906,5 +803,120 @@ namespace Ryujinx.Ui.App.Common
 
             return false;
         }
+
+        public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, IFileSystem pfs, int programIndex)
+        {
+            Nca mainNca = null;
+            Nca patchNca = null;
+            Nca controlNca = null;
+
+            fileSystem.ImportTickets(pfs);
+
+            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
+            {
+                using var ncaFile = new UniqueRef<IFile>();
+
+                pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage());
+
+                int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
+
+                if (ncaProgramIndex != programIndex)
+                {
+                    continue;
+                }
+
+                if (nca.Header.ContentType == NcaContentType.Program)
+                {
+                    int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+
+                    if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
+                    {
+                        patchNca = nca;
+                    }
+                    else
+                    {
+                        mainNca = nca;
+                    }
+                }
+                else if (nca.Header.ContentType == NcaContentType.Control)
+                {
+                    controlNca = nca;
+                }
+            }
+
+            return (mainNca, patchNca, controlNca);
+        }
+
+        public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex)
+        {
+            Nca patchNca = null;
+            Nca controlNca = null;
+
+            fileSystem.ImportTickets(pfs);
+
+            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
+            {
+                using var ncaFile = new UniqueRef<IFile>();
+
+                pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                Nca nca = new(fileSystem.KeySet, ncaFile.Release().AsStorage());
+
+                int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF);
+
+                if (ncaProgramIndex != programIndex)
+                {
+                    continue;
+                }
+
+                if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId)
+                {
+                    break;
+                }
+
+                if (nca.Header.ContentType == NcaContentType.Program)
+                {
+                    patchNca = nca;
+                }
+                else if (nca.Header.ContentType == NcaContentType.Control)
+                {
+                    controlNca = nca;
+                }
+            }
+
+            return (patchNca, controlNca);
+        }
+
+        public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath)
+        {
+            updatePath = null;
+
+            if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
+            {
+                // Clear the program index part.
+                titleIdBase &= ~0xFUL;
+
+                // Load update information if exists.
+                string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
+
+                if (File.Exists(titleUpdateMetadataPath))
+                {
+                    updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
+
+                    if (File.Exists(updatePath))
+                    {
+                        FileStream file = new(updatePath, FileMode.Open, FileAccess.Read);
+                        PartitionFileSystem nsp = new();
+                        nsp.Initialize(file.AsStorage()).ThrowIfFailure();
+
+                        return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex);
+                    }
+                }
+            }
+
+            return (null, null);
+        }
     }
 }
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index 14062481a..afb6a9925 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -7,7 +7,6 @@ using Ryujinx.Common.SystemInterop;
 using Ryujinx.Modules;
 using Ryujinx.SDL2.Common;
 using Ryujinx.Ui;
-using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Helper;
@@ -333,12 +332,7 @@ namespace Ryujinx
 
             if (CommandLineState.LaunchPathArg != null)
             {
-                ApplicationData applicationData = new()
-                {
-                    Path = CommandLineState.LaunchPathArg,
-                };
-
-                mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg);
+                mainWindow.RunApplication(CommandLineState.LaunchPathArg, CommandLineState.StartFullscreenArg);
             }
 
             if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false))
diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs
index 884f6687e..8b0b35e6c 100644
--- a/src/Ryujinx/Ui/MainWindow.cs
+++ b/src/Ryujinx/Ui/MainWindow.cs
@@ -39,7 +39,6 @@ using Silk.NET.Vulkan;
 using SPB.Graphics.Vulkan;
 using System;
 using System.Diagnostics;
-using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Threading;
@@ -71,7 +70,7 @@ namespace Ryujinx.Ui
         private bool _gameLoaded;
         private bool _ending;
 
-        private ApplicationData _currentApplicationData = null;
+        private string _currentEmulatedGamePath = null;
 
         private string _lastScannedAmiiboId = "";
         private bool _lastScannedAmiiboShowAll = false;
@@ -182,12 +181,8 @@ namespace Ryujinx.Ui
             _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
             _userChannelPersistence = new UserChannelPersistence();
 
-            IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
-
             // Instantiate GUI objects.
-            _applicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel);
+            _applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
             _uiHandler = new GtkHostUiHandler(this);
             _deviceExitStatus = new AutoResetEvent(false);
 
@@ -789,7 +784,7 @@ namespace Ryujinx.Ui
             }
         }
 
-        private bool LoadApplication(string path, ulong titleId, bool isFirmwareTitle)
+        private bool LoadApplication(string path, bool isFirmwareTitle)
         {
             SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
 
@@ -863,7 +858,7 @@ namespace Ryujinx.Ui
                     case ".xci":
                         Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
 
-                        return _emulationContext.LoadXci(path, titleId);
+                        return _emulationContext.LoadXci(path);
                     case ".nca":
                         Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
 
@@ -872,7 +867,7 @@ namespace Ryujinx.Ui
                     case ".pfs0":
                         Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
 
-                        return _emulationContext.LoadNsp(path, titleId);
+                        return _emulationContext.LoadNsp(path);
                     default:
                         Logger.Info?.Print(LogClass.Application, "Loading as Homebrew.");
                         try
@@ -893,7 +888,7 @@ namespace Ryujinx.Ui
             return false;
         }
 
-        public void RunApplication(ApplicationData application, bool startFullscreen = false)
+        public void RunApplication(string path, bool startFullscreen = false)
         {
             if (_gameLoaded)
             {
@@ -915,14 +910,14 @@ namespace Ryujinx.Ui
 
                 bool isFirmwareTitle = false;
 
-                if (application.Path.StartsWith("@SystemContent"))
+                if (path.StartsWith("@SystemContent"))
                 {
-                    application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path);
+                    path = VirtualFileSystem.SwitchPathToSystemPath(path);
 
                     isFirmwareTitle = true;
                 }
 
-                if (!LoadApplication(application.Path, application.Id, isFirmwareTitle))
+                if (!LoadApplication(path, isFirmwareTitle))
                 {
                     _emulationContext.Dispose();
                     SwitchToGameTable();
@@ -932,7 +927,7 @@ namespace Ryujinx.Ui
 
                 SetupProgressUiHandlers();
 
-                _currentApplicationData = application;
+                _currentEmulatedGamePath = path;
 
                 _deviceExitStatus.Reset();
 
@@ -1173,7 +1168,7 @@ namespace Ryujinx.Ui
                 _tableStore.AppendValues(
                     args.AppData.Favorite,
                     new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
-                    $"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}",
+                    $"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
                     args.AppData.Developer,
                     args.AppData.Version,
                     args.AppData.TimePlayedString,
@@ -1261,22 +1256,9 @@ namespace Ryujinx.Ui
         {
             _gameTableSelection.GetSelected(out TreeIter treeIter);
 
-            ApplicationData application = new()
-            {
-                Favorite = (bool)_tableStore.GetValue(treeIter, 0),
-                Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
-                Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
-                Developer = (string)_tableStore.GetValue(treeIter, 3),
-                Version = (string)_tableStore.GetValue(treeIter, 4),
-                TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
-                LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
-                FileExtension = (string)_tableStore.GetValue(treeIter, 7),
-                FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
-                Path = (string)_tableStore.GetValue(treeIter, 9),
-                ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
-            };
+            string path = (string)_tableStore.GetValue(treeIter, 9);
 
-            RunApplication(application);
+            RunApplication(path);
         }
 
         private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args)
@@ -1334,22 +1316,13 @@ namespace Ryujinx.Ui
                 return;
             }
 
-            ApplicationData application = new()
-            {
-                Favorite = (bool)_tableStore.GetValue(treeIter, 0),
-                Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0],
-                Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber),
-                Developer = (string)_tableStore.GetValue(treeIter, 3),
-                Version = (string)_tableStore.GetValue(treeIter, 4),
-                TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)),
-                LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)),
-                FileExtension = (string)_tableStore.GetValue(treeIter, 7),
-                FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)),
-                Path = (string)_tableStore.GetValue(treeIter, 9),
-                ControlHolder = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10),
-            };
+            string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
+            string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
+            string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
 
-            _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application);
+            BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
+
+            _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, titleFilePath, titleName, titleId, controlData);
         }
 
         private void Load_Application_File(object sender, EventArgs args)
@@ -1371,12 +1344,7 @@ namespace Ryujinx.Ui
 
             if (fileChooser.Run() == (int)ResponseType.Accept)
             {
-                ApplicationData applicationData = new()
-                {
-                    Path = fileChooser.Filename,
-                };
-
-                RunApplication(applicationData);
+                RunApplication(fileChooser.Filename);
             }
         }
 
@@ -1386,13 +1354,7 @@ namespace Ryujinx.Ui
 
             if (fileChooser.Run() == (int)ResponseType.Accept)
             {
-                ApplicationData applicationData = new()
-                {
-                    Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename),
-                    Path = fileChooser.Filename,
-                };
-
-                RunApplication(applicationData);
+                RunApplication(fileChooser.Filename);
             }
         }
 
@@ -1407,14 +1369,7 @@ namespace Ryujinx.Ui
         {
             string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
 
-            ApplicationData applicationData = new()
-            {
-                Name = "miiEdit",
-                Id = 0x0100000000001009ul,
-                Path = contentPath,
-            };
-
-            RunApplication(applicationData);
+            RunApplication(contentPath);
         }
 
         private void Open_Ryu_Folder(object sender, EventArgs args)
@@ -1690,13 +1645,13 @@ namespace Ryujinx.Ui
             {
                 _userChannelPersistence.ShouldRestart = false;
 
-                RunApplication(_currentApplicationData);
+                RunApplication(_currentEmulatedGamePath);
             }
             else
             {
                 // otherwise, clear state.
                 _userChannelPersistence = new UserChannelPersistence();
-                _currentApplicationData = null;
+                _currentEmulatedGamePath = null;
                 _actionMenu.Sensitive = false;
                 _firmwareInstallFile.Sensitive = true;
                 _firmwareInstallDirectory.Sensitive = true;
@@ -1758,7 +1713,7 @@ namespace Ryujinx.Ui
                 _emulationContext.Processes.ActiveApplication.ProgramId,
                 _emulationContext.Processes.ActiveApplication.ApplicationControlProperties
                     .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
-                _currentApplicationData.Path);
+                _currentEmulatedGamePath);
 
             window.Destroyed += CheatWindow_Destroyed;
             window.Show();
diff --git a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
index 6903c9419..5af181b08 100644
--- a/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
+++ b/src/Ryujinx/Ui/Widgets/GameTableContextMenu.cs
@@ -16,7 +16,6 @@ using Ryujinx.Common.Logging;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
 using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
 using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Common.Helper;
@@ -24,6 +23,7 @@ using Ryujinx.Ui.Windows;
 using System;
 using System.Buffers;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Reflection;
 using System.Threading;
@@ -36,13 +36,17 @@ namespace Ryujinx.Ui.Widgets
         private readonly VirtualFileSystem _virtualFileSystem;
         private readonly AccountManager _accountManager;
         private readonly HorizonClient _horizonClient;
+        private readonly BlitStruct<ApplicationControlProperty> _controlData;
 
-        private readonly ApplicationData _title;
+        private readonly string _titleFilePath;
+        private readonly string _titleName;
+        private readonly string _titleIdText;
+        private readonly ulong _titleId;
 
         private MessageDialog _dialog;
         private bool _cancel;
 
-        public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData)
+        public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, string titleFilePath, string titleName, string titleId, BlitStruct<ApplicationControlProperty> controlData)
         {
             _parent = parent;
 
@@ -51,13 +55,23 @@ namespace Ryujinx.Ui.Widgets
             _virtualFileSystem = virtualFileSystem;
             _accountManager = accountManager;
             _horizonClient = horizonClient;
-            _title = applicationData;
+            _titleFilePath = titleFilePath;
+            _titleName = titleName;
+            _titleIdText = titleId;
+            _controlData = controlData;
 
-            _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.UserAccountSaveDataSize > 0;
-            _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.DeviceSaveDataSize > 0;
-            _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(_title.ControlHolder.ByteSpan) && _title.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
+            if (!ulong.TryParse(_titleIdText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out _titleId))
+            {
+                GtkDialog.CreateErrorDialog("The selected game did not have a valid Title Id");
 
-            string fileExt = System.IO.Path.GetExtension(_title.Path).ToLower();
+                return;
+            }
+
+            _openSaveUserDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.UserAccountSaveDataSize > 0;
+            _openSaveDeviceDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.DeviceSaveDataSize > 0;
+            _openSaveBcatDirMenuItem.Sensitive = !Utilities.IsZeros(controlData.ByteSpan) && controlData.Value.BcatDeliveryCacheStorageSize > 0;
+
+            string fileExt = System.IO.Path.GetExtension(_titleFilePath).ToLower();
             bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci";
 
             _extractRomFsMenuItem.Sensitive = hasNca;
@@ -123,7 +137,7 @@ namespace Ryujinx.Ui.Widgets
 
         private void OpenSaveDir(in SaveDataFilter saveDataFilter)
         {
-            if (!TryFindSaveData(_title.Name, _title.Id, _title.ControlHolder, in saveDataFilter, out ulong saveDataId))
+            if (!TryFindSaveData(_titleName, _titleId, _controlData, in saveDataFilter, out ulong saveDataId))
             {
                 return;
             }
@@ -176,7 +190,7 @@ namespace Ryujinx.Ui.Widgets
                         {
                             Title = "Ryujinx - NCA Section Extractor",
                             Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"),
-                            SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_title.Path)}...",
+                            SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_titleFilePath)}...",
                             WindowPosition = WindowPosition.Center,
                         };
 
@@ -188,18 +202,18 @@ namespace Ryujinx.Ui.Widgets
                         }
                     });
 
-                    using FileStream file = new(_title.Path, FileMode.Open, FileAccess.Read);
+                    using FileStream file = new(_titleFilePath, FileMode.Open, FileAccess.Read);
 
                     Nca mainNca = null;
                     Nca patchNca = null;
 
-                    if ((System.IO.Path.GetExtension(_title.Path).ToLower() == ".nsp") ||
-                        (System.IO.Path.GetExtension(_title.Path).ToLower() == ".pfs0") ||
-                        (System.IO.Path.GetExtension(_title.Path).ToLower() == ".xci"))
+                    if ((System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nsp") ||
+                        (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".pfs0") ||
+                        (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".xci"))
                     {
                         IFileSystem pfs;
 
-                        if (System.IO.Path.GetExtension(_title.Path).ToLower() == ".xci")
+                        if (System.IO.Path.GetExtension(_titleFilePath) == ".xci")
                         {
                             Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
 
@@ -235,7 +249,7 @@ namespace Ryujinx.Ui.Widgets
                             }
                         }
                     }
-                    else if (System.IO.Path.GetExtension(_title.Path).ToLower() == ".nca")
+                    else if (System.IO.Path.GetExtension(_titleFilePath).ToLower() == ".nca")
                     {
                         mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
                     }
@@ -252,11 +266,7 @@ namespace Ryujinx.Ui.Widgets
                         return;
                     }
 
-                    IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                        ? IntegrityCheckLevel.ErrorOnInvalid
-                        : IntegrityCheckLevel.None;
-
-                    (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _);
+                    (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
 
                     if (updatePatchNca != null)
                     {
@@ -450,44 +460,44 @@ namespace Ryujinx.Ui.Widgets
         private void OpenSaveUserDir_Clicked(object sender, EventArgs args)
         {
             var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
-            var saveDataFilter = SaveDataFilter.Make(_title.Id, saveType: default, userId, saveDataId: default, index: default);
+            var saveDataFilter = SaveDataFilter.Make(_titleId, saveType: default, userId, saveDataId: default, index: default);
 
             OpenSaveDir(in saveDataFilter);
         }
 
         private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args)
         {
-            var saveDataFilter = SaveDataFilter.Make(_title.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default);
+            var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Device, userId: default, saveDataId: default, index: default);
 
             OpenSaveDir(in saveDataFilter);
         }
 
         private void OpenSaveBcatDir_Clicked(object sender, EventArgs args)
         {
-            var saveDataFilter = SaveDataFilter.Make(_title.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
+            var saveDataFilter = SaveDataFilter.Make(_titleId, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
 
             OpenSaveDir(in saveDataFilter);
         }
 
         private void ManageTitleUpdates_Clicked(object sender, EventArgs args)
         {
-            new TitleUpdateWindow(_parent, _virtualFileSystem, _title).Show();
+            new TitleUpdateWindow(_parent, _virtualFileSystem, _titleIdText, _titleName).Show();
         }
 
         private void ManageDlc_Clicked(object sender, EventArgs args)
         {
-            new DlcWindow(_virtualFileSystem, _title.IdString, _title).Show();
+            new DlcWindow(_virtualFileSystem, _titleIdText, _titleName).Show();
         }
 
         private void ManageCheats_Clicked(object sender, EventArgs args)
         {
-            new CheatWindow(_virtualFileSystem, _title.Id, _title.Name, _title.Path).Show();
+            new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
         }
 
         private void OpenTitleModDir_Clicked(object sender, EventArgs args)
         {
             string modsBasePath = ModLoader.GetModsBasePath();
-            string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _title.IdString);
+            string titleModsPath = ModLoader.GetTitleDir(modsBasePath, _titleIdText);
 
             OpenHelper.OpenFolder(titleModsPath);
         }
@@ -495,7 +505,7 @@ namespace Ryujinx.Ui.Widgets
         private void OpenTitleSdModDir_Clicked(object sender, EventArgs args)
         {
             string sdModsBasePath = ModLoader.GetSdModsBasePath();
-            string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _title.IdString);
+            string titleModsPath = ModLoader.GetTitleDir(sdModsBasePath, _titleIdText);
 
             OpenHelper.OpenFolder(titleModsPath);
         }
@@ -517,7 +527,7 @@ namespace Ryujinx.Ui.Widgets
 
         private void OpenPtcDir_Clicked(object sender, EventArgs args)
         {
-            string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu");
+            string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu");
 
             string mainPath = System.IO.Path.Combine(ptcDir, "0");
             string backupPath = System.IO.Path.Combine(ptcDir, "1");
@@ -534,7 +544,7 @@ namespace Ryujinx.Ui.Widgets
 
         private void OpenShaderCacheDir_Clicked(object sender, EventArgs args)
         {
-            string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "shader");
+            string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader");
 
             if (!Directory.Exists(shaderCacheDir))
             {
@@ -546,10 +556,10 @@ namespace Ryujinx.Ui.Widgets
 
         private void PurgePtcCache_Clicked(object sender, EventArgs args)
         {
-            DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu", "0"));
-            DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "cpu", "1"));
+            DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "0"));
+            DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "cpu", "1"));
 
-            MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_title.Name}</b>\n\nAre you sure you want to proceed?");
+            MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
 
             List<FileInfo> cacheFiles = new();
 
@@ -583,9 +593,9 @@ namespace Ryujinx.Ui.Widgets
 
         private void PurgeShaderCache_Clicked(object sender, EventArgs args)
         {
-            DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _title.IdString, "cache", "shader"));
+            DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleIdText, "cache", "shader"));
 
-            using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_title.Name}</b>\n\nAre you sure you want to proceed?");
+            using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n<b>{_titleName}</b>\n\nAre you sure you want to proceed?");
 
             List<DirectoryInfo> oldCacheDirectories = new();
             List<FileInfo> newCacheFiles = new();
@@ -627,11 +637,8 @@ namespace Ryujinx.Ui.Widgets
 
         private void CreateShortcut_Clicked(object sender, EventArgs args)
         {
-            IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
-            byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_title.Path, ConfigurationState.Instance.System.Language, _title.Id);
-            ShortcutHelper.CreateAppShortcut(_title.Path, _title.Name, _title.IdString, appIcon);
+            byte[] appIcon = new ApplicationLibrary(_virtualFileSystem).GetApplicationIcon(_titleFilePath, ConfigurationState.Instance.System.Language);
+            ShortcutHelper.CreateAppShortcut(_titleFilePath, _titleName, _titleIdText, appIcon);
         }
     }
 }
diff --git a/src/Ryujinx/Ui/Windows/CheatWindow.cs b/src/Ryujinx/Ui/Windows/CheatWindow.cs
index 9bbae1c6c..1eca732b2 100644
--- a/src/Ryujinx/Ui/Windows/CheatWindow.cs
+++ b/src/Ryujinx/Ui/Windows/CheatWindow.cs
@@ -1,9 +1,7 @@
 using Gtk;
-using LibHac.Tools.FsSystem;
 using Ryujinx.HLE.FileSystem;
 using Ryujinx.HLE.HOS;
 using Ryujinx.Ui.App.Common;
-using Ryujinx.Ui.Common.Configuration;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -29,13 +27,8 @@ namespace Ryujinx.Ui.Windows
         private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow"))
         {
             builder.Autoconnect(this);
-
-            IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                ? IntegrityCheckLevel.ErrorOnInvalid
-                : IntegrityCheckLevel.None;
-
             _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
-            _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath)}";
+            _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";
 
             string modsBasePath = ModLoader.GetModsBasePath();
             string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));
diff --git a/src/Ryujinx/Ui/Windows/DlcWindow.cs b/src/Ryujinx/Ui/Windows/DlcWindow.cs
index dbffc4209..9f7179467 100644
--- a/src/Ryujinx/Ui/Windows/DlcWindow.cs
+++ b/src/Ryujinx/Ui/Windows/DlcWindow.cs
@@ -9,12 +9,9 @@ using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
-using Ryujinx.Ui.App.Common;
 using Ryujinx.Ui.Widgets;
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.IO;
 using GUI = Gtk.Builder.ObjectAttribute;
 
@@ -23,7 +20,7 @@ namespace Ryujinx.Ui.Windows
     public class DlcWindow : Window
     {
         private readonly VirtualFileSystem _virtualFileSystem;
-        private readonly string _applicationId;
+        private readonly string _titleId;
         private readonly string _dlcJsonPath;
         private readonly List<DownloadableContentContainer> _dlcContainerList;
 
@@ -35,16 +32,16 @@ namespace Ryujinx.Ui.Windows
         [GUI] TreeSelection _dlcTreeSelection;
 #pragma warning restore CS0649, IDE0044
 
-        public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData title) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, title) { }
+        public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.DlcWindow.glade"), virtualFileSystem, titleId, titleName) { }
 
-        private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData title) : base(builder.GetRawOwnedObject("_dlcWindow"))
+        private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_dlcWindow"))
         {
             builder.Autoconnect(this);
 
-            _applicationId = applicationId;
+            _titleId = titleId;
             _virtualFileSystem = virtualFileSystem;
-            _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json");
-            _baseTitleInfoLabel.Text = $"DLC Available for {title.Name} [{applicationId.ToUpper()}]";
+            _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "dlc.json");
+            _baseTitleInfoLabel.Text = $"DLC Available for {titleName} [{titleId.ToUpper()}]";
 
             try
             {
@@ -75,12 +72,9 @@ namespace Ryujinx.Ui.Windows
             };
 
             _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0);
-            _dlcTreeView.AppendColumn("ApplicationId", new CellRendererText(), "text", 1);
+            _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1);
             _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2);
 
-            // NOTE: Try to load downloadable contents from PFS first.
-            AddDlc(title.Path, true);
-
             foreach (DownloadableContentContainer dlcContainer in _dlcContainerList)
             {
                 if (File.Exists(dlcContainer.ContainerPath))
@@ -95,10 +89,7 @@ namespace Ryujinx.Ui.Windows
                     using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath);
 
                     PartitionFileSystem pfs = new();
-                    if (pfs.Initialize(containerFile.AsStorage()).IsFailure())
-                    {
-                        continue;
-                    }
+                    pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
 
                     _virtualFileSystem.ImportTickets(pfs);
 
@@ -137,57 +128,6 @@ namespace Ryujinx.Ui.Windows
             return null;
         }
 
-        private void AddDlc(string path, bool ignoreNotFound = false)
-        {
-            if (!File.Exists(path))
-            {
-                return;
-            }
-
-            using FileStream containerFile = File.OpenRead(path);
-
-            PartitionFileSystem pfs = new();
-            pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
-
-            bool containsDlc = false;
-
-            _virtualFileSystem.ImportTickets(pfs);
-
-            TreeIter? parentIter = null;
-
-            foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
-            {
-                using var ncaFile = new UniqueRef<IFile>();
-
-                pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
-                Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
-
-                if (nca == null)
-                {
-                    continue;
-                }
-
-                if (nca.Header.ContentType == NcaContentType.PublicData)
-                {
-                    if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL))
-                    {
-                        break;
-                    }
-
-                    parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", path);
-
-                    ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
-                    containsDlc = true;
-                }
-            }
-
-            if (!containsDlc && !ignoreNotFound)
-            {
-                GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!");
-            }
-        }
-
         private void AddButton_Clicked(object sender, EventArgs args)
         {
             FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel")
@@ -207,7 +147,52 @@ namespace Ryujinx.Ui.Windows
             {
                 foreach (string containerPath in fileChooser.Filenames)
                 {
-                    AddDlc(containerPath);
+                    if (!File.Exists(containerPath))
+                    {
+                        return;
+                    }
+
+                    using FileStream containerFile = File.OpenRead(containerPath);
+
+                    PartitionFileSystem pfs = new();
+                    pfs.Initialize(containerFile.AsStorage()).ThrowIfFailure();
+                    bool containsDlc = false;
+
+                    _virtualFileSystem.ImportTickets(pfs);
+
+                    TreeIter? parentIter = null;
+
+                    foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
+                    {
+                        using var ncaFile = new UniqueRef<IFile>();
+
+                        pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+                        Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), containerPath);
+
+                        if (nca == null)
+                        {
+                            continue;
+                        }
+
+                        if (nca.Header.ContentType == NcaContentType.PublicData)
+                        {
+                            if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000).ToString("x16") != _titleId)
+                            {
+                                break;
+                            }
+
+                            parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", containerPath);
+
+                            ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath);
+                            containsDlc = true;
+                        }
+                    }
+
+                    if (!containsDlc)
+                    {
+                        GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!");
+                    }
                 }
             }
 
diff --git a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs
index 2f7f14f1f..51918eeab 100644
--- a/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs
+++ b/src/Ryujinx/Ui/Windows/TitleUpdateWindow.cs
@@ -4,15 +4,12 @@ using LibHac.Fs;
 using LibHac.Fs.Fsa;
 using LibHac.FsSystem;
 using LibHac.Ns;
-using LibHac.Tools.Fs;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
-using Ryujinx.HLE.Loaders.Processes.Extensions;
 using Ryujinx.Ui.App.Common;
-using Ryujinx.Ui.Common.Configuration;
 using Ryujinx.Ui.Widgets;
 using System;
 using System.Collections.Generic;
@@ -27,7 +24,7 @@ namespace Ryujinx.Ui.Windows
     {
         private readonly MainWindow _parent;
         private readonly VirtualFileSystem _virtualFileSystem;
-        private readonly ApplicationData _title;
+        private readonly string _titleId;
         private readonly string _updateJsonPath;
 
         private TitleUpdateMetadata _titleUpdateWindowData;
@@ -41,17 +38,17 @@ namespace Ryujinx.Ui.Windows
         [GUI] RadioButton _noUpdateRadioButton;
 #pragma warning restore CS0649, IDE0044
 
-        public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, applicationData) { }
+        public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, titleId, titleName) { }
 
-        private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_titleUpdateWindow"))
+        private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, string titleId, string titleName) : base(builder.GetRawOwnedObject("_titleUpdateWindow"))
         {
             _parent = parent;
 
             builder.Autoconnect(this);
 
-            _title = applicationData;
+            _titleId = titleId;
             _virtualFileSystem = virtualFileSystem;
-            _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json");
+            _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _titleId, "updates.json");
             _radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
 
             try
@@ -67,10 +64,7 @@ namespace Ryujinx.Ui.Windows
                 };
             }
 
-            _baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]";
-
-            // Try to get updates from PFS first
-            AddUpdate(_title.Path, true);
+            _baseTitleInfoLabel.Text = $"Updates Available for {titleName} [{titleId.ToUpper()}]";
 
             foreach (string path in _titleUpdateWindowData.Paths)
             {
@@ -90,41 +84,18 @@ namespace Ryujinx.Ui.Windows
             }
         }
 
-        private void AddUpdate(string path, bool ignoreNotFound = false)
+        private void AddUpdate(string path)
         {
             if (File.Exists(path))
             {
-                IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
-                    ? IntegrityCheckLevel.ErrorOnInvalid
-                    : IntegrityCheckLevel.None;
-
                 using FileStream file = new(path, FileMode.Open, FileAccess.Read);
 
-                IFileSystem pfs;
+                PartitionFileSystem nsp = new();
+                nsp.Initialize(file.AsStorage()).ThrowIfFailure();
 
                 try
                 {
-                    if (System.IO.Path.GetExtension(path).ToLower() == ".xci")
-                    {
-                        pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
-                    }
-                    else
-                    {
-                        var pfsTemp = new PartitionFileSystem();
-                        pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure();
-                        pfs = pfsTemp;
-                    }
-
-                    Dictionary<ulong, ContentCollection> updates = pfs.GetUpdateData(_virtualFileSystem, checkLevel);
-
-                    Nca patchNca = null;
-                    Nca controlNca = null;
-
-                    if (updates.TryGetValue(_title.Id, out ContentCollection update))
-                    {
-                        patchNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Program);
-                        controlNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Control);
-                    }
+                    (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, nsp, _titleId, 0);
 
                     if (controlNca != null && patchNca != null)
                     {
@@ -135,14 +106,7 @@ namespace Ryujinx.Ui.Windows
                         controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
                         nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
 
-                        string radioLabel = $"Version {controlData.DisplayVersionString.ToString()} - {path}";
-
-                        if (System.IO.Path.GetExtension(path).ToLower() == ".xci")
-                        {
-                            radioLabel = "Bundled: " + radioLabel;
-                        }
-
-                        RadioButton radioButton = new(radioLabel);
+                        RadioButton radioButton = new($"Version {controlData.DisplayVersionString.ToString()} - {path}");
                         radioButton.JoinGroup(_noUpdateRadioButton);
 
                         _availableUpdatesBox.Add(radioButton);
@@ -153,10 +117,7 @@ namespace Ryujinx.Ui.Windows
                     }
                     else
                     {
-                        if (!ignoreNotFound)
-                        {
-                            GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
-                        }
+                        GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!");
                     }
                 }
                 catch (Exception exception)