2020-08-23 02:30:12 +00:00
|
|
|
/*!
|
|
|
|
* @file main.cpp
|
|
|
|
* Main for the game. Launches the runtime.
|
|
|
|
*/
|
2020-10-11 23:55:29 +00:00
|
|
|
|
2022-07-10 23:03:24 +00:00
|
|
|
#define STBI_WINDOWS_UTF8
|
|
|
|
|
2020-10-11 23:55:29 +00:00
|
|
|
#include <string>
|
2022-06-23 03:37:46 +00:00
|
|
|
|
2020-08-23 02:30:12 +00:00
|
|
|
#include "runtime.h"
|
2022-06-23 03:37:46 +00:00
|
|
|
|
2023-04-25 03:46:55 +00:00
|
|
|
#include "common/global_profiler/GlobalProfiler.h"
|
2021-01-06 17:16:39 +00:00
|
|
|
#include "common/log/log.h"
|
|
|
|
#include "common/util/FileUtil.h"
|
2023-07-01 01:05:58 +00:00
|
|
|
#include "common/util/dialogs.h"
|
2022-02-17 03:13:18 +00:00
|
|
|
#include "common/util/os.h"
|
2023-08-08 16:59:37 +00:00
|
|
|
#include "common/util/term_util.h"
|
2022-12-22 23:12:59 +00:00
|
|
|
#include "common/util/unicode_util.h"
|
2023-04-22 18:13:57 +00:00
|
|
|
#include "common/versions/versions.h"
|
2022-06-23 03:37:46 +00:00
|
|
|
|
2023-03-10 04:13:01 +00:00
|
|
|
#include "game/common/game_common_types.h"
|
2023-07-01 01:05:58 +00:00
|
|
|
#include "graphics/gfx_test.h"
|
2023-03-10 04:13:01 +00:00
|
|
|
|
|
|
|
#include "third-party/CLI11.hpp"
|
|
|
|
|
2022-07-31 17:32:22 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
extern "C" {
|
|
|
|
__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
|
|
|
|
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-04-18 01:12:24 +00:00
|
|
|
/*!
|
|
|
|
* Set up logging system to log to file.
|
|
|
|
* @param verbose : should we print debug-level messages to stdout?
|
|
|
|
*/
|
2023-08-08 16:59:37 +00:00
|
|
|
void setup_logging(const std::string& game_name, bool verbose, bool disable_ansi_colors) {
|
|
|
|
lg::set_file(game_name);
|
2020-10-11 23:55:29 +00:00
|
|
|
if (verbose) {
|
2021-02-21 16:02:28 +00:00
|
|
|
lg::set_file_level(lg::level::debug);
|
|
|
|
lg::set_stdout_level(lg::level::debug);
|
|
|
|
lg::set_flush_level(lg::level::debug);
|
2020-10-11 23:55:29 +00:00
|
|
|
} else {
|
2022-04-18 01:12:24 +00:00
|
|
|
lg::set_file_level(lg::level::debug);
|
2024-04-28 19:29:20 +00:00
|
|
|
lg::set_stdout_level(lg::level::info);
|
2021-01-06 17:16:39 +00:00
|
|
|
lg::set_flush_level(lg::level::warn);
|
2020-10-11 23:55:29 +00:00
|
|
|
}
|
2023-08-08 16:59:37 +00:00
|
|
|
if (disable_ansi_colors) {
|
|
|
|
lg::disable_ansi_colors();
|
|
|
|
}
|
2021-01-06 17:16:39 +00:00
|
|
|
lg::initialize();
|
2020-10-11 23:55:29 +00:00
|
|
|
}
|
2020-10-06 22:03:33 +00:00
|
|
|
|
2023-03-10 04:13:01 +00:00
|
|
|
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");
|
2023-10-01 03:28:30 +00:00
|
|
|
output += " -demo Boot the game in demo mode\n";
|
2023-03-10 04:13:01 +00:00
|
|
|
// Jak 2 only
|
|
|
|
output += fmt::format(fmt::emphasis::bold | fmt::fg(fmt::color::purple), "Jak 2:\n");
|
2023-10-01 03:28:30 +00:00
|
|
|
output += " -demo Boot the game in demo mode\n";
|
|
|
|
output += " -kiosk Boot the game in kiosk demo mode\n";
|
|
|
|
output += " -preview Boot the game in preview demo mode\n";
|
2023-03-10 04:13:01 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-04-18 01:12:24 +00:00
|
|
|
/*!
|
|
|
|
* Entry point for the game.
|
|
|
|
*/
|
2020-10-11 23:55:29 +00:00
|
|
|
int main(int argc, char** argv) {
|
2022-07-23 14:30:23 +00:00
|
|
|
ArgumentGuard u8_guard(argc, argv);
|
2022-07-10 23:03:24 +00:00
|
|
|
|
2023-03-10 04:13:01 +00:00
|
|
|
// CLI flags
|
2023-04-22 18:13:57 +00:00
|
|
|
bool show_version = false;
|
2023-03-10 04:13:01 +00:00
|
|
|
std::string game_name = "jak1";
|
|
|
|
bool verbose_logging = false;
|
|
|
|
bool disable_avx2 = false;
|
|
|
|
bool disable_display = false;
|
2023-06-04 19:34:37 +00:00
|
|
|
bool enable_profiling = false;
|
2024-04-28 19:29:20 +00:00
|
|
|
bool enable_portable = false;
|
|
|
|
bool disable_save_location_override = false;
|
2024-02-23 19:44:17 +00:00
|
|
|
std::string profile_until_event = "";
|
2023-07-01 01:05:58 +00:00
|
|
|
std::string gpu_test = "";
|
|
|
|
std::string gpu_test_out_path = "";
|
2023-04-02 04:57:21 +00:00
|
|
|
int port_number = -1;
|
2023-03-10 04:13:01 +00:00
|
|
|
fs::path project_path_override;
|
2024-04-28 19:29:20 +00:00
|
|
|
fs::path user_config_dir_override;
|
2023-03-10 04:13:01 +00:00
|
|
|
std::vector<std::string> game_args;
|
|
|
|
CLI::App app{"OpenGOAL Game Runtime"};
|
2023-04-22 18:13:57 +00:00
|
|
|
app.add_flag("--version", show_version, "Display the built revision");
|
2023-03-10 04:13:01 +00:00
|
|
|
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");
|
2023-04-02 04:57:21 +00:00
|
|
|
app.add_flag(
|
|
|
|
"--port", port_number,
|
|
|
|
"Specify port number for listener connection (default is 8112 for Jak 1 and 8113 for Jak 2)");
|
2024-01-17 00:24:02 +00:00
|
|
|
app.add_flag("--no-avx2", disable_avx2, "Disable AVX2 for testing");
|
2023-03-10 04:13:01 +00:00
|
|
|
app.add_flag("--no-display", disable_display, "Disable video display");
|
2023-06-04 19:34:37 +00:00
|
|
|
app.add_flag("--profile", enable_profiling, "Enables profiling immediately from startup");
|
2024-04-28 19:29:20 +00:00
|
|
|
app.add_flag("--portable", enable_portable,
|
|
|
|
"Save settings and saves relative to the game's executable, takes precedence over "
|
|
|
|
"--config-path");
|
|
|
|
app.add_flag("--disable_save_location_override", disable_save_location_override,
|
|
|
|
"If --config-path is provided along with this flag, saves will still be loaded and "
|
|
|
|
"stored to the default location");
|
2024-02-23 19:44:17 +00:00
|
|
|
app.add_option("--profile-until-event", profile_until_event,
|
|
|
|
"Stops recording profile events once an event with this name is seen");
|
2023-07-01 01:05:58 +00:00
|
|
|
app.add_option("--gpu-test", gpu_test,
|
|
|
|
"Tests for minimum graphics requirements. Valid Options are: [opengl]");
|
|
|
|
app.add_option("--gpu-test-out-path", gpu_test_out_path,
|
|
|
|
"Where to store the gpu test result file");
|
2023-03-10 04:13:01 +00:00
|
|
|
app.add_option("--proj-path", project_path_override,
|
|
|
|
"Specify the location of the 'data/' folder");
|
2024-04-28 19:29:20 +00:00
|
|
|
app.add_option("--config-path", user_config_dir_override,
|
|
|
|
"Override the location where all user configuration and saves are saved");
|
2023-03-10 04:13:01 +00:00
|
|
|
app.footer(game_arg_documentation());
|
|
|
|
app.add_option("Game Args", game_args,
|
|
|
|
"Remaining arguments (after '--') that are passed-through to the game itself");
|
2023-08-08 16:59:37 +00:00
|
|
|
define_common_cli_arguments(app);
|
2023-03-10 04:13:01 +00:00
|
|
|
app.allow_extras();
|
|
|
|
CLI11_PARSE(app, argc, argv);
|
|
|
|
|
2024-04-28 19:29:20 +00:00
|
|
|
// Override the user's config dir, potentially (either because it was explicitly provided
|
|
|
|
// or because it's portable mode)
|
|
|
|
if (enable_portable) {
|
|
|
|
lg::info("Portable mod enabled");
|
2024-05-26 04:40:33 +00:00
|
|
|
user_config_dir_override = fs::path(file_util::get_current_executable_path()).parent_path();
|
2024-04-28 19:29:20 +00:00
|
|
|
}
|
|
|
|
if (!user_config_dir_override.empty()) {
|
|
|
|
lg::info("Overriding config directory with: {}", user_config_dir_override.string());
|
|
|
|
file_util::override_user_config_dir(user_config_dir_override, !disable_save_location_override);
|
|
|
|
}
|
|
|
|
|
2023-04-22 18:13:57 +00:00
|
|
|
if (show_version) {
|
2024-01-14 12:02:08 +00:00
|
|
|
lg::print("{}", build_revision());
|
2023-04-22 18:13:57 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-01 01:05:58 +00:00
|
|
|
if (!gpu_test.empty() && !gpu_test_out_path.empty()) {
|
|
|
|
const auto output = tests::run_gpu_test(gpu_test);
|
|
|
|
json data = output;
|
|
|
|
try {
|
|
|
|
file_util::write_text_file(gpu_test_out_path, data.dump(2));
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-04 19:34:37 +00:00
|
|
|
prof().set_enable(enable_profiling);
|
2024-02-23 19:44:17 +00:00
|
|
|
prof().set_waiting_for_event(profile_until_event);
|
2023-06-04 19:34:37 +00:00
|
|
|
|
2023-03-10 04:13:01 +00:00
|
|
|
// Create struct with all non-kmachine handled args to pass to the runtime
|
|
|
|
GameLaunchOptions game_options;
|
|
|
|
game_options.disable_display = disable_display;
|
|
|
|
game_options.game_version = game_name_to_version(game_name);
|
2023-04-02 04:57:21 +00:00
|
|
|
game_options.server_port =
|
|
|
|
port_number == -1 ? DECI2_PORT - 1 + (int)game_options.game_version : port_number;
|
2022-07-06 00:38:13 +00:00
|
|
|
|
2022-04-18 01:12:24 +00:00
|
|
|
// Figure out if the CPU has AVX2 to enable higher performance AVX2 versions of functions.
|
2022-02-17 03:13:18 +00:00
|
|
|
setup_cpu_info();
|
2022-04-18 01:12:24 +00:00
|
|
|
// If the CPU doesn't have AVX, GOAL code won't work and we exit.
|
2022-02-17 03:13:18 +00:00
|
|
|
if (!get_cpu_info().has_avx) {
|
2023-03-10 04:13:01 +00:00
|
|
|
lg::info("Your CPU does not support AVX, which is required for OpenGOAL.");
|
2023-07-01 01:05:58 +00:00
|
|
|
dialogs::create_error_message_dialog(
|
|
|
|
"Unmet Requirements", "Your CPU does not support AVX, which is required for OpenGOAL.");
|
2022-02-17 03:13:18 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-04-18 01:12:24 +00:00
|
|
|
// 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
|
2023-03-10 04:13:01 +00:00
|
|
|
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)) {
|
2022-04-03 23:17:03 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-02-17 03:13:18 +00:00
|
|
|
if (disable_avx2) {
|
|
|
|
// for debugging the non-avx2 code paths, there's a flag to manually disable.
|
2023-03-10 04:13:01 +00:00
|
|
|
lg::info("Note: AVX2 code has been manually disabled.");
|
2022-02-17 03:13:18 +00:00
|
|
|
get_cpu_info().has_avx2 = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef __AVX2__
|
|
|
|
if (get_cpu_info().has_avx2) {
|
2022-06-22 04:16:34 +00:00
|
|
|
// printf("Note: your CPU supports AVX2, but this build was not compiled with AVX2 support\n");
|
2022-02-17 03:13:18 +00:00
|
|
|
get_cpu_info().has_avx2 = false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (get_cpu_info().has_avx2) {
|
2023-03-10 04:13:01 +00:00
|
|
|
lg::info("AVX2 mode enabled");
|
2022-02-17 03:13:18 +00:00
|
|
|
} else {
|
2023-03-10 04:13:01 +00:00
|
|
|
lg::info("AVX2 mode disabled");
|
2022-02-17 03:13:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-01 22:52:33 +00:00
|
|
|
try {
|
2023-08-08 16:59:37 +00:00
|
|
|
setup_logging(game_name, verbose_logging, _cli_flag_disable_ansi);
|
2023-03-01 22:52:33 +00:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
lg::error("Failed to setup logging: {}", e.what());
|
|
|
|
return 1;
|
|
|
|
}
|
2020-10-11 23:55:29 +00:00
|
|
|
|
2022-04-30 18:55:13 +00:00
|
|
|
bool force_debug_next_time = false;
|
2023-03-10 04:13:01 +00:00
|
|
|
// always start with an empty arg, as internally kmachine starts at `1` not `0`
|
2023-03-18 00:35:26 +00:00
|
|
|
std::vector<const char*> arg_ptrs = {""};
|
2023-03-10 04:13:01 +00:00
|
|
|
for (auto& str : game_args) {
|
|
|
|
arg_ptrs.push_back(str.data());
|
|
|
|
}
|
|
|
|
|
2020-10-11 23:55:29 +00:00
|
|
|
while (true) {
|
2022-04-30 18:55:13 +00:00
|
|
|
if (force_debug_next_time) {
|
2023-04-11 21:58:19 +00:00
|
|
|
// I'd like to check and not add duplicates, unfortunately since the game
|
|
|
|
// cares about ordering...that's likely error prone if the user passed args in the wrong order
|
|
|
|
// ie. -debug -boot (we'd skip adding things, but the order would be wrong).
|
2023-03-10 04:13:01 +00:00
|
|
|
game_args.push_back("-boot");
|
|
|
|
game_args.push_back("-debug");
|
2022-04-30 18:55:13 +00:00
|
|
|
force_debug_next_time = false;
|
2023-04-11 21:58:19 +00:00
|
|
|
arg_ptrs = {""}; // see above for rationale
|
2023-03-10 04:13:01 +00:00
|
|
|
for (auto& str : game_args) {
|
|
|
|
arg_ptrs.push_back(str.data());
|
|
|
|
}
|
2022-04-30 18:55:13 +00:00
|
|
|
}
|
|
|
|
|
2020-08-23 02:30:12 +00:00
|
|
|
// run the runtime in a loop so we can reset the game and have it restart cleanly
|
2021-01-06 17:16:39 +00:00
|
|
|
lg::info("OpenGOAL Runtime {}.{}", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
2022-07-10 23:03:24 +00:00
|
|
|
try {
|
2023-05-01 03:06:11 +00:00
|
|
|
MasterExit = RuntimeExitStatus::RUNNING;
|
2023-03-10 04:13:01 +00:00
|
|
|
auto exit_status = exec_runtime(game_options, arg_ptrs.size(), arg_ptrs.data());
|
2022-07-10 23:03:24 +00:00
|
|
|
switch (exit_status) {
|
|
|
|
case RuntimeExitStatus::EXIT:
|
|
|
|
return 0;
|
|
|
|
case RuntimeExitStatus::RESTART_RUNTIME:
|
|
|
|
case RuntimeExitStatus::RUNNING:
|
|
|
|
break;
|
|
|
|
case RuntimeExitStatus::RESTART_IN_DEBUG:
|
|
|
|
force_debug_next_time = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (std::exception& ex) {
|
|
|
|
lg::error("Unexpected exception occurred - {}", ex.what());
|
|
|
|
throw ex;
|
2020-09-06 20:58:25 +00:00
|
|
|
}
|
2020-08-23 02:30:12 +00:00
|
|
|
}
|
|
|
|
return 0;
|
2020-10-01 16:27:58 +00:00
|
|
|
}
|