Kaynağa Gözat

Fix application list (#891)

* Fix application list

* Convert file extensions to lowercase before comparing

* AcK's requested changes

* fixed bug found by gdkchan's requested changes

* Account for mismatch between LibHac.TitleLanguage and ...System.Language
Xpl0itR 6 yıl önce
ebeveyn
işleme
7b0576db71

+ 7 - 7
Ryujinx.HLE/HOS/Horizon.cs

@@ -617,19 +617,19 @@ namespace Ryujinx.HLE.HOS
                                     metaData.TitleName = nacp.Titles.ToArray().FirstOrDefault(x => x.Name[0] != 0).Name.ToString();
                                 }
 
-                                metaData.Aci0.TitleId = nacp.PresenceGroupId;
-
-                                if (metaData.Aci0.TitleId == 0)
+                                if (nacp.PresenceGroupId != 0)
+                                {
+                                    metaData.Aci0.TitleId = nacp.PresenceGroupId;
+                                }
+                                else if (nacp.SaveDataOwnerId.Value != 0)
                                 {
                                     metaData.Aci0.TitleId = nacp.SaveDataOwnerId.Value;
                                 }
-
-                                if (metaData.Aci0.TitleId == 0)
+                                else if (nacp.AddOnContentBaseId != 0)
                                 {
                                     metaData.Aci0.TitleId = nacp.AddOnContentBaseId - 0x1000;
                                 }
-
-                                if (metaData.Aci0.TitleId.ToString("x16") == "fffffffffffff000")
+                                else
                                 {
                                     metaData.Aci0.TitleId = 0000000000000000;
                                 }

+ 2 - 4
Ryujinx/Ui/ApplicationAddedEventArgs.cs

@@ -4,8 +4,6 @@ namespace Ryujinx.Ui
 {
     public class ApplicationAddedEventArgs : EventArgs
     {
-        public ApplicationData AppData       { get; set; }
-        public int             NumAppsFound  { get; set; }
-        public int             NumAppsLoaded { get; set; }
+        public ApplicationData AppData { get; set; }
     }
-}
+}

+ 10 - 0
Ryujinx/Ui/ApplicationCountUpdatedEventArgs.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Ryujinx.Ui
+{
+    public class ApplicationCountUpdatedEventArgs : EventArgs
+    {
+        public int NumAppsFound  { get; set; }
+        public int NumAppsLoaded { get; set; }
+    }
+}

+ 200 - 142
Ryujinx/Ui/ApplicationLibrary.cs

@@ -26,7 +26,8 @@ namespace Ryujinx.Ui
 {
     public class ApplicationLibrary
     {
-        public static event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
+        public static event EventHandler<ApplicationAddedEventArgs>        ApplicationAdded;
+        public static event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
 
         private static readonly byte[] _nspIcon = GetResourceBytes("Ryujinx.Ui.assets.NSPIcon.png");
         private static readonly byte[] _xciIcon = GetResourceBytes("Ryujinx.Ui.assets.XCIIcon.png");
@@ -36,12 +37,14 @@ namespace Ryujinx.Ui
 
         private static VirtualFileSystem _virtualFileSystem;
         private static Language          _desiredTitleLanguage;
+        private static bool              _loadingError;
 
         public static void LoadApplications(List<string> appDirs, VirtualFileSystem virtualFileSystem, Language desiredTitleLanguage)
         {
             int numApplicationsFound  = 0;
             int numApplicationsLoaded = 0;
 
+            _loadingError         = false;
             _virtualFileSystem    = virtualFileSystem;
             _desiredTitleLanguage = desiredTitleLanguage;
 
@@ -49,7 +52,7 @@ namespace Ryujinx.Ui
             List<string> applications = new List<string>();
             foreach (string appDir in appDirs)
             {
-                if (Directory.Exists(appDir) == false)
+                if (!Directory.Exists(appDir))
                 {
                     Logger.PrintWarning(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\"");
 
@@ -58,67 +61,16 @@ namespace Ryujinx.Ui
 
                 foreach (string app in Directory.GetFiles(appDir, "*.*", SearchOption.AllDirectories))
                 {
-                    if ((Path.GetExtension(app) == ".xci") ||
-                        (Path.GetExtension(app) == ".nro") ||
-                        (Path.GetExtension(app) == ".nso") ||
-                        (Path.GetFileName(app)  == "hbl.nsp"))
+                    if ((Path.GetExtension(app).ToLower() == ".nsp") ||
+                        (Path.GetExtension(app).ToLower() == ".pfs0")||
+                        (Path.GetExtension(app).ToLower() == ".xci") ||
+                        (Path.GetExtension(app).ToLower() == ".nca") ||
+                        (Path.GetExtension(app).ToLower() == ".nro") ||
+                        (Path.GetExtension(app).ToLower() == ".nso"))
                     {
                         applications.Add(app);
                         numApplicationsFound++;
                     }
-                    else if ((Path.GetExtension(app) == ".nsp") || (Path.GetExtension(app) == ".pfs0"))
-                    {
-                        try
-                        {
-                            bool hasMainNca = false;
-
-                            PartitionFileSystem nsp = new PartitionFileSystem(new FileStream(app, FileMode.Open, FileAccess.Read).AsStorage());
-                            foreach (DirectoryEntryEx fileEntry in nsp.EnumerateEntries("/", "*.nca"))
-                            {
-                                nsp.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure();
-
-                                Nca nca       = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
-                                int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
-
-                                if (nca.Header.ContentType == NcaContentType.Program && !nca.Header.GetFsHeader(dataIndex).IsPatchSection())
-                                {
-                                    hasMainNca = true;
-                                }
-                            }
-
-                            if (!hasMainNca)
-                            {
-                                continue;
-                            }
-                        }
-                        catch (InvalidDataException)
-                        {
-                            Logger.PrintWarning(LogClass.Application, $"{app}: The header key is incorrect or missing and therefore the NCA header content type check has failed.");
-                        }
-
-                        applications.Add(app);
-                        numApplicationsFound++;
-                    }
-                    else if (Path.GetExtension(app) == ".nca")
-                    {
-                        try
-                        {
-                            Nca nca       = new Nca(_virtualFileSystem.KeySet, new FileStream(app, FileMode.Open, FileAccess.Read).AsStorage());
-                            int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
-
-                            if (nca.Header.ContentType != NcaContentType.Program || nca.Header.GetFsHeader(dataIndex).IsPatchSection())
-                            {
-                                continue;
-                            }
-                        }
-                        catch (InvalidDataException)
-                        {
-                            Logger.PrintWarning(LogClass.Application, $"{app}: The header key is incorrect or missing and therefore the NCA header content type check has failed.");
-                        }
-
-                        applications.Add(app);
-                        numApplicationsFound++;
-                    }
                 }
             }
 
@@ -135,15 +87,16 @@ namespace Ryujinx.Ui
 
                 using (FileStream file = new FileStream(applicationPath, FileMode.Open, FileAccess.Read))
                 {
-                    if ((Path.GetExtension(applicationPath) == ".nsp")  ||
-                        (Path.GetExtension(applicationPath) == ".pfs0") ||
-                        (Path.GetExtension(applicationPath) == ".xci"))
+                    if ((Path.GetExtension(applicationPath).ToLower() == ".nsp")  ||
+                        (Path.GetExtension(applicationPath).ToLower() == ".pfs0") ||
+                        (Path.GetExtension(applicationPath).ToLower() == ".xci"))
                     {
                         try
                         {
                             PartitionFileSystem pfs;
-                             
-                            if (Path.GetExtension(applicationPath) == ".xci")
+                            bool isExeFs = false;
+
+                            if (Path.GetExtension(applicationPath).ToLower() == ".xci")
                             {
                                 Xci xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage());
 
@@ -152,13 +105,41 @@ namespace Ryujinx.Ui
                             else
                             {
                                 pfs = new PartitionFileSystem(file.AsStorage());
-                            }
 
-                            // Store the ControlFS in variable called controlFs
-                            IFileSystem controlFs = GetControlFs(pfs);
+                                // 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")
+                                    {
+                                        pfs.OpenFile(out IFile ncaFile, fileEntry.FullPath, OpenMode.Read).ThrowIfFailure();
+
+                                        Nca nca       = new Nca(_virtualFileSystem.KeySet, ncaFile.AsStorage());
+                                        int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
+
+                                        if (nca.Header.ContentType == NcaContentType.Program && !nca.Header.GetFsHeader(dataIndex).IsPatchSection())
+                                        {
+                                            hasMainNca = true;
+
+                                            break;
+                                        }
+                                    }
+                                    else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main")
+                                    {
+                                        isExeFs = true;
+                                    }
+                                }
+
+                                if (!hasMainNca && !isExeFs)
+                                {
+                                    numApplicationsFound--;
+                                    
+                                    continue;
+                                }
+                            }
 
-                            // If this is null then this is probably not a normal NSP, it's probably an ExeFS as an NSP
-                            if (controlFs == null)
+                            if (isExeFs)
                             {
                                 applicationIcon = _nspIcon;
 
@@ -174,6 +155,9 @@ namespace Ryujinx.Ui
                             }
                             else
                             {
+                                // Store the ControlFS in variable called controlFs
+                                IFileSystem controlFs = GetControlFs(pfs);
+
                                 // Creates NACP class from the NACP file
                                 controlFs.OpenFile(out IFile controlNacpFile, "/control.nacp", OpenMode.Read).ThrowIfFailure();
 
@@ -182,31 +166,7 @@ namespace Ryujinx.Ui
                                 // Get the title name, title ID, developer name and version number from the NACP
                                 version = controlData.DisplayVersion;
 
-                                titleName = controlData.Descriptions[(int)_desiredTitleLanguage].Title;
-
-                                if (string.IsNullOrWhiteSpace(titleName))
-                                {
-                                    titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
-                                }
-
-                                titleId = controlData.PresenceGroupId.ToString("x16");
-
-                                if (string.IsNullOrWhiteSpace(titleId))
-                                {
-                                    titleId = controlData.SaveDataOwnerId.ToString("x16");
-                                }
-
-                                if (string.IsNullOrWhiteSpace(titleId))
-                                {
-                                    titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16");
-                                }
-
-                                developer = controlData.Descriptions[(int)_desiredTitleLanguage].Developer;
-
-                                if (string.IsNullOrWhiteSpace(developer))
-                                {
-                                    developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer;
-                                }
+                                GetNameIdDeveloper(controlData, out titleName, out titleId, out developer);
 
                                 // Read the icon from the ControlFS and store it as a byte array
                                 try
@@ -244,25 +204,35 @@ namespace Ryujinx.Ui
 
                                     if (applicationIcon == null)
                                     {
-                                        applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon;
+                                        applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon;
                                     }
                                 }
                             }
                         }
                         catch (MissingKeyException exception)
                         {
-                            applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon;
+                            applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon;
 
                             Logger.PrintWarning(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
                         }
                         catch (InvalidDataException)
                         {
-                            applicationIcon = Path.GetExtension(applicationPath) == ".xci" ? _xciIcon : _nspIcon;
+                            applicationIcon = Path.GetExtension(applicationPath).ToLower() == ".xci" ? _xciIcon : _nspIcon;
 
                             Logger.PrintWarning(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.PrintError(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+                            Logger.PrintDebug(LogClass.Application, exception.ToString());
+                            
+                            numApplicationsFound--;
+                            _loadingError = true;
+
+                            continue;
+                        }
                     }
-                    else if (Path.GetExtension(applicationPath) == ".nro")
+                    else if (Path.GetExtension(applicationPath).ToLower() == ".nro")
                     {
                         BinaryReader reader = new BinaryReader(file);
 
@@ -273,67 +243,87 @@ namespace Ryujinx.Ui
                             return reader.ReadBytes(size);
                         }
 
-                        file.Seek(24, SeekOrigin.Begin);
-                        int assetOffset = reader.ReadInt32();
-
-                        if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
+                        try
                         {
-                            byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
-
-                            long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
-                            long iconSize   = BitConverter.ToInt64(iconSectionInfo, 8);
+                            file.Seek(24, SeekOrigin.Begin);
 
-                            ulong nacpOffset = reader.ReadUInt64();
-                            ulong nacpSize   = reader.ReadUInt64();
+                            int assetOffset = reader.ReadInt32();
 
-                            // Reads and stores game icon as byte array
-                            applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
-
-                            // Creates memory stream out of byte array which is the NACP
-                            using (MemoryStream stream = new MemoryStream(Read(assetOffset + (int)nacpOffset, (int)nacpSize)))
+                            if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
                             {
-                                // Creates NACP class from the memory stream
-                                Nacp controlData = new Nacp(stream);
+                                byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
 
-                                // Get the title name, title ID, developer name and version number from the NACP
-                                version = controlData.DisplayVersion;
+                                long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
+                                long iconSize   = BitConverter.ToInt64(iconSectionInfo, 8);
+
+                                ulong nacpOffset = reader.ReadUInt64();
+                                ulong nacpSize   = reader.ReadUInt64();
 
-                                titleName = controlData.Descriptions[(int)_desiredTitleLanguage].Title;
+                                // Reads and stores game icon as byte array
+                                applicationIcon = Read(assetOffset + iconOffset, (int) iconSize);
 
-                                if (string.IsNullOrWhiteSpace(titleName))
+                                // Creates memory stream out of byte array which is the NACP
+                                using (MemoryStream stream = new MemoryStream(Read(assetOffset + (int) nacpOffset, (int) nacpSize)))
                                 {
-                                    titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
-                                }
+                                    // Creates NACP class from the memory stream
+                                    Nacp controlData = new Nacp(stream);
 
-                                titleId = controlData.PresenceGroupId.ToString("x16");
+                                    // Get the title name, title ID, developer name and version number from the NACP
+                                    version = controlData.DisplayVersion;
 
-                                if (string.IsNullOrWhiteSpace(titleId))
-                                {
-                                    titleId = controlData.SaveDataOwnerId.ToString("x16");
+                                    GetNameIdDeveloper(controlData, out titleName, out titleId, out developer);
                                 }
+                            }
+                            else
+                            {
+                                applicationIcon = _nroIcon;
+                                titleName       = Path.GetFileNameWithoutExtension(applicationPath);
+                            }
+                        }
+                        catch
+                        {
+                            Logger.PrintError(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
 
-                                if (string.IsNullOrWhiteSpace(titleId))
-                                {
-                                    titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16");
-                                }
+                            numApplicationsFound--;
+
+                            continue;
+                        }
+                    }
+                    else if (Path.GetExtension(applicationPath).ToLower() == ".nca") 
+                    {
+                        try
+                        {
+                            Nca nca       = new Nca(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
+                            int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
 
-                                developer = controlData.Descriptions[(int)_desiredTitleLanguage].Developer;
+                            if (nca.Header.ContentType != NcaContentType.Program || nca.Header.GetFsHeader(dataIndex).IsPatchSection())
+                            {
+                                numApplicationsFound--;
 
-                                if (string.IsNullOrWhiteSpace(developer))
-                                {
-                                    developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer;
-                                }
+                                continue;
                             }
                         }
-                        else
+                        catch (InvalidDataException)
                         {
-                            applicationIcon = _nroIcon;
+                            Logger.PrintWarning(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.PrintError(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
+
+                            numApplicationsFound--;
+                            _loadingError = true;
+                                
+                            continue;
+                        }
+
+                        applicationIcon = _ncaIcon;
+                        titleName       = Path.GetFileNameWithoutExtension(applicationPath);
                     }
-                    // If its an NCA or NSO we just set defaults
-                    else if ((Path.GetExtension(applicationPath) == ".nca") || (Path.GetExtension(applicationPath) == ".nso"))
+                    // If its an NSO we just set defaults
+                    else if (Path.GetExtension(applicationPath).ToLower() == ".nso")
                     {
-                        applicationIcon = Path.GetExtension(applicationPath) == ".nca" ? _ncaIcon : _nsoIcon;
+                        applicationIcon = _nsoIcon;
                         titleName       = Path.GetFileNameWithoutExtension(applicationPath);
                     }
                 }
@@ -373,12 +363,30 @@ namespace Ryujinx.Ui
                 numApplicationsLoaded++;
 
                 OnApplicationAdded(new ApplicationAddedEventArgs()
-                { 
-                    AppData       = data,
+                {
+                    AppData = data
+                });
+
+                OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs()
+                {
                     NumAppsFound  = numApplicationsFound,
                     NumAppsLoaded = numApplicationsLoaded
                 });
             }
+
+            OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs()
+            {
+                NumAppsFound  = numApplicationsFound,
+                NumAppsLoaded = numApplicationsLoaded
+            });
+
+            if (_loadingError)
+            {
+                Gtk.Application.Invoke(delegate
+                {
+                    GtkDialog.CreateErrorDialog("One or more files encountered were not of a valid type, check logs for more info.");
+                });
+            }
         }
 
         protected static void OnApplicationAdded(ApplicationAddedEventArgs e)
@@ -386,6 +394,11 @@ namespace Ryujinx.Ui
             ApplicationAdded?.Invoke(null, e);
         }
 
+        protected static void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e)
+        {
+            ApplicationCountUpdated?.Invoke(null, e);
+        }
+
         private static byte[] GetResourceBytes(string resourceName)
         {
             Stream resourceStream    = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
@@ -433,7 +446,7 @@ namespace Ryujinx.Ui
         internal static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
         {
             string metadataFolder = Path.Combine(_virtualFileSystem.GetBasePath(), "games", titleId, "gui");
-            string metadataFile = Path.Combine(metadataFolder, "metadata.json");
+            string metadataFile   = Path.Combine(metadataFolder, "metadata.json");
 
             IJsonFormatterResolver resolver = CompositeResolver.Create(new[] { StandardResolver.AllowPrivateSnakeCase });
 
@@ -497,5 +510,50 @@ namespace Ryujinx.Ui
 
             return readableString;
         }
+
+        private static void GetNameIdDeveloper(Nacp controlData, out string titleName, out string titleId, out string developer)
+        {
+            Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
+
+            NacpDescription nacpDescription = controlData.Descriptions.ToList().Find(x => x.Language == desiredTitleLanguage);
+
+            if (nacpDescription != null)
+            {
+                titleName = nacpDescription.Title;
+                developer = nacpDescription.Developer;
+            }
+            else
+            {
+                titleName = null;
+                developer = null;
+            }
+
+            if (string.IsNullOrWhiteSpace(titleName))
+            {
+                titleName = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Title)).Title;
+            }
+
+            if (string.IsNullOrWhiteSpace(developer))
+            {
+                developer = controlData.Descriptions.ToList().Find(x => !string.IsNullOrWhiteSpace(x.Developer)).Developer;
+            }
+
+            if (controlData.PresenceGroupId != 0)
+            {
+                titleId = controlData.PresenceGroupId.ToString("x16");
+            }
+            else if (controlData.SaveDataOwnerId != 0)
+            {
+                titleId = controlData.SaveDataOwnerId.ToString("x16");
+            }
+            else if (controlData.AddOnContentBaseId != 0)
+            {
+                titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16");
+            }
+            else
+            {
+                titleId = "0000000000000000";
+            }
+        }
     }
 }

+ 26 - 10
Ryujinx/Ui/MainWindow.cs

@@ -72,7 +72,8 @@ namespace Ryujinx.Ui
 
             DeleteEvent += Window_Close;
 
-            ApplicationLibrary.ApplicationAdded += Application_Added;
+            ApplicationLibrary.ApplicationAdded        += Application_Added;
+            ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
 
             _gameTable.ButtonReleaseEvent += Row_Clicked;
 
@@ -135,9 +136,7 @@ namespace Ryujinx.Ui
             _tableStore.SetSortColumnId(0, SortType.Descending);
 
             UpdateColumns();
-#pragma warning disable CS4014
             UpdateGameTable();
-#pragma warning restore CS4014
 
             Task.Run(RefreshFirmwareLabel);
         }
@@ -209,7 +208,7 @@ namespace Ryujinx.Ui
             return instance;
         }
 
-        internal static async Task UpdateGameTable()
+        internal static void UpdateGameTable()
         {
             if (_updatingGameTable)
             {
@@ -220,10 +219,16 @@ namespace Ryujinx.Ui
 
             _tableStore.Clear();
 
-            await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs,
-                _virtualFileSystem, ConfigurationState.Instance.System.Language));
+            Thread applicationLibraryThread = new Thread(() =>
+            {
+                ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs,
+                    _virtualFileSystem, ConfigurationState.Instance.System.Language);
 
-            _updatingGameTable = false;
+                _updatingGameTable = false;
+            });
+            applicationLibraryThread.Name = "GUI.ApplicationLibraryThread";
+            applicationLibraryThread.IsBackground = true;
+            applicationLibraryThread.Start();
         }
 
         internal void LoadApplication(string path)
@@ -423,9 +428,22 @@ namespace Ryujinx.Ui
                     args.AppData.FileExtension,
                     args.AppData.FileSize,
                     args.AppData.Path);
+            });
+        }
 
+        private void ApplicationCount_Updated(object sender, ApplicationCountUpdatedEventArgs args)
+        {
+            Application.Invoke(delegate
+            {
                 _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
-                _progressBar.Value  = (float)args.NumAppsLoaded / args.NumAppsFound;
+                float barValue      = 0;
+
+                if (args.NumAppsFound != 0)
+                {
+                    barValue = (float)args.NumAppsLoaded / args.NumAppsFound;
+                }
+
+                _progressBar.Value = barValue;
             });
         }
 
@@ -838,9 +856,7 @@ namespace Ryujinx.Ui
 
         private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args)
         {
-#pragma warning disable CS4014
             UpdateGameTable();
-#pragma warning restore CS4014
         }
 
         private static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b)

+ 0 - 2
Ryujinx/Ui/SwitchSettings.cs

@@ -438,9 +438,7 @@ namespace Ryujinx.Ui
 
             MainWindow.SaveConfig();
             MainWindow.ApplyTheme();
-#pragma warning disable CS4014
             MainWindow.UpdateGameTable();
-#pragma warning restore CS4014
             Dispose();
         }