From 4b4dcff7ca24d8eb5933f54c22fa68ea60c5dda8 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Mon, 22 Jul 2024 23:56:48 -0400 Subject: [PATCH] Implemented writing import and exports in the mod tool --- RecompModTool/main.cpp | 54 ++++++++++++++----- include/n64recomp.h | 4 ++ src/mod_symbols.cpp | 115 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 14 deletions(-) diff --git a/RecompModTool/main.cpp b/RecompModTool/main.cpp index 74c2a30..d98c3d3 100644 --- a/RecompModTool/main.cpp +++ b/RecompModTool/main.cpp @@ -199,7 +199,7 @@ static inline uint32_t round_up_16(uint32_t value) { return (value + 15) & (~15); } -N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, bool& good) { +N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, std::vector&& import_symbol_mod_ids, bool& good) { N64Recomp::ModContext ret{}; good = false; @@ -330,14 +330,23 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, ); } + std::string name_out; + + if (export_section) { + ret.exported_funcs.push_back(output_func_index); + // Names are required for exported funcs, so copy the input function's name if we're in the export section. + name_out = cur_func.name; + } + ret.base_context.section_functions[output_section_index].push_back(output_func_index); + // Add this function to the output context. ret.base_context.functions.emplace_back( cur_func.vram, cur_func.rom, std::vector{}, // words - "", // name + std::move(name_out), // name (uint16_t)output_section_index, false, // ignored false, // reimplemented @@ -427,10 +436,29 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, }); } } + } - // TODO exports + ret.import_symbol_mod_ids = std::move(import_symbol_mod_ids); - // TODO imports + // Copy the import reference symbols from the end of the input context to this context. + size_t num_imports = ret.import_symbol_mod_ids.size(); + size_t import_symbol_start_index = input_context.reference_symbols.size() - num_imports; + ret.base_context.reference_symbols.resize(num_imports); + ret.base_context.reference_symbol_names.resize(num_imports); + for (size_t import_symbol_index = 0; import_symbol_index < num_imports; import_symbol_index++) { + size_t reference_symbol_index = import_symbol_index + import_symbol_start_index; + const auto& reference_symbol = input_context.reference_symbols[reference_symbol_index]; + const std::string& reference_symbol_name = input_context.reference_symbol_names[reference_symbol_index]; + + if (reference_symbol.section_index != N64Recomp::SectionImport) { + fmt::print("Import symbol index {} (reference symbol index {}, name {}) is not in the import section!\n", + import_symbol_index, reference_symbol_index, reference_symbol_name); + return {}; + } + + ret.base_context.reference_symbols[import_symbol_index] = reference_symbol; + ret.base_context.reference_symbol_names[import_symbol_index] = reference_symbol_name; + ret.base_context.reference_symbols_by_name[reference_symbol_name] = import_symbol_index; } good = true; @@ -467,7 +495,14 @@ int main(int argc, const char** argv) { context.import_reference_context(reference_context); } - size_t import_section_symbol_start = context.reference_symbols.size(); + for (const std::filesystem::path& cur_data_sym_path : config.data_reference_syms_file_paths) { + if (!context.read_data_reference_syms(cur_data_sym_path)) { + fmt::print(stderr, "Failed to load provided data reference symbol file: {}\n", cur_data_sym_path.string()); + return EXIT_FAILURE; + } + } + + // Read the imported symbols, placing them at the end of the reference symbol list. std::vector import_symbol_mod_ids{}; for (const std::filesystem::path& dependency_path : config.dependency_paths) { @@ -477,13 +512,6 @@ int main(int argc, const char** argv) { } } - for (const std::filesystem::path& cur_data_sym_path : config.data_reference_syms_file_paths) { - if (!context.read_data_reference_syms(cur_data_sym_path)) { - fmt::print(stderr, "Failed to load provided data reference symbol file: {}\n", cur_data_sym_path.string()); - return EXIT_FAILURE; - } - } - N64Recomp::ElfParsingConfig elf_config { .bss_section_suffix = {}, .manually_sized_funcs = {}, @@ -509,7 +537,7 @@ int main(int argc, const char** argv) { } bool mod_context_good; - N64Recomp::ModContext mod_context = build_mod_context(context, mod_context_good); + N64Recomp::ModContext mod_context = build_mod_context(context, std::move(import_symbol_mod_ids), mod_context_good); std::vector symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context); std::ofstream output_syms_file{ config.output_syms_path, std::ios::binary }; diff --git a/include/n64recomp.h b/include/n64recomp.h index bf88e63..6efc718 100644 --- a/include/n64recomp.h +++ b/include/n64recomp.h @@ -167,6 +167,10 @@ namespace N64Recomp { struct ModContext { Context base_context; std::vector replacements; + // Mod id of every imported function (which exist at the end of `base_context.reference_symbols`). + std::vector import_symbol_mod_ids; + // Indices of every exported function. + std::vector exported_funcs; }; enum class ModSymbolsError { Good, diff --git a/src/mod_symbols.cpp b/src/mod_symbols.cpp index feab9d3..989044e 100644 --- a/src/mod_symbols.cpp +++ b/src/mod_symbols.cpp @@ -10,6 +10,7 @@ struct FileSubHeaderV1 { uint32_t num_replacements; uint32_t num_exports; uint32_t num_imports; + uint32_t string_data_size; }; struct SectionHeaderV1 { @@ -40,6 +41,19 @@ struct ReplacementV1 { uint32_t flags; // force }; +struct ExportV1 { + uint32_t func_index; + uint32_t name_start; // offset into the string data + uint32_t name_size; +}; + +struct ImportV1 { + uint32_t name_start; + uint32_t name_size; + uint32_t mod_id_start; + uint32_t mod_id_size; +}; + constexpr uint32_t SectionSelfVromV1 = 0xFFFFFFFF; constexpr uint32_t SectionImportVromV1 = 0xFFFFFFFE; @@ -219,7 +233,13 @@ template void vec_put(std::vector& vec, const T* data) { size_t start_size = vec.size(); vec.resize(vec.size() + sizeof(T)); - memcpy(vec.data() + start_size, reinterpret_cast(data), sizeof(T)); + memcpy(vec.data() + start_size, data, sizeof(T)); +} + +void vec_put(std::vector& vec, const std::string& data) { + size_t start_size = vec.size(); + vec.resize(vec.size() + data.size()); + memcpy(vec.data() + start_size, data.data(), data.size()); } std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& mod_context) { @@ -234,13 +254,67 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m vec_put(ret, &header); + size_t num_exported_funcs = mod_context.exported_funcs.size(); + size_t num_imported_funcs = mod_context.import_symbol_mod_ids.size(); + FileSubHeaderV1 sub_header { .num_sections = static_cast(context.sections.size()), .num_replacements = static_cast(mod_context.replacements.size()), + .num_exports = static_cast(num_exported_funcs), + .num_imports = static_cast(num_imported_funcs), + .string_data_size = 0, }; + // Record the sub-header offset so the string data size can be filled in later. + size_t sub_header_offset = ret.size(); vec_put(ret, &sub_header); + // Build the string data from the exports and imports. + size_t strings_start = ret.size(); + + // Track the start of every exported function's name in the string data. Size comes from the function, so no need to store it. + std::vector exported_func_name_positions{}; + exported_func_name_positions.resize(num_exported_funcs); + for (size_t export_index = 0; export_index < num_exported_funcs; export_index++) { + size_t function_index = mod_context.exported_funcs[export_index]; + const Function& exported_func = mod_context.base_context.functions[function_index]; + + exported_func_name_positions[export_index] = static_cast(ret.size() - strings_start); + vec_put(ret, exported_func.name); + } + + // Track the start of every imported function's name in the string data, as well as any mod ids that have been written to the string data. + std::vector imported_func_name_positions{}; + imported_func_name_positions.resize(num_imported_funcs); + std::unordered_map mod_id_name_positions{}; + // Calculate the index of the first import symbol. Import symbols are all grouped at the end of the reference symbol list. + size_t import_symbol_base_index = mod_context.base_context.reference_symbols.size() - num_imported_funcs; + for (size_t import_index = 0; import_index < num_imported_funcs; import_index++) { + // Get the index of the reference symbol for this import. + size_t reference_symbol_index = import_symbol_base_index + import_index; + const ReferenceSymbol& imported_func = mod_context.base_context.reference_symbols[reference_symbol_index]; + const std::string& imported_func_name = mod_context.base_context.reference_symbol_names[reference_symbol_index]; + const std::string& cur_mod_id = mod_context.import_symbol_mod_ids[import_index]; + + // If this import's mod id hasn't been written into the strings data, write it now. + auto mod_id_find_it = mod_id_name_positions.find(cur_mod_id); + if (mod_id_find_it == mod_id_name_positions.end()) { + mod_id_name_positions.emplace(cur_mod_id, static_cast(ret.size() - strings_start)); + vec_put(ret, cur_mod_id); + } + + // Write this import's name into the strings data. + imported_func_name_positions[import_index] = static_cast(ret.size() - strings_start); + vec_put(ret, imported_func_name); + } + + // Align the data after the strings to 4 bytes. + size_t strings_size = round_up_4(ret.size() - strings_start); + ret.resize(strings_size + strings_start); + + // Fill in the string data size in the sub-header. + reinterpret_cast(ret.data() + sub_header_offset)->string_data_size = strings_size; + for (size_t section_index = 0; section_index < context.sections.size(); section_index++) { const Section& cur_section = context.sections[section_index]; SectionHeaderV1 section_out { @@ -289,6 +363,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m } } + // Write the function replacements. for (const FunctionReplacement& cur_replacement : mod_context.replacements) { uint32_t flags = 0; if ((cur_replacement.flags & ReplacementFlags::Force) == ReplacementFlags::Force) { @@ -305,5 +380,43 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m vec_put(ret, &replacement_out); }; + // Write the exported functions. + for (size_t export_index = 0; export_index < num_exported_funcs; export_index++) { + size_t function_index = mod_context.exported_funcs[export_index]; + const Function& exported_func = mod_context.base_context.functions[function_index]; + + ExportV1 export_out { + .func_index = static_cast(function_index), + .name_start = exported_func_name_positions[export_index], + .name_size = static_cast(exported_func.name.size()) + }; + + vec_put(ret, &export_out); + } + + // Write the imported functions. + for (size_t import_index = 0; import_index < num_imported_funcs; import_index++) { + // Get the index of the reference symbol for this import. + size_t reference_symbol_index = import_symbol_base_index + import_index; + const ReferenceSymbol& imported_func = mod_context.base_context.reference_symbols[reference_symbol_index]; + const std::string& imported_func_name = mod_context.base_context.reference_symbol_names[reference_symbol_index]; + const std::string& cur_mod_id = mod_context.import_symbol_mod_ids[import_index]; + + auto mod_id_find_it = mod_id_name_positions.find(cur_mod_id); + if (mod_id_find_it == mod_id_name_positions.end()) { + fprintf(stderr, "Internal error: failed to find position of mod id %s in string data\n", cur_mod_id.c_str()); + return {}; + } + + ImportV1 import_out { + .name_start = imported_func_name_positions[import_index], + .name_size = static_cast(imported_func_name.size()), + .mod_id_start = mod_id_find_it->second, + .mod_id_size = static_cast(cur_mod_id.size()) + }; + + vec_put(ret, &import_out); + } + return ret; }