mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2025-12-19 14:32:44 +00:00
Integrate ROM patcher and add ROM patch content type
This commit is contained in:
parent
8eb1ca7639
commit
396b45d496
5 changed files with 74 additions and 1 deletions
|
|
@ -23,6 +23,7 @@ add_library(librecomp STATIC
|
|||
"${CMAKE_CURRENT_SOURCE_DIR}/src/mod_config_api.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/overlays.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/pak.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/patcher.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/pi.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/print.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/recomp.cpp"
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ namespace recomp {
|
|||
MissingDependency,
|
||||
WrongDependencyVersion,
|
||||
FailedToLoadCode,
|
||||
RomPatchConflict,
|
||||
FailedToLoadPatch,
|
||||
};
|
||||
|
||||
std::string error_to_string(ModLoadError);
|
||||
|
|
@ -315,6 +317,7 @@ namespace recomp {
|
|||
// This is just a wrapper around a number for type safety purposes.
|
||||
struct ModContentTypeId {
|
||||
size_t value;
|
||||
bool operator==(const ModContentTypeId& rhs) const = default;
|
||||
};
|
||||
|
||||
struct ModContainerType {
|
||||
|
|
@ -439,6 +442,7 @@ namespace recomp {
|
|||
std::vector<char> empty_bytes;
|
||||
size_t num_events = 0;
|
||||
ModContentTypeId code_content_type_id;
|
||||
ModContentTypeId rom_patch_content_type_id;
|
||||
size_t active_game = (size_t)-1;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -973,6 +973,10 @@ std::string recomp::mods::error_to_string(ModLoadError error) {
|
|||
return "Wrong dependency version";
|
||||
case ModLoadError::FailedToLoadCode:
|
||||
return "Failed to load mod code";
|
||||
case ModLoadError::RomPatchConflict:
|
||||
return "ROM patch mod conflict";
|
||||
case ModLoadError::FailedToLoadPatch:
|
||||
return "Invalid ROM patch";
|
||||
}
|
||||
return "Unknown mod loading error " + std::to_string((int)error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "librecomp/mods.hpp"
|
||||
#include "librecomp/overlays.hpp"
|
||||
#include "librecomp/game.hpp"
|
||||
#include "librecomp/patcher.hpp"
|
||||
#include "recompiler/context.h"
|
||||
#include "recompiler/live_recompiler.h"
|
||||
|
||||
|
|
@ -252,6 +253,7 @@ namespace modpaths {
|
|||
constexpr std::string_view default_mod_extension = "nrm";
|
||||
constexpr std::string_view binary_path = "mod_binary.bin";
|
||||
constexpr std::string_view binary_syms_path = "mod_syms.bin";
|
||||
constexpr std::string_view rom_patch_path = "patch.bps";
|
||||
};
|
||||
|
||||
recomp::mods::CodeModLoadError recomp::mods::validate_api_version(uint32_t api_version, std::string& error_param) {
|
||||
|
|
@ -916,6 +918,16 @@ recomp::mods::ModContext::ModContext() {
|
|||
.on_reordered = nullptr
|
||||
};
|
||||
code_content_type_id = register_content_type(code_content_type);
|
||||
|
||||
// Register the ROM patch content type.
|
||||
ModContentType rom_patch_content_type {
|
||||
.content_filename = std::string{modpaths::rom_patch_path},
|
||||
.allow_runtime_toggle = false,
|
||||
.on_enabled = nullptr,
|
||||
.on_disabled = nullptr,
|
||||
.on_reordered = nullptr
|
||||
};
|
||||
rom_patch_content_type_id = register_content_type(rom_patch_content_type);
|
||||
|
||||
// Register the default mod container type (.nrm) and allow it to have any content type by passing an empty vector.
|
||||
register_container_type(std::string{ modpaths::default_mod_extension }, {}, true);
|
||||
|
|
@ -1610,6 +1622,51 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Check for ROM patches.
|
||||
size_t rom_patch_mod_index = (size_t)-1;
|
||||
for (size_t mod_index : active_mods) {
|
||||
auto& mod = opened_mods[mod_index];
|
||||
auto find_it = std::find(mod.content_types.begin(), mod.content_types.end(), rom_patch_content_type_id);
|
||||
if (find_it != mod.content_types.end()) {
|
||||
// If a mod has already provided a patch, mark the two as incompatible.
|
||||
if (rom_patch_mod_index != (size_t)-1) {
|
||||
ret.emplace_back(mod.manifest.mod_id, ModLoadError::RomPatchConflict, "conflicts with " + opened_mods[rom_patch_mod_index].manifest.display_name);
|
||||
}
|
||||
else {
|
||||
rom_patch_mod_index = mod_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit early if errors were found.
|
||||
if (!ret.empty()) {
|
||||
unload_mods();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Apply a ROM patch if one was found.
|
||||
if (rom_patch_mod_index != (size_t)-1) {
|
||||
auto& mod = opened_mods[rom_patch_mod_index];
|
||||
|
||||
bool patch_exists;
|
||||
std::vector<char> patch_data = mod.manifest.file_handle->read_file(std::string{ modpaths::rom_patch_path }, patch_exists);
|
||||
std::vector<uint8_t> patched_rom;
|
||||
|
||||
// This should never happen, as the content type's presence means the patch file exists. Catch it just in case regardless.
|
||||
if (!patch_exists) {
|
||||
ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadPatch, "Internal error");
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto patch_result = recomp::patcher::patch_rom(recomp::get_rom(), std::span{ reinterpret_cast<const uint8_t*>(patch_data.data()), patch_data.size() }, patched_rom);
|
||||
if (patch_result != recomp::patcher::PatcherResult::Success) {
|
||||
ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadPatch, std::string{});
|
||||
return ret;
|
||||
}
|
||||
|
||||
recomp::set_rom_contents(std::move(patched_rom));
|
||||
}
|
||||
|
||||
// Check that mod dependencies are met.
|
||||
for (size_t mod_index : active_mods) {
|
||||
auto& mod = opened_mods[mod_index];
|
||||
|
|
|
|||
|
|
@ -666,7 +666,14 @@ bool wait_for_game_started(uint8_t* rdram, recomp_context* context) {
|
|||
std::ostringstream mod_error_stream;
|
||||
mod_error_stream << "Error loading mods:\n\n";
|
||||
for (const auto& cur_error : mod_load_errors) {
|
||||
mod_error_stream << cur_error.mod_id.c_str() << ": " << recomp::mods::error_to_string(cur_error.error);
|
||||
auto mod_details = recomp::mods::get_details_for_mod(cur_error.mod_id);
|
||||
if (mod_details) {
|
||||
mod_error_stream << mod_details->display_name;
|
||||
}
|
||||
else {
|
||||
mod_error_stream << cur_error.mod_id.c_str();
|
||||
}
|
||||
mod_error_stream << ": " << recomp::mods::error_to_string(cur_error.error);
|
||||
if (!cur_error.error_param.empty()) {
|
||||
mod_error_stream << " (" << cur_error.error_param.c_str() << ")";
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue