mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-10-01 12:30:00 +02:00
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
This commit is contained in:
parent
0202f150d4
commit
7b0576db71
6 changed files with 248 additions and 168 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Ryujinx/Ui/ApplicationCountUpdatedEventArgs.cs
Normal file
10
Ryujinx/Ui/ApplicationCountUpdatedEventArgs.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Ui
|
||||
{
|
||||
public class ApplicationCountUpdatedEventArgs : EventArgs
|
||||
{
|
||||
public int NumAppsFound { get; set; }
|
||||
public int NumAppsLoaded { get; set; }
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the ControlFS in variable called controlFs
|
||||
IFileSystem controlFs = GetControlFs(pfs);
|
||||
|
||||
// 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);
|
||||
file.Seek(24, SeekOrigin.Begin);
|
||||
|
||||
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
|
||||
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
||||
int assetOffset = reader.ReadInt32();
|
||||
|
||||
ulong nacpOffset = reader.ReadUInt64();
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
// 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);
|
||||
|
||||
titleName = controlData.Descriptions[(int)_desiredTitleLanguage].Title;
|
||||
ulong nacpOffset = reader.ReadUInt64();
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(titleName))
|
||||
// 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)))
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
applicationIcon = _nroIcon;
|
||||
titleName = Path.GetFileNameWithoutExtension(applicationPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
applicationIcon = _nroIcon;
|
||||
Logger.PrintError(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
|
||||
|
||||
numApplicationsFound--;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// If its an NCA or NSO we just set defaults
|
||||
else if ((Path.GetExtension(applicationPath) == ".nca") || (Path.GetExtension(applicationPath) == ".nso"))
|
||||
else if (Path.GetExtension(applicationPath).ToLower() == ".nca")
|
||||
{
|
||||
applicationIcon = Path.GetExtension(applicationPath) == ".nca" ? _ncaIcon : _nsoIcon;
|
||||
try
|
||||
{
|
||||
Nca nca = new Nca(_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.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||
{
|
||||
numApplicationsFound--;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
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 NSO we just set defaults
|
||||
else if (Path.GetExtension(applicationPath).ToLower() == ".nso")
|
||||
{
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -438,9 +438,7 @@ namespace Ryujinx.Ui
|
|||
|
||||
MainWindow.SaveConfig();
|
||||
MainWindow.ApplyTheme();
|
||||
#pragma warning disable CS4014
|
||||
MainWindow.UpdateGameTable();
|
||||
#pragma warning restore CS4014
|
||||
Dispose();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue