diff --git a/Directory.Packages.props b/Directory.Packages.props index fd61602a8..0029ecee2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index 64a76a3e4..fea7895c4 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Common.Logging { public static class Logger { + public static readonly TextWriter WriterProxy = new TextWriterProxy(); + private static readonly Stopwatch _time; private static readonly bool[] _enabledClasses; diff --git a/src/Ryujinx.Common/Logging/TextWriterProxy.cs b/src/Ryujinx.Common/Logging/TextWriterProxy.cs new file mode 100644 index 000000000..c923983fa --- /dev/null +++ b/src/Ryujinx.Common/Logging/TextWriterProxy.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Text; + +namespace Ryujinx.Common.Logging +{ + internal class TextWriterProxy : TextWriter + { + public override Encoding Encoding => Console.OutputEncoding; + + public override void Write(string value) + { + if (value is null) return; + + foreach (var line in value.Split(Console.Out.NewLine)) + { + Logger.Info?.PrintMsg(LogClass.Application, line); + } + } + } +} diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 8d03f81da..3949bf6d8 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -98,7 +98,14 @@ namespace Ryujinx.Ava return 0; } - Initialize(args); + try + { + Initialize(args); + } + catch + { + return 0; + } LoggerAdapter.Register(); @@ -143,7 +150,7 @@ namespace Ryujinx.Ava DiscordIntegrationModule.EmulatorStartedAt = Timestamps.Now; // Parse arguments - CommandLineState.ParseArguments(args); + RyujinxOptions.Read(args, out RyujinxOptions options); if (OperatingSystem.IsMacOS()) { @@ -163,7 +170,7 @@ namespace Ryujinx.Ava AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); // Setup base data directory. - AppDataManager.Initialize(CommandLineState.BaseDirPathArg); + AppDataManager.Initialize(options.EmuDataBaseDirPath); // Initialize the configuration. ConfigurationState.Initialize(); @@ -196,9 +203,9 @@ namespace Ryujinx.Ava } } - if (CommandLineState.LaunchPathArg != null) + if (options.LaunchPath != null) { - MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.LaunchApplicationId, CommandLineState.StartFullscreenArg); + MainWindow.DeferLoadApplication(options.LaunchPath, options.LaunchApplicationId, options.StartFullscreen); } } @@ -222,7 +229,6 @@ namespace Ryujinx.Ava public static void ReloadConfig(bool isRunGameWithCustomConfig = false) { - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); @@ -273,74 +279,50 @@ namespace Ryujinx.Ava UseHardwareAcceleration = ConfigurationState.Instance.EnableHardwareAcceleration; // Check if graphics backend was overridden - if (CommandLineState.OverrideGraphicsBackend is not null) - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = CommandLineState.OverrideGraphicsBackend.ToLower() switch - { - "opengl" => GraphicsBackend.OpenGl, - "vulkan" => GraphicsBackend.Vulkan, - _ => ConfigurationState.Instance.Graphics.GraphicsBackend - }; + if (RyujinxOptions.Shared.GraphicsBackendOverride is not null) + ConfigurationState.Instance.Graphics.GraphicsBackend.Value = + RyujinxOptions.Shared.GraphicsBackendOverride.Value; // Check if backend threading was overridden - if (CommandLineState.OverrideBackendThreading is not null) - ConfigurationState.Instance.Graphics.BackendThreading.Value = CommandLineState.OverrideBackendThreading.ToLower() switch - { - "auto" => BackendThreading.Auto, - "off" => BackendThreading.Off, - "on" => BackendThreading.On, - _ => ConfigurationState.Instance.Graphics.BackendThreading - }; + if (RyujinxOptions.Shared.BackendThreadingOverride is not null) + ConfigurationState.Instance.Graphics.BackendThreading.Value = + RyujinxOptions.Shared.BackendThreadingOverride.Value; + + if (RyujinxOptions.Shared.BackendThreadingOverrideAfterReboot is not null) + BackendThreadingArg = RyujinxOptions.Shared.BackendThreadingOverrideAfterReboot.Value.ToString(); - 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; + if (RyujinxOptions.Shared.DockedModeOverride.HasValue) + ConfigurationState.Instance.System.EnableDockedMode.Value = + RyujinxOptions.Shared.DockedModeOverride.Value; // Check if HideCursor was overridden. - if (CommandLineState.OverrideHideCursor is not null) - ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor.ToLower() switch - { - "never" => HideCursorMode.Never, - "onidle" => HideCursorMode.OnIdle, - "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor, - }; + if (RyujinxOptions.Shared.HideCursorOverride is not null) + ConfigurationState.Instance.HideCursor.Value = RyujinxOptions.Shared.HideCursorOverride.Value; // Check if memoryManagerMode was overridden. - if (CommandLineState.OverrideMemoryManagerMode is not null) - if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result)) - { - ConfigurationState.Instance.System.MemoryManagerMode.Value = result; - } + if (RyujinxOptions.Shared.MemoryManagerModeOverride is not null) + ConfigurationState.Instance.System.MemoryManagerMode.Value = RyujinxOptions.Shared.MemoryManagerModeOverride.Value; // Check if PPTC was overridden. - if (CommandLineState.OverridePPTC is not null) - if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result)) + if (RyujinxOptions.Shared.PptcOverride is not null) + if (Enum.TryParse(RyujinxOptions.Shared.PptcOverride, true, out bool result)) { ConfigurationState.Instance.System.EnablePtc.Value = result; } // Check if region was overridden. - if (CommandLineState.OverrideSystemRegion is not null) - if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result)) - { - ConfigurationState.Instance.System.Region.Value = result; - } + if (RyujinxOptions.Shared.SystemRegionOverride is not null) + ConfigurationState.Instance.System.Region.Value = RyujinxOptions.Shared.SystemRegionOverride.Value; //Check if language was overridden. - if (CommandLineState.OverrideSystemLanguage is not null) - if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result)) - { - ConfigurationState.Instance.System.Language.Value = result; - } + if (RyujinxOptions.Shared.SystemLanguageOverride is not null) + ConfigurationState.Instance.System.Language.Value = RyujinxOptions.Shared.SystemLanguageOverride.Value; // Check if hardware-acceleration was overridden. - if (CommandLineState.OverrideHardwareAcceleration != null) - UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value; + if (RyujinxOptions.Shared.HardwareAccelerationOverride is not null) + UseHardwareAcceleration = RyujinxOptions.Shared.HardwareAccelerationOverride.Value; } internal static void PrintSystemInfo() diff --git a/src/Ryujinx/Systems/Updater/Updater.cs b/src/Ryujinx/Systems/Updater/Updater.cs index bc45f8ff6..2df71f4e4 100644 --- a/src/Ryujinx/Systems/Updater/Updater.cs +++ b/src/Ryujinx/Systems/Updater/Updater.cs @@ -181,7 +181,7 @@ namespace Ryujinx.Ava.Systems if (shouldRestart) { - List arguments = CommandLineState.Arguments.ToList(); + List arguments = RyujinxOptions.Shared.InputArguments.ToList(); string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; // On macOS we perform the update at relaunch. @@ -218,7 +218,7 @@ namespace Ryujinx.Ava.Systems WorkingDirectory = executableDirectory, }; - foreach (string argument in CommandLineState.Arguments) + foreach (string argument in arguments) { processStart.ArgumentList.Add(argument); } diff --git a/src/Ryujinx/UI/RyujinxApp.axaml.cs b/src/Ryujinx/UI/RyujinxApp.axaml.cs index c778f27fb..1fcf31ca3 100644 --- a/src/Ryujinx/UI/RyujinxApp.axaml.cs +++ b/src/Ryujinx/UI/RyujinxApp.axaml.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Ava if (result == UserResult.Yes) { - _ = Process.Start(Environment.ProcessPath!, CommandLineState.Arguments); + _ = Process.Start(Environment.ProcessPath!, RyujinxOptions.Shared.InputArguments); desktop.Shutdown(); Environment.Exit(0); } diff --git a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs index 053972c2c..e668f2658 100644 --- a/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/AmiiboWindowViewModel.cs @@ -255,41 +255,27 @@ namespace Ryujinx.Ava.UI.ViewModels return amiiboJson; } - private async Task ReadLocalJsonFileAsync() + private AmiiboJson? ReadLocalJsonFile() { bool isValid = false; AmiiboJson amiiboJson = new(); try { - try + if (File.Exists(_amiiboJsonPath)) { - if (File.Exists(_amiiboJsonPath)) - { - isValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson); - } - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}"); - isValid = false; - } - - if (!isValid) - { - return null; + isValid = TryGetAmiiboJson(File.ReadAllText(_amiiboJsonPath), out amiiboJson); } } catch (Exception exception) { - if (!isValid) - { - Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}"); + Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}"); + isValid = false; + } - // Neither local file is not valid JSON, close window. - await ShowInfoDialog(); - Close(); - } + if (!isValid) + { + return null; } return amiiboJson; @@ -299,8 +285,8 @@ namespace Ryujinx.Ava.UI.ViewModels { AmiiboJson? amiiboJson; - if (CommandLineState.OnlyLocalAmiibo) - amiiboJson = await ReadLocalJsonFileAsync(); + if (RyujinxOptions.Shared.OnlyLocalAmiibo) + amiiboJson = ReadLocalJsonFile(); else amiiboJson = await GetMostRecentAmiiboListOrDefaultJson(); diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index e7934f38a..b3e2af754 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -139,16 +139,11 @@ namespace Ryujinx.Ava.UI.Windows Executor.ExecuteBackgroundAsync(async () => { await ShowIntelMacWarningAsync(); - if (CommandLineState.FirmwareToInstallPathArg.TryGet(out FilePath fwPath)) + if (RyujinxOptions.Shared.FirmwareToInstallPath.TryGet(out FilePath fwPath)) { - if (fwPath is { ExistsAsFile: true, Extension: "xci" or "zip" } || fwPath.ExistsAsDirectory) - { - await Dispatcher.UIThread.InvokeAsync(() => - ViewModel.HandleFirmwareInstallation(fwPath)); - CommandLineState.FirmwareToInstallPathArg = default; - } - else - Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file."); + await Dispatcher.UIThread.InvokeAsync(() => + ViewModel.HandleFirmwareInstallation(fwPath)); + RyujinxOptions.Shared.FirmwareToInstallPath = default; } }); } @@ -278,7 +273,7 @@ namespace Ryujinx.Ava.UI.Windows // Consider removing this at some point in the future when we don't need to worry about old saves. VirtualFileSystem.FixExtraData(LibHacHorizonManager.RyujinxClient); - AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, CommandLineState.Profile); + AccountManager = new AccountManager(LibHacHorizonManager.RyujinxClient, RyujinxOptions.Shared.Profile); VirtualFileSystem.ReloadKeySet(); @@ -406,7 +401,7 @@ namespace Ryujinx.Ava.UI.Windows await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); } - if (!Updater.CanUpdate() || CommandLineState.HideAvailableUpdates) + if (!Updater.CanUpdate() || RyujinxOptions.Shared.HideAvailableUpdates) return; switch (ConfigurationState.Instance.UpdateCheckerType.Value) diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs deleted file mode 100644 index a4e6cd811..000000000 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ /dev/null @@ -1,221 +0,0 @@ -using Gommon; -using Ryujinx.Common.Logging; -using System.Collections.Generic; - -namespace Ryujinx.Ava.Utilities -{ - public static class CommandLineState - { - public static string[] Arguments { get; private set; } - public static int CountArguments { get; private set; } - public static bool? OverrideDockedMode { get; private set; } - 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; } - public static string OverrideSystemLanguage { get; private set; } - public static string OverrideHideCursor { get; private set; } - public static string BaseDirPathArg { get; private set; } - - public static string RenderDocCaptureTitleFormat { get; private set; } = - "{EmuVersion}\n{GuestName} {GuestVersion} {GuestTitleId} {GuestArch}"; - public static Optional FirmwareToInstallPathArg { get; set; } - public static string Profile { get; private set; } - public static string LaunchPathArg { get; private set; } - public static string LaunchApplicationId { get; private set; } - public static bool StartFullscreenArg { get; private set; } - public static bool HideAvailableUpdates { get; private set; } - public static bool OnlyLocalAmiibo { get; private set; } - - public static void ParseArguments(string[] args) - { - List arguments = []; - - // Parse Arguments. - for (int i = 0; i < args.Length; ++i) - { - string arg = args[i]; - - if (arg.Contains('-') || arg.Contains("--")) - { - CountArguments++; - } - - switch (arg) - { - case "-r": - case "--root-data-dir": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - BaseDirPathArg = args[++i]; - - arguments.Add(arg); - arguments.Add(args[i]); - break; - case "-rdct": - case "--rd-capture-title-format": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - RenderDocCaptureTitleFormat = args[++i]; - - arguments.Add(arg); - arguments.Add(args[i]); - break; - case "--install-firmware": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - FirmwareToInstallPathArg = new FilePath(args[++i]); - - arguments.Add(arg); - arguments.Add(args[i]); - break; - case "-p": - case "--profile": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - Profile = args[++i]; - - arguments.Add(arg); - arguments.Add(args[i]); - break; - case "-f": - case "--fullscreen": - StartFullscreenArg = true; - - arguments.Add(arg); - break; - case "-g": - case "--graphics-backend": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideGraphicsBackend = args[++i]; - break; - case "--backend-threading": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideBackendThreading = args[++i]; - break; - case "--bt": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideBackendThreadingAfterReboot = args[++i]; - break; - case "--pptc": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverridePPTC = args[++i]; - break; - case "-la": - case "--local-only-amiibo": - OnlyLocalAmiibo = true; - break; - case "-m": - case "--memory-manager-mode": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideMemoryManagerMode = args[++i]; - break; - case "--system-region": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideSystemRegion = args[++i]; - break; - case "--system-language": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideSystemLanguage = args[++i]; - break; - case "-i": - case "--application-id": - LaunchApplicationId = args[++i]; - break; - case "--docked-mode": - OverrideDockedMode = true; - break; - case "--handheld-mode": - OverrideDockedMode = false; - break; - case "--hide-cursor": - if (i + 1 >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); - - continue; - } - - OverrideHideCursor = args[++i]; - break; - case "--hide-updates": - HideAvailableUpdates = true; - break; - case "--software-gui": - OverrideHardwareAcceleration = false; - break; - default: - LaunchPathArg = arg; - break; - } - } - - Arguments = arguments.ToArray(); - } - } -} diff --git a/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs b/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs new file mode 100644 index 000000000..d34b74a76 --- /dev/null +++ b/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs @@ -0,0 +1,69 @@ +using CommandLine; +using Gommon; +using Ryujinx.Common.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using Error = Gommon.Error; + +namespace Ryujinx.Ava.Utilities +{ + public partial class RyujinxOptions + { + public static RyujinxOptions Shared { get; private set; } + + public static Result Read(string[] args, out RyujinxOptions options) + { + options = null; + args = PatchLegacyArgumentNames(args); + + ParserResult parseResult = + Parser.ParseArguments(args); + + if (parseResult is NotParsed notParsed) + { + if (notParsed.Errors.None(x => + x.Tag is ErrorType.HelpRequestedError or ErrorType.HelpVerbRequestedError)) + { + Logger.Notice.Print(LogClass.Application, "Failed to parse command-line arguments:"); + foreach (var error in notParsed.Errors) + { + Logger.Notice.Print(LogClass.Application, $" - {error.Tag}"); + } + } + + return Result.Fail; + } + + options = Shared = parseResult.Value; + + return parseResult.Value.Init(args); + } + + private static readonly Lazy _parser = new(() => new Parser(settings => + { + settings.HelpWriter = Logger.WriterProxy; + settings.CaseInsensitiveEnumValues = true; + settings.CaseSensitive = false; + settings.MaximumDisplayWidth -= (int)(settings.MaximumDisplayWidth * 0.175); + })); + + public static Parser Parser => _parser.Value; + + private static readonly Dictionary _legacyArgs = new() + { + { "-rdct", "--rd-capture-title-format" }, + { "-la", "--local-only-amiibo" } + }; + + public static string[] PatchLegacyArgumentNames(string[] args) + { + for (int i = 0; i < args.Length; i++) + args[i] = Patch(args[i]); + + return args; + + string Patch(string arg) => _legacyArgs.TryGetValue(arg, out string newArgName) ? newArgName : arg; + } + } +} diff --git a/src/Ryujinx/Utilities/RyujinxOptions.cs b/src/Ryujinx/Utilities/RyujinxOptions.cs new file mode 100644 index 000000000..1c0f13dbd --- /dev/null +++ b/src/Ryujinx/Utilities/RyujinxOptions.cs @@ -0,0 +1,130 @@ +using Avalonia.Controls; +using CommandLine; +using Gommon; +using Ryujinx.Ava.Systems.Configuration.System; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; + +namespace Ryujinx.Ava.Utilities +{ + public partial class RyujinxOptions + { + public string[] InputArguments { get; private set; } + + public bool? DockedModeOverride { get; private set; } + + public bool? HardwareAccelerationOverride { get; private set; } + + public Optional FirmwareToInstallPath { get; set; } + + // Ideally I'd use an enum parse, like --docked-mode=Handheld, + // but I want to maintain backwards compatibility with shortcuts made a long time ago, as best we can. + public Result Init(string[] args) + { + InputArguments = args; + + { // Docked Mode Override + if (DockedMode && HandheldMode) + { + return Result.MessageFailure( + "Cannot be in both docked and handheld mode at the same time; choose only one."); + } + + if (DockedMode) DockedModeOverride = true; + if (HandheldMode) DockedModeOverride = false; + } + { // Hardware Acceleration Override + if (SoftwareGui) + { + HardwareAccelerationOverride = false; + } + } + + FirmwareToInstallPath = Optional.Of(FirmwareToInstallPathRaw) + .Convert(x => new FilePath(x)) + .OnlyIf(fp => + { + bool result = fp is { ExistsAsFile: true, Extension: "xci" or "zip" } || fp.ExistsAsDirectory; + if (!result) + { + Logger.Notice.PrintMsg(LogClass.UI, + "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file."); + } + + return result; + }); + + return Result.Success; + } + + [Option("docked-mode", Required = false, Default = false, HelpText = "Launch the game in Docked mode.")] + public bool DockedMode { get; set; } + + [Option("handheld-mode", Required = false, Default = false, HelpText = "Launch the game in Handheld mode.")] + public bool HandheldMode { get; set; } + + [Option("software-gui", Required = false, Default = false, + HelpText = "Disables hardware-accelerated rendering for Avalonia. Required for launching with RenderDoc.")] + public bool SoftwareGui { get; set; } + + [Option('g', "graphics-backend", Required = false, Default = null, + HelpText = "Select the Graphics backend to use when launching.")] + public GraphicsBackend? GraphicsBackendOverride { get; set; } + + [Option("backend-threading", Required = false, Default = null, + HelpText = "Select the Graphics backend threading option to use when launching.")] + public BackendThreading? BackendThreadingOverride { get; set; } + + [Option("bt", Required = false, Default = null, Hidden = true)] + public BackendThreading? BackendThreadingOverrideAfterReboot { get; set; } + + [Option("pptc", Required = false, Default = null, + HelpText = "Enable/disable PPTC regardless of your settings when launching.")] + public string PptcOverride { get; set; } + + [Option('m', "memory-manager-mode", Required = false, Default = null, + HelpText = "Select the memory manager mode to use when launching.")] + public MemoryManagerMode? MemoryManagerModeOverride { get; set; } + + [Option("system-region", Required = false, Default = null, + HelpText = "Select the Region to use for the emulated Switch when launching.")] + public Region? SystemRegionOverride { get; set; } + + [Option("system-language", Required = false, Default = null, + HelpText = "Select the Language to use for the emulated Switch when launching.")] + public Language? SystemLanguageOverride { get; set; } + + [Option("hide-cursor", Required = false, Default = null, + HelpText = "Select the cursor hiding strategy to use when launching.")] + public HideCursorMode? HideCursorOverride { get; set; } + + [Option('r', "root-data-dir", Required = false, Default = null, + HelpText = "Select the folder to use for all of your Ryujinx save data, configs, etc.")] + public string EmuDataBaseDirPath { get; set; } + + [Option("rd-capture-title-format", Required = false, + HelpText = + "Set the format string used for RenderDoc Capture titles when using the Start/Stop Capture buttons in Ryujinx.", + Default = "{EmuVersion}\n{GuestName} {GuestVersion} {GuestTitleId} {GuestArch}")] + public string RenderDocCaptureTitleFormat { get; set; } + + [Option("install-firmware", Required = false, Default = null, HelpText = "Specify a file path containing Switch firmware to install immediately after starting. Must be a directory or a .zip or .xci file.")] + public string FirmwareToInstallPathRaw { get; set; } + + [Option('p', "profile", Required = false, Default = null, HelpText = "The profile name to open the application with. Defaults to your last used profile.")] + public string Profile { get; set; } + [Option('i', "application-id", Required = false, Default = null, HelpText = "Specify which application ID out of the specified content archive path to launch.")] + public string LaunchApplicationId { get; set; } + [Option('f', "fullscreen", Required = false, Default = false, HelpText = "Start the emulator in fullscreen mode.")] + public bool StartFullscreen { get; set; } + [Option("hide-updates", Required = false, Default = false, HelpText = "Hides update prompt/notification.")] + public bool HideAvailableUpdates { get; set; } + [Option("local-only-amiibo", Required = false, Default = false, HelpText = "Only use the local Amiibo cache; do not update it even if there is an update.")] + public bool OnlyLocalAmiibo { get; set; } + + [Value(0, Default = null, Required = false, + HelpText = + "The Nintendo Switch application content archive to launch immediately after starting, if desired.")] + public string LaunchPath { get; set; } + } +} diff --git a/src/Ryujinx/Utilities/ShortcutHelper.cs b/src/Ryujinx/Utilities/ShortcutHelper.cs index b92f97f55..4f3d2a7cf 100644 --- a/src/Ryujinx/Utilities/ShortcutHelper.cs +++ b/src/Ryujinx/Utilities/ShortcutHelper.cs @@ -126,10 +126,10 @@ namespace Ryujinx.Ava.Utilities // args are first defined as a list, for easier adjustments in the future List argsList = []; - if (!string.IsNullOrEmpty(CommandLineState.BaseDirPathArg)) + if (!string.IsNullOrEmpty(RyujinxOptions.Shared.EmuDataBaseDirPath)) { argsList.Add("--root-data-dir"); - argsList.Add($"\"{CommandLineState.BaseDirPathArg}\""); + argsList.Add($"\"{RyujinxOptions.Shared.EmuDataBaseDirPath}\""); } if (!string.IsNullOrEmpty(config)) diff --git a/src/Ryujinx/Utilities/TitleHelper.cs b/src/Ryujinx/Utilities/TitleHelper.cs index 3d1e53fd7..b2b3def80 100644 --- a/src/Ryujinx/Utilities/TitleHelper.cs +++ b/src/Ryujinx/Utilities/TitleHelper.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Ava.Utilities string titleIdSection = $"({activeProcess.ProgramIdText.ToUpper()})"; string titleArchSection = activeProcess.Is64Bit ? "(64-bit)" : "(32-bit)"; - return CommandLineState.RenderDocCaptureTitleFormat + return RyujinxOptions.Shared.RenderDocCaptureTitleFormat .ReplaceIgnoreCase("{EmuVersion}", applicationVersion) .ReplaceIgnoreCase("{GuestName}", titleNameSection) .ReplaceIgnoreCase("{GuestVersion}", titleVersionSection)