mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-10 19:01:53 +00:00
Begin game registration
This commit is contained in:
parent
b47b52f540
commit
137009940c
5 changed files with 83 additions and 55 deletions
|
|
@ -6,3 +6,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|||
|
||||
add_subdirectory(librecomp)
|
||||
add_subdirectory(ultramodern)
|
||||
|
||||
set(RT64_STATIC TRUE)
|
||||
add_subdirectory(rt64)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -6,23 +6,20 @@
|
|||
|
||||
#include "recomp.h"
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
#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<const char> 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<uint8_t>&& 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
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@
|
|||
#include "recomp.h"
|
||||
#include "recomp_overlays.h"
|
||||
#include "recomp_game.h"
|
||||
#include "recomp_config.h"
|
||||
#include "xxHash/xxh3.h"
|
||||
#include <ultramodern/ultramodern.hpp>
|
||||
#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<uint64_t, recomp::GameEntry> game_roms {};
|
||||
|
||||
const std::unordered_map<recomp::Game, RomEntry> 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<uint8_t>& 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<uint8_t>& 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<recomp::Game> valid_game_roms;
|
||||
static std::unordered_set<uint64_t> 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<uint8_t> stored_rom_data = read_file(recomp::get_app_folder_path() / find_it->second.stored_filename);
|
||||
std::vector<uint8_t> 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<uint8_t>& 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<uint8_t> 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<const char*>(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<const char*>(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<const char*>(rom_data.data()) + 0x20, 19 } == "THE LEGEND OF ZELDA") {
|
||||
if (game_entry.is_enabled && std::string_view{ reinterpret_cast<const char*>(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<recomp::Game> game_started = recomp::Game::None;
|
||||
std::atomic<std::optional<recomp::GameHandle>> 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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue