Implemented writing import and exports in the mod tool

This commit is contained in:
Mr-Wiseguy 2024-07-22 23:56:48 -04:00
parent ab80ff962e
commit 4b4dcff7ca
3 changed files with 159 additions and 14 deletions

View file

@ -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<std::string>&& 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<uint32_t>{}, // 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<std::string> 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<uint8_t> symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context);
std::ofstream output_syms_file{ config.output_syms_path, std::ios::binary };

View file

@ -167,6 +167,10 @@ namespace N64Recomp {
struct ModContext {
Context base_context;
std::vector<FunctionReplacement> replacements;
// Mod id of every imported function (which exist at the end of `base_context.reference_symbols`).
std::vector<std::string> import_symbol_mod_ids;
// Indices of every exported function.
std::vector<size_t> exported_funcs;
};
enum class ModSymbolsError {
Good,

View file

@ -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 <typename T>
void vec_put(std::vector<uint8_t>& vec, const T* data) {
size_t start_size = vec.size();
vec.resize(vec.size() + sizeof(T));
memcpy(vec.data() + start_size, reinterpret_cast<const uint8_t*>(data), sizeof(T));
memcpy(vec.data() + start_size, data, sizeof(T));
}
void vec_put(std::vector<uint8_t>& 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<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& mod_context) {
@ -234,13 +254,67 @@ std::vector<uint8_t> 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<uint32_t>(context.sections.size()),
.num_replacements = static_cast<uint32_t>(mod_context.replacements.size()),
.num_exports = static_cast<uint32_t>(num_exported_funcs),
.num_imports = static_cast<uint32_t>(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<uint32_t> 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<uint32_t>(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<uint32_t> imported_func_name_positions{};
imported_func_name_positions.resize(num_imported_funcs);
std::unordered_map<std::string, uint32_t> 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<uint32_t>(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<uint32_t>(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<FileSubHeaderV1*>(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<uint8_t> 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<uint8_t> 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<uint32_t>(function_index),
.name_start = exported_func_name_positions[export_index],
.name_size = static_cast<uint32_t>(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<uint32_t>(imported_func_name.size()),
.mod_id_start = mod_id_find_it->second,
.mod_id_size = static_cast<uint32_t>(cur_mod_id.size())
};
vec_put(ret, &import_out);
}
return ret;
}