From 412d4065b89df44ddeb86e96e8e0cc54545e8d05 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Wed, 25 Dec 2024 00:56:01 -0600 Subject: [PATCH] UI: Abstract applet launch logic for future potential applets Optimize locale loading (remove always loading english, that was only needed with the old system) --- .../Helper/AppletMetadata.cs | 64 +++++++++++++++++++ src/Ryujinx/Common/LocaleManager.cs | 47 ++++---------- .../UI/Views/Main/MainMenuBarView.axaml.cs | 25 ++------ 3 files changed, 81 insertions(+), 55 deletions(-) create mode 100644 src/Ryujinx.UI.Common/Helper/AppletMetadata.cs diff --git a/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs b/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs new file mode 100644 index 000000000..644b7fe74 --- /dev/null +++ b/src/Ryujinx.UI.Common/Helper/AppletMetadata.cs @@ -0,0 +1,64 @@ +using LibHac.Common; +using LibHac.Ncm; +using LibHac.Ns; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.HLE; +using Ryujinx.HLE.FileSystem; +using Ryujinx.UI.App.Common; + +namespace Ryujinx.UI.Common.Helper +{ + public readonly struct AppletMetadata + { + private readonly ContentManager _contentManager; + + public string Name { get; } + public ulong ProgramId { get; } + + public string Version { get; } + + public AppletMetadata(ContentManager contentManager, string name, ulong programId, string version = "1.0.0") + : this(name, programId, version) + { + _contentManager = contentManager; + } + + public AppletMetadata(string name, ulong programId, string version = "1.0.0") + { + Name = name; + ProgramId = programId; + Version = version; + } + + public string GetContentPath(ContentManager contentManager) + => (contentManager ?? _contentManager) + .GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program); + + public bool CanStart(ContentManager contentManager, out ApplicationData appData, out BlitStruct appControl) + { + contentManager ??= _contentManager; + if (contentManager == null) + { + appData = null; + appControl = new BlitStruct(0); + return false; + } + + appData = new() + { + Name = Name, + Id = ProgramId, + Path = GetContentPath(contentManager) + }; + + if (string.IsNullOrEmpty(appData.Path)) + { + appControl = new BlitStruct(0); + return false; + } + + appControl = StructHelpers.CreateCustomNacpData(Name, Version); + return true; + } + } +} diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index e788a3adc..70b04ec95 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -1,7 +1,6 @@ using Gommon; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common; -using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.UI.Common.Configuration; using System; @@ -17,7 +16,6 @@ namespace Ryujinx.Ava.Common.Locale private const string DefaultLanguageCode = "en_US"; private readonly Dictionary _localeStrings; - private Dictionary _localeDefaultStrings; private readonly ConcurrentDictionary _dynamicValues; private string _localeLanguageCode; @@ -27,7 +25,6 @@ namespace Ryujinx.Ava.Common.Locale public LocaleManager() { _localeStrings = new Dictionary(); - _localeDefaultStrings = new Dictionary(); _dynamicValues = new ConcurrentDictionary(); Load(); @@ -37,9 +34,7 @@ namespace Ryujinx.Ava.Common.Locale { var localeLanguageCode = !string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value) ? ConfigurationState.Instance.UI.LanguageCode.Value : CultureInfo.CurrentCulture.Name.Replace('-', '_'); - - // Load en_US as default, if the target language translation is missing or incomplete. - LoadDefaultLanguage(); + LoadLanguage(localeLanguageCode); // Save whatever we ended up with. @@ -66,26 +61,14 @@ namespace Ryujinx.Ava.Common.Locale } catch { - // If formatting failed use the default text instead. - if (_localeDefaultStrings.TryGetValue(key, out value)) - try - { - return string.Format(value, dynamicValue); - } - catch - { - // If formatting the default text failed return the key. - return key.ToString(); - } + // If formatting the text failed, + // continue to the below line & return the text without formatting. } return value; } - - // If the locale doesn't contain the key return the default one. - return _localeDefaultStrings.TryGetValue(key, out string defaultValue) - ? defaultValue - : key.ToString(); // If the locale text doesn't exist return the key. + + return key.ToString(); // If the locale text doesn't exist return the key. } set { @@ -114,11 +97,6 @@ namespace Ryujinx.Ava.Common.Locale return this[key]; } - private void LoadDefaultLanguage() - { - _localeDefaultStrings = LoadJsonLanguage(DefaultLanguageCode); - } - public void LoadLanguage(string languageCode) { var locale = LoadJsonLanguage(languageCode); @@ -126,7 +104,7 @@ namespace Ryujinx.Ava.Common.Locale if (locale == null) { _localeLanguageCode = DefaultLanguageCode; - locale = _localeDefaultStrings; + locale = LoadJsonLanguage(_localeLanguageCode); } else { @@ -167,15 +145,16 @@ namespace Ryujinx.Ava.Common.Locale if (!Enum.TryParse(locale.ID, out var localeKey)) continue; - localeStrings[localeKey] = - locale.Translations.TryGetValue(languageCode, out string val) && !string.IsNullOrEmpty(val) - ? val - : locale.Translations[DefaultLanguageCode]; - - if (string.IsNullOrEmpty(localeStrings[localeKey])) + var str = locale.Translations.TryGetValue(languageCode, out string val) && !string.IsNullOrEmpty(val) + ? val + : locale.Translations[DefaultLanguageCode]; + + if (string.IsNullOrEmpty(str)) { throw new Exception($"Locale key '{locale.ID}' has no valid translations for desired language {languageCode}! {DefaultLanguageCode} is an empty string or null"); } + + localeStrings[localeKey] = str; } return localeStrings; diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index fa900be81..be8a000e7 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -3,8 +3,6 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; using Gommon; -using LibHac.Ncm; -using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; @@ -12,8 +10,6 @@ using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; -using Ryujinx.HLE; -using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; @@ -124,26 +120,13 @@ namespace Ryujinx.Ava.UI.Views.Main ViewModel.LoadConfigurableHotKeys(); } + public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009); + public async void OpenMiiApplet(object sender, RoutedEventArgs e) { - const string AppletName = "miiEdit"; - const ulong AppletProgramId = 0x0100000000001009; - const string AppletVersion = "1.0.0"; - - string contentPath = ViewModel.ContentManager.GetInstalledContentPath(AppletProgramId, StorageId.BuiltInSystem, NcaContentType.Program); - - if (!string.IsNullOrEmpty(contentPath)) + if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData)) { - ApplicationData applicationData = new() - { - Name = AppletName, - Id = AppletProgramId, - Path = contentPath - }; - - var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion); - - await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); + await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); } }