mirror of
https://github.com/open-goal/jak-project
synced 2026-06-21 00:31:41 -04:00
game: cleanup gk's CLI documentation (#2189)
An attempt to cleanup the last CLI interface we have left to cleanup. - `gk` args now follow the typical convention ie. `--proj-path` instead of `-proj-path`. - args that are passed through to the rest of the application / the game's runtime use the typical convention of following a `--` - I'm thinking some args shouldn't be handled at this level ie (`-nodisplay`, `-vm`, `-novm` or `-jak2`) These could be better documented as legitimate flags and passed in via a nice struct. They don't seem to be used in `InitParams` but I'll triple check. There's a temporary shim here so there is no coupled release with the launcher (right now it executes `gk` with a few args). So I just change the old args into the new format. After one release cycle, I can change it in the launcher and delete it here. I am unsure if this will break the bash shellscript usages -- not sure which args were usually passed into `$@` 
This commit is contained in:
+135
-38
@@ -15,6 +15,10 @@
|
||||
#include "common/util/unicode_util.h"
|
||||
#include "common/versions.h"
|
||||
|
||||
#include "game/common/game_common_types.h"
|
||||
|
||||
#include "third-party/CLI11.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
||||
@@ -40,51 +44,142 @@ void setup_logging(bool verbose) {
|
||||
lg::initialize();
|
||||
}
|
||||
|
||||
std::string game_arg_documentation() {
|
||||
// clang-format off
|
||||
std::string output = fmt::format(fmt::emphasis::bold, "Game Args (passed through to the game runtime after '--')\n");
|
||||
output += fmt::format(fmt::fg(fmt::color::gray), "Order matters, some args will negate others (see kmachine.cpp for details)\n");
|
||||
output += fmt::format(fmt::fg(fmt::color::gray), "Args with `*` are not well supported\n\n");
|
||||
// Common args
|
||||
output += fmt::format(fmt::emphasis::bold, "Common:\n");
|
||||
output += " -cd * Use the DVD drive for everything. This is how the game runs in retail\n";
|
||||
output += " -cddata * Use the DVD drive for everything but IOP modules\n";
|
||||
output += " -deviso * One of two modes for testing without the need for DVDs\n";
|
||||
output += " -fakeiso The other of two modes for testing without the need for DVDs\n";
|
||||
output += " -boot Used to set GOAL up for running the game in retail mode\n";
|
||||
output += " -debug Used to set GOAL up for debugging/development\n";
|
||||
output += " -debug-mem Used to set up GOAL in debug mode, but not to load debug-segments\n";
|
||||
output += " -nokernel An added mode to allow booting without a KERNEL.CGO for testing\n";
|
||||
output += " -nosound An added mode to allow booting without sound for testing\n";
|
||||
output += " -level [name] Used to inform the game to boot a specific level the default level is `#f`\n";
|
||||
// Jak 1 Related
|
||||
output += fmt::format(fmt::emphasis::bold | fmt::fg(fmt::color::orange), "Jak 1:\n");
|
||||
output += " -demo Used to pass the message `demo` to the gkernel in the DebugBootMessage (instead of play)\n";
|
||||
// Jak 2 only
|
||||
output += fmt::format(fmt::emphasis::bold | fmt::fg(fmt::color::purple), "Jak 2:\n");
|
||||
output += " -kiosk A demo mode, TODO on specifics\n";
|
||||
output += " -preview A demo mode, TODO on specifics\n";
|
||||
output += " -debug-boot Used to boot the game in retail mode, but with debug segments\n";
|
||||
output += " -user [name] Specify the debugging username, the default is `unknown`\n";
|
||||
output += " -art [name] Specify the art-group name to set `DebugBootArtGroup`, there is no default\n";
|
||||
// clang-format on
|
||||
return output;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Entry point for the game.
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
ArgumentGuard u8_guard(argc, argv);
|
||||
|
||||
// TODO - replace with CLI11 and just propagate args through
|
||||
// - https://github.com/CLIUtils/CLI11/issues/744
|
||||
// TODO - this is a temporary shim to convert the old arg format
|
||||
// into the new
|
||||
//
|
||||
// This is needed to avoid a coupled release with the launcher and
|
||||
// can be removed after one release cycle
|
||||
//
|
||||
// Normal users launch gk with _no_ args
|
||||
//
|
||||
// Only handling args the launcher provides, all others can be changed
|
||||
// in this repo at the time of merge.
|
||||
std::vector<char*> adjusted_argv_vals;
|
||||
std::vector<char*> adjusted_argv_vals_passthru;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
const auto& val = std::string(argv[i]);
|
||||
// Handle all args that aren't passed through
|
||||
if (val == "-proj-path") {
|
||||
adjusted_argv_vals.push_back("--proj-path");
|
||||
i++;
|
||||
if (i > argc) {
|
||||
return 1;
|
||||
}
|
||||
adjusted_argv_vals.push_back(argv[i]);
|
||||
} // now handle all the ones that get passed to the game
|
||||
else if (val == "-boot") {
|
||||
adjusted_argv_vals_passthru.push_back("-boot");
|
||||
} else if (val == "-fakeiso") {
|
||||
adjusted_argv_vals_passthru.push_back("-fakeiso");
|
||||
} else if (val == "-debug") {
|
||||
adjusted_argv_vals_passthru.push_back("-debug");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char*> new_argv;
|
||||
if (!adjusted_argv_vals.empty() || !adjusted_argv_vals_passthru.empty()) {
|
||||
new_argv.push_back(argv[0]);
|
||||
for (const auto& arg : adjusted_argv_vals) {
|
||||
new_argv.push_back(arg);
|
||||
}
|
||||
if (!adjusted_argv_vals_passthru.empty()) {
|
||||
new_argv.push_back("--");
|
||||
for (const auto& arg : adjusted_argv_vals_passthru) {
|
||||
new_argv.push_back(arg);
|
||||
}
|
||||
}
|
||||
argv = new_argv.data();
|
||||
argc = new_argv.size();
|
||||
}
|
||||
// --- END temporary shim
|
||||
|
||||
// CLI flags
|
||||
std::string game_name = "jak1";
|
||||
bool verbose_logging = false;
|
||||
bool disable_avx2 = false;
|
||||
bool disable_display = false;
|
||||
bool disable_debug_vm = false;
|
||||
fs::path project_path_override;
|
||||
std::vector<std::string> game_args;
|
||||
CLI::App app{"OpenGOAL Game Runtime"};
|
||||
app.add_option("-g,--game", game_name, "The game name: 'jak1' or 'jak2'");
|
||||
app.add_flag("-v,--verbose", verbose_logging, "Enable verbose logging on stdout");
|
||||
app.add_flag("--no-avx2", verbose_logging, "Disable AVX2 for testing");
|
||||
app.add_flag("--no-display", disable_display, "Disable video display");
|
||||
app.add_flag("--no-vm", disable_debug_vm, "Disable debug PS2 VM (defaulted to on)");
|
||||
app.add_option("--proj-path", project_path_override,
|
||||
"Specify the location of the 'data/' folder");
|
||||
app.footer(game_arg_documentation());
|
||||
app.add_option("Game Args", game_args,
|
||||
"Remaining arguments (after '--') that are passed-through to the game itself");
|
||||
app.allow_extras();
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
// Create struct with all non-kmachine handled args to pass to the runtime
|
||||
GameLaunchOptions game_options;
|
||||
game_options.disable_debug_vm = disable_debug_vm;
|
||||
game_options.disable_display = disable_display;
|
||||
game_options.game_version = game_name_to_version(game_name);
|
||||
|
||||
// Figure out if the CPU has AVX2 to enable higher performance AVX2 versions of functions.
|
||||
setup_cpu_info();
|
||||
// If the CPU doesn't have AVX, GOAL code won't work and we exit.
|
||||
if (!get_cpu_info().has_avx) {
|
||||
printf("Your CPU does not support AVX, which is required for OpenGOAL.\n");
|
||||
lg::info("Your CPU does not support AVX, which is required for OpenGOAL.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parse arguments
|
||||
bool verbose = false;
|
||||
bool disable_avx2 = false;
|
||||
std::optional<fs::path> project_path_override = std::nullopt;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (std::string("-v") == argv[i]) {
|
||||
verbose = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::string("-no-avx2") == argv[i]) {
|
||||
disable_avx2 = true;
|
||||
}
|
||||
|
||||
if (std::string("-proj-path") == argv[i] && i + 1 < argc) {
|
||||
project_path_override = std::make_optional(fs::path(argv[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
// set up file paths for resources. This is the full repository when developing, and the data
|
||||
// directory (a subset of the full repo) in release versions
|
||||
if (!file_util::setup_project_path(project_path_override)) {
|
||||
if (project_path_override.empty()) {
|
||||
lg::info("No project path provided, looking for data/ folder in current directory");
|
||||
if (!file_util::setup_project_path({})) {
|
||||
return 1;
|
||||
}
|
||||
} else if (!file_util::setup_project_path(project_path_override)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (disable_avx2) {
|
||||
// for debugging the non-avx2 code paths, there's a flag to manually disable.
|
||||
printf("Note: AVX2 code has been manually disabled.\n");
|
||||
lg::info("Note: AVX2 code has been manually disabled.");
|
||||
get_cpu_info().has_avx2 = false;
|
||||
}
|
||||
|
||||
@@ -96,38 +191,40 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
|
||||
if (get_cpu_info().has_avx2) {
|
||||
printf("AVX2 mode enabled\n");
|
||||
lg::info("AVX2 mode enabled");
|
||||
} else {
|
||||
printf("AVX2 mode disabled\n");
|
||||
lg::info("AVX2 mode disabled");
|
||||
}
|
||||
|
||||
try {
|
||||
setup_logging(verbose);
|
||||
setup_logging(verbose_logging);
|
||||
} catch (const std::exception& e) {
|
||||
lg::error("Failed to setup logging: {}", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool force_debug_next_time = false;
|
||||
// always start with an empty arg, as internally kmachine starts at `1` not `0`
|
||||
std::vector<char*> arg_ptrs = {""};
|
||||
for (auto& str : game_args) {
|
||||
arg_ptrs.push_back(str.data());
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::vector<std::string> args;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
args.push_back(argv[i]);
|
||||
}
|
||||
if (force_debug_next_time) {
|
||||
args.push_back("-boot");
|
||||
args.push_back("-debug");
|
||||
game_args.push_back("-boot");
|
||||
game_args.push_back("-debug");
|
||||
force_debug_next_time = false;
|
||||
}
|
||||
std::vector<char*> ptrs;
|
||||
for (auto& str : args) {
|
||||
ptrs.push_back(str.data());
|
||||
arg_ptrs.clear();
|
||||
for (auto& str : game_args) {
|
||||
arg_ptrs.push_back(str.data());
|
||||
}
|
||||
}
|
||||
|
||||
// run the runtime in a loop so we can reset the game and have it restart cleanly
|
||||
lg::info("OpenGOAL Runtime {}.{}", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
||||
try {
|
||||
auto exit_status = exec_runtime(ptrs.size(), ptrs.data());
|
||||
auto exit_status = exec_runtime(game_options, arg_ptrs.size(), arg_ptrs.data());
|
||||
switch (exit_status) {
|
||||
case RuntimeExitStatus::EXIT:
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user