mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-05-08 01:41:46 +00:00
Rework callbacks and imports to use the section name for identifying the dependency instead of relying on per-dependency tomls
This commit is contained in:
parent
9402cf8d45
commit
7372681481
6 changed files with 333 additions and 165 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <array>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
@ -12,7 +13,7 @@ struct ModConfig {
|
||||||
std::filesystem::path elf_path;
|
std::filesystem::path elf_path;
|
||||||
std::filesystem::path func_reference_syms_file_path;
|
std::filesystem::path func_reference_syms_file_path;
|
||||||
std::vector<std::filesystem::path> data_reference_syms_file_paths;
|
std::vector<std::filesystem::path> data_reference_syms_file_paths;
|
||||||
std::vector<std::filesystem::path> dependency_paths;
|
std::vector<N64Recomp::Dependency> dependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) {
|
static std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) {
|
||||||
|
|
@ -22,6 +23,72 @@ static std::filesystem::path concat_if_not_empty(const std::filesystem::path& pa
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool parse_version_string(std::string_view str, uint8_t& major, uint8_t& minor, uint8_t& patch) {
|
||||||
|
std::array<size_t, 2> period_indices;
|
||||||
|
size_t num_periods = 0;
|
||||||
|
size_t cur_pos = 0;
|
||||||
|
|
||||||
|
// Find the 2 required periods.
|
||||||
|
cur_pos = str.find('.', cur_pos);
|
||||||
|
period_indices[0] = cur_pos;
|
||||||
|
cur_pos = str.find('.', cur_pos + 1);
|
||||||
|
period_indices[1] = cur_pos;
|
||||||
|
|
||||||
|
// Check that both were found.
|
||||||
|
if (period_indices[0] == std::string::npos || period_indices[1] == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the 3 numbers formed by splitting the string via the periods.
|
||||||
|
std::array<std::from_chars_result, 3> parse_results;
|
||||||
|
std::array<size_t, 3> parse_starts { 0, period_indices[0] + 1, period_indices[1] + 1 };
|
||||||
|
std::array<size_t, 3> parse_ends { period_indices[0], period_indices[1], str.size() };
|
||||||
|
parse_results[0] = std::from_chars(str.data() + parse_starts[0], str.data() + parse_ends[0], major);
|
||||||
|
parse_results[1] = std::from_chars(str.data() + parse_starts[1], str.data() + parse_ends[1], minor);
|
||||||
|
parse_results[2] = std::from_chars(str.data() + parse_starts[2], str.data() + parse_ends[2], patch);
|
||||||
|
|
||||||
|
// Check that all 3 parsed correctly.
|
||||||
|
auto did_parse = [&](size_t i) {
|
||||||
|
return parse_results[i].ec == std::errc{} && parse_results[i].ptr == str.data() + parse_ends[i];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (!did_parse(0) || !did_parse(1) || !did_parse(2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_dependency_string(const std::string& val, N64Recomp::Dependency& dep) {
|
||||||
|
N64Recomp::Dependency ret;
|
||||||
|
size_t id_pos = 0;
|
||||||
|
size_t id_length = 0;
|
||||||
|
|
||||||
|
size_t colon_pos = val.find(':');
|
||||||
|
if (colon_pos == std::string::npos) {
|
||||||
|
id_length = val.size();
|
||||||
|
ret.major_version = 0;
|
||||||
|
ret.minor_version = 0;
|
||||||
|
ret.patch_version = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
id_length = colon_pos;
|
||||||
|
uint8_t major, minor, patch;
|
||||||
|
if (!parse_version_string(std::string_view{val.begin() + colon_pos + 1, val.end()}, major, minor, patch)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ret.major_version = major;
|
||||||
|
ret.minor_version = minor;
|
||||||
|
ret.patch_version = patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.mod_id = val.substr(id_pos, id_length);
|
||||||
|
|
||||||
|
dep = std::move(ret);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<std::filesystem::path> get_toml_path_array(const toml::array* toml_array, const std::filesystem::path& basedir) {
|
static std::vector<std::filesystem::path> get_toml_path_array(const toml::array* toml_array, const std::filesystem::path& basedir) {
|
||||||
std::vector<std::filesystem::path> ret;
|
std::vector<std::filesystem::path> ret;
|
||||||
|
|
||||||
|
|
@ -39,113 +106,6 @@ static std::vector<std::filesystem::path> get_toml_path_array(const toml::array*
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool read_dependency_file(const std::filesystem::path& dependency_path, N64Recomp::Context& context) {
|
|
||||||
toml::table toml_data{};
|
|
||||||
|
|
||||||
try {
|
|
||||||
toml_data = toml::parse_file(dependency_path.native());
|
|
||||||
|
|
||||||
const auto dependency_data = toml_data["dependency"];
|
|
||||||
if (!dependency_data.is_array()) {
|
|
||||||
if (dependency_data) {
|
|
||||||
throw toml::parse_error("No dependency array found", dependency_data.node()->source());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw toml::parse_error("Invalid dependency array", dependency_data.node()->source());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toml::array* dependency_array = dependency_data.as_array();
|
|
||||||
for (const auto& dependency_node : *dependency_array) {
|
|
||||||
if (!dependency_node.is_table()) {
|
|
||||||
throw toml::parse_error("Invalid dependency entry", dependency_node.source());
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dependency_index = context.dependencies.size();
|
|
||||||
|
|
||||||
auto read_number = [](const toml::node& node, const std::string& key, const std::string& name, int64_t min_limit, int64_t max_limit) {
|
|
||||||
toml::node_view mod_id_node = node[toml::path{key}];
|
|
||||||
if (!mod_id_node.is_number()) {
|
|
||||||
if (mod_id_node) {
|
|
||||||
throw toml::parse_error(fmt::format("Invalid {}", name).c_str(), mod_id_node.node()->source());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw toml::parse_error(fmt::format("Dependency entry is missing {}", name).c_str(), node.source());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int64_t number_value = mod_id_node.ref<int64_t>();
|
|
||||||
if (number_value < min_limit || number_value > max_limit) {
|
|
||||||
throw toml::parse_error(fmt::format("Dependency {} out of range", name).c_str(), mod_id_node.node()->source());
|
|
||||||
}
|
|
||||||
return number_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Version number
|
|
||||||
uint8_t major_version = static_cast<uint8_t>(read_number(dependency_node, "major_version", "major version", 0, std::numeric_limits<uint8_t>::max()));
|
|
||||||
uint8_t minor_version = static_cast<uint8_t>(read_number(dependency_node, "minor_version", "minor version", 0, std::numeric_limits<uint8_t>::max()));
|
|
||||||
uint8_t patch_version = static_cast<uint8_t>(read_number(dependency_node, "patch_version", "patch version", 0, std::numeric_limits<uint8_t>::max()));
|
|
||||||
|
|
||||||
// Mod ID
|
|
||||||
toml::node_view mod_id_node = dependency_node[toml::path{"mod_id"}];
|
|
||||||
if (!mod_id_node.is_string()) {
|
|
||||||
if (mod_id_node) {
|
|
||||||
throw toml::parse_error("Invalid mod id", mod_id_node.node()->source());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw toml::parse_error("Dependency entry is missing mod id", dependency_node.source());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& mod_id = mod_id_node.ref<std::string>();
|
|
||||||
context.dependencies.emplace_back(N64Recomp::Dependency{
|
|
||||||
.major_version = major_version,
|
|
||||||
.minor_version = minor_version,
|
|
||||||
.patch_version = patch_version,
|
|
||||||
.mod_id = mod_id
|
|
||||||
});
|
|
||||||
|
|
||||||
// Function list (optional)
|
|
||||||
toml::node_view functions_data = dependency_node[toml::path{"functions"}];
|
|
||||||
if (functions_data.is_array()) {
|
|
||||||
const toml::array* functions_array = functions_data.as_array();
|
|
||||||
for (const auto& function_node : *functions_array) {
|
|
||||||
if (!function_node.is_string()) {
|
|
||||||
throw toml::parse_error("Invalid dependency function", function_node.source());
|
|
||||||
}
|
|
||||||
const std::string& function_name = function_node.ref<std::string>();
|
|
||||||
context.add_import_symbol(function_name, dependency_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (functions_data) {
|
|
||||||
throw toml::parse_error("Invalid dependency function list", functions_data.node()->source());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event list (optional)
|
|
||||||
toml::node_view events_data = dependency_node[toml::path{"events"}];
|
|
||||||
if (events_data.is_array()) {
|
|
||||||
const toml::array* events_array = events_data.as_array();
|
|
||||||
for (const auto& event_node : *events_array) {
|
|
||||||
if (!event_node.is_string()) {
|
|
||||||
throw toml::parse_error("Invalid dependency event", event_node.source());
|
|
||||||
}
|
|
||||||
const std::string& event_name = event_node.ref<std::string>();
|
|
||||||
context.add_dependency_event(event_name, dependency_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (events_data) {
|
|
||||||
throw toml::parse_error("Invalid dependency event list", events_data.node()->source());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (const toml::parse_error& err) {
|
|
||||||
std::cerr << "Syntax error parsing symbol import file: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModConfig parse_mod_config(const std::filesystem::path& config_path, bool& good) {
|
ModConfig parse_mod_config(const std::filesystem::path& config_path, bool& good) {
|
||||||
ModConfig ret{};
|
ModConfig ret{};
|
||||||
good = false;
|
good = false;
|
||||||
|
|
@ -209,14 +169,27 @@ ModConfig parse_mod_config(const std::filesystem::path& config_path, bool& good)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imported symbols files (optional)
|
// Dependency list (optional)
|
||||||
toml::node_view dependency_data = config_data["dependencies"];
|
toml::node_view dependency_data = config_data["dependencies"];
|
||||||
if (dependency_data.is_array()) {
|
if (dependency_data.is_array()) {
|
||||||
const toml::array* array = dependency_data.as_array();
|
const toml::array* dependency_array = dependency_data.as_array();
|
||||||
ret.dependency_paths = get_toml_path_array(array, basedir);
|
// Reserve room for all the dependencies.
|
||||||
|
ret.dependencies.reserve(dependency_array->size());
|
||||||
|
dependency_array->for_each([&ret](auto&& el) {
|
||||||
|
if constexpr (toml::is_string<decltype(el)>) {
|
||||||
|
N64Recomp::Dependency dep;
|
||||||
|
if (!parse_dependency_string(el.ref<std::string>(), dep)) {
|
||||||
|
throw toml::parse_error("Invalid dependency entry", el.source());
|
||||||
|
}
|
||||||
|
ret.dependencies.emplace_back(std::move(dep));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw toml::parse_error("Invalid toml type for dependency", el.source());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (dependency_data) {
|
else if (dependency_data) {
|
||||||
throw toml::parse_error("Invalid imported symbols file list", dependency_data.node()->source());
|
throw toml::parse_error("Invalid mod dependency list", dependency_data.node()->source());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const toml::parse_error& err) {
|
catch (const toml::parse_error& err) {
|
||||||
|
|
@ -232,6 +205,25 @@ static inline uint32_t round_up_16(uint32_t value) {
|
||||||
return (value + 15) & (~15);
|
return (value + 15) & (~15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool parse_callback_name(std::string_view data, std::string& dependency_name, std::string& event_name) {
|
||||||
|
size_t period_pos = data.find(':');
|
||||||
|
|
||||||
|
if (period_pos == std::string::npos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view dependency_name_view = std::string_view{data}.substr(0, period_pos);
|
||||||
|
std::string_view event_name_view = std::string_view{data}.substr(period_pos + 1);
|
||||||
|
|
||||||
|
if (!N64Recomp::validate_mod_name(dependency_name_view)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_name = dependency_name_view;
|
||||||
|
event_name = event_name_view;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bool& good) {
|
N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bool& good) {
|
||||||
N64Recomp::Context ret{};
|
N64Recomp::Context ret{};
|
||||||
good = false;
|
good = false;
|
||||||
|
|
@ -262,9 +254,11 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
|
|
||||||
// Copy the dependency data from the input context.
|
// Copy the dependency data from the input context.
|
||||||
ret.dependencies = input_context.dependencies;
|
ret.dependencies = input_context.dependencies;
|
||||||
|
ret.dependencies_by_name = input_context.dependencies_by_name;
|
||||||
ret.import_symbols = input_context.import_symbols;
|
ret.import_symbols = input_context.import_symbols;
|
||||||
ret.dependency_events = input_context.dependency_events;
|
ret.dependency_events = input_context.dependency_events;
|
||||||
ret.dependency_events_by_name = input_context.dependency_events_by_name;
|
ret.dependency_events_by_name = input_context.dependency_events_by_name;
|
||||||
|
ret.dependency_imports_by_name = input_context.dependency_imports_by_name;
|
||||||
|
|
||||||
uint32_t rom_to_ram = (uint32_t)-1;
|
uint32_t rom_to_ram = (uint32_t)-1;
|
||||||
size_t output_section_index = (size_t)-1;
|
size_t output_section_index = (size_t)-1;
|
||||||
|
|
@ -323,6 +317,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
bool force_patch_section = cur_section.name == N64Recomp::ForcedPatchSectionName;
|
bool force_patch_section = cur_section.name == N64Recomp::ForcedPatchSectionName;
|
||||||
bool export_section = cur_section.name == N64Recomp::ExportSectionName;
|
bool export_section = cur_section.name == N64Recomp::ExportSectionName;
|
||||||
bool event_section = cur_section.name == N64Recomp::EventSectionName;
|
bool event_section = cur_section.name == N64Recomp::EventSectionName;
|
||||||
|
bool import_section = cur_section.name.starts_with(N64Recomp::ImportSectionPrefix);
|
||||||
bool callback_section = cur_section.name.starts_with(N64Recomp::CallbackSectionPrefix);
|
bool callback_section = cur_section.name.starts_with(N64Recomp::CallbackSectionPrefix);
|
||||||
|
|
||||||
// Add the functions from the current input section to the current output section.
|
// Add the functions from the current input section to the current output section.
|
||||||
|
|
@ -345,6 +340,28 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Skip the functions and relocs in this section if it's an import section, instead opting to create
|
||||||
|
// import symbols from the section's functions.
|
||||||
|
else if (import_section) {
|
||||||
|
for (const auto& input_func_index : cur_section_funcs) {
|
||||||
|
const auto& cur_func = input_context.functions[input_func_index];
|
||||||
|
std::string dependency_name = cur_section.name.substr(N64Recomp::ImportSectionPrefix.size());
|
||||||
|
if (!N64Recomp::validate_mod_name(dependency_name)) {
|
||||||
|
fmt::print("Failed to import function {} as {} is an invalid mod name.\n",
|
||||||
|
cur_func.name, dependency_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dependency_index;
|
||||||
|
if (!ret.find_dependency(dependency_name, dependency_index)) {
|
||||||
|
fmt::print("Failed to import function {} from mod {} as the mod is not a registered dependency.\n",
|
||||||
|
cur_func.name, dependency_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.add_import_symbol(cur_func.name, dependency_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Normal section, copy the functions and relocs over.
|
// Normal section, copy the functions and relocs over.
|
||||||
else {
|
else {
|
||||||
for (size_t section_function_index = 0; section_function_index < cur_section_funcs.size(); section_function_index++) {
|
for (size_t section_function_index = 0; section_function_index < cur_section_funcs.size(); section_function_index++) {
|
||||||
|
|
@ -360,14 +377,14 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
|
|
||||||
// Check that the function being patched exists in the original reference symbols.
|
// Check that the function being patched exists in the original reference symbols.
|
||||||
if (!original_func_exists) {
|
if (!original_func_exists) {
|
||||||
fmt::print("Function {} is marked as a patch but doesn't exist in the original ROM!\n", cur_func.name);
|
fmt::print("Function {} is marked as a patch but doesn't exist in the original ROM.\n", cur_func.name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the reference symbol is actually a function.
|
// Check that the reference symbol is actually a function.
|
||||||
const auto& reference_symbol = input_context.get_reference_symbol(cur_reference);
|
const auto& reference_symbol = input_context.get_reference_symbol(cur_reference);
|
||||||
if (!reference_symbol.is_function) {
|
if (!reference_symbol.is_function) {
|
||||||
fmt::print("Function {0} is marked as a patch, but {0} was a variable in the original ROM!\n", cur_func.name);
|
fmt::print("Function {0} is marked as a patch, but {0} was a variable in the original ROM.\n", cur_func.name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,14 +411,32 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback_section) {
|
if (callback_section) {
|
||||||
std::string event_name = cur_section.name.substr(N64Recomp::CallbackSectionPrefix.size());
|
std::string dependency_name, event_name;
|
||||||
size_t event_index;
|
if (!parse_callback_name(std::string_view{ cur_section.name }.substr(N64Recomp::CallbackSectionPrefix.size()), dependency_name, event_name)) {
|
||||||
if (!ret.get_dependency_event(event_name, event_index)) {
|
fmt::print("Invalid mod name or event name for callback function {}.\n",
|
||||||
fmt::print("Failed to find event {} for callback {}!\n",
|
cur_func.name);
|
||||||
event_name, cur_func.name);
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dependency_index;
|
||||||
|
if (!ret.find_dependency(dependency_name, dependency_index)) {
|
||||||
|
fmt::print("Failed to register callback {} to event {} from mod {} as the mod is not a registered dependency.\n",
|
||||||
|
cur_func.name, event_name, dependency_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t event_index;
|
||||||
|
if (!ret.add_dependency_event(event_name, dependency_index, event_index)) {
|
||||||
|
fmt::print("Internal error: Failed to register event {} for dependency {}. Please report this issue.\n",
|
||||||
|
event_name, dependency_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret.add_callback(event_index, output_func_index)) {
|
||||||
|
fmt::print("Internal error: Failed to add callback {} to event {} in dependency {}. Please report this issue.\n",
|
||||||
|
cur_func.name, event_name, dependency_name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
ret.add_callback(event_index, output_func_index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.section_functions[output_section_index].push_back(output_func_index);
|
ret.section_functions[output_section_index].push_back(output_func_index);
|
||||||
|
|
@ -464,7 +499,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
reloc_word |= reloc_target_address & 0xFFFF;
|
reloc_word |= reloc_target_address & 0xFFFF;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fmt::print("Unsupported or unknown relocation type {} in reloc at address 0x{:08X} in section {}!\n",
|
fmt::print("Unsupported or unknown relocation type {} in reloc at address 0x{:08X} in section {}.\n",
|
||||||
(int)cur_reloc.type, cur_reloc.address, cur_section.name);
|
(int)cur_reloc.type, cur_reloc.address, cur_section.name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -484,7 +519,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
// to the event symbol, creating the event symbol if necessary.
|
// to the event symbol, creating the event symbol if necessary.
|
||||||
if (target_section.name == N64Recomp::EventSectionName) {
|
if (target_section.name == N64Recomp::EventSectionName) {
|
||||||
if (cur_reloc.type != N64Recomp::RelocType::R_MIPS_26) {
|
if (cur_reloc.type != N64Recomp::RelocType::R_MIPS_26) {
|
||||||
fmt::print("Symbol {} is an event and cannot have its address taken!\n",
|
fmt::print("Symbol {} is an event and cannot have its address taken.\n",
|
||||||
cur_section.name);
|
cur_section.name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +527,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
uint32_t target_function_vram = cur_reloc.target_section_offset + target_section.ram_addr;
|
uint32_t target_function_vram = cur_reloc.target_section_offset + target_section.ram_addr;
|
||||||
size_t target_function_index = input_context.find_function_by_vram_section(target_function_vram, cur_reloc.target_section);
|
size_t target_function_index = input_context.find_function_by_vram_section(target_function_vram, cur_reloc.target_section);
|
||||||
if (target_function_index == (size_t)-1) {
|
if (target_function_index == (size_t)-1) {
|
||||||
fmt::print("Internal error: Failed to find event symbol in section {} with offset 0x{:08X} (vram 0x{:08X})!\n",
|
fmt::print("Internal error: Failed to find event symbol in section {} with offset 0x{:08X} (vram 0x{:08X}). Please report this issue.\n",
|
||||||
target_section.name, cur_reloc.target_section_offset, target_function_vram);
|
target_section.name, cur_reloc.target_section_offset, target_function_vram);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -517,12 +552,58 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
||||||
.reference_symbol = true,
|
.reference_symbol = true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Not the event section, so handle the reloc normally.
|
// Check if the target is an import section. If so, create a reference symbol reloc
|
||||||
|
// to the import symbol, creating the import symbol if necessary.
|
||||||
|
else if (target_section.name.starts_with(N64Recomp::ImportSectionPrefix)) {
|
||||||
|
if (cur_reloc.type != N64Recomp::RelocType::R_MIPS_26) {
|
||||||
|
fmt::print("Symbol {} is an import and cannot have its address taken.\n",
|
||||||
|
cur_section.name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t target_function_vram = cur_reloc.target_section_offset + target_section.ram_addr;
|
||||||
|
size_t target_function_index = input_context.find_function_by_vram_section(target_function_vram, cur_reloc.target_section);
|
||||||
|
if (target_function_index == (size_t)-1) {
|
||||||
|
fmt::print("Internal error: Failed to find import symbol in section {} with offset 0x{:08X} (vram 0x{:08X}). Please report this issue.\n",
|
||||||
|
target_section.name, cur_reloc.target_section_offset, target_function_vram);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& target_function = input_context.functions[target_function_index];
|
||||||
|
|
||||||
|
// Find the dependency that this import belongs to.
|
||||||
|
std::string dependency_name = target_section.name.substr(N64Recomp::ImportSectionPrefix.size());
|
||||||
|
size_t dependency_index;
|
||||||
|
if (!ret.find_dependency(dependency_name, dependency_index)) {
|
||||||
|
fmt::print("Failed to import function {} from mod {} as the mod is not a registered dependency.\n",
|
||||||
|
target_function.name, dependency_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this event already has a symbol to prevent creating a duplicate.
|
||||||
|
N64Recomp::SymbolReference import_ref;
|
||||||
|
if (!ret.find_import_symbol(target_function.name, dependency_index, import_ref)) {
|
||||||
|
ret.add_import_symbol(target_function.name, dependency_index);
|
||||||
|
// Update the event symbol reference now that the symbol was created.
|
||||||
|
ret.find_import_symbol(target_function.name, dependency_index, import_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a reloc to the event symbol.
|
||||||
|
section_out.relocs.emplace_back(N64Recomp::Reloc{
|
||||||
|
.address = cur_reloc.address,
|
||||||
|
.target_section_offset = output_section_offset,
|
||||||
|
.symbol_index = static_cast<uint32_t>(import_ref.symbol_index),
|
||||||
|
.target_section = N64Recomp::SectionImport,
|
||||||
|
.type = cur_reloc.type,
|
||||||
|
.reference_symbol = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Not an import or event section, so handle the reloc normally.
|
||||||
else {
|
else {
|
||||||
uint32_t target_rom_to_ram = target_section.ram_addr - target_section.rom_addr;
|
uint32_t target_rom_to_ram = target_section.ram_addr - target_section.rom_addr;
|
||||||
bool is_noload = target_section.rom_addr == (uint32_t)-1;
|
bool is_noload = target_section.rom_addr == (uint32_t)-1;
|
||||||
if (!is_noload && target_rom_to_ram != cur_rom_to_ram) {
|
if (!is_noload && target_rom_to_ram != cur_rom_to_ram) {
|
||||||
fmt::print("Reloc at address 0x{:08X} in section {} points to a different section!\n",
|
fmt::print("Reloc at address 0x{:08X} in section {} points to a different section.\n",
|
||||||
cur_reloc.address, cur_section.name);
|
cur_reloc.address, cur_section.name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -599,7 +680,7 @@ int main(int argc, const char** argv) {
|
||||||
|
|
||||||
// Use the reference context to build a reference symbol list for the actual context.
|
// Use the reference context to build a reference symbol list for the actual context.
|
||||||
if (!context.import_reference_context(reference_context)) {
|
if (!context.import_reference_context(reference_context)) {
|
||||||
fmt::print(stderr, "Internal error: failed to import reference context\n");
|
fmt::print(stderr, "Internal error: failed to import reference context. Please report this issue.\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -611,13 +692,8 @@ int main(int argc, const char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the dependency files.
|
// Copy the dependencies from the config into the context.
|
||||||
for (const std::filesystem::path& dependency_path : config.dependency_paths) {
|
context.add_dependencies(config.dependencies);
|
||||||
if (!read_dependency_file(dependency_path, context)) {
|
|
||||||
fmt::print(stderr, "Failed to read dependency file: {}\n", dependency_path.string());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
N64Recomp::ElfParsingConfig elf_config {
|
N64Recomp::ElfParsingConfig elf_config {
|
||||||
.bss_section_suffix = {},
|
.bss_section_suffix = {},
|
||||||
|
|
|
||||||
|
|
@ -57,14 +57,23 @@ namespace N64Recomp {
|
||||||
bool reference_symbol;
|
bool reference_symbol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Special section indices.
|
||||||
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
|
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
|
||||||
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
|
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
|
||||||
constexpr uint16_t SectionEvent = (uint16_t)-4;
|
constexpr uint16_t SectionEvent = (uint16_t)-4;
|
||||||
|
|
||||||
|
// Special section names.
|
||||||
constexpr std::string_view PatchSectionName = ".recomp_patch";
|
constexpr std::string_view PatchSectionName = ".recomp_patch";
|
||||||
constexpr std::string_view ForcedPatchSectionName = ".recomp_force_patch";
|
constexpr std::string_view ForcedPatchSectionName = ".recomp_force_patch";
|
||||||
constexpr std::string_view ExportSectionName = ".recomp_export";
|
constexpr std::string_view ExportSectionName = ".recomp_export";
|
||||||
constexpr std::string_view EventSectionName = ".recomp_event";
|
constexpr std::string_view EventSectionName = ".recomp_event";
|
||||||
|
constexpr std::string_view ImportSectionPrefix = ".recomp_import.";
|
||||||
constexpr std::string_view CallbackSectionPrefix = ".recomp_callback.";
|
constexpr std::string_view CallbackSectionPrefix = ".recomp_callback.";
|
||||||
|
|
||||||
|
// Special mod names.
|
||||||
|
constexpr std::string_view ModSelf = ".";
|
||||||
|
constexpr std::string_view ModBaseRecomp = "*";
|
||||||
|
|
||||||
struct Section {
|
struct Section {
|
||||||
uint32_t rom_addr = 0;
|
uint32_t rom_addr = 0;
|
||||||
uint32_t ram_addr = 0;
|
uint32_t ram_addr = 0;
|
||||||
|
|
@ -193,13 +202,18 @@ namespace N64Recomp {
|
||||||
//// Mod dependencies and their symbols
|
//// Mod dependencies and their symbols
|
||||||
|
|
||||||
//// Imported values
|
//// Imported values
|
||||||
|
// List of dependencies.
|
||||||
std::vector<Dependency> dependencies;
|
std::vector<Dependency> dependencies;
|
||||||
|
// Mapping of dependency name to dependency index.
|
||||||
|
std::unordered_map<std::string, size_t> dependencies_by_name;
|
||||||
// List of symbols imported from dependencies.
|
// List of symbols imported from dependencies.
|
||||||
std::vector<ImportSymbol> import_symbols;
|
std::vector<ImportSymbol> import_symbols;
|
||||||
// List of events imported from dependencies.
|
// List of events imported from dependencies.
|
||||||
std::vector<DependencyEvent> dependency_events;
|
std::vector<DependencyEvent> dependency_events;
|
||||||
// Mapping of dependency event name to the index in dependency_events.
|
// Mappings of dependency event name to the index in dependency_events, all indexed by dependency.
|
||||||
std::unordered_map<std::string, size_t> dependency_events_by_name;
|
std::vector<std::unordered_map<std::string, size_t>> dependency_events_by_name;
|
||||||
|
// Mappings of dependency import name to index in import_symbols, all indexed by dependency.
|
||||||
|
std::vector<std::unordered_map<std::string, size_t>> dependency_imports_by_name;
|
||||||
|
|
||||||
//// Exported values
|
//// Exported values
|
||||||
// List of function replacements, which contains the original function to replace and the function index to replace it with.
|
// List of function replacements, which contains the original function to replace and the function index to replace it with.
|
||||||
|
|
@ -221,6 +235,57 @@ namespace N64Recomp {
|
||||||
|
|
||||||
Context() = default;
|
Context() = default;
|
||||||
|
|
||||||
|
bool add_dependency(const std::string& id, uint8_t major_version, uint8_t minor_version, uint8_t patch_version) {
|
||||||
|
if (dependencies_by_name.contains(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t dependency_index = dependencies.size();
|
||||||
|
dependencies.emplace_back(N64Recomp::Dependency {
|
||||||
|
.major_version = major_version,
|
||||||
|
.minor_version = minor_version,
|
||||||
|
.patch_version = patch_version,
|
||||||
|
.mod_id = id
|
||||||
|
});
|
||||||
|
|
||||||
|
dependencies_by_name.emplace(id, dependency_index);
|
||||||
|
dependency_events_by_name.resize(dependencies.size());
|
||||||
|
dependency_imports_by_name.resize(dependencies.size());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add_dependencies(const std::vector<Dependency>& new_dependencies) {
|
||||||
|
dependencies.reserve(dependencies.size() + new_dependencies.size());
|
||||||
|
dependencies_by_name.reserve(dependencies_by_name.size() + new_dependencies.size());
|
||||||
|
|
||||||
|
// Check if any of the dependencies already exist and fail if so.
|
||||||
|
for (const Dependency& dep : new_dependencies) {
|
||||||
|
if (dependencies_by_name.contains(dep.mod_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Dependency& dep : new_dependencies) {
|
||||||
|
size_t dependency_index = dependencies.size();
|
||||||
|
dependencies.emplace_back(dep);
|
||||||
|
dependencies_by_name.emplace(dep.mod_id, dependency_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_events_by_name.resize(dependencies.size());
|
||||||
|
dependency_imports_by_name.resize(dependencies.size());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_dependency(const std::string& mod_id, size_t& dependency_index) {
|
||||||
|
auto find_it = dependencies_by_name.find(mod_id);
|
||||||
|
if (find_it == dependencies_by_name.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dependency_index = find_it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
size_t find_function_by_vram_section(uint32_t vram, size_t section_index) const {
|
size_t find_function_by_vram_section(uint32_t vram, size_t section_index) const {
|
||||||
auto find_it = functions_by_vram.find(vram);
|
auto find_it = functions_by_vram.find(vram);
|
||||||
if (find_it == functions_by_vram.end()) {
|
if (find_it == functions_by_vram.end()) {
|
||||||
|
|
@ -338,11 +403,8 @@ namespace N64Recomp {
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_import_symbol(const std::string& symbol_name, size_t dependency_index) {
|
void add_import_symbol(const std::string& symbol_name, size_t dependency_index) {
|
||||||
// TODO Check if reference_symbols_by_name already contains the name and show a conflict error if so.
|
// TODO Check if dependency_imports_by_name[dependency_index] already contains the name and show a conflict error if so.
|
||||||
reference_symbols_by_name[symbol_name] = N64Recomp::SymbolReference {
|
dependency_imports_by_name[dependency_index][symbol_name] = import_symbols.size();
|
||||||
.section_index = N64Recomp::SectionImport,
|
|
||||||
.symbol_index = import_symbols.size()
|
|
||||||
};
|
|
||||||
import_symbols.emplace_back(
|
import_symbols.emplace_back(
|
||||||
N64Recomp::ImportSymbol {
|
N64Recomp::ImportSymbol {
|
||||||
.base = N64Recomp::ReferenceSymbol {
|
.base = N64Recomp::ReferenceSymbol {
|
||||||
|
|
@ -356,6 +418,21 @@ namespace N64Recomp {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool find_import_symbol(const std::string& symbol_name, size_t dependency_index, SymbolReference& ref_out) const {
|
||||||
|
if (dependency_index >= dependencies.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto find_it = dependency_imports_by_name[dependency_index].find(symbol_name);
|
||||||
|
if (find_it == dependency_imports_by_name[dependency_index].end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref_out.section_index = SectionImport;
|
||||||
|
ref_out.symbol_index = find_it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void add_event_symbol(const std::string& symbol_name) {
|
void add_event_symbol(const std::string& symbol_name) {
|
||||||
// TODO Check if reference_symbols_by_name already contains the name and show a conflict error if so.
|
// TODO Check if reference_symbols_by_name already contains the name and show a conflict error if so.
|
||||||
reference_symbols_by_name[symbol_name] = N64Recomp::SymbolReference {
|
reference_symbols_by_name[symbol_name] = N64Recomp::SymbolReference {
|
||||||
|
|
@ -389,23 +466,25 @@ namespace N64Recomp {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add_dependency_event(const std::string& event_name, size_t dependency_index) {
|
bool add_dependency_event(const std::string& event_name, size_t dependency_index, size_t& dependency_event_index) {
|
||||||
size_t dependency_event_index = dependency_events.size();
|
if (dependency_index >= dependencies.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent adding the same event to a dependency twice. This isn't an error, since a mod could register
|
||||||
|
// multiple callbacks to the same event.
|
||||||
|
auto find_it = dependency_events_by_name[dependency_index].find(event_name);
|
||||||
|
if (find_it != dependency_events_by_name[dependency_index].end()) {
|
||||||
|
dependency_event_index = find_it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_event_index = dependency_events.size();
|
||||||
dependency_events.emplace_back(DependencyEvent{
|
dependency_events.emplace_back(DependencyEvent{
|
||||||
.dependency_index = dependency_index,
|
.dependency_index = dependency_index,
|
||||||
.event_name = event_name
|
.event_name = event_name
|
||||||
});
|
});
|
||||||
// TODO Check if dependency_events_by_name already contains the name and show a conflict error if so.
|
dependency_events_by_name[dependency_index][event_name] = dependency_event_index;
|
||||||
dependency_events_by_name[event_name] = dependency_event_index;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_dependency_event(const std::string& event_name, size_t& event_index) const {
|
|
||||||
auto find_it = dependency_events_by_name.find(event_name);
|
|
||||||
if (find_it == dependency_events_by_name.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
event_index = find_it->second;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -458,6 +537,20 @@ namespace N64Recomp {
|
||||||
|
|
||||||
ModSymbolsError parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, Context& context_out);
|
ModSymbolsError parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, Context& context_out);
|
||||||
std::vector<uint8_t> symbols_to_bin_v1(const Context& mod_context);
|
std::vector<uint8_t> symbols_to_bin_v1(const Context& mod_context);
|
||||||
|
|
||||||
|
inline bool validate_mod_name(std::string_view str) {
|
||||||
|
// Disallow mod names with a colon in them, since you can't specify that in a dependency string orin callbacks.
|
||||||
|
for (char c : str) {
|
||||||
|
if (c == ':') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool validate_mod_name(const std::string& str) {
|
||||||
|
return validate_mod_name(std::string_view{str});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -722,7 +722,7 @@ bool N64Recomp::Context::read_data_reference_syms(const std::filesystem::path& d
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->add_reference_symbol(name.value(), ref_section_index, vram_addr.value(), false)) {
|
if (!this->add_reference_symbol(name.value(), ref_section_index, vram_addr.value(), false)) {
|
||||||
throw toml::parse_error("Internal error: Failed to add reference symbol to context", data_sym_el.source());
|
throw toml::parse_error("Internal error: Failed to add reference symbol to context. Please report this issue.", data_sym_el.source());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
// Use the reference context to build a reference symbol list for the actual context.
|
// Use the reference context to build a reference symbol list for the actual context.
|
||||||
if (!context.import_reference_context(reference_context)) {
|
if (!context.import_reference_context(reference_context)) {
|
||||||
exit_failure("Internal error: Failed to import reference context\n");
|
exit_failure("Internal error: Failed to import reference context. Please report this issue.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
||||||
|
|
||||||
// TODO add proper creation methods for the remaining vectors and change these to reserves instead.
|
// TODO add proper creation methods for the remaining vectors and change these to reserves instead.
|
||||||
mod_context.sections.resize(num_sections); // Add method
|
mod_context.sections.resize(num_sections); // Add method
|
||||||
mod_context.dependencies.resize(num_dependencies); // Add method
|
mod_context.dependencies.reserve(num_dependencies);
|
||||||
|
mod_context.dependencies_by_name.reserve(num_dependencies);
|
||||||
mod_context.import_symbols.reserve(num_imports);
|
mod_context.import_symbols.reserve(num_imports);
|
||||||
mod_context.dependency_events.reserve(num_dependency_events);
|
mod_context.dependency_events.reserve(num_dependency_events);
|
||||||
mod_context.replacements.resize(num_replacements); // Add method
|
mod_context.replacements.resize(num_replacements); // Add method
|
||||||
|
|
@ -270,11 +271,8 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
||||||
dependency_index, mod_id_start, mod_id_size, string_data_size);
|
dependency_index, mod_id_start, mod_id_size, string_data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& dependency_out = mod_context.dependencies[dependency_index];
|
std::string_view mod_id{ string_data + mod_id_start, string_data + mod_id_start + mod_id_size };
|
||||||
dependency_out.major_version = dependency_in.major_version;
|
mod_context.add_dependency(std::string{mod_id}, dependency_in.major_version, dependency_in.minor_version, dependency_in.patch_version);
|
||||||
dependency_out.minor_version = dependency_in.minor_version;
|
|
||||||
dependency_out.patch_version = dependency_in.patch_version;
|
|
||||||
dependency_out.mod_id = std::string_view(string_data + mod_id_start, string_data + mod_id_start + mod_id_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImportV1* imports = reinterpret_data<ImportV1>(data, offset, num_imports);
|
const ImportV1* imports = reinterpret_data<ImportV1>(data, offset, num_imports);
|
||||||
|
|
@ -323,7 +321,8 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
||||||
|
|
||||||
std::string_view dependency_event_name{ string_data + name_start, string_data + name_start + name_size };
|
std::string_view dependency_event_name{ string_data + name_start, string_data + name_start + name_size };
|
||||||
|
|
||||||
mod_context.add_dependency_event(std::string{dependency_event_name}, dependency_index);
|
size_t dummy_dependency_event_index;
|
||||||
|
mod_context.add_dependency_event(std::string{dependency_event_name}, dependency_index, dummy_dependency_event_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReplacementV1* replacements = reinterpret_data<ReplacementV1>(data, offset, num_replacements);
|
const ReplacementV1* replacements = reinterpret_data<ReplacementV1>(data, offset, num_replacements);
|
||||||
|
|
@ -602,7 +601,7 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
||||||
uint32_t target_section_vrom;
|
uint32_t target_section_vrom;
|
||||||
uint32_t target_section_offset_or_index = cur_reloc.target_section_offset;
|
uint32_t target_section_offset_or_index = cur_reloc.target_section_offset;
|
||||||
if (cur_reloc.target_section == SectionAbsolute) {
|
if (cur_reloc.target_section == SectionAbsolute) {
|
||||||
printf("Internal error: reloc %zu in section %zu references an absolute symbol and should have been relocated already\n",
|
printf("Internal error: reloc %zu in section %zu references an absolute symbol and should have been relocated already. Please report this issue.\n",
|
||||||
reloc_index, section_index);
|
reloc_index, section_index);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -619,7 +618,7 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (cur_reloc.target_section >= context.sections.size()) {
|
if (cur_reloc.target_section >= context.sections.size()) {
|
||||||
printf("Internal error: reloc %zu in section %zu references section %u, but only %zu exist\n",
|
printf("Internal error: reloc %zu in section %zu references section %u, but only %zu exist. Please report this issue.\n",
|
||||||
reloc_index, section_index, cur_reloc.target_section, context.sections.size());
|
reloc_index, section_index, cur_reloc.target_section, context.sections.size());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
|
||||||
jal_target_name = fmt::format("LOOKUP_FUNC(0x{:08X})", target_func_vram);
|
jal_target_name = fmt::format("LOOKUP_FUNC(0x{:08X})", target_func_vram);
|
||||||
break;
|
break;
|
||||||
case JalResolutionResult::Error:
|
case JalResolutionResult::Error:
|
||||||
fmt::print(stderr, "Internal error when resolving jal to address 0x{:08X} in function {}\n", target_func_vram, func.name);
|
fmt::print(stderr, "Internal error when resolving jal to address 0x{:08X} in function {}. Please report this issue.\n", target_func_vram, func.name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue