From 73726814813ecc68bdceefabc80ccc64da1e9f1e Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 26 Aug 2024 01:35:49 -0400 Subject: [PATCH] Rework callbacks and imports to use the section name for identifying the dependency instead of relying on per-dependency tomls --- RecompModTool/main.cpp | 342 +++++++++++++++++++++++++---------------- include/n64recomp.h | 133 +++++++++++++--- src/config.cpp | 2 +- src/main.cpp | 2 +- src/mod_symbols.cpp | 17 +- src/recompilation.cpp | 2 +- 6 files changed, 333 insertions(+), 165 deletions(-) diff --git a/RecompModTool/main.cpp b/RecompModTool/main.cpp index c61a813..8f6a61b 100644 --- a/RecompModTool/main.cpp +++ b/RecompModTool/main.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -12,7 +13,7 @@ struct ModConfig { std::filesystem::path elf_path; std::filesystem::path func_reference_syms_file_path; std::vector data_reference_syms_file_paths; - std::vector dependency_paths; + std::vector dependencies; }; 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; } +static bool parse_version_string(std::string_view str, uint8_t& major, uint8_t& minor, uint8_t& patch) { + std::array 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 parse_results; + std::array parse_starts { 0, period_indices[0] + 1, period_indices[1] + 1 }; + std::array 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 get_toml_path_array(const toml::array* toml_array, const std::filesystem::path& basedir) { std::vector ret; @@ -39,113 +106,6 @@ static std::vector get_toml_path_array(const toml::array* 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(); - 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(read_number(dependency_node, "major_version", "major version", 0, std::numeric_limits::max())); - uint8_t minor_version = static_cast(read_number(dependency_node, "minor_version", "minor version", 0, std::numeric_limits::max())); - uint8_t patch_version = static_cast(read_number(dependency_node, "patch_version", "patch version", 0, std::numeric_limits::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(); - 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(); - 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(); - 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 ret{}; 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"]; if (dependency_data.is_array()) { - const toml::array* array = dependency_data.as_array(); - ret.dependency_paths = get_toml_path_array(array, basedir); + const toml::array* dependency_array = dependency_data.as_array(); + // Reserve room for all the dependencies. + ret.dependencies.reserve(dependency_array->size()); + dependency_array->for_each([&ret](auto&& el) { + if constexpr (toml::is_string) { + N64Recomp::Dependency dep; + if (!parse_dependency_string(el.ref(), 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) { - 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) { @@ -232,6 +205,25 @@ static inline uint32_t round_up_16(uint32_t value) { 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 ret{}; 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. ret.dependencies = input_context.dependencies; + ret.dependencies_by_name = input_context.dependencies_by_name; ret.import_symbols = input_context.import_symbols; ret.dependency_events = input_context.dependency_events; 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; 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 export_section = cur_section.name == N64Recomp::ExportSectionName; 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); // 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. else { 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. 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 {}; } // Check that the reference symbol is actually a function. const auto& reference_symbol = input_context.get_reference_symbol(cur_reference); 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 {}; } @@ -394,14 +411,32 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo } if (callback_section) { - std::string event_name = cur_section.name.substr(N64Recomp::CallbackSectionPrefix.size()); - size_t event_index; - if (!ret.get_dependency_event(event_name, event_index)) { - fmt::print("Failed to find event {} for callback {}!\n", - event_name, cur_func.name); + std::string dependency_name, event_name; + if (!parse_callback_name(std::string_view{ cur_section.name }.substr(N64Recomp::CallbackSectionPrefix.size()), dependency_name, event_name)) { + fmt::print("Invalid mod name or event name for callback function {}.\n", + 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 {}; } - ret.add_callback(event_index, 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; break; 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); 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. if (target_section.name == N64Recomp::EventSectionName) { 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); 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; 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 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); return {}; } @@ -517,12 +552,58 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo .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(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 { uint32_t target_rom_to_ram = target_section.ram_addr - target_section.rom_addr; bool is_noload = target_section.rom_addr == (uint32_t)-1; 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); 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. 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; } } @@ -611,13 +692,8 @@ int main(int argc, const char** argv) { } } - // Read the dependency files. - for (const std::filesystem::path& dependency_path : config.dependency_paths) { - if (!read_dependency_file(dependency_path, context)) { - fmt::print(stderr, "Failed to read dependency file: {}\n", dependency_path.string()); - return EXIT_FAILURE; - } - } + // Copy the dependencies from the config into the context. + context.add_dependencies(config.dependencies); N64Recomp::ElfParsingConfig elf_config { .bss_section_suffix = {}, diff --git a/include/n64recomp.h b/include/n64recomp.h index 2fd9e52..aaaa6a5 100644 --- a/include/n64recomp.h +++ b/include/n64recomp.h @@ -57,14 +57,23 @@ namespace N64Recomp { bool reference_symbol; }; + // Special section indices. constexpr uint16_t SectionAbsolute = (uint16_t)-2; constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods constexpr uint16_t SectionEvent = (uint16_t)-4; + + // Special section names. constexpr std::string_view PatchSectionName = ".recomp_patch"; constexpr std::string_view ForcedPatchSectionName = ".recomp_force_patch"; constexpr std::string_view ExportSectionName = ".recomp_export"; constexpr std::string_view EventSectionName = ".recomp_event"; + constexpr std::string_view ImportSectionPrefix = ".recomp_import."; constexpr std::string_view CallbackSectionPrefix = ".recomp_callback."; + + // Special mod names. + constexpr std::string_view ModSelf = "."; + constexpr std::string_view ModBaseRecomp = "*"; + struct Section { uint32_t rom_addr = 0; uint32_t ram_addr = 0; @@ -193,13 +202,18 @@ namespace N64Recomp { //// Mod dependencies and their symbols //// Imported values + // List of dependencies. std::vector dependencies; + // Mapping of dependency name to dependency index. + std::unordered_map dependencies_by_name; // List of symbols imported from dependencies. std::vector import_symbols; // List of events imported from dependencies. std::vector dependency_events; - // Mapping of dependency event name to the index in dependency_events. - std::unordered_map dependency_events_by_name; + // Mappings of dependency event name to the index in dependency_events, all indexed by dependency. + std::vector> dependency_events_by_name; + // Mappings of dependency import name to index in import_symbols, all indexed by dependency. + std::vector> dependency_imports_by_name; //// Exported values // 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; + 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& 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 { auto find_it = functions_by_vram.find(vram); 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) { - // 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 { - .section_index = N64Recomp::SectionImport, - .symbol_index = import_symbols.size() - }; + // TODO Check if dependency_imports_by_name[dependency_index] already contains the name and show a conflict error if so. + dependency_imports_by_name[dependency_index][symbol_name] = import_symbols.size(); import_symbols.emplace_back( N64Recomp::ImportSymbol { .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) { // 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 { @@ -389,23 +466,25 @@ namespace N64Recomp { return true; } - bool add_dependency_event(const std::string& event_name, size_t dependency_index) { - size_t dependency_event_index = dependency_events.size(); + bool add_dependency_event(const std::string& event_name, size_t dependency_index, size_t& dependency_event_index) { + 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_index = dependency_index, .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[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; + dependency_events_by_name[dependency_index][event_name] = dependency_event_index; return true; } @@ -458,6 +537,20 @@ namespace N64Recomp { ModSymbolsError parse_mod_symbols(std::span data, std::span binary, const std::unordered_map& sections_by_vrom, const Context& reference_context, Context& context_out); std::vector 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 diff --git a/src/config.cpp b/src/config.cpp index e32217b..4471923 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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)) { - 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 { diff --git a/src/main.cpp b/src/main.cpp index fd28eb9..ff98df3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -327,7 +327,7 @@ int main(int argc, char** argv) { // Use the reference context to build a reference symbol list for the actual 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"); } } diff --git a/src/mod_symbols.cpp b/src/mod_symbols.cpp index 83f362b..2de7aa1 100644 --- a/src/mod_symbols.cpp +++ b/src/mod_symbols.cpp @@ -143,7 +143,8 @@ bool parse_v1(std::span data, const std::unordered_map data, const std::unordered_map(data, offset, num_imports); @@ -323,7 +321,8 @@ bool parse_v1(std::span data, const std::unordered_map(data, offset, num_replacements); @@ -602,7 +601,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont uint32_t target_section_vrom; uint32_t target_section_offset_or_index = cur_reloc.target_section_offset; 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); return {}; } @@ -619,7 +618,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont } else { 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()); return {}; } diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 9ee1f66..26ca4cc 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -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); break; 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; } }