mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-04-28 04:51:43 +00:00
Add events (from dependencies and exported) and callbacks to the mod symbol format and add support to them in elf parsing
This commit is contained in:
parent
f300b6dccc
commit
21504041b9
5 changed files with 621 additions and 255 deletions
|
|
@ -39,7 +39,7 @@ static std::vector<std::filesystem::path> get_toml_path_array(const toml::array*
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool read_dependency_file(const std::filesystem::path& dependency_path, N64Recomp::Context& context, std::vector<N64Recomp::Dependency>& dependencies, std::vector<size_t>& import_symbol_dependency_indices) {
|
||||
static bool read_dependency_file(const std::filesystem::path& dependency_path, N64Recomp::Context& context, std::vector<N64Recomp::Dependency>& dependencies) {
|
||||
toml::table toml_data{};
|
||||
|
||||
try {
|
||||
|
|
@ -113,10 +113,7 @@ static bool read_dependency_file(const std::filesystem::path& dependency_path, N
|
|||
throw toml::parse_error("Invalid dependency function", function_node.source());
|
||||
}
|
||||
const std::string& function_name = function_node.ref<std::string>();
|
||||
size_t symbol_index = import_symbol_dependency_indices.size();
|
||||
|
||||
context.add_import_symbol(function_name, dependency_index, symbol_index);
|
||||
import_symbol_dependency_indices.emplace_back(dependency_index);
|
||||
context.add_import_symbol(function_name, dependency_index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -224,7 +221,7 @@ static inline uint32_t round_up_16(uint32_t value) {
|
|||
return (value + 15) & (~15);
|
||||
}
|
||||
|
||||
N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, std::vector<N64Recomp::Dependency>&& dependencies, std::vector<size_t>&& import_symbol_dependency_indices, bool& good) {
|
||||
N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, std::vector<N64Recomp::Dependency>&& dependencies, bool& good) {
|
||||
N64Recomp::Context ret{};
|
||||
good = false;
|
||||
|
||||
|
|
@ -302,149 +299,223 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
|
|||
bool patch_section = cur_section.name == N64Recomp::PatchSectionName;
|
||||
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 callback_section = cur_section.name.starts_with(N64Recomp::CallbackSectionPrefix);
|
||||
|
||||
// Add the functions from the current input section to the current output section.
|
||||
auto& section_out = ret.sections[output_section_index];
|
||||
|
||||
const auto& cur_section_funcs = input_context.section_functions[section_index];
|
||||
|
||||
for (size_t section_function_index = 0; section_function_index < cur_section_funcs.size(); section_function_index++) {
|
||||
size_t output_func_index = ret.functions.size();
|
||||
size_t input_func_index = cur_section_funcs[section_function_index];
|
||||
const auto& cur_func = input_context.functions[input_func_index];
|
||||
// Skip the functions and relocs in this section if it's the event section, instead opting to create
|
||||
// event functions from the section's functions.
|
||||
if (event_section) {
|
||||
// Create event reference symbols for any functions in the event section. Ignore functions that already
|
||||
// have a symbol, since relocs from previous sections may have triggered creation of the event's reference symbol already.
|
||||
for (const auto& input_func_index : cur_section_funcs) {
|
||||
const auto& cur_func = input_context.functions[input_func_index];
|
||||
|
||||
// If this is the patch section, create a replacement for this function.
|
||||
if (patch_section || force_patch_section) {
|
||||
// Find the corresponding symbol in the reference symbols.
|
||||
N64Recomp::SymbolReference cur_reference;
|
||||
bool original_func_exists = input_context.find_regular_reference_symbol(cur_func.name, cur_reference);
|
||||
|
||||
// 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);
|
||||
return {};
|
||||
// Check if this event already has a symbol to prevent creating a duplicate.
|
||||
N64Recomp::SymbolReference event_ref;
|
||||
if (!ret.find_event_symbol(cur_func.name, event_ref)) {
|
||||
ret.add_event_symbol(cur_func.name);
|
||||
}
|
||||
|
||||
// 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);
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t reference_section_vram = input_context.get_reference_section_vram(reference_symbol.section_index);
|
||||
uint32_t reference_section_rom = input_context.get_reference_section_rom(reference_symbol.section_index);
|
||||
|
||||
// Add a replacement for this function to the output context.
|
||||
ret.replacements.emplace_back(
|
||||
N64Recomp::FunctionReplacement {
|
||||
.func_index = (uint32_t)output_func_index,
|
||||
.original_section_vrom = reference_section_rom,
|
||||
.original_vram = reference_section_vram + reference_symbol.section_offset,
|
||||
.flags = force_patch_section ? N64Recomp::ReplacementFlags::Force : N64Recomp::ReplacementFlags{}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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.section_functions[output_section_index].push_back(output_func_index);
|
||||
|
||||
|
||||
// Add this function to the output context.
|
||||
ret.functions.emplace_back(
|
||||
cur_func.vram,
|
||||
cur_func.rom,
|
||||
std::vector<uint32_t>{}, // words
|
||||
std::move(name_out), // name
|
||||
(uint16_t)output_section_index,
|
||||
false, // ignored
|
||||
false, // reimplemented
|
||||
false // stubbed
|
||||
);
|
||||
|
||||
// Resize the words vector so the function has the correct size. No need to copy the words, as they aren't used when making a mod symbol file.
|
||||
ret.functions[output_func_index].words.resize(cur_func.words.size());
|
||||
}
|
||||
// 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++) {
|
||||
size_t output_func_index = ret.functions.size();
|
||||
size_t input_func_index = cur_section_funcs[section_function_index];
|
||||
const auto& cur_func = input_context.functions[input_func_index];
|
||||
|
||||
// Copy relocs and patch HI16/LO16/26 relocs for non-relocatable reference symbols
|
||||
section_out.relocs.reserve(cur_section.relocs.size());
|
||||
for (const auto& cur_reloc : cur_section.relocs) {
|
||||
// Skip null relocs.
|
||||
if (cur_reloc.type == N64Recomp::RelocType::R_MIPS_NONE) {
|
||||
continue;
|
||||
}
|
||||
// Reloc to a special section symbol.
|
||||
if (!input_context.is_regular_reference_section(cur_reloc.target_section)) {
|
||||
section_out.relocs.emplace_back(cur_reloc);
|
||||
}
|
||||
// Reloc to a reference symbol.
|
||||
else if (cur_reloc.reference_symbol) {
|
||||
bool is_relocatable = input_context.is_reference_section_relocatable(cur_reloc.target_section);
|
||||
uint32_t section_vram = input_context.get_reference_section_vram(cur_reloc.target_section);
|
||||
// Patch relocations to non-relocatable reference sections.
|
||||
if (!is_relocatable) {
|
||||
uint32_t reloc_target_address = section_vram + cur_reloc.target_section_offset;
|
||||
uint32_t reloc_rom_address = cur_reloc.address - cur_section.ram_addr + cur_section.rom_addr;
|
||||
|
||||
uint32_t* reloc_word_ptr = reinterpret_cast<uint32_t*>(ret.rom.data() + reloc_rom_address);
|
||||
uint32_t reloc_word = byteswap(*reloc_word_ptr);
|
||||
switch (cur_reloc.type) {
|
||||
case N64Recomp::RelocType::R_MIPS_32:
|
||||
// Don't patch MIPS32 relocations, as they've already been patched during elf parsing.
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_26:
|
||||
// Don't patch MIPS26 relocations, as there may be multiple functions with the same vram. Emit the reloc instead.
|
||||
section_out.relocs.emplace_back(cur_reloc);
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_NONE:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_HI16:
|
||||
reloc_word &= 0xFFFF0000;
|
||||
reloc_word |= (reloc_target_address - (int16_t)(reloc_target_address & 0xFFFF)) >> 16 & 0xFFFF;
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_LO16:
|
||||
reloc_word &= 0xFFFF0000;
|
||||
reloc_word |= reloc_target_address & 0xFFFF;
|
||||
break;
|
||||
default:
|
||||
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 {};
|
||||
// If this is the patch section, create a replacement for this function.
|
||||
if (patch_section || force_patch_section) {
|
||||
// Find the corresponding symbol in the reference symbols.
|
||||
N64Recomp::SymbolReference cur_reference;
|
||||
bool original_func_exists = input_context.find_regular_reference_symbol(cur_func.name, cur_reference);
|
||||
|
||||
// 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);
|
||||
return {};
|
||||
}
|
||||
*reloc_word_ptr = byteswap(reloc_word);
|
||||
|
||||
// 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);
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t reference_section_vram = input_context.get_reference_section_vram(reference_symbol.section_index);
|
||||
uint32_t reference_section_rom = input_context.get_reference_section_rom(reference_symbol.section_index);
|
||||
|
||||
// Add a replacement for this function to the output context.
|
||||
ret.replacements.emplace_back(
|
||||
N64Recomp::FunctionReplacement {
|
||||
.func_index = (uint32_t)output_func_index,
|
||||
.original_section_vrom = reference_section_rom,
|
||||
.original_vram = reference_section_vram + reference_symbol.section_offset,
|
||||
.flags = force_patch_section ? N64Recomp::ReplacementFlags::Force : N64Recomp::ReplacementFlags{}
|
||||
}
|
||||
);
|
||||
}
|
||||
// Copy relocations to relocatable reference sections as-is.
|
||||
else {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
return {};
|
||||
}
|
||||
ret.callbacks.emplace_back(N64Recomp::Callback {
|
||||
output_func_index,
|
||||
event_index
|
||||
});
|
||||
}
|
||||
|
||||
ret.section_functions[output_section_index].push_back(output_func_index);
|
||||
|
||||
// Add this function to the output context.
|
||||
ret.functions.emplace_back(
|
||||
cur_func.vram,
|
||||
cur_func.rom,
|
||||
std::vector<uint32_t>{}, // words
|
||||
std::move(name_out), // name
|
||||
(uint16_t)output_section_index,
|
||||
false, // ignored
|
||||
false, // reimplemented
|
||||
false // stubbed
|
||||
);
|
||||
|
||||
// Resize the words vector so the function has the correct size. No need to copy the words, as they aren't used when making a mod symbol file.
|
||||
ret.functions[output_func_index].words.resize(cur_func.words.size());
|
||||
}
|
||||
|
||||
// Copy relocs and patch HI16/LO16/26 relocs for non-relocatable reference symbols
|
||||
section_out.relocs.reserve(section_out.relocs.size() + cur_section.relocs.size());
|
||||
for (const auto& cur_reloc : cur_section.relocs) {
|
||||
// Skip null relocs.
|
||||
if (cur_reloc.type == N64Recomp::RelocType::R_MIPS_NONE) {
|
||||
continue;
|
||||
}
|
||||
// Reloc to a special section symbol.
|
||||
if (!input_context.is_regular_reference_section(cur_reloc.target_section)) {
|
||||
section_out.relocs.emplace_back(cur_reloc);
|
||||
}
|
||||
}
|
||||
// Reloc to an internal symbol.
|
||||
else {
|
||||
const N64Recomp::Section& target_section = input_context.sections[cur_reloc.target_section];
|
||||
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",
|
||||
cur_reloc.address, cur_section.name);
|
||||
return {};
|
||||
// Reloc to a reference symbol.
|
||||
else if (cur_reloc.reference_symbol) {
|
||||
bool is_relocatable = input_context.is_reference_section_relocatable(cur_reloc.target_section);
|
||||
uint32_t section_vram = input_context.get_reference_section_vram(cur_reloc.target_section);
|
||||
// Patch relocations to non-relocatable reference sections.
|
||||
if (!is_relocatable) {
|
||||
uint32_t reloc_target_address = section_vram + cur_reloc.target_section_offset;
|
||||
uint32_t reloc_rom_address = cur_reloc.address - cur_section.ram_addr + cur_section.rom_addr;
|
||||
|
||||
uint32_t* reloc_word_ptr = reinterpret_cast<uint32_t*>(ret.rom.data() + reloc_rom_address);
|
||||
uint32_t reloc_word = byteswap(*reloc_word_ptr);
|
||||
switch (cur_reloc.type) {
|
||||
case N64Recomp::RelocType::R_MIPS_32:
|
||||
// Don't patch MIPS32 relocations, as they've already been patched during elf parsing.
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_26:
|
||||
// Don't patch MIPS26 relocations, as there may be multiple functions with the same vram. Emit the reloc instead.
|
||||
section_out.relocs.emplace_back(cur_reloc);
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_NONE:
|
||||
// Nothing to do.
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_HI16:
|
||||
reloc_word &= 0xFFFF0000;
|
||||
reloc_word |= (reloc_target_address - (int16_t)(reloc_target_address & 0xFFFF)) >> 16 & 0xFFFF;
|
||||
break;
|
||||
case N64Recomp::RelocType::R_MIPS_LO16:
|
||||
reloc_word &= 0xFFFF0000;
|
||||
reloc_word |= reloc_target_address & 0xFFFF;
|
||||
break;
|
||||
default:
|
||||
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 {};
|
||||
}
|
||||
*reloc_word_ptr = byteswap(reloc_word);
|
||||
}
|
||||
// Copy relocations to relocatable reference sections as-is.
|
||||
else {
|
||||
section_out.relocs.emplace_back(cur_reloc);
|
||||
}
|
||||
}
|
||||
// Reloc to an internal symbol.
|
||||
else {
|
||||
const N64Recomp::Section& target_section = input_context.sections[cur_reloc.target_section];
|
||||
uint32_t output_section_offset = cur_reloc.target_section_offset + target_section.ram_addr - cur_section.ram_addr;
|
||||
|
||||
// Check if the target section is the event section. If so, create a reference symbol reloc
|
||||
// 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",
|
||||
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 event symbol in section {} with offset 0x{:08X} (vram 0x{:08X})!\n",
|
||||
target_section.name, cur_reloc.target_section_offset, target_function_vram);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& target_function = input_context.functions[target_function_index];
|
||||
|
||||
// Check if this event already has a symbol to prevent creating a duplicate.
|
||||
N64Recomp::SymbolReference event_ref;
|
||||
if (!ret.find_event_symbol(target_function.name, event_ref)) {
|
||||
ret.add_event_symbol(target_function.name);
|
||||
// Update the event symbol reference now that the symbol was created.
|
||||
ret.find_event_symbol(target_function.name, event_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>(event_ref.symbol_index),
|
||||
.target_section = N64Recomp::SectionEvent,
|
||||
.type = cur_reloc.type,
|
||||
.reference_symbol = true,
|
||||
});
|
||||
}
|
||||
// Not the 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",
|
||||
cur_reloc.address, cur_section.name);
|
||||
return {};
|
||||
}
|
||||
section_out.relocs.emplace_back(N64Recomp::Reloc{
|
||||
.address = cur_reloc.address,
|
||||
.target_section_offset = output_section_offset,
|
||||
.symbol_index = 0,
|
||||
.target_section = N64Recomp::SectionSelf,
|
||||
.type = cur_reloc.type,
|
||||
.reference_symbol = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
uint32_t output_section_offset = cur_reloc.target_section_offset + target_section.ram_addr - cur_section.ram_addr;
|
||||
section_out.relocs.emplace_back(N64Recomp::Reloc{
|
||||
.address = cur_reloc.address,
|
||||
.target_section_offset = output_section_offset,
|
||||
.symbol_index = 0,
|
||||
.target_section = N64Recomp::SectionSelf,
|
||||
.type = cur_reloc.type,
|
||||
.reference_symbol = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -501,10 +572,9 @@ int main(int argc, const char** argv) {
|
|||
|
||||
// Read the imported symbols, placing them at the end of the reference symbol list.
|
||||
std::vector<N64Recomp::Dependency> dependencies{};
|
||||
std::vector<size_t> import_symbol_dependency_indices{};
|
||||
|
||||
for (const std::filesystem::path& dependency_path : config.dependency_paths) {
|
||||
if (!read_dependency_file(dependency_path, context, dependencies, import_symbol_dependency_indices)) {
|
||||
if (!read_dependency_file(dependency_path, context, dependencies)) {
|
||||
fmt::print(stderr, "Failed to read dependency file: {}\n", dependency_path.string());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -535,7 +605,7 @@ int main(int argc, const char** argv) {
|
|||
}
|
||||
|
||||
bool mod_context_good;
|
||||
N64Recomp::Context mod_context = build_mod_context(context, std::move(dependencies), std::move(import_symbol_dependency_indices), mod_context_good);
|
||||
N64Recomp::Context mod_context = build_mod_context(context, std::move(dependencies), 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 };
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ namespace N64Recomp {
|
|||
struct Reloc {
|
||||
uint32_t address;
|
||||
uint32_t target_section_offset;
|
||||
uint32_t symbol_index; // Only used for reference symbols and import symbols
|
||||
uint32_t symbol_index; // Only used for reference symbols and special section symbols
|
||||
uint16_t target_section;
|
||||
RelocType type;
|
||||
bool reference_symbol;
|
||||
|
|
@ -60,10 +60,12 @@ namespace N64Recomp {
|
|||
constexpr uint16_t SectionSelf = (uint16_t)-1;
|
||||
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
|
||||
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
|
||||
constexpr uint16_t SectionHook = (uint16_t)-4;
|
||||
constexpr uint16_t SectionEvent = (uint16_t)-4;
|
||||
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 CallbackSectionPrefix = ".recomp_callback.";
|
||||
struct Section {
|
||||
uint32_t rom_addr = 0;
|
||||
uint32_t ram_addr = 0;
|
||||
|
|
@ -130,9 +132,18 @@ namespace N64Recomp {
|
|||
size_t dependency_index;
|
||||
};
|
||||
|
||||
struct HookSymbol {
|
||||
ReferenceSymbol base;
|
||||
struct DependencyEvent {
|
||||
size_t dependency_index;
|
||||
std::string event_name;
|
||||
};
|
||||
|
||||
struct EventSymbol {
|
||||
ReferenceSymbol base;
|
||||
};
|
||||
|
||||
struct Callback {
|
||||
size_t function_index;
|
||||
size_t dependency_event_index;
|
||||
};
|
||||
|
||||
struct SymbolReference {
|
||||
|
|
@ -179,13 +190,25 @@ namespace N64Recomp {
|
|||
std::unordered_map<std::string, size_t> functions_by_name;
|
||||
|
||||
//// Mod dependencies and their symbols
|
||||
|
||||
//// Imported values
|
||||
std::vector<Dependency> dependencies;
|
||||
// List of symbols imported from dependencies.
|
||||
std::vector<ImportSymbol> import_symbols;
|
||||
std::vector<HookSymbol> hook_symbols;
|
||||
// List of events imported from dependencies.
|
||||
std::vector<DependencyEvent> dependency_events;
|
||||
// Mapping of dependency event name to the index in dependency_events.
|
||||
std::unordered_map<std::string, size_t> dependency_events_by_name;
|
||||
|
||||
//// Exported values
|
||||
// List of function replacements, which contains the original function to replace and the function index to replace it with.
|
||||
std::vector<FunctionReplacement> replacements;
|
||||
// Indices of every exported function.
|
||||
std::vector<size_t> exported_funcs;
|
||||
|
||||
std::vector<FunctionReplacement> replacements;
|
||||
// List of callbacks, which contains the function for the callback and the dependency event it attaches to.
|
||||
std::vector<Callback> callbacks;
|
||||
// List of symbols from events, which contains the names of events that this context provides.
|
||||
std::vector<EventSymbol> event_symbols;
|
||||
|
||||
// Imports sections and function symbols from a provided context into this context's reference sections and reference functions.
|
||||
bool import_reference_context(const Context& reference_context);
|
||||
|
|
@ -197,12 +220,27 @@ namespace N64Recomp {
|
|||
|
||||
Context() = default;
|
||||
|
||||
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()) {
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
for (size_t function_index : find_it->second) {
|
||||
if (functions[function_index].section_index == section_index) {
|
||||
return function_index;
|
||||
}
|
||||
}
|
||||
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
bool has_reference_symbols() const {
|
||||
return !reference_symbols.empty() || !import_symbols.empty() || !hook_symbols.empty();
|
||||
return !reference_symbols.empty() || !import_symbols.empty() || !event_symbols.empty();
|
||||
}
|
||||
|
||||
bool is_regular_reference_section(uint16_t section_index) const {
|
||||
return section_index != SectionImport && section_index != SectionHook;
|
||||
return section_index != SectionImport && section_index != SectionEvent;
|
||||
}
|
||||
|
||||
bool find_reference_symbol(const std::string& symbol_name, SymbolReference& ref_out) const {
|
||||
|
|
@ -241,8 +279,8 @@ namespace N64Recomp {
|
|||
if (section_index == SectionImport) {
|
||||
return import_symbols[symbol_index].base;
|
||||
}
|
||||
else if (section_index == SectionHook) {
|
||||
return hook_symbols[symbol_index].base;
|
||||
else if (section_index == SectionEvent) {
|
||||
return event_symbols[symbol_index].base;
|
||||
}
|
||||
return reference_symbols[symbol_index];
|
||||
}
|
||||
|
|
@ -263,7 +301,7 @@ namespace N64Recomp {
|
|||
if (section_index == SectionAbsolute) {
|
||||
return false;
|
||||
}
|
||||
else if (section_index == SectionImport || section_index == SectionHook) {
|
||||
else if (section_index == SectionImport || section_index == SectionEvent) {
|
||||
return true;
|
||||
}
|
||||
return reference_sections[section_index].relocatable;
|
||||
|
|
@ -298,10 +336,11 @@ namespace N64Recomp {
|
|||
return true;
|
||||
}
|
||||
|
||||
void add_import_symbol(const std::string& symbol_name, size_t dependency_index, size_t symbol_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.
|
||||
reference_symbols_by_name[symbol_name] = N64Recomp::SymbolReference {
|
||||
.section_index = N64Recomp::SectionImport,
|
||||
.symbol_index = symbol_index
|
||||
.symbol_index = import_symbols.size()
|
||||
};
|
||||
import_symbols.emplace_back(
|
||||
N64Recomp::ImportSymbol {
|
||||
|
|
@ -316,6 +355,91 @@ namespace N64Recomp {
|
|||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
.section_index = N64Recomp::SectionEvent,
|
||||
.symbol_index = event_symbols.size()
|
||||
};
|
||||
event_symbols.emplace_back(
|
||||
N64Recomp::EventSymbol {
|
||||
.base = N64Recomp::ReferenceSymbol {
|
||||
.name = symbol_name,
|
||||
.section_index = N64Recomp::SectionEvent,
|
||||
.section_offset = 0,
|
||||
.is_function = true
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool find_event_symbol(const std::string& symbol_name, SymbolReference& ref_out) const {
|
||||
SymbolReference ref_found;
|
||||
if (!find_reference_symbol(symbol_name, ref_found)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore reference symbols that aren't in the event section.
|
||||
if (ref_found.section_index != SectionEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ref_out = ref_found;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_dependency_event(size_t dependency_index, const std::string& event_name, size_t& dependency_event_index_out) {
|
||||
size_t 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;
|
||||
dependency_event_index_out = 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;
|
||||
}
|
||||
|
||||
bool add_callback(size_t dependency_index, const std::string& event_name, size_t function_index) {
|
||||
auto find_it = dependency_events_by_name.find(event_name);
|
||||
size_t dependency_event_index;
|
||||
if (find_it == dependency_events_by_name.end()) {
|
||||
// Event doesn't already exist, so add it.
|
||||
if (!add_dependency_event(dependency_index, event_name, dependency_event_index)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dependency_event_index = find_it->second;
|
||||
// Make sure the event that we found was for this dependency.
|
||||
if (dependency_events[dependency_event_index].dependency_index != dependency_index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
callbacks.emplace_back(Callback{
|
||||
.function_index = function_index,
|
||||
.dependency_event_index = dependency_event_index
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_callback_by_dependency_event(size_t dependency_event_index, size_t function_index) {
|
||||
callbacks.emplace_back(Callback{
|
||||
.function_index = function_index,
|
||||
.dependency_event_index = dependency_event_index
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t get_reference_section_vram(uint16_t section_index) const {
|
||||
if (section_index == N64Recomp::SectionAbsolute) {
|
||||
return 0;
|
||||
|
|
|
|||
15
src/elf.cpp
15
src/elf.cpp
|
|
@ -395,6 +395,12 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
|
|||
uint32_t rel_section_vram = 0;
|
||||
uint32_t rel_symbol_offset = 0;
|
||||
|
||||
// Remap relocations from the current section's bss section to itself.
|
||||
// TODO Do this for any bss section and not just the current section's bss section?
|
||||
if (rel_symbol_section_index == section_out.bss_section_index) {
|
||||
rel_symbol_section_index = section_index;
|
||||
}
|
||||
|
||||
// Check if the symbol is undefined and to know whether to look for it in the reference symbols.
|
||||
if (rel_symbol_section_index == ELFIO::SHN_UNDEF) {
|
||||
// Undefined sym, check the reference symbols.
|
||||
|
|
@ -515,7 +521,14 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
|
|||
|
||||
if (reloc_out.type == N64Recomp::RelocType::R_MIPS_26) {
|
||||
uint32_t rel_immediate = (reloc_rom_word & 0x3FFFFFF) << 2;
|
||||
reloc_out.target_section_offset = rel_immediate + rel_symbol_offset;
|
||||
if (reloc_out.reference_symbol) {
|
||||
// Reference symbol relocs have their section offset already calculated, so don't apply the R_MIPS26 rule for the upper 4 bits.
|
||||
// TODO Find a way to unify this with the else case.
|
||||
reloc_out.target_section_offset = rel_immediate + rel_symbol_offset - rel_section_vram;
|
||||
}
|
||||
else {
|
||||
reloc_out.target_section_offset = rel_immediate + rel_symbol_offset + (section_out.ram_addr & 0xF0000000) - rel_section_vram;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ struct FileHeader {
|
|||
|
||||
struct FileSubHeaderV1 {
|
||||
uint32_t num_sections;
|
||||
uint32_t num_replacements;
|
||||
uint32_t num_exports;
|
||||
uint32_t num_dependencies;
|
||||
uint32_t num_imports;
|
||||
uint32_t num_dependency_events;
|
||||
uint32_t num_replacements;
|
||||
uint32_t num_exports;
|
||||
uint32_t num_callbacks;
|
||||
uint32_t num_provided_events;
|
||||
uint32_t string_data_size;
|
||||
};
|
||||
|
||||
|
|
@ -33,7 +36,7 @@ constexpr uint32_t SectionSelfVromV1 = 0xFFFFFFFF;
|
|||
|
||||
// Special sections
|
||||
constexpr uint32_t SectionImportVromV1 = 0xFFFFFFFE;
|
||||
constexpr uint32_t SectionHookVromV1 = 0xFFFFFFFD;
|
||||
constexpr uint32_t SectionEventVromV1 = 0xFFFFFFFD;
|
||||
|
||||
struct RelocV1 {
|
||||
uint32_t section_offset;
|
||||
|
|
@ -42,19 +45,6 @@ struct RelocV1 {
|
|||
uint32_t target_section_vrom;
|
||||
};
|
||||
|
||||
struct ReplacementV1 {
|
||||
uint32_t func_index;
|
||||
uint32_t original_section_vrom;
|
||||
uint32_t original_vram;
|
||||
uint32_t flags; // force
|
||||
};
|
||||
|
||||
struct ExportV1 {
|
||||
uint32_t func_index;
|
||||
uint32_t name_start; // offset into the string data
|
||||
uint32_t name_size;
|
||||
};
|
||||
|
||||
struct DependencyV1 {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
|
|
@ -70,6 +60,35 @@ struct ImportV1 {
|
|||
uint32_t dependency;
|
||||
};
|
||||
|
||||
struct DependencyEventV1 {
|
||||
uint32_t name_start;
|
||||
uint32_t name_size;
|
||||
uint32_t dependency;
|
||||
};
|
||||
|
||||
struct ReplacementV1 {
|
||||
uint32_t func_index;
|
||||
uint32_t original_section_vrom;
|
||||
uint32_t original_vram;
|
||||
uint32_t flags; // force
|
||||
};
|
||||
|
||||
struct ExportV1 {
|
||||
uint32_t func_index;
|
||||
uint32_t name_start; // offset into the string data
|
||||
uint32_t name_size;
|
||||
};
|
||||
|
||||
struct CallbackV1 {
|
||||
uint32_t dependency_event_index;
|
||||
uint32_t function_index;
|
||||
};
|
||||
|
||||
struct EventV1 {
|
||||
uint32_t name_start;
|
||||
uint32_t name_size;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const T* reinterpret_data(std::span<const char> data, size_t& offset, size_t count = 1) {
|
||||
if (offset + (sizeof(T) * count) > data.size()) {
|
||||
|
|
@ -100,10 +119,13 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
}
|
||||
|
||||
size_t num_sections = subheader->num_sections;
|
||||
size_t num_replacements = subheader->num_replacements;
|
||||
size_t num_exports = subheader->num_exports;
|
||||
size_t num_dependencies = subheader->num_dependencies;
|
||||
size_t num_imports = subheader->num_imports;
|
||||
size_t num_dependency_events = subheader->num_dependency_events;
|
||||
size_t num_replacements = subheader->num_replacements;
|
||||
size_t num_exports = subheader->num_exports;
|
||||
size_t num_callbacks = subheader->num_callbacks;
|
||||
size_t num_provided_events = subheader->num_provided_events;
|
||||
size_t string_data_size = subheader->string_data_size;
|
||||
|
||||
if (string_data_size & 0b11) {
|
||||
|
|
@ -116,14 +138,16 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO add proper add methods for these vectors and change these to reserves instead.
|
||||
mod_context.sections.resize(num_sections);
|
||||
mod_context.replacements.resize(num_replacements);
|
||||
mod_context.exported_funcs.resize(num_exports);
|
||||
mod_context.dependencies.resize(num_dependencies);
|
||||
|
||||
// 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.dependencies.resize(num_dependencies); // Add method
|
||||
mod_context.import_symbols.reserve(num_imports);
|
||||
|
||||
mod_context.dependency_events.reserve(num_dependency_events);
|
||||
mod_context.replacements.resize(num_replacements); // Add method
|
||||
mod_context.exported_funcs.resize(num_exports); // Add method
|
||||
mod_context.callbacks.reserve(num_callbacks);
|
||||
mod_context.event_symbols.reserve(num_provided_events);
|
||||
|
||||
for (size_t section_index = 0; section_index < num_sections; section_index++) {
|
||||
const SectionHeaderV1* section_header = reinterpret_data<SectionHeaderV1>(data, offset);
|
||||
if (section_header == nullptr) {
|
||||
|
|
@ -217,49 +241,6 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
}
|
||||
}
|
||||
|
||||
const ReplacementV1* replacements = reinterpret_data<ReplacementV1>(data, offset, num_replacements);
|
||||
if (replacements == nullptr) {
|
||||
printf("Failed to read replacements (count: %zu)\n", num_replacements);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t replacement_index = 0; replacement_index < num_replacements; replacement_index++) {
|
||||
N64Recomp::FunctionReplacement& cur_replacement = mod_context.replacements[replacement_index];
|
||||
|
||||
cur_replacement.func_index = replacements[replacement_index].func_index;
|
||||
cur_replacement.original_section_vrom = replacements[replacement_index].original_section_vrom;
|
||||
cur_replacement.original_vram = replacements[replacement_index].original_vram;
|
||||
cur_replacement.flags = static_cast<N64Recomp::ReplacementFlags>(replacements[replacement_index].flags);
|
||||
}
|
||||
|
||||
const ExportV1* exports = reinterpret_data<ExportV1>(data, offset, num_exports);
|
||||
if (exports == nullptr) {
|
||||
printf("Failed to read exports (count: %zu)\n", num_exports);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t export_index = 0; export_index < num_exports; export_index++) {
|
||||
const ExportV1& export_in = exports[export_index];
|
||||
uint32_t func_index = export_in.func_index;
|
||||
uint32_t name_start = export_in.name_start;
|
||||
uint32_t name_size = export_in.name_size;
|
||||
|
||||
if (func_index >= mod_context.functions.size()) {
|
||||
printf("Export %zu has a function index of %u, but the symbol file only has %zu functions\n",
|
||||
export_index, func_index, mod_context.functions.size());
|
||||
}
|
||||
|
||||
if (name_start + name_size > string_data_size) {
|
||||
printf("Export %zu has a name start of %u and size of %u, which extend beyond the string data's total size of %zu\n",
|
||||
export_index, name_start, name_size, string_data_size);
|
||||
}
|
||||
|
||||
// Add the function to the exported function list.
|
||||
mod_context.exported_funcs[export_index] = func_index;
|
||||
// Populate the exported function's name from the string data.
|
||||
mod_context.functions[func_index].name = std::string_view(string_data + name_start, string_data + name_start + name_size);
|
||||
}
|
||||
|
||||
const DependencyV1* dependencies = reinterpret_data<DependencyV1>(data, offset, num_dependencies);
|
||||
if (dependencies == nullptr) {
|
||||
printf("Failed to read dependencies (count: %zu)\n", num_dependencies);
|
||||
|
|
@ -307,7 +288,120 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
|
||||
std::string_view import_name{ string_data + name_start, string_data + name_start + name_size };
|
||||
|
||||
mod_context.add_import_symbol(std::string{import_name}, dependency_index, import_index);
|
||||
mod_context.add_import_symbol(std::string{import_name}, dependency_index);
|
||||
}
|
||||
|
||||
const DependencyEventV1* dependency_events = reinterpret_data<DependencyEventV1>(data, offset, num_dependency_events);
|
||||
if (dependency_events == nullptr) {
|
||||
printf("Failed to read dependency events (count: %zu)\n", num_dependency_events);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t dependency_event_index = 0; dependency_event_index < num_dependency_events; dependency_event_index++) {
|
||||
const DependencyEventV1& dependency_event_in = dependency_events[dependency_event_index];
|
||||
uint32_t name_start = dependency_event_in.name_start;
|
||||
uint32_t name_size = dependency_event_in.name_size;
|
||||
uint32_t dependency_index = dependency_event_in.dependency;
|
||||
|
||||
if (name_start + name_size > string_data_size) {
|
||||
printf("Dependency event %zu has a name start of %u and size of %u, which extend beyond the string data's total size of %zu\n",
|
||||
dependency_event_index, name_start, name_size, string_data_size);
|
||||
}
|
||||
|
||||
std::string_view dependency_event_name{ string_data + name_start, string_data + name_start + name_size };
|
||||
|
||||
size_t dummy_dependency_event_index;
|
||||
mod_context.add_dependency_event(dependency_index, std::string{dependency_event_name}, dummy_dependency_event_index);
|
||||
}
|
||||
|
||||
const ReplacementV1* replacements = reinterpret_data<ReplacementV1>(data, offset, num_replacements);
|
||||
if (replacements == nullptr) {
|
||||
printf("Failed to read replacements (count: %zu)\n", num_replacements);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t replacement_index = 0; replacement_index < num_replacements; replacement_index++) {
|
||||
N64Recomp::FunctionReplacement& cur_replacement = mod_context.replacements[replacement_index];
|
||||
|
||||
cur_replacement.func_index = replacements[replacement_index].func_index;
|
||||
cur_replacement.original_section_vrom = replacements[replacement_index].original_section_vrom;
|
||||
cur_replacement.original_vram = replacements[replacement_index].original_vram;
|
||||
cur_replacement.flags = static_cast<N64Recomp::ReplacementFlags>(replacements[replacement_index].flags);
|
||||
}
|
||||
|
||||
const ExportV1* exports = reinterpret_data<ExportV1>(data, offset, num_exports);
|
||||
if (exports == nullptr) {
|
||||
printf("Failed to read exports (count: %zu)\n", num_exports);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t export_index = 0; export_index < num_exports; export_index++) {
|
||||
const ExportV1& export_in = exports[export_index];
|
||||
uint32_t func_index = export_in.func_index;
|
||||
uint32_t name_start = export_in.name_start;
|
||||
uint32_t name_size = export_in.name_size;
|
||||
|
||||
if (func_index >= mod_context.functions.size()) {
|
||||
printf("Export %zu has a function index of %u, but the symbol file only has %zu functions\n",
|
||||
export_index, func_index, mod_context.functions.size());
|
||||
}
|
||||
|
||||
if (name_start + name_size > string_data_size) {
|
||||
printf("Export %zu has a name start of %u and size of %u, which extend beyond the string data's total size of %zu\n",
|
||||
export_index, name_start, name_size, string_data_size);
|
||||
}
|
||||
|
||||
// Add the function to the exported function list.
|
||||
mod_context.exported_funcs[export_index] = func_index;
|
||||
// Populate the exported function's name from the string data.
|
||||
mod_context.functions[func_index].name = std::string_view(string_data + name_start, string_data + name_start + name_size);
|
||||
}
|
||||
|
||||
const CallbackV1* callbacks = reinterpret_data<CallbackV1>(data, offset, num_callbacks);
|
||||
if (callbacks == nullptr) {
|
||||
printf("Failed to read callbacks (count: %zu)\n", num_callbacks);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t callback_index = 0; callback_index < num_callbacks; callback_index++) {
|
||||
const CallbackV1& callback_in = callbacks[callback_index];
|
||||
uint32_t dependency_event_index = callback_in.dependency_event_index;
|
||||
uint32_t function_index = callback_in.function_index;
|
||||
|
||||
if (dependency_event_index >= num_dependency_events) {
|
||||
printf("Callback %zu is connected to dependency event %u, but only %zu dependency events were specified\n",
|
||||
callback_index, dependency_event_index, num_dependency_events);
|
||||
}
|
||||
|
||||
if (function_index >= mod_context.functions.size()) {
|
||||
printf("Callback %zu uses function %u, but only %zu functions were specified\n",
|
||||
callback_index, function_index, mod_context.functions.size());
|
||||
}
|
||||
|
||||
if (!mod_context.add_callback_by_dependency_event(dependency_event_index, function_index)) {
|
||||
printf("Failed to add callback %zu\n", callback_index);
|
||||
}
|
||||
}
|
||||
|
||||
const EventV1* events = reinterpret_data<EventV1>(data, offset, num_provided_events);
|
||||
if (events == nullptr) {
|
||||
printf("Failed to read events (count: %zu)\n", num_provided_events);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t event_index = 0; event_index < num_provided_events; event_index++) {
|
||||
const EventV1& event_in = events[event_index];
|
||||
uint32_t name_start = event_in.name_start;
|
||||
uint32_t name_size = event_in.name_size;
|
||||
|
||||
if (name_start + name_size > string_data_size) {
|
||||
printf("Event %zu has a name start of %u and size of %u, which extend beyond the string data's total size of %zu\n",
|
||||
event_index, name_start, name_size, string_data_size);
|
||||
}
|
||||
|
||||
std::string_view import_name{ string_data + name_start, string_data + name_start + name_size };
|
||||
|
||||
mod_context.add_event_symbol(std::string{import_name});
|
||||
}
|
||||
|
||||
return offset == data.size();
|
||||
|
|
@ -382,16 +476,24 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
|
||||
vec_put(ret, &header);
|
||||
|
||||
size_t num_exported_funcs = context.exported_funcs.size();
|
||||
size_t num_dependencies = context.dependencies.size();
|
||||
size_t num_imported_funcs = context.import_symbols.size();
|
||||
size_t num_dependency_events = context.dependency_events.size();
|
||||
|
||||
size_t num_exported_funcs = context.exported_funcs.size();
|
||||
size_t num_events = context.event_symbols.size();
|
||||
size_t num_callbacks = context.callbacks.size();
|
||||
size_t num_provided_events = context.event_symbols.size();
|
||||
|
||||
FileSubHeaderV1 sub_header {
|
||||
.num_sections = static_cast<uint32_t>(context.sections.size()),
|
||||
.num_replacements = static_cast<uint32_t>(context.replacements.size()),
|
||||
.num_exports = static_cast<uint32_t>(num_exported_funcs),
|
||||
.num_dependencies = static_cast<uint32_t>(num_dependencies),
|
||||
.num_imports = static_cast<uint32_t>(num_imported_funcs),
|
||||
.num_dependency_events = static_cast<uint32_t>(num_dependency_events),
|
||||
.num_replacements = static_cast<uint32_t>(context.replacements.size()),
|
||||
.num_exports = static_cast<uint32_t>(num_exported_funcs),
|
||||
.num_callbacks = static_cast<uint32_t>(num_callbacks),
|
||||
.num_provided_events = static_cast<uint32_t>(num_provided_events),
|
||||
.string_data_size = 0,
|
||||
};
|
||||
|
||||
|
|
@ -402,17 +504,6 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
// 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 = context.exported_funcs[export_index];
|
||||
const Function& exported_func = 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 dependency's name in the string data.
|
||||
std::vector<uint32_t> dependency_name_positions{};
|
||||
dependency_name_positions.resize(num_dependencies);
|
||||
|
|
@ -426,7 +517,6 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
// Track the start of every imported function's name in 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{};
|
||||
for (size_t import_index = 0; import_index < num_imported_funcs; import_index++) {
|
||||
const ImportSymbol& imported_func = context.import_symbols[import_index];
|
||||
|
||||
|
|
@ -435,6 +525,38 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
vec_put(ret, imported_func.base.name);
|
||||
}
|
||||
|
||||
// Track the start of every dependency event's name in the string data.
|
||||
std::vector<uint32_t> dependency_event_name_positions{};
|
||||
dependency_event_name_positions.resize(num_dependency_events);
|
||||
for (size_t dependency_event_index = 0; dependency_event_index < num_dependency_events; dependency_event_index++) {
|
||||
const DependencyEvent& dependency_event = context.dependency_events[dependency_event_index];
|
||||
|
||||
dependency_event_name_positions[dependency_event_index] = static_cast<uint32_t>(ret.size() - strings_start);
|
||||
vec_put(ret, dependency_event.event_name);
|
||||
}
|
||||
|
||||
// Track the start of every exported function's name in the string data.
|
||||
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 = context.exported_funcs[export_index];
|
||||
const Function& exported_func = 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 provided event's name in the string data.
|
||||
std::vector<uint32_t> event_name_positions{};
|
||||
event_name_positions.resize(num_events);
|
||||
for (size_t event_index = 0; event_index < num_events; event_index++) {
|
||||
const EventSymbol& event_symbol = context.event_symbols[event_index];
|
||||
|
||||
// Write this event's name into the strings data.
|
||||
event_name_positions[event_index] = static_cast<uint32_t>(ret.size() - strings_start);
|
||||
vec_put(ret, event_symbol.base.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);
|
||||
|
|
@ -492,6 +614,48 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
}
|
||||
}
|
||||
|
||||
// Write the dependencies.
|
||||
for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) {
|
||||
const Dependency& dependency = context.dependencies[dependency_index];
|
||||
|
||||
DependencyV1 dependency_out {
|
||||
.major_version = dependency.major_version,
|
||||
.minor_version = dependency.minor_version,
|
||||
.patch_version = dependency.patch_version,
|
||||
.mod_id_start = dependency_name_positions[dependency_index],
|
||||
.mod_id_size = static_cast<uint32_t>(dependency.mod_id.size())
|
||||
};
|
||||
|
||||
vec_put(ret, &dependency_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.
|
||||
const ImportSymbol& imported_func = context.import_symbols[import_index];
|
||||
|
||||
ImportV1 import_out {
|
||||
.name_start = imported_func_name_positions[import_index],
|
||||
.name_size = static_cast<uint32_t>(imported_func.base.name.size()),
|
||||
.dependency = static_cast<uint32_t>(imported_func.dependency_index)
|
||||
};
|
||||
|
||||
vec_put(ret, &import_out);
|
||||
}
|
||||
|
||||
// Write the dependency events.
|
||||
for (size_t dependency_event_index = 0; dependency_event_index < num_dependency_events; dependency_event_index++) {
|
||||
const DependencyEvent& dependency_event = context.dependency_events[dependency_event_index];
|
||||
|
||||
DependencyEventV1 dependency_event_out {
|
||||
.name_start = dependency_event_name_positions[dependency_event_index],
|
||||
.name_size = static_cast<uint32_t>(dependency_event.event_name.size()),
|
||||
.dependency = static_cast<uint32_t>(dependency_event.dependency_index)
|
||||
};
|
||||
|
||||
vec_put(ret, &dependency_event_out);
|
||||
}
|
||||
|
||||
// Write the function replacements.
|
||||
for (const FunctionReplacement& cur_replacement : context.replacements) {
|
||||
uint32_t flags = 0;
|
||||
|
|
@ -523,33 +687,28 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
vec_put(ret, &export_out);
|
||||
}
|
||||
|
||||
// Write the dependencies.
|
||||
for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) {
|
||||
const Dependency& dependency = context.dependencies[dependency_index];
|
||||
// Write the callbacks.
|
||||
for (size_t callback_index = 0; callback_index < num_callbacks; callback_index++) {
|
||||
const Callback& callback = context.callbacks[callback_index];
|
||||
|
||||
DependencyV1 dependency_out {
|
||||
.major_version = dependency.major_version,
|
||||
.minor_version = dependency.minor_version,
|
||||
.patch_version = dependency.patch_version,
|
||||
.mod_id_start = dependency_name_positions[dependency_index],
|
||||
.mod_id_size = static_cast<uint32_t>(dependency.mod_id.size())
|
||||
CallbackV1 callback_out {
|
||||
.dependency_event_index = static_cast<uint32_t>(callback.dependency_event_index),
|
||||
.function_index = static_cast<uint32_t>(callback.function_index)
|
||||
};
|
||||
|
||||
vec_put(ret, &dependency_out);
|
||||
vec_put(ret, &callback_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.
|
||||
const ImportSymbol& imported_func = context.import_symbols[import_index];
|
||||
// Write the provided events.
|
||||
for (size_t event_index = 0; event_index < num_events; event_index++) {
|
||||
const EventSymbol& event_symbol = context.event_symbols[event_index];
|
||||
|
||||
ImportV1 import_out {
|
||||
.name_start = imported_func_name_positions[import_index],
|
||||
.name_size = static_cast<uint32_t>(imported_func.base.name.size()),
|
||||
.dependency = static_cast<uint32_t>(imported_func.dependency_index)
|
||||
EventV1 event_out {
|
||||
.name_start = event_name_positions[event_index],
|
||||
.name_size = static_cast<uint32_t>(event_symbol.base.name.size())
|
||||
};
|
||||
|
||||
vec_put(ret, &import_out);
|
||||
vec_put(ret, &event_out);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
|
|||
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
|
||||
}
|
||||
|
||||
// The reloc has been processed, so delete it to none to prevent it getting processed a second time during instruction code generation.
|
||||
// The reloc has been processed, so set it to none to prevent it getting processed a second time during instruction code generation.
|
||||
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
|
||||
reloc_reference_symbol = (size_t)-1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue