Browse Source
[frontend] Unify cmdline parsing (#4424)
[frontend] Unify cmdline parsing (#4424)
Addresses #4383. Makes it so both the Qt and SDL frontends now share the same options (bar `-hlaunch` and `-qlaunch`). Signed-off-by: lizzie <lizzie@eden-emu.dev> - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4424 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: Maufeat <sahyno1996@gmail.com>xbzk/buffer-kepler-maxwell-fixes
committed by
crueter
No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
7 changed files with 317 additions and 279 deletions
-
50docs/user/CommandLine.md
-
5src/core/CMakeLists.txt
-
205src/core/launch_params.cpp
-
43src/core/launch_params.h
-
86src/yuzu/main_window.cpp
-
3src/yuzu_cmd/CMakeLists.txt
-
204src/yuzu_cmd/yuzu.cpp
@ -1,32 +1,44 @@ |
|||
# User Handbook - Command Line |
|||
|
|||
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. |
|||
|
|||
## eden |
|||
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments. |
|||
|
|||
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application. |
|||
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. |
|||
- `-f`: Use fullscreen. |
|||
- `-u <number>`: Select the index of the user to load as. |
|||
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only). |
|||
- `-qlaunch`: Launch QLaunch. |
|||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. |
|||
- Requires a copy of Atmosphere to be extracted onto `sdmc`. |
|||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`. |
|||
- `-setup`: Launch setup applet. |
|||
|
|||
## eden-cli |
|||
|
|||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` |
|||
- `--config/-c`: Specify alternate configuration file. |
|||
- `--fullscreen/-f`: Set fullscreen. |
|||
- `--fullscreen/-f`: Use fullscreen. |
|||
- `--help/-h`: Display help. |
|||
- `--game/-g`: Specify the game to run. |
|||
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. |
|||
- `--multiplayer/-m`: Specify multiplayer options. |
|||
- `--program/-p`: Specify the program arguments to pass (optional). |
|||
- `--user/-u`: Specify the user index. |
|||
- `--user/-u <number>`: Specify the user index. |
|||
- `--version/-v`: Display version and quit. |
|||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only). |
|||
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only). |
|||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings. |
|||
- `--filter/-x`: Sets the debug log filter irrespective of settings. |
|||
- `--singlecore/-s`: Forces single-core regardless of settings. |
|||
|
|||
Only the Qt frontend supports the following arguments: |
|||
|
|||
- `-qlaunch`: Launch QLaunch. |
|||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. |
|||
- Requires a copy of Atmosphere to be extracted onto `sdmc`. |
|||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`. |
|||
- `-setup`: Launch setup applet. |
|||
|
|||
`eden` (provided with `--room`), and `eden-room` supports the following options as well: |
|||
|
|||
- `-n/--room-name`: The name of the room. |
|||
- `-d/--room-description`: The room description. |
|||
- `-s/--bind-address`: The bind address for the room. |
|||
- `-p/--port`: The port used for the room. |
|||
- `-m/--max-members`: The maximum number of players for this room. |
|||
- `-w/--password`: The password for the room. |
|||
- `-g/--preferred-game`: The preferred game for this room. |
|||
- `-i/--preferred-game-id`: The preferred game-id for this room. |
|||
- `-u/--username`: The username used for announce. |
|||
- `-t/--token`: The token used for announce. |
|||
- `-a/--web-api-url`: yuzu Web API url. |
|||
- `-b/--ban-list-file`: The file for storing the room ban list. |
|||
- `-l/--log-file`: The file for storing the room log. |
|||
- `-h/--help`: Display this help and exit. |
|||
- `-v/--version`: Output version information and exit. |
|||
@ -0,0 +1,205 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|||
|
|||
#include <regex>
|
|||
#include <cctype>
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "common/logging.h"
|
|||
#include "common/settings.h"
|
|||
#include "common/string_util.h"
|
|||
#include "common/scm_rev.h"
|
|||
#include "core/core.h"
|
|||
#include "core/hle/service/acc/profile_manager.h"
|
|||
#include "core/launch_params.h"
|
|||
|
|||
#undef _UNICODE
|
|||
#include <getopt.h>
|
|||
#ifndef _MSC_VER
|
|||
#include <unistd.h>
|
|||
#endif
|
|||
|
|||
namespace Core { |
|||
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept { |
|||
LaunchParams p{}; |
|||
|
|||
int option_index = 0; |
|||
static struct option long_options[] = { |
|||
// clang-format off
|
|||
{"debug", no_argument, 0, 'd'}, |
|||
{"config", required_argument, 0, 'c'}, |
|||
{"fullscreen", no_argument, 0, 'f'}, |
|||
{"help", no_argument, 0, 'h'}, |
|||
{"game", required_argument, 0, 'g'}, |
|||
{"multiplayer", required_argument, 0, 'm'}, |
|||
{"program", optional_argument, 0, 'p'}, |
|||
{"user", required_argument, 0, 'u'}, |
|||
{"version", no_argument, 0, 'v'}, |
|||
{"input-profile", no_argument, 0, 'i'}, |
|||
{"null-render", no_argument, 0, 'n'}, |
|||
{"singlecore", no_argument, 0, 's'}, |
|||
{"filter", no_argument, 0, 'x'}, |
|||
{0, 0, 0, 0}, |
|||
// clang-format on
|
|||
}; |
|||
|
|||
while (optind < argc) { |
|||
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) { |
|||
switch (char(arg)) { |
|||
case 'd': |
|||
p.override_gdb_port = uint16_t(atoi(optarg)); |
|||
break; |
|||
case 'c': |
|||
p.config_path = optarg; |
|||
break; |
|||
case 'f': |
|||
p.fullscreen = true; |
|||
LOG_INFO(Frontend, "Starting in fullscreen mode..."); |
|||
break; |
|||
case 'h': |
|||
p.print_help = true; |
|||
break; |
|||
case 'g': |
|||
p.filepath = std::string(optarg); |
|||
break; |
|||
case 'i': { |
|||
p.input_profile = std::string(optarg); |
|||
break; |
|||
} |
|||
case 'm': { |
|||
p.use_multiplayer = true; |
|||
const std::string str_arg(optarg); |
|||
// regex to check if the format is nickname:password@ip:port
|
|||
// with optional :password
|
|||
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); |
|||
if (std::regex_match(str_arg, re)) { |
|||
std::smatch match; |
|||
std::regex_search(str_arg, match, re); |
|||
ASSERT(match.size() == 5); |
|||
p.nickname = match[1]; |
|||
p.password = match[2]; |
|||
p.address = match[3]; |
|||
if (!match[4].str().empty()) { |
|||
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0)); |
|||
} |
|||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); |
|||
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters"); |
|||
ASSERT(!p.address.empty() && "Address is empty"); |
|||
} |
|||
break; |
|||
} |
|||
case 'p': |
|||
p.program_args = argv[optind]; |
|||
++optind; |
|||
break; |
|||
case 'u': { |
|||
// Launch game with a specific user
|
|||
bool argument_ok = isdigit(optarg[0]); |
|||
p.selected_user = atoi(optarg); |
|||
if (!argument_ok) { |
|||
// try to look it up by username, only finds the first username that matches.
|
|||
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg); |
|||
if (user_idx != std::nullopt) { |
|||
p.selected_user = user_idx.value(); |
|||
} else { |
|||
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg); |
|||
break; |
|||
} |
|||
} |
|||
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) { |
|||
Settings::values.current_user = s32(*p.selected_user); |
|||
} else { |
|||
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user); |
|||
} |
|||
break; |
|||
} |
|||
case 'v': |
|||
p.print_version = true; |
|||
break; |
|||
case 'n': |
|||
p.force_null_render = true; |
|||
break; |
|||
case 's': |
|||
p.force_single_core = true; |
|||
break; |
|||
case 'x': |
|||
p.log_filter = argv[optind]; |
|||
++optind; |
|||
break; |
|||
} |
|||
} else { |
|||
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
|
|||
if (strcmp(argv[optind], "-hlaunch") == 0) { |
|||
p.launch_hlaunch = true; |
|||
} else if (strcmp(argv[optind], "-qlaunch") == 0) { |
|||
p.launch_qlaunch = true; |
|||
} else if (strcmp(argv[optind], "-setup") == 0) { |
|||
p.launch_setup = true; |
|||
} else { |
|||
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
|
|||
#ifdef _WIN32
|
|||
p.filepath = argv_w != nullptr |
|||
? Common::UTF16ToUTF8(argv_w[optind]) |
|||
: argv[optind]; |
|||
#else
|
|||
p.filepath = argv[optind]; |
|||
#endif
|
|||
} |
|||
optind++; |
|||
} |
|||
} |
|||
p.argv0 = argv[0]; |
|||
return p; |
|||
} |
|||
|
|||
void ApplyLaunchParams(LaunchParams const& lp) noexcept { |
|||
// apply the log_filter setting
|
|||
// the logger was initialized before and doesn't pick up the filter on its own
|
|||
Common::Log::Filter filter{}; |
|||
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue())); |
|||
Common::Log::SetGlobalFilter(filter); |
|||
|
|||
if (!lp.program_args.empty()) { |
|||
Settings::values.program_args = lp.program_args; |
|||
} |
|||
if (!lp.input_profile.empty()) { |
|||
auto& players = Settings::values.players.GetValue(); |
|||
players[0].profile_name = lp.input_profile; |
|||
} |
|||
if (lp.selected_user.has_value()) { |
|||
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7); |
|||
} |
|||
if (lp.override_gdb_port.has_value()) { |
|||
Settings::values.use_gdbstub = true; |
|||
Settings::values.gdbstub_port = *lp.override_gdb_port; |
|||
} |
|||
if (lp.force_single_core) { |
|||
Settings::values.use_multi_core = false; |
|||
} |
|||
if (lp.force_null_render) { |
|||
Settings::values.renderer_backend = Settings::RendererBackend::Null; |
|||
} |
|||
|
|||
if (lp.print_version) { |
|||
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc); |
|||
} |
|||
if (lp.print_help) { |
|||
LOG_INFO(Frontend, |
|||
"Usage: {}" |
|||
" [options] <filename>\n" |
|||
"-c, --config Load the specified configuration file\n" |
|||
"-f, --fullscreen Start in fullscreen mode\n" |
|||
"-g, --game File path of the game to load\n" |
|||
"-h, --help Display this help and exit\n" |
|||
"-m, --multiplayer=nick:password@address:port" |
|||
" Nickname, password, address and port for multiplayer\n" |
|||
"-p, --program Pass following string as arguments to executable\n" |
|||
"-u, --user Select a specific user profile from 0 to 7\n" |
|||
"-d, --debug Run the GDB stub on a port from 1 to 65535\n" |
|||
"-v, --version Output version information and exit\n", |
|||
lp.argv0 |
|||
); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project |
|||
// SPDX-License-Identifier: GPL-3.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <optional> |
|||
#include "common/common_types.h" |
|||
#include "network/room.h" |
|||
|
|||
namespace Core { |
|||
|
|||
class System; |
|||
struct LaunchParams { |
|||
std::string argv0{}; |
|||
std::string filepath{}; |
|||
std::string nickname{}; |
|||
std::string password{}; |
|||
std::string address{}; |
|||
std::string input_profile{}; |
|||
std::string program_args{}; |
|||
std::optional<std::string> config_path{}; |
|||
std::optional<int> selected_user{}; |
|||
std::optional<u16> override_gdb_port{}; |
|||
std::optional<std::string> log_filter{}; |
|||
u16 port = Network::DefaultRoomPort; |
|||
bool use_multiplayer = false; |
|||
bool fullscreen = false; |
|||
bool force_null_render = false; |
|||
bool force_single_core = false; |
|||
// print |
|||
bool print_version = false; |
|||
bool print_help = false; |
|||
// qt |
|||
bool launch_hlaunch = false; |
|||
bool launch_qlaunch = false; |
|||
bool launch_setup = false; |
|||
}; |
|||
|
|||
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept; |
|||
void ApplyLaunchParams(LaunchParams const& lp) noexcept; |
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue