mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-28 13:28:01 +03:00
Emulator: Implement config CLI args, add barrier for host CLI args
Some checks are pending
Some checks are pending
This commit is contained in:
parent
bd41774960
commit
783079266e
5 changed files with 128 additions and 32 deletions
|
@ -337,6 +337,18 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||||
out << YAML::EndMap;
|
out << YAML::EndMap;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case type::node_map:
|
||||||
|
{
|
||||||
|
//out << YAML::Block; // Does nothing, output is in Flow mode still (TODO)
|
||||||
|
out << YAML::BeginMap;
|
||||||
|
for (const auto& np : static_cast<const node_map_entry&>(rhs).get_map())
|
||||||
|
{
|
||||||
|
out << YAML::Key << np.first;
|
||||||
|
out << YAML::Value << fmt::format("%s", np.second);
|
||||||
|
}
|
||||||
|
out << YAML::EndMap;
|
||||||
|
return;
|
||||||
|
}
|
||||||
case type::log:
|
case type::log:
|
||||||
{
|
{
|
||||||
out << YAML::BeginMap;
|
out << YAML::BeginMap;
|
||||||
|
@ -417,6 +429,7 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case type::map:
|
case type::map:
|
||||||
|
case type::node_map:
|
||||||
{
|
{
|
||||||
if (!data.IsMap())
|
if (!data.IsMap())
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,7 @@ namespace cfg
|
||||||
string, // cfg::string type
|
string, // cfg::string type
|
||||||
set, // cfg::set_entry type
|
set, // cfg::set_entry type
|
||||||
map, // cfg::map_entry type
|
map, // cfg::map_entry type
|
||||||
|
node_map, // cfg::node_map_entry type
|
||||||
log, // cfg::log_entry type
|
log, // cfg::log_entry type
|
||||||
device, // cfg::device_entry type
|
device, // cfg::device_entry type
|
||||||
};
|
};
|
||||||
|
@ -627,13 +628,13 @@ namespace cfg
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using map_of_type = std::map<std::string, T, std::less<>>;
|
using map_of_type = std::map<std::string, T, std::less<>>;
|
||||||
|
|
||||||
class map_entry final : public _base
|
class map_entry : public _base
|
||||||
{
|
{
|
||||||
map_of_type<std::string> m_map{};
|
map_of_type<std::string> m_map{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
map_entry(node* owner, const std::string& name)
|
map_entry(node* owner, const std::string& name, type _type = type::map)
|
||||||
: _base(type::map, owner, name, true)
|
: _base(_type, owner, name, true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,6 +653,15 @@ namespace cfg
|
||||||
void from_default() override;
|
void from_default() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class node_map_entry final : public map_entry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
node_map_entry(node* owner, const std::string& name)
|
||||||
|
: map_entry(owner, name, type::node_map)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class log_entry final : public _base
|
class log_entry final : public _base
|
||||||
{
|
{
|
||||||
map_of_type<logs::level> m_map{};
|
map_of_type<logs::level> m_map{};
|
||||||
|
|
|
@ -1418,6 +1418,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||||
|
|
||||||
if (resolve_path_as_vfs_path)
|
if (resolve_path_as_vfs_path)
|
||||||
{
|
{
|
||||||
|
ensure(!argv.empty());
|
||||||
|
|
||||||
if (argv[0].starts_with("/dev_hdd0"sv))
|
if (argv[0].starts_with("/dev_hdd0"sv))
|
||||||
{
|
{
|
||||||
m_path = rpcs3::utils::get_hdd0_dir();
|
m_path = rpcs3::utils::get_hdd0_dir();
|
||||||
|
@ -1464,10 +1466,10 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||||
sys_log.error("Unknown source for path redirection: %s", argv[0]);
|
sys_log.error("Unknown source for path redirection: %s", argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argv.size() == 1)
|
if (argv.size() >= 1)
|
||||||
{
|
{
|
||||||
// Resolve later properly as if booted through host path
|
// Resolve later properly as if booted through host path
|
||||||
argv.clear();
|
argv[0].clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_log.notice("Restored executable path: \'%s\'", m_path);
|
sys_log.notice("Restored executable path: \'%s\'", m_path);
|
||||||
|
@ -2285,6 +2287,21 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
|
||||||
argv.resize(1);
|
argv.resize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& [arg_name, arg] : g_cfg.sys.sup_argv.get_map())
|
||||||
|
{
|
||||||
|
if (m_ar)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arg_name is unused here
|
||||||
|
// It exists solely for the user's convenience
|
||||||
|
|
||||||
|
sys_log.success("Passing CLI argument %d - \'%s\': \"%s\"", argv.size(), arg_name, arg);
|
||||||
|
|
||||||
|
argv.emplace_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
if (argv[0].empty())
|
if (argv[0].empty())
|
||||||
{
|
{
|
||||||
auto unescape = [](std::string_view path)
|
auto unescape = [](std::string_view path)
|
||||||
|
|
|
@ -305,6 +305,7 @@ struct cfg_root : cfg::node
|
||||||
cfg::uint<0, umax> console_psid_low{this, "PSID low"};
|
cfg::uint<0, umax> console_psid_low{this, "PSID low"};
|
||||||
cfg::string hdd_model{this, "HDD Model Name", ""};
|
cfg::string hdd_model{this, "HDD Model Name", ""};
|
||||||
cfg::string hdd_serial{this, "HDD Serial Number", ""};
|
cfg::string hdd_serial{this, "HDD Serial Number", ""};
|
||||||
|
cfg::node_map_entry sup_argv{ this, "Process ARGV" };
|
||||||
} sys{ this };
|
} sys{ this };
|
||||||
|
|
||||||
struct node_net : cfg::node
|
struct node_net : cfg::node
|
||||||
|
|
109
rpcs3/main.cpp
109
rpcs3/main.cpp
|
@ -4,6 +4,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
@ -398,22 +399,34 @@ constexpr auto arg_stdout = "stdout";
|
||||||
constexpr auto arg_stderr = "stderr";
|
constexpr auto arg_stderr = "stderr";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int find_arg(std::string arg, int& argc, char* argv[])
|
constexpr auto arg_emulation_barrier = "";
|
||||||
|
|
||||||
|
int find_arg(const char* to_search, std::span<char* const> argv)
|
||||||
{
|
{
|
||||||
arg = "--" + arg;
|
// It's not guaranteed that argv 0 is the executable.
|
||||||
for (int i = 0; i < argc; ++i) // It's not guaranteed that argv 0 is the executable.
|
for (usz i = 0; i != argv.size(); i++)
|
||||||
if (!strcmp(arg.c_str(), argv[i]))
|
{
|
||||||
|
const auto argp = argv[i];
|
||||||
|
|
||||||
|
if (argp[0] == '-' && argp[1] == '-' && !std::strcmp(to_search, argp + 2))
|
||||||
|
{
|
||||||
return i;
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication* create_application(int& argc, char* argv[])
|
QCoreApplication* create_application(std::span<char* const> qt_argv)
|
||||||
{
|
{
|
||||||
if (find_arg(arg_headless, argc, argv) != -1 ||
|
static int s_argc = static_cast<int>(qt_argv.size());
|
||||||
find_arg(arg_decrypt, argc, argv) != -1 ||
|
static char** const s_argv = const_cast<char**>(qt_argv.data());
|
||||||
find_arg(arg_commit_db, argc, argv) != -1)
|
|
||||||
|
if (find_arg(arg_headless, qt_argv) != -1 ||
|
||||||
|
find_arg(arg_decrypt, qt_argv) != -1 ||
|
||||||
|
find_arg(arg_commit_db, qt_argv) != -1)
|
||||||
{
|
{
|
||||||
return new headless_application(argc, argv);
|
return new headless_application(s_argc, s_argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -432,12 +445,12 @@ QCoreApplication* create_application(int& argc, char* argv[])
|
||||||
|
|
||||||
bool use_high_dpi = true;
|
bool use_high_dpi = true;
|
||||||
|
|
||||||
const int i_hdpi = find_arg(arg_high_dpi, argc, argv);
|
const int i_hdpi = find_arg(arg_high_dpi, qt_argv);
|
||||||
if (i_hdpi != -1)
|
if (i_hdpi != -1)
|
||||||
{
|
{
|
||||||
const std::string cmp_str = "0";
|
const std::string cmp_str = "0";
|
||||||
const auto i_hdpi_2 = (argc > (i_hdpi + 1)) ? (i_hdpi + 1) : 0;
|
const auto i_hdpi_2 = (s_argc > (i_hdpi + 1)) ? (i_hdpi + 1) : 0;
|
||||||
const auto high_dpi_setting = (i_hdpi_2 && !strcmp(cmp_str.c_str(), argv[i_hdpi_2])) ? "0" : "1";
|
const auto high_dpi_setting = (i_hdpi_2 && !std::strcmp(cmp_str.c_str(), qt_argv[i_hdpi_2])) ? "0" : "1";
|
||||||
|
|
||||||
// Set QT_ENABLE_HIGHDPI_SCALING from environment. Defaults to cli argument, which defaults to 1.
|
// Set QT_ENABLE_HIGHDPI_SCALING from environment. Defaults to cli argument, which defaults to 1.
|
||||||
use_high_dpi = "1" == qEnvironmentVariable("QT_ENABLE_HIGHDPI_SCALING", high_dpi_setting);
|
use_high_dpi = "1" == qEnvironmentVariable("QT_ENABLE_HIGHDPI_SCALING", high_dpi_setting);
|
||||||
|
@ -462,11 +475,11 @@ QCoreApplication* create_application(int& argc, char* argv[])
|
||||||
return ok;
|
return ok;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (const int i_rounding = find_arg(arg_rounding, argc, argv); i_rounding != -1)
|
if (const int i_rounding = find_arg(arg_rounding, qt_argv); i_rounding != -1)
|
||||||
{
|
{
|
||||||
if (const int i_rounding_2 = i_rounding + 1; argc > i_rounding_2)
|
if (const int i_rounding_2 = i_rounding + 1; s_argc > i_rounding_2)
|
||||||
{
|
{
|
||||||
if (const auto arg_val = argv[i_rounding_2]; !check_dpi_rounding_arg(arg_val))
|
if (const auto arg_val = qt_argv[i_rounding_2]; !check_dpi_rounding_arg(arg_val))
|
||||||
{
|
{
|
||||||
const std::string msg = fmt::format("The command line value %s for %s is not allowed. Please use a valid value for Qt::HighDpiScaleFactorRoundingPolicy.", arg_val, arg_rounding);
|
const std::string msg = fmt::format("The command line value %s for %s is not allowed. Please use a valid value for Qt::HighDpiScaleFactorRoundingPolicy.", arg_val, arg_rounding);
|
||||||
sys_log.error("%s", msg); // Don't exit with fatal error. The resulting dialog might be unreadable with dpi problems.
|
sys_log.error("%s", msg); // Don't exit with fatal error. The resulting dialog might be unreadable with dpi problems.
|
||||||
|
@ -488,7 +501,7 @@ QCoreApplication* create_application(int& argc, char* argv[])
|
||||||
QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_val);
|
QApplication::setHighDpiScaleFactorRoundingPolicy(rounding_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new gui_application(argc, argv);
|
return new gui_application(s_argc, s_argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -537,8 +550,22 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
s_argv0 = argv[0]; // Save for report_fatal_error
|
s_argv0 = argv[0]; // Save for report_fatal_error
|
||||||
|
|
||||||
|
std::span<char* const> argv_span{ argv, argc + 0u };
|
||||||
|
std::span<char* const> emu_argv;
|
||||||
|
std::span<char* const> qt_argv;
|
||||||
|
|
||||||
|
if (int emu_pos = find_arg(arg_emulation_barrier, argv_span); emu_pos != -1)
|
||||||
|
{
|
||||||
|
emu_argv = argv_span.subspan(emu_pos + 1);
|
||||||
|
qt_argv = argv_span.subspan(0, emu_pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qt_argv = argv_span;
|
||||||
|
}
|
||||||
|
|
||||||
// Only run RPCS3 to display an error
|
// Only run RPCS3 to display an error
|
||||||
if (int err_pos = find_arg(arg_error, argc, argv); err_pos != -1)
|
if (int err_pos = find_arg(arg_error, qt_argv); err_pos != -1)
|
||||||
{
|
{
|
||||||
// Reconstruction of the error from multiple args
|
// Reconstruction of the error from multiple args
|
||||||
std::string error;
|
std::string error;
|
||||||
|
@ -559,7 +586,7 @@ int main(int argc, char** argv)
|
||||||
static fs::file instance_lock;
|
static fs::file instance_lock;
|
||||||
|
|
||||||
// True if an argument --updating found
|
// True if an argument --updating found
|
||||||
const bool is_updating = find_arg(arg_updating, argc, argv) != -1;
|
const bool is_updating = find_arg(arg_updating, qt_argv) != -1;
|
||||||
|
|
||||||
// Keep trying to lock the file for ~2s normally, and for ~10s in the case of --updating
|
// Keep trying to lock the file for ~2s normally, and for ~10s in the case of --updating
|
||||||
for (u32 num = 0; num < (is_updating ? 500u : 100u) && !instance_lock.open(lock_name, fs::rewrite + fs::lock); num++)
|
for (u32 num = 0; num < (is_updating ? 500u : 100u) && !instance_lock.open(lock_name, fs::rewrite + fs::lock); num++)
|
||||||
|
@ -735,9 +762,9 @@ int main(int argc, char** argv)
|
||||||
// The constructor of QApplication eats the --style and --stylesheet arguments.
|
// The constructor of QApplication eats the --style and --stylesheet arguments.
|
||||||
// By checking for stylesheet().isEmpty() we could implicitly know if a stylesheet was passed,
|
// By checking for stylesheet().isEmpty() we could implicitly know if a stylesheet was passed,
|
||||||
// but I haven't found an implicit way to check for style yet, so we naively check them both here for now.
|
// but I haven't found an implicit way to check for style yet, so we naively check them both here for now.
|
||||||
const bool use_cli_style = find_arg(arg_style, argc, argv) != -1 || find_arg(arg_stylesheet, argc, argv) != -1;
|
const bool use_cli_style = find_arg(arg_style, qt_argv) != -1 || find_arg(arg_stylesheet, qt_argv) != -1;
|
||||||
|
|
||||||
QScopedPointer<QCoreApplication> app(create_application(argc, argv));
|
QScopedPointer<QCoreApplication> app(create_application(qt_argv));
|
||||||
app->setApplicationVersion(QString::fromStdString(rpcs3::get_version().to_string()));
|
app->setApplicationVersion(QString::fromStdString(rpcs3::get_version().to_string()));
|
||||||
app->setApplicationName("RPCS3");
|
app->setApplicationName("RPCS3");
|
||||||
app->setOrganizationName("RPCS3");
|
app->setOrganizationName("RPCS3");
|
||||||
|
@ -791,6 +818,8 @@ int main(int argc, char** argv)
|
||||||
parser.addOption(QCommandLineOption(arg_stderr, "Attach the console window and listen to error output stream. (STDERR)"));
|
parser.addOption(QCommandLineOption(arg_stderr, "Attach the console window and listen to error output stream. (STDERR)"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
parser.addOption(QCommandLineOption("N/A", "Arguments after \"--\" are considered emulation arguments."));
|
||||||
|
|
||||||
parser.process(app->arguments());
|
parser.process(app->arguments());
|
||||||
|
|
||||||
// Don't start up the full rpcs3 gui if we just want the version or help.
|
// Don't start up the full rpcs3 gui if we just want the version or help.
|
||||||
|
@ -851,15 +880,15 @@ int main(int argc, char** argv)
|
||||||
#endif
|
#endif
|
||||||
std::string from_sha;
|
std::string from_sha;
|
||||||
|
|
||||||
if (const int i_arg_commit_db = find_arg(arg_commit_db, argc, argv); i_arg_commit_db != -1)
|
if (const int i_arg_commit_db = find_arg(arg_commit_db, qt_argv); i_arg_commit_db != -1)
|
||||||
{
|
{
|
||||||
if (int i = i_arg_commit_db + 1; argc > i)
|
if (int i = i_arg_commit_db + 1; argc > i)
|
||||||
{
|
{
|
||||||
path = argv[i++];
|
path = qt_argv[i++];
|
||||||
|
|
||||||
if (argc > i)
|
if (argc > i)
|
||||||
{
|
{
|
||||||
from_sha = argv[i];
|
from_sha = qt_argv[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -1385,9 +1414,9 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (const QStringList args = parser.positionalArguments(); !args.isEmpty() && !is_updating && !parser.isSet(arg_installfw) && !parser.isSet(arg_installpkg))
|
else if (const QStringList args = parser.positionalArguments(); (!args.isEmpty() || !emu_argv.empty()) && !is_updating && !parser.isSet(arg_installfw) && !parser.isSet(arg_installpkg))
|
||||||
{
|
{
|
||||||
const std::string spath = ::at32(args, 0).toStdString();
|
const std::string spath = (args.isEmpty() ? emu_argv[0] : ::at32(args, 0).toStdString());
|
||||||
|
|
||||||
if (spath.starts_with(Emulator::vfs_boot_prefix))
|
if (spath.starts_with(Emulator::vfs_boot_prefix))
|
||||||
{
|
{
|
||||||
|
@ -1407,13 +1436,39 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
if (args.length() > 1)
|
if (args.length() > 1)
|
||||||
{
|
{
|
||||||
|
// Reserve empty string for executable path
|
||||||
rpcs3_argv.emplace_back();
|
rpcs3_argv.emplace_back();
|
||||||
|
|
||||||
for (int i = 1; i < args.length(); i++)
|
rpcs3_argv.emplace_back();
|
||||||
|
|
||||||
|
for (int i = 1; i != args.length(); i++)
|
||||||
{
|
{
|
||||||
const std::string arg = args[i].toStdString();
|
const std::string arg = args[i].toStdString();
|
||||||
rpcs3_argv.emplace_back(arg);
|
rpcs3_argv.emplace_back(arg);
|
||||||
sys_log.notice("Optional command line argument %d: %s", i, arg);
|
|
||||||
|
sys_log.error("Optional command line argument %d: %s"
|
||||||
|
"\nPlease pass emulation arguments after an empty \"--\" paramater."
|
||||||
|
"\nIn the future, the emulator would not support optional arguments without it.", i, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional arguments passed after "--"
|
||||||
|
if (emu_argv.size() > (args.isEmpty() ? 1 : 0))
|
||||||
|
{
|
||||||
|
// Reserve empty string for executable path
|
||||||
|
if (rpcs3_argv.empty())
|
||||||
|
{
|
||||||
|
rpcs3_argv.emplace_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcs3_argv.emplace_back();
|
||||||
|
|
||||||
|
for (usz i = args.isEmpty() ? 1 : 0; i != emu_argv.size(); i++)
|
||||||
|
{
|
||||||
|
const std::string arg = args[i].toStdString();
|
||||||
|
rpcs3_argv.emplace_back(arg);
|
||||||
|
|
||||||
|
sys_log.success("Optional command line argument %d: %s", i, arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue