From ae364d1436e4428bfcd6d102cf3bf1b1127ddc21 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 6 Jan 2025 02:30:33 +0100 Subject: [PATCH 1/4] added command line argument to specify which mods the game should start with, not implemented in headless --- .../Configuration/AppDataManager.cs | 4 ++- src/Ryujinx.HLE/HOS/ModLoader.cs | 26 +++++++++++--- src/Ryujinx/Headless/HeadlessRyujinx.cs | 2 +- src/Ryujinx/Program.cs | 2 +- src/Ryujinx/Utilities/CommandLineState.cs | 36 +++++++++++++++++++ 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs index ca8e389ba..08ce0c5f8 100644 --- a/src/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -32,6 +32,7 @@ namespace Ryujinx.Common.Configuration public static string KeysDirPathUser { get; } public static string LogsDirPath { get; private set; } + public static string[] CommandLineArgMods { get; private set; } public const string DefaultNandDir = "bis"; public const string DefaultSdcardDir = "sdcard"; @@ -47,8 +48,9 @@ namespace Ryujinx.Common.Configuration KeysDirPathUser = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".switch"); } - public static void Initialize(string baseDirPath) + public static void Initialize(string baseDirPath, string[] commandLineArgMods) { + CommandLineArgMods = commandLineArgMods; string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); if (appDataPath.Length == 0) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index 4bd695ae5..ee1c21db3 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -41,11 +41,11 @@ namespace Ryujinx.HLE.HOS private static readonly ModMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public readonly struct Mod where T : FileSystemInfo + public struct Mod where T : FileSystemInfo { public readonly string Name; public readonly T Path; - public readonly bool Enabled; + public bool Enabled; public Mod(string name, T path, bool enabled) { @@ -169,7 +169,16 @@ namespace Ryujinx.HLE.HOS if (StrEquals(RomfsDir, modDir.Name)) { var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); - var enabled = modData?.Enabled ?? true; + + bool enabled; + if (AppDataManager.CommandLineArgMods.Length == 0) + { + enabled = modData?.Enabled ?? true; + } + else + { + enabled = AppDataManager.CommandLineArgMods.Contains(modData.Name); + } mods.RomfsDirs.Add(mod = new Mod(dir.Name, modDir, enabled)); types.Append('R'); @@ -177,7 +186,16 @@ namespace Ryujinx.HLE.HOS else if (StrEquals(ExefsDir, modDir.Name)) { var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); - var enabled = modData?.Enabled ?? true; + + bool enabled; + if (AppDataManager.CommandLineArgMods.Length == 0) + { + enabled = modData?.Enabled ?? true; + } + else + { + enabled = AppDataManager.CommandLineArgMods.Contains(modData.Name); + } mods.ExefsDirs.Add(mod = new Mod(dir.Name, modDir, enabled)); types.Append('E'); diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 5730254f7..243ab547a 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -147,7 +147,7 @@ namespace Ryujinx.Headless option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out useLastUsedProfile); } - AppDataManager.Initialize(option.BaseDataDir); + AppDataManager.Initialize(option.BaseDataDir, []); if (useLastUsedProfile && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile)) option.UserProfile = profile.Name; diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 6f0f3e12e..279a469fa 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Ava AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); // Setup base data directory. - AppDataManager.Initialize(CommandLineState.BaseDirPathArg); + AppDataManager.Initialize(CommandLineState.BaseDirPathArg, CommandLineState.Mods); // Initialize the configuration. ConfigurationState.Initialize(); diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs index 6fb8e92d6..5905f6081 100644 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Ava.Utilities public static class CommandLineState { public static string[] Arguments { get; private set; } + public static string[] Mods { get; private set; } public static bool? OverrideDockedMode { get; private set; } public static bool? OverrideHardwareAcceleration { get; private set; } @@ -21,6 +22,7 @@ namespace Ryujinx.Ava.Utilities public static void ParseArguments(string[] args) { List arguments = new(); + List mods = new(); // Parse Arguments. for (int i = 0; i < args.Length; ++i) @@ -78,6 +80,39 @@ namespace Ryujinx.Ava.Utilities case "--application-id": LaunchApplicationId = args[++i]; break; + case "-m": + case "--mod": + int numMods; + if (i + 1 >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"Argument '{arg}' expects a number of mods as next argument"); + + continue; + } + if (!int.TryParse(args[++i], out numMods)) + { + i--; + Logger.Error?.Print(LogClass.Application, $"Expected number of mods, got '{arg}'"); + + continue; + } + + if (i + numMods >= args.Length) + { + Logger.Error?.Print(LogClass.Application, $"The number of expected mods exceeds the number of command line arguments left"); + + continue; + } + + for (int j = i + 1; j <= i + numMods; j++) + { + mods.Add(args[j]); + + Logger.Info?.Print(LogClass.Application, $"Enabled mod '{args[j]}' through cli"); + } + + i += numMods; + break; case "--docked-mode": OverrideDockedMode = true; break; @@ -107,6 +142,7 @@ namespace Ryujinx.Ava.Utilities } Arguments = arguments.ToArray(); + Mods = mods.ToArray(); } } } From 35f2382c0828399fcb16be403b06a52d39b24f10 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 6 Jan 2025 22:34:41 +0100 Subject: [PATCH 2/4] made logic for mods in CommandLineState less complicated --- src/Ryujinx/Program.cs | 2 +- src/Ryujinx/Utilities/CommandLineState.cs | 31 +++-------------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 279a469fa..cd4db712c 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Ava AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit(); // Setup base data directory. - AppDataManager.Initialize(CommandLineState.BaseDirPathArg, CommandLineState.Mods); + AppDataManager.Initialize(CommandLineState.BaseDirPathArg, CommandLineState.OverrideMods); // Initialize the configuration. ConfigurationState.Initialize(); diff --git a/src/Ryujinx/Utilities/CommandLineState.cs b/src/Ryujinx/Utilities/CommandLineState.cs index 5905f6081..f8aa7c81a 100644 --- a/src/Ryujinx/Utilities/CommandLineState.cs +++ b/src/Ryujinx/Utilities/CommandLineState.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.Ava.Utilities @@ -6,7 +7,7 @@ namespace Ryujinx.Ava.Utilities public static class CommandLineState { public static string[] Arguments { get; private set; } - public static string[] Mods { get; private set; } + public static string[] OverrideMods { get; private set; } public static bool? OverrideDockedMode { get; private set; } public static bool? OverrideHardwareAcceleration { get; private set; } @@ -22,7 +23,6 @@ namespace Ryujinx.Ava.Utilities public static void ParseArguments(string[] args) { List arguments = new(); - List mods = new(); // Parse Arguments. for (int i = 0; i < args.Length; ++i) @@ -82,36 +82,14 @@ namespace Ryujinx.Ava.Utilities break; case "-m": case "--mod": - int numMods; if (i + 1 >= args.Length) { - Logger.Error?.Print(LogClass.Application, $"Argument '{arg}' expects a number of mods as next argument"); - - continue; - } - if (!int.TryParse(args[++i], out numMods)) - { - i--; - Logger.Error?.Print(LogClass.Application, $"Expected number of mods, got '{arg}'"); + Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'"); continue; } - if (i + numMods >= args.Length) - { - Logger.Error?.Print(LogClass.Application, $"The number of expected mods exceeds the number of command line arguments left"); - - continue; - } - - for (int j = i + 1; j <= i + numMods; j++) - { - mods.Add(args[j]); - - Logger.Info?.Print(LogClass.Application, $"Enabled mod '{args[j]}' through cli"); - } - - i += numMods; + OverrideMods = args[++i].Split(','); break; case "--docked-mode": OverrideDockedMode = true; @@ -142,7 +120,6 @@ namespace Ryujinx.Ava.Utilities } Arguments = arguments.ToArray(); - Mods = mods.ToArray(); } } } From 8827bce2232490597b8bea089db219656b46fd68 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 6 Jan 2025 23:13:21 +0100 Subject: [PATCH 3/4] changed variable name from CommandLineArgMods to OverrideMods --- src/Ryujinx.Common/Configuration/AppDataManager.cs | 4 ++-- src/Ryujinx.HLE/HOS/ModLoader.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs index 08ce0c5f8..203fd42fb 100644 --- a/src/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Common.Configuration public static string KeysDirPathUser { get; } public static string LogsDirPath { get; private set; } - public static string[] CommandLineArgMods { get; private set; } + public static string[] OverrideMods { get; private set; } public const string DefaultNandDir = "bis"; public const string DefaultSdcardDir = "sdcard"; @@ -50,7 +50,7 @@ namespace Ryujinx.Common.Configuration public static void Initialize(string baseDirPath, string[] commandLineArgMods) { - CommandLineArgMods = commandLineArgMods; + OverrideMods = commandLineArgMods; string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); if (appDataPath.Length == 0) diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index ee1c21db3..52081ee18 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -171,13 +171,13 @@ namespace Ryujinx.HLE.HOS var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); bool enabled; - if (AppDataManager.CommandLineArgMods.Length == 0) + if (AppDataManager.OverrideMods.Length == 0) { enabled = modData?.Enabled ?? true; } else { - enabled = AppDataManager.CommandLineArgMods.Contains(modData.Name); + enabled = AppDataManager.OverrideMods.Contains(modData.Name); } mods.RomfsDirs.Add(mod = new Mod(dir.Name, modDir, enabled)); @@ -188,13 +188,13 @@ namespace Ryujinx.HLE.HOS var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path)); bool enabled; - if (AppDataManager.CommandLineArgMods.Length == 0) + if (AppDataManager.OverrideMods.Length == 0) { enabled = modData?.Enabled ?? true; } else { - enabled = AppDataManager.CommandLineArgMods.Contains(modData.Name); + enabled = AppDataManager.OverrideMods.Contains(modData.Name); } mods.ExefsDirs.Add(mod = new Mod(dir.Name, modDir, enabled)); From dcd19facff31ba342cefa135adfebab7d9892b25 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 6 Jan 2025 23:19:43 +0100 Subject: [PATCH 4/4] added OverrideMods argument for headless ryujinx --- src/Ryujinx/Headless/HeadlessRyujinx.cs | 29 ++++----- src/Ryujinx/Headless/Options.cs | 78 +++++++++++++------------ 2 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs index 243ab547a..e6ebd7b3b 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs @@ -1,6 +1,7 @@ using CommandLine; using Gommon; using Ryujinx.Ava; +using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -84,23 +85,23 @@ namespace Ryujinx.Headless .WithNotParsed(errors => { Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:"); - + errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}")); }); } - + public static void ReloadConfig(string customConfigPath = null) { string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); string configurationPath = null; - + // Now load the configuration as the other subsystems are now registered if (customConfigPath != null && File.Exists(customConfigPath)) { configurationPath = customConfigPath; - } + } else if (File.Exists(localConfigurationPath)) { configurationPath = localConfigurationPath; @@ -147,11 +148,11 @@ namespace Ryujinx.Headless option.InheritMainConfig(originalArgs, ConfigurationState.Instance, out useLastUsedProfile); } - AppDataManager.Initialize(option.BaseDataDir, []); - + AppDataManager.Initialize(option.BaseDataDir, option.OverrideMods.Split(",")); + if (useLastUsedProfile && AccountSaveDataManager.GetLastUsedUser().TryGet(out var profile)) option.UserProfile = profile.Name; - + // Check if keys exists. if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"))) { @@ -160,14 +161,14 @@ namespace Ryujinx.Headless Logger.Error?.Print(LogClass.Application, "Keys not found"); } } - + ReloadConfig(); if (option.InheritConfig) { option.InheritMainConfigInput(originalArgs, ConfigurationState.Instance); } - + _virtualFileSystem = VirtualFileSystem.CreateInstance(); _libHacHorizonManager = new LibHacHorizonManager(); @@ -230,9 +231,9 @@ namespace Ryujinx.Headless _enableMouse = option.EnableMouse; - + LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1); - LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); + LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2); LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3); LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4); LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5); @@ -240,7 +241,7 @@ namespace Ryujinx.Headless LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7); LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8); LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld); - + if (_inputConfiguration.Count == 0) { return; @@ -304,7 +305,7 @@ namespace Ryujinx.Headless _inputManager.Dispose(); return; - + void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) { if (index == PlayerIndex.Handheld && _inputConfiguration.Count > 0) @@ -312,7 +313,7 @@ namespace Ryujinx.Headless Logger.Info?.Print(LogClass.Configuration, "Skipping handheld configuration as there are already other players configured."); return; } - + InputConfig inputConfig = option.InheritedInputConfigs[index] ?? HandlePlayerConfiguration(inputProfileName, inputId, index); if (inputConfig != null) diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index 0d7e46285..76406408f 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(EnableKeyboard))) EnableKeyboard = configurationState.Hid.EnableKeyboard; - + if (NeedsOverride(nameof(EnableMouse))) EnableMouse = configurationState.Hid.EnableMouse; @@ -39,40 +39,40 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(DisableFsIntegrityChecks))) DisableFsIntegrityChecks = configurationState.System.EnableFsIntegrityChecks; - + if (NeedsOverride(nameof(FsGlobalAccessLogMode))) FsGlobalAccessLogMode = configurationState.System.FsGlobalAccessLogMode; - + if (NeedsOverride(nameof(VSyncMode))) VSyncMode = configurationState.Graphics.VSyncMode; - + if (NeedsOverride(nameof(CustomVSyncInterval))) CustomVSyncInterval = configurationState.Graphics.CustomVSyncInterval; - + if (NeedsOverride(nameof(DisableShaderCache))) DisableShaderCache = !configurationState.Graphics.EnableShaderCache; if (NeedsOverride(nameof(EnableTextureRecompression))) EnableTextureRecompression = configurationState.Graphics.EnableTextureRecompression; - + if (NeedsOverride(nameof(DisableDockedMode))) DisableDockedMode = !configurationState.System.EnableDockedMode; if (NeedsOverride(nameof(SystemLanguage))) SystemLanguage = (SystemLanguage)(int)configurationState.System.Language.Value; - + if (NeedsOverride(nameof(SystemRegion))) SystemRegion = (RegionCode)(int)configurationState.System.Region.Value; - + if (NeedsOverride(nameof(SystemTimeZone))) SystemTimeZone = configurationState.System.TimeZone; - + if (NeedsOverride(nameof(SystemTimeOffset))) SystemTimeOffset = configurationState.System.SystemTimeOffset; - + if (NeedsOverride(nameof(MemoryManagerMode))) MemoryManagerMode = configurationState.System.MemoryManagerMode; - + if (NeedsOverride(nameof(AudioVolume))) AudioVolume = configurationState.System.AudioVolume; @@ -81,28 +81,28 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(MultiplayerLanInterfaceId))) MultiplayerLanInterfaceId = configurationState.Multiplayer.LanInterfaceId; - + if (NeedsOverride(nameof(DisableFileLog))) DisableFileLog = !configurationState.Logger.EnableFileLog; - + if (NeedsOverride(nameof(LoggingEnableDebug))) LoggingEnableDebug = configurationState.Logger.EnableDebug; - + if (NeedsOverride(nameof(LoggingDisableStub))) LoggingDisableStub = !configurationState.Logger.EnableStub; - + if (NeedsOverride(nameof(LoggingDisableInfo))) LoggingDisableInfo = !configurationState.Logger.EnableInfo; - + if (NeedsOverride(nameof(LoggingDisableWarning))) LoggingDisableWarning = !configurationState.Logger.EnableWarn; - + if (NeedsOverride(nameof(LoggingDisableError))) LoggingDisableError = !configurationState.Logger.EnableError; - + if (NeedsOverride(nameof(LoggingEnableTrace))) LoggingEnableTrace = configurationState.Logger.EnableTrace; - + if (NeedsOverride(nameof(LoggingDisableGuest))) LoggingDisableGuest = !configurationState.Logger.EnableGuest; @@ -114,45 +114,45 @@ namespace Ryujinx.Headless if (NeedsOverride(nameof(ResScale))) ResScale = configurationState.Graphics.ResScale; - + if (NeedsOverride(nameof(MaxAnisotropy))) MaxAnisotropy = configurationState.Graphics.MaxAnisotropy; - + if (NeedsOverride(nameof(AspectRatio))) AspectRatio = configurationState.Graphics.AspectRatio; - + if (NeedsOverride(nameof(BackendThreading))) BackendThreading = configurationState.Graphics.BackendThreading; - + if (NeedsOverride(nameof(DisableMacroHLE))) DisableMacroHLE = !configurationState.Graphics.EnableMacroHLE; - + if (NeedsOverride(nameof(GraphicsShadersDumpPath))) GraphicsShadersDumpPath = configurationState.Graphics.ShadersDumpPath; - + if (NeedsOverride(nameof(GraphicsBackend))) GraphicsBackend = configurationState.Graphics.GraphicsBackend; - + if (NeedsOverride(nameof(AntiAliasing))) AntiAliasing = configurationState.Graphics.AntiAliasing; - + if (NeedsOverride(nameof(ScalingFilter))) ScalingFilter = configurationState.Graphics.ScalingFilter; - + if (NeedsOverride(nameof(ScalingFilterLevel))) ScalingFilterLevel = configurationState.Graphics.ScalingFilterLevel; if (NeedsOverride(nameof(DramSize))) DramSize = configurationState.System.DramSize; - + if (NeedsOverride(nameof(IgnoreMissingServices))) IgnoreMissingServices = configurationState.System.IgnoreMissingServices; - + if (NeedsOverride(nameof(IgnoreControllerApplet))) IgnoreControllerApplet = configurationState.IgnoreApplet; - + return; - + bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey))); } @@ -179,15 +179,15 @@ namespace Ryujinx.Headless } return; - + bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey))); } private static string OptionName(string propertyName) => typeof(Options)!.GetProperty(propertyName)!.GetCustomAttribute()!.LongName; - + // General - + [Option("use-main-config", Required = false, Default = false, HelpText = "Use the settings from what was configured via the UI.")] public bool InheritConfig { get; set; } @@ -410,7 +410,7 @@ namespace Ryujinx.Headless [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] public bool IgnoreMissingServices { get; set; } - + [Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")] public bool IgnoreControllerApplet { get; set; } @@ -420,5 +420,11 @@ namespace Ryujinx.Headless public string InputPath { get; set; } public SafeDictionary InheritedInputConfigs = new(); + + // Mod + + [Option('m', "mod", Required = false, Default = "", HelpText = "Overrides enabled mods with user input (Format: \"mod1\",\"mod2\").")] + public string OverrideMods { get; set; } + } }