launcher: option to make command line args take precedence (#327)

## Link to GitHub Issue, if one exists
Fixes #190 

## Description of change
When `-cmdoverride` is specified, any command line arguments are taken
first before merging with what is in the config file.

## Testing
Still testing...
This commit is contained in:
bicarus-dev
2025-05-15 23:26:38 -07:00
committed by GitHub
parent 7c15452c1e
commit c5eb1e999b
4 changed files with 81 additions and 6 deletions
+1
View File
@@ -43,6 +43,7 @@ public:
std::string value; std::string value;
std::vector<Option> alternatives; std::vector<Option> alternatives;
bool disabled = false; bool disabled = false;
bool conflicting = false;
explicit Option(OptionDefinition definition, std::string value = "") : explicit Option(OptionDefinition definition, std::string value = "") :
definition(std::move(definition)), value(std::move(value)) { definition(std::move(definition)), value(std::move(value)) {
+38
View File
@@ -244,6 +244,11 @@ int main_implementation(int argc, char *argv[]) {
// parse arguments // parse arguments
LAUNCHER_OPTIONS = launcher::parse_options(argc, argv); LAUNCHER_OPTIONS = launcher::parse_options(argc, argv);
// command line override (must be done before merging options with cfg)
if (LAUNCHER_OPTIONS->at(launcher::Options::OptionConflictResolution).value_bool()) {
launcher::USE_CMD_OVERRIDE = true;
}
// determine config file path - must be done before anything else // determine config file path - must be done before anything else
const auto &cfg_path = LAUNCHER_OPTIONS->at(launcher::Options::ConfigurationPath); const auto &cfg_path = LAUNCHER_OPTIONS->at(launcher::Options::ConfigurationPath);
if (cfg_path.is_active()) { if (cfg_path.is_active()) {
@@ -285,6 +290,7 @@ int main_implementation(int argc, char *argv[]) {
cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config; cfg::CONFIGURATOR_TYPE = cfg::ConfigType::Config;
cfg_run = true; cfg_run = true;
} }
if (options[launcher::Options::EAmusementEmulation].value_bool() && if (options[launcher::Options::EAmusementEmulation].value_bool() &&
options[launcher::Options::ServiceURL].is_active() && options[launcher::Options::ServiceURL].is_active() &&
!cfg::CONFIGURATOR_STANDALONE) { !cfg::CONFIGURATOR_STANDALONE) {
@@ -1220,6 +1226,38 @@ int main_implementation(int argc, char *argv[]) {
} }
log_info("launcher", "arguments:\n{}", arguments.str()); log_info("launcher", "arguments:\n{}", arguments.str());
// print out conflicts
size_t conflicts = 0;
for (const auto &option : options) {
if (option.conflicting && option.get_definition().type != OptionType::Bool) {
conflicts += 1;
if (launcher::USE_CMD_OVERRIDE) {
log_warning(
"launcher",
"multiple values for -{}, command line args take precedence: {}",
option.get_definition().name,
option.value);
} else {
log_warning(
"launcher",
"multiple values for -{}, spicecfg values take precedence: {}",
option.get_definition().name,
option.value);
}
}
}
if (conflicts) {
if (launcher::USE_CMD_OVERRIDE) {
log_info(
"launcher",
"user specified -cmdoverride, therefore command line args took precedence over spicecfg");
} else {
log_warning(
"launcher",
"spicecfg values take precedence over command line args; to change this behavior, use -cmdoverride");
}
}
if (options[launcher::Options::FullscreenResolution].is_active()) { if (options[launcher::Options::FullscreenResolution].is_active()) {
std::pair<uint32_t, uint32_t> result; std::pair<uint32_t, uint32_t> result;
if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) { if (parse_width_height(options[launcher::Options::FullscreenResolution].value_text(), result)) {
+39 -6
View File
@@ -43,6 +43,8 @@ static const std::vector<std::string> CATEGORY_ORDER_NONE = {
"" ""
}; };
bool launcher::USE_CMD_OVERRIDE = false;
/* /*
* Option Definitions * Option Definitions
* Be aware that the order must be the same as in the enum launcher::Options! * Be aware that the order must be the same as in the enum launcher::Options!
@@ -81,6 +83,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Network", .category = "Network",
}, },
{ {
// ServiceURL
.title = "EA Service URL", .title = "EA Service URL",
.name = "url", .name = "url",
.desc = "Sets a custom service URL override", .desc = "Sets a custom service URL override",
@@ -142,6 +145,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Graphics (Windowed)", .category = "Graphics (Windowed)",
}, },
{ {
// InjectHook
.title = "Inject DLL Hooks", .title = "Inject DLL Hooks",
.name = "k", .name = "k",
.desc = "Multiple files are allowed; use multiple -k flags, or in SpiceCfg, separate by " .desc = "Multiple files are allowed; use multiple -k flags, or in SpiceCfg, separate by "
@@ -152,6 +156,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Common", .category = "Common",
}, },
{ {
// EarlyInjectHook
.title = "Inject Early DLL Hooks", .title = "Inject Early DLL Hooks",
.name = "z", .name = "z",
.desc = "Equivalent to 'Inject DLL Hooks' option, but ensures hooks are injected before " .desc = "Equivalent to 'Inject DLL Hooks' option, but ensures hooks are injected before "
@@ -668,6 +673,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{ {
// SDVXPrinterOutputPath
.title = "SDVX Printer Output Path", .title = "SDVX Printer Output Path",
.name = "printerpath", .name = "printerpath",
.desc = "Path to folder where images will be stored", .desc = "Path to folder where images will be stored",
@@ -692,6 +698,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{ {
// SDVXPrinterOutputFormat
.title = "SDVX Printer Output Format", .title = "SDVX Printer Output Format",
.name = "printerformat", .name = "printerformat",
.desc = "File format for printer output", .desc = "File format for printer output",
@@ -1311,6 +1318,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Touch Parameters", .category = "Touch Parameters",
}, },
{ {
// ICCAReaderPort
.title = "ICCA Reader Port", .title = "ICCA Reader Port",
.name = "reader", .name = "reader",
.desc = "Connects to and uses a ICCA on a given COM port", .desc = "Connects to and uses a ICCA on a given COM port",
@@ -1387,6 +1395,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
}, },
}, },
{ {
// SextetStreamPort
.title = "SextetStream Port", .title = "SextetStream Port",
.name = "sextet", .name = "sextet",
.desc = "Use a SextetStream device on a given COM port", .desc = "Use a SextetStream device on a given COM port",
@@ -2260,6 +2269,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options", .category = "Game Options",
}, },
{ {
// LovePlusPrinterOutputPath
.title = "LovePlus Printer Output Path", .title = "LovePlus Printer Output Path",
.name = "lpprinterpath", .name = "lpprinterpath",
.desc = "Path to folder where images will be stored", .desc = "Path to folder where images will be stored",
@@ -2284,6 +2294,7 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{ {
// LovePlusPrinterOutputFormat
.title = "LovePlus Printer Output Format", .title = "LovePlus Printer Output Format",
.name = "lpprinterformat", .name = "lpprinterformat",
.desc = "File format for printer output", .desc = "File format for printer output",
@@ -2301,6 +2312,16 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.game_name = "LovePlus", .game_name = "LovePlus",
.category = "Game Options (Advanced)", .category = "Game Options (Advanced)",
}, },
{
// OptionConflictResolution
.title = "Command Line Args Override",
.name = "cmdoverride",
.desc = "By default, option values in spicecfg take precedence over command-line args, for legacy compat.\n"
"When this is specified in command line, command-line args take precedence instead",
.type = OptionType::Bool,
.category = "Development",
.disabled = true,
},
}; };
const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) { const std::vector<std::string> &launcher::get_categories(Options::OptionsCategory category) {
@@ -2482,12 +2503,25 @@ std::vector<Option> launcher::merge_options(
auto &new_option = merged.emplace_back(option.get_definition(), ""); auto &new_option = merged.emplace_back(option.get_definition(), "");
new_option.disabled = true; new_option.disabled = true;
for (auto &value : option.values()) { if (USE_CMD_OVERRIDE) {
new_option.value_add(value); // command-line arguments take precedence (opt-in)
} for (auto &value : override.values()) {
for (auto &value : override.values()) { new_option.value_add(value);
new_option.value_add(value); }
for (auto &value : option.values()) {
new_option.value_add(value);
}
} else {
// spicecfg options take precedence (default)
// this sucks, but it's the default for legacy spicetools compat
for (auto &value : option.values()) {
new_option.value_add(value);
}
for (auto &value : override.values()) {
new_option.value_add(value);
}
} }
new_option.conflicting = true;
} else { } else {
auto &new_option = merged.emplace_back(override.get_definition(), ""); auto &new_option = merged.emplace_back(override.get_definition(), "");
new_option.disabled = true; new_option.disabled = true;
@@ -2503,7 +2537,6 @@ std::vector<Option> launcher::merge_options(
} }
} }
} }
return merged; return merged;
} }
+3
View File
@@ -246,6 +246,7 @@ namespace launcher {
LovePlusPrinterOutputOverwrite, LovePlusPrinterOutputOverwrite,
LovePlusPrinterOutputFormat, LovePlusPrinterOutputFormat,
LovePlusPrinterJPGQuality, LovePlusPrinterJPGQuality,
OptionConflictResolution,
}; };
enum class OptionsCategory { enum class OptionsCategory {
@@ -257,6 +258,8 @@ namespace launcher {
}; };
} }
extern bool USE_CMD_OVERRIDE;
const std::vector<std::string> &get_categories(Options::OptionsCategory category); const std::vector<std::string> &get_categories(Options::OptionsCategory category);
const std::vector<OptionDefinition> &get_option_definitions(); const std::vector<OptionDefinition> &get_option_definitions();
std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]); std::unique_ptr<std::vector<Option>> parse_options(int argc, char *argv[]);