From 137009940c180bfa7556aa8beb201f33323a997a Mon Sep 17 00:00:00 2001 From: dcvz Date: Sun, 19 May 2024 23:38:51 +0200 Subject: [PATCH] Begin game registration --- CMakeLists.txt | 3 ++ librecomp/CMakeLists.txt | 3 +- librecomp/include/recomp_game.h | 37 +++++++------ librecomp/src/recomp.cpp | 94 +++++++++++++++++++++------------ ultramodern/CMakeLists.txt | 1 + 5 files changed, 83 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d8c423..45b14ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,3 +6,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) add_subdirectory(librecomp) add_subdirectory(ultramodern) + +set(RT64_STATIC TRUE) +add_subdirectory(rt64) diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 4661b1d..2445453 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -30,5 +30,6 @@ add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/vi.cpp") target_include_directories(librecomp PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") - +target_include_directories(librecomp PRIVATE "${CMAKE_SOURCE_DIR}/rt64/src/contrib") +target_compile_options(librecomp PRIVATE -Wno-deprecated-declarations) target_link_libraries(librecomp PRIVATE ultramodern) diff --git a/librecomp/include/recomp_game.h b/librecomp/include/recomp_game.h index b448a17..ca61b25 100644 --- a/librecomp/include/recomp_game.h +++ b/librecomp/include/recomp_game.h @@ -6,23 +6,20 @@ #include "recomp.h" #include -#include "rt64_layer.h" namespace recomp { - // TODO refactor this to allow a particular project to specify games. - // A register game function should be added which takes: - // * Expected ROM hash - // * Internal ROM name (for error messages) - // * Entrypoint function to run for the game - // * Shader cache (optional) - // The register game function should return a handle struct (probably containing an ID) so the project can check - // which game is running (if any) by querying it. - enum class Game { - OoT, - MM, - None, - Quit - }; + struct GameHandle { + uint64_t id; + }; + struct GameEntry { + uint64_t rom_hash; + std::string internal_name; + void (*entrypoint)(); + std::span cache_data; + bool is_enabled; + + std::string stored_filename() const; + }; enum class RomValidationError { Good, FailedToOpen, @@ -32,17 +29,19 @@ namespace recomp { IncorrectVersion, OtherError }; + GameHandle register_game(const recomp::GameEntry& entry); void check_all_stored_roms(); - bool load_stored_rom(Game game); - RomValidationError select_rom(const std::filesystem::path& rom_path, Game game); - bool is_rom_valid(Game game); + bool load_stored_rom(GameHandle game); + RomValidationError select_rom(const std::filesystem::path& rom_path, GameHandle game); + bool is_rom_valid(GameHandle game); bool is_rom_loaded(); void set_rom_contents(std::vector&& new_rom); void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes); void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr); void start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks); - void start_game(Game game); + void start_game(GameHandle game); void message_box(const char* message); + std::filesystem::path get_app_folder_path(); } #endif diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 6678366..9e81b19 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -13,11 +13,9 @@ #include "recomp.h" #include "recomp_overlays.h" #include "recomp_game.h" -#include "recomp_config.h" #include "xxHash/xxh3.h" #include -#include "../../RecompiledPatches/patches_bin.h" -#include "mm_shader_cache.h" +//#include "../../RecompiledPatches/patches_bin.h" #ifdef _MSC_VER inline uint32_t byteswap(uint32_t val) { @@ -29,19 +27,19 @@ constexpr uint32_t byteswap(uint32_t val) { } #endif -struct RomEntry { - uint64_t xxhash3_value; - std::u8string stored_filename; - std::string internal_rom_name; -}; +std::unordered_map game_roms {}; -const std::unordered_map game_roms { - { recomp::Game::MM, { 0xEF18B4A9E2386169ULL, std::u8string{recomp::mm_game_id} + u8".z64", "ZELDA MAJORA'S MASK" }}, -}; +std::string recomp::GameEntry::stored_filename() const { + return std::to_string(rom_hash) + ".z64"; +} + +recomp::GameHandle recomp::register_game(const recomp::GameEntry& entry) { + game_roms.insert({ entry.rom_hash, entry }); + return { entry.rom_hash }; +} bool check_hash(const std::vector& rom_data, uint64_t expected_hash) { uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size()); - return calculated_hash == expected_hash; } @@ -73,23 +71,49 @@ bool write_file(const std::filesystem::path& path, const std::vector& d return true; } -bool check_stored_rom(const RomEntry& game_entry) { +std::filesystem::path recomp::get_app_folder_path() { + std::filesystem::path recomp_dir{}; - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / game_entry.stored_filename); +#if defined(_WIN32) + // Deduce local app data path. + PWSTR known_path = NULL; + HRESULT result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &known_path); + if (result == S_OK) { + recomp_dir = std::filesystem::path{known_path} / recomp::program_id; + } - if (!check_hash(stored_rom_data, game_entry.xxhash3_value)) { + CoTaskMemFree(known_path); +#elif defined(__linux__) + const char *homedir; + + if ((homedir = getenv("HOME")) == nullptr) { + homedir = getpwuid(getuid())->pw_dir; + } + + if (homedir != nullptr) { + recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + std::u8string{recomp::program_id}); + } +#endif + + return recomp_dir; +} + +bool check_stored_rom(const recomp::GameEntry& game_entry) { + std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / game_entry.stored_filename()); + + if (!check_hash(stored_rom_data, game_entry.rom_hash)) { // Incorrect hash, remove the stored ROM file if it exists. - std::filesystem::remove(recomp::get_app_folder_path() / game_entry.stored_filename); + std::filesystem::remove(recomp::get_app_folder_path() / game_entry.stored_filename()); return false; } return true; } -static std::unordered_set valid_game_roms; +static std::unordered_set valid_game_roms; -bool recomp::is_rom_valid(recomp::Game game) { - return valid_game_roms.contains(game); +bool recomp::is_rom_valid(recomp::GameHandle game) { + return valid_game_roms.contains(game.id); } void recomp::check_all_stored_roms() { @@ -100,18 +124,18 @@ void recomp::check_all_stored_roms() { } } -bool recomp::load_stored_rom(recomp::Game game) { - auto find_it = game_roms.find(game); +bool recomp::load_stored_rom(recomp::GameHandle game) { + auto find_it = game_roms.find(game.id); if (find_it == game_roms.end()) { return false; } - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / find_it->second.stored_filename); + std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / find_it->second.stored_filename()); - if (!check_hash(stored_rom_data, find_it->second.xxhash3_value)) { + if (!check_hash(stored_rom_data, find_it->second.rom_hash)) { // The ROM no longer has the right hash, delete it. - std::filesystem::remove(recomp::get_app_folder_path() / find_it->second.stored_filename); + std::filesystem::remove(recomp::get_app_folder_path() / find_it->second.stored_filename()); return false; } @@ -176,14 +200,14 @@ void byteswap_data(std::vector& rom_data, size_t index_xor) { } } -recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_path, Game game) { - auto find_it = game_roms.find(game); +recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_path, recomp::GameHandle game) { + auto find_it = game_roms.find(game.id); if (find_it == game_roms.end()) { return recomp::RomValidationError::OtherError; } - const RomEntry& game_entry = find_it->second; + const recomp::GameEntry& game_entry = find_it->second; std::vector rom_data = read_file(rom_path); @@ -209,13 +233,13 @@ recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_p break; } - if (!check_hash(rom_data, game_entry.xxhash3_value)) { - const std::string_view name{ reinterpret_cast(rom_data.data()) + 0x20, game_entry.internal_rom_name.size()}; - if (name == game_entry.internal_rom_name) { + if (!check_hash(rom_data, game_entry.rom_hash)) { + const std::string_view name{ reinterpret_cast(rom_data.data()) + 0x20, game_entry.internal_name.size()}; + if (name == game_entry.internal_name) { return recomp::RomValidationError::IncorrectVersion; } else { - if (game == recomp::Game::MM && std::string_view{ reinterpret_cast(rom_data.data()) + 0x20, 19 } == "THE LEGEND OF ZELDA") { + if (game_entry.is_enabled && std::string_view{ reinterpret_cast(rom_data.data()) + 0x20, 19 } == game_entry.internal_name) { return recomp::RomValidationError::NotYet; } else { @@ -224,7 +248,7 @@ recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_p } } - write_file(recomp::get_app_folder_path() / game_entry.stored_filename, rom_data); + write_file(recomp::get_app_folder_path() / game_entry.stored_filename(), rom_data); return recomp::RomValidationError::Good; } @@ -347,15 +371,15 @@ void init(uint8_t* rdram, recomp_context* ctx) { MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB } -std::atomic game_started = recomp::Game::None; +std::atomic> game_started = std::nullopt; -void recomp::start_game(recomp::Game game) { +void recomp::start_game(recomp::GameHandle game) { game_started.store(game); game_started.notify_all(); } bool ultramodern::is_game_started() { - return game_started.load() != recomp::Game::None; + return game_started.load() != std::nullopt; } void set_audio_callbacks(const ultramodern::audio_callbacks_t& callbacks); diff --git a/ultramodern/CMakeLists.txt b/ultramodern/CMakeLists.txt index ded4d93..7d135c9 100644 --- a/ultramodern/CMakeLists.txt +++ b/ultramodern/CMakeLists.txt @@ -16,3 +16,4 @@ add_library(ultramodern STATIC target_include_directories(ultramodern PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_SOURCE_DIR}/thirdparty/concurrentqueue") +target_link_libraries(ultramodern PRIVATE rt64)