diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 7f6bf5b56..6bbb88706 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -12472,6 +12472,31 @@ "zh_TW": "正在下載更新..." } }, + { + "ID": "DialogRebooterMessage", + "Translations": { + "ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل", + "de_DE": "Bitte warten Sie, der Emulator wird neu gestartet", + "el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται", + "en_US": "Please wait, the emulator is restarting", + "es_ES": "Por favor, espere, el emulador se está reiniciando", + "fr_FR": "Veuillez patienter, l'émulateur est en train de redémarrer", + "he_IL": "אנא המתן, המחקה מתארגן מחדש", + "it_IT": "Attendere prego, l'emulatore si sta riavviando", + "ja_JP": "お待ちください、エミュレーターが再起動しています", + "ko_KR": "잠시만 기다려 주세요, 에뮬레이터가 재시작 중입니다", + "no_NO": "Vennligst vent, emulatoren starter på nytt", + "pl_PL": "Proszę czekać, emulator jest w trakcie ponownego uruchamiania", + "pt_BR": "Por favor, aguarde, o emulador está reiniciando", + "ru_RU": "Пожалуйста, подождите, эмулятор перезапускается", + "sv_SE": "Vänligen vänta, emulatorn startar om", + "th_TH": "กรุณารอสักครู่, ตัวจำลองกำลังเริ่มใหม่", + "tr_TR": "Lütfen bekleyin, emülatör yeniden başlatılıyor", + "uk_UA": "Будь ласка, зачекайте, емулятор перезавантажується", + "zh_CN": "请稍等,模拟器正在重新启动", + "zh_TW": "請稍候,模擬器正在重新啟動" + } + }, { "ID": "DialogUpdaterExtractionMessage", "Translations": { @@ -19272,6 +19297,31 @@ "zh_TW": "{0} 更新程式" } }, + { + "ID": "RyujinxRebooter", + "Translations": { + "ar_SA": "إعادة تشغيل {0}", + "de_DE": "Neustart von {0}", + "el_GR": "Επανεκκίνηση {0}", + "en_US": "{0} Reboot", + "es_ES": "Reinicio de {0}", + "fr_FR": "Redémarrage de {0}", + "he_IL": "אתחול {0}", + "it_IT": "Riavvio di {0}", + "ja_JP": "{0} 再起動", + "ko_KR": "{0} 재부팅", + "no_NO": "Omstart av {0}", + "pl_PL": "Ponowne uruchomienie {0}", + "pt_BR": "Reinício de {0}", + "ru_RU": "{0} Перезагрузка", + "sv_SE": "Ominläsning av {0}", + "th_TH": "เริ่มต้นใหม่ {0}", + "tr_TR": "{0} Yeniden Başlatma", + "uk_UA": "Перезавантаження {0}", + "zh_CN": "{0} 重启", + "zh_TW": "{0} 重新啟動" + } + }, { "ID": "SettingsTabHotkeys", "Translations": { diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 4c86a6177..f60cff49b 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -54,6 +54,7 @@ namespace Ryujinx.Ava.Common.Locale SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName); SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName); + SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName); } public string this[LocaleKeys key] diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 298442b9f..f1adbe25a 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Ava public static string GlobalConfigurationPath { get; private set; } public static bool PreviewerDetached { get; private set; } public static bool UseHardwareAcceleration { get; private set; } + public static string BackendThreadingArg { get; private set; } [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); @@ -250,6 +251,11 @@ namespace Ryujinx.Ava _ => ConfigurationState.Instance.Graphics.BackendThreading }; + if (CommandLineState.OverrideBackendThreadingAfterReboot is not null) + { + BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot; + } + // Check if docked mode was overriden. if (CommandLineState.OverrideDockedMode.HasValue) ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; diff --git a/src/Ryujinx/Rebooter.cs b/src/Ryujinx/Rebooter.cs new file mode 100644 index 000000000..21fa650bd --- /dev/null +++ b/src/Ryujinx/Rebooter.cs @@ -0,0 +1,95 @@ +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.Utilities; +using SkiaSharp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ryujinx.Ava +{ + internal static class Rebooter + { + + private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); + + + public static void RebootAppWithGame(string gamePath, List args) + { + _ = Reboot(gamePath, args); + + } + + private static async Task Reboot(string gamePath, List args) + { + + bool shouldRestart = true; + + TaskDialog taskDialog = new() + { + Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter], + SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage], + IconSource = new SymbolIconSource { Symbol = Symbol.Games }, + XamlRoot = RyujinxApp.MainWindow, + }; + + if (shouldRestart) + { + List arguments = CommandLineState.Arguments.ToList(); + string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; + + // On macOS we perform the update at relaunch. + if (OperatingSystem.IsMacOS()) + { + string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); + string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app"); + string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); + string currentPid = Environment.ProcessId.ToString(); + + arguments.InsertRange(0, new List { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); + Process.Start("/bin/bash", arguments); + } + else + { + var dialogTask = taskDialog.ShowAsync(true); + await Task.Delay(500); + + // Find the process name. + string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty; + + // Some operating systems can see the renamed executable, so strip off the .ryuold if found. + if (ryuName.EndsWith(".ryuold")) + { + ryuName = ryuName[..^7]; + } + + // Fallback if the executable could not be found. + if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName))) + { + ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; + } + + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = executableDirectory, + }; + + foreach (var arg in args) + { + processStart.ArgumentList.Add(arg); + } + + processStart.ArgumentList.Add(gamePath); + + Process.Start(processStart); + } + Environment.Exit(0); + } + } + } +} diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 73455aa24..219048401 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -1523,10 +1523,16 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public void InitializeUserConfig(ApplicationData application) + public bool InitializeUserConfig(ApplicationData application) { - // Code where conditions will be met before loading the user configuration + // Code where conditions will be met before loading the user configuration (Global Config) BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value; + string BackendThreadingInit = Program.BackendThreadingArg; + + if (BackendThreadingInit is null) + { + BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString(); + } // If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting. string idGame = application.IdBaseString; @@ -1537,19 +1543,29 @@ namespace Ryujinx.Ava.UI.ViewModels } // Code where conditions will be executed after loading user configuration - if (ConfigurationState.Instance.Graphics.BackendThreading != backendThreadingValue) + if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit) { - /* - * The function to restart the emulator together with the selected game - Task.Run(async () => await Rebooter.RebootAppWithGame(application.Path)); - */ + + List Arguments = new List + { + "--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading + }; + + Rebooter.RebootAppWithGame(application.Path, Arguments); + + return true; } + + return false; } public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct? customNacpData = null) { - InitializeUserConfig(application); + if (InitializeUserConfig(application)) + { + return; + } if (AppHost != null) { diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index e5f6460ac..aea2afec7 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -71,13 +71,14 @@ namespace Ryujinx.Ava.UI.ViewModels public SettingsHacksViewModel DirtyHacks { get; } + private readonly bool _isGameRunning; private Bitmap _gameIcon; private string _gameTitle; + private string _gamePath; private string _gameId; - private readonly bool _isGameRunning; - public bool IsGameRunning => _isGameRunning; public Bitmap GameIcon => _gameIcon; + public string GamePath => _gamePath; public string GameTitle => _gameTitle; public string GameId => _gameId; @@ -377,7 +378,8 @@ namespace Ryujinx.Ava.UI.ViewModels } _isGameRunning = gameRunning; - _gameTitle = gameName; + _gamePath = gamePath; + _gameTitle = gameName; _gameId = gameId; if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs index ee242157f..6c54e78a5 100644 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Ava.Utilities public static bool? OverrideHardwareAcceleration { get; private set; } public static string OverrideGraphicsBackend { get; private set; } public static string OverrideBackendThreading { get; private set; } + public static string OverrideBackendThreadingAfterReboot { get; private set; } public static string OverridePPTC { get; private set; } public static string OverrideMemoryManagerMode { get; private set; } public static string OverrideSystemRegion { get; private set; } @@ -99,6 +100,16 @@ namespace Ryujinx.Ava.Utilities OverrideBackendThreading = args[++i]; break; + case "--bt": + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); + + continue; + } + + OverrideBackendThreadingCustom = args[++i]; + break; case "--pptc": if (i + 1 >= args.Length) {