diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 3949bf6d8..c70504682 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -1,5 +1,6 @@ using Avalonia; using Avalonia.Threading; +using CommandLine; using DiscordRPC; using Gommon; using Projektanker.Icons.Avalonia; @@ -78,35 +79,38 @@ namespace Ryujinx.Ava } } - bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui"); - bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps"); + PreviewerDetached = true; + + if (ConsumeCommandLineArgument(ref args, "--no-gui") + || ConsumeCommandLineArgument(ref args, "nogui")) + { + try + { + HeadlessRyujinx.Entrypoint(args); + return 0; + } + catch (Exception e) + { + Logger.Error?.PrintMsg(LogClass.Application, $"Exception occurred when running Headless Ryujinx: {e.Message}\n{e.StackTrace}"); + return 1; + } + } + + if (!Initialize(args, out RyujinxOptions options)) + { + Logger.Flush(); + return 1; + } // TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception. // This is undesirable and causes very odd behavior during development (the process stops responding, // the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user. // This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be. - if (!coreDumpArg) + if (!options.CoreDumpsEnabled) { OsUtils.SetCoreDumpable(false); } - PreviewerDetached = true; - - if (noGuiArg) - { - HeadlessRyujinx.Entrypoint(args); - return 0; - } - - try - { - Initialize(args); - } - catch - { - return 0; - } - LoggerAdapter.Register(); IconProvider.Current @@ -144,13 +148,14 @@ namespace Ryujinx.Ava return found; } - private static void Initialize(string[] args) + private static Result Initialize(string[] args, out RyujinxOptions options) { // Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched DiscordIntegrationModule.EmulatorStartedAt = Timestamps.Now; // Parse arguments - RyujinxOptions.Read(args, out RyujinxOptions options); + Result res = RyujinxOptions.Read(args, out options); + if (!res) return res; if (OperatingSystem.IsMacOS()) { @@ -207,6 +212,8 @@ namespace Ryujinx.Ava { MainWindow.DeferLoadApplication(options.LaunchPath, options.LaunchApplicationId, options.StartFullscreen); } + + return Result.Success; } public static string GetDirGameUserConfig(string gameId, bool changeFolderForGame = false) diff --git a/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs b/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs index d34b74a76..72b60a2d4 100644 --- a/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs +++ b/src/Ryujinx/Utilities/RyujinxOptions.Parse.cs @@ -12,6 +12,7 @@ namespace Ryujinx.Ava.Utilities { public static RyujinxOptions Shared { get; private set; } + // ReSharper disable once UnusedMethodReturnValue.Global public static Result Read(string[] args, out RyujinxOptions options) { options = null; @@ -20,20 +21,8 @@ namespace Ryujinx.Ava.Utilities 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}"); - } - } - + if (parseResult is NotParsed) return Result.Fail; - } options = Shared = parseResult.Value; diff --git a/src/Ryujinx/Utilities/RyujinxOptions.cs b/src/Ryujinx/Utilities/RyujinxOptions.cs index 1c0f13dbd..d7adbdacc 100644 --- a/src/Ryujinx/Utilities/RyujinxOptions.cs +++ b/src/Ryujinx/Utilities/RyujinxOptions.cs @@ -23,7 +23,8 @@ namespace Ryujinx.Ava.Utilities { InputArguments = args; - { // Docked Mode Override + { + // Docked Mode Override if (DockedMode && HandheldMode) { return Result.MessageFailure( @@ -33,7 +34,8 @@ namespace Ryujinx.Ava.Utilities if (DockedMode) DockedModeOverride = true; if (HandheldMode) DockedModeOverride = false; } - { // Hardware Acceleration Override + { + // Hardware Acceleration Override if (SoftwareGui) { HardwareAccelerationOverride = false; @@ -57,10 +59,12 @@ namespace Ryujinx.Ava.Utilities return Result.Success; } - [Option("docked-mode", Required = false, Default = false, HelpText = "Launch the game in Docked mode.")] + [Option("docked-mode", Required = false, Default = false, + HelpText = "Launch the game in Docked mode. Causes an error if used in tandem with --handheld-mode.")] public bool DockedMode { get; set; } - [Option("handheld-mode", Required = false, Default = false, HelpText = "Launch the game in Handheld mode.")] + [Option("handheld-mode", Required = false, Default = false, + HelpText = "Launch the game in Handheld mode. Causes an error if used in tandem with --docked-mode.")] public bool HandheldMode { get; set; } [Option("software-gui", Required = false, Default = false, @@ -108,20 +112,34 @@ namespace Ryujinx.Ava.Utilities 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.")] + [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.")] + [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.")] + + [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.")] + + [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.")] + + [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; } + [Option("core-dumps", Required = false, Default = false, + HelpText = "Enable coredumps on Linux platforms. They are disabled by default.")] + public bool CoreDumpsEnabled { get; set; } + [Value(0, Default = null, Required = false, HelpText = "The Nintendo Switch application content archive to launch immediately after starting, if desired.")]