UI: Abstract applet launch logic for future potential applets
Some checks are pending
Canary release job / Create tag (push) Waiting to run
Canary release job / Release for linux-arm64 (push) Waiting to run
Canary release job / Release for linux-x64 (push) Waiting to run
Canary release job / Release for win-x64 (push) Waiting to run
Canary release job / Release MacOS universal (push) Waiting to run

Optimize locale loading (remove always loading english, that was only needed with the old system)
This commit is contained in:
Evan Husted 2024-12-25 00:56:01 -06:00
parent e6644626fc
commit 412d4065b8
3 changed files with 81 additions and 55 deletions

View file

@ -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<ApplicationControlProperty> appControl)
{
contentManager ??= _contentManager;
if (contentManager == null)
{
appData = null;
appControl = new BlitStruct<ApplicationControlProperty>(0);
return false;
}
appData = new()
{
Name = Name,
Id = ProgramId,
Path = GetContentPath(contentManager)
};
if (string.IsNullOrEmpty(appData.Path))
{
appControl = new BlitStruct<ApplicationControlProperty>(0);
return false;
}
appControl = StructHelpers.CreateCustomNacpData(Name, Version);
return true;
}
}
}

View file

@ -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<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
private string _localeLanguageCode;
@ -27,7 +25,6 @@ namespace Ryujinx.Ava.Common.Locale
public LocaleManager()
{
_localeStrings = new Dictionary<LocaleKeys, string>();
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
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<LocaleKeys>(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;

View file

@ -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);
}
}