Finished moving N64Recomp from toml11 to tomlplusplus

This commit is contained in:
Mr-Wiseguy 2024-05-15 02:38:41 -04:00
parent 6c0b3015b1
commit a67337c7c3
7 changed files with 263 additions and 202 deletions

3
.gitmodules vendored
View file

@ -10,3 +10,6 @@
[submodule "lib/toml11"] [submodule "lib/toml11"]
path = lib/toml11 path = lib/toml11
url = https://github.com/ToruNiina/toml11 url = https://github.com/ToruNiina/toml11
[submodule "lib/tomlplusplus"]
path = lib/tomlplusplus
url = https://github.com/marzer/tomlplusplus

View file

@ -5,9 +5,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
# set(CMAKE_CXX_VISIBILITY_PRESET hidden) # set(CMAKE_CXX_VISIBILITY_PRESET hidden)
# fmtlib
add_subdirectory(lib/fmt)
# Rabbitizer # Rabbitizer
project(rabbitizer) project(rabbitizer)
add_library(rabbitizer STATIC) add_library(rabbitizer STATIC)
@ -51,19 +48,18 @@ target_sources(rabbitizer PRIVATE
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/src/instructions/RabbitizerRegister.c" "${CMAKE_SOURCE_DIR}/lib/rabbitizer/src/instructions/RabbitizerRegister.c"
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/src/instructions/RabbitizerRegisterDescriptor.c") "${CMAKE_SOURCE_DIR}/lib/rabbitizer/src/instructions/RabbitizerRegisterDescriptor.c")
target_include_directories(rabbitizer PRIVATE target_include_directories(rabbitizer PUBLIC
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/include" "${CMAKE_SOURCE_DIR}/lib/rabbitizer/include"
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/cplusplus/include" "${CMAKE_SOURCE_DIR}/lib/rabbitizer/cplusplus/include")
target_include_directories(rabbitizer PRIVATE
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/tables") "${CMAKE_SOURCE_DIR}/lib/rabbitizer/tables")
# toml++ # fmtlib
include(FetchContent) add_subdirectory(lib/fmt)
FetchContent_Declare(
tomlplusplus # tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git add_subdirectory(lib/tomlplusplus)
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(tomlplusplus)
# N64 recompiler # N64 recompiler
project(N64Recomp) project(N64Recomp)
@ -76,10 +72,7 @@ target_sources(N64Recomp PRIVATE
${CMAKE_SOURCE_DIR}/src/recompilation.cpp) ${CMAKE_SOURCE_DIR}/src/recompilation.cpp)
target_include_directories(N64Recomp PRIVATE target_include_directories(N64Recomp PRIVATE
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/include"
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/cplusplus/include"
"${CMAKE_SOURCE_DIR}/lib/ELFIO" "${CMAKE_SOURCE_DIR}/lib/ELFIO"
"${CMAKE_SOURCE_DIR}/lib/fmt/include"
"${CMAKE_SOURCE_DIR}/include") "${CMAKE_SOURCE_DIR}/include")
target_link_libraries(N64Recomp fmt rabbitizer tomlplusplus::tomlplusplus) target_link_libraries(N64Recomp fmt rabbitizer tomlplusplus::tomlplusplus)
@ -89,12 +82,10 @@ project(RSPRecomp)
add_executable(RSPRecomp) add_executable(RSPRecomp)
target_include_directories(RSPRecomp PRIVATE target_include_directories(RSPRecomp PRIVATE
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/include" "${CMAKE_SOURCE_DIR}/include"
"${CMAKE_SOURCE_DIR}/lib/rabbitizer/cplusplus/include" "${CMAKE_SOURCE_DIR}/lib/toml11")
"${CMAKE_SOURCE_DIR}/lib/fmt/include"
"${CMAKE_SOURCE_DIR}/include")
target_link_libraries(RSPRecomp fmt rabbitizer tomlplusplus::tomlplusplus) target_link_libraries(RSPRecomp fmt rabbitizer)
target_sources(RSPRecomp PRIVATE target_sources(RSPRecomp PRIVATE
${CMAKE_SOURCE_DIR}/RSPRecomp/src/rsp_recomp.cpp) ${CMAKE_SOURCE_DIR}/RSPRecomp/src/rsp_recomp.cpp)

View file

@ -113,6 +113,7 @@ namespace RecompPort {
Function(uint32_t vram, uint32_t rom, std::vector<uint32_t> words, std::string name, ELFIO::Elf_Half section_index, bool ignored = false, bool reimplemented = false, bool stubbed = false) Function(uint32_t vram, uint32_t rom, std::vector<uint32_t> words, std::string name, ELFIO::Elf_Half section_index, bool ignored = false, bool reimplemented = false, bool stubbed = false)
: vram(vram), rom(rom), words(std::move(words)), name(std::move(name)), section_index(section_index), ignored(ignored), reimplemented(reimplemented), stubbed(stubbed) {} : vram(vram), rom(rom), words(std::move(words)), name(std::move(name)), section_index(section_index), ignored(ignored), reimplemented(reimplemented), stubbed(stubbed) {}
Function() = default;
}; };
enum class RelocType : uint8_t { enum class RelocType : uint8_t {

@ -1 +1 @@
Subproject commit d2e89c8b080394e996d449371267365c223ca76b Subproject commit 8e728044f673774160f43b44a07c6b185352310f

1
lib/tomlplusplus Submodule

@ -0,0 +1 @@
Subproject commit 1f7884e59165e517462f922e7b6de131bd9844f3

View file

@ -22,6 +22,9 @@ std::vector<RecompPort::ManualFunction> get_manual_funcs(const toml::array* manu
fmt::print(stderr, "Missing required value in manual_funcs array\n"); fmt::print(stderr, "Missing required value in manual_funcs array\n");
} }
} }
else {
fmt::print(stderr, "Missing required value in manual_funcs array\n");
}
}); });
return ret; return ret;
@ -37,13 +40,16 @@ std::vector<std::string> get_stubbed_funcs(const toml::table* patches_data) {
const toml::array* stubs_array = stubs_data.as_array(); const toml::array* stubs_array = stubs_data.as_array();
// Make room for all the stubs in the array. // Make room for all the stubs in the array.
stubbed_funcs.resize(stubs_array->size()); stubbed_funcs.reserve(stubs_array->size());
// Gather the stubs and place them into the array. // Gather the stubs and place them into the array.
stubs_array->for_each([&stubbed_funcs](auto&& el) { stubs_array->for_each([&stubbed_funcs](auto&& el) {
if constexpr (toml::is_string<decltype(el)>) { if constexpr (toml::is_string<decltype(el)>) {
stubbed_funcs.push_back(*el); stubbed_funcs.push_back(*el);
} }
else {
throw toml::parse_error{ "Invalid stubbed function", el.source()};
}
}); });
} }
@ -60,7 +66,7 @@ std::vector<std::string> get_ignored_funcs(const toml::table* patches_data) {
const toml::array* ignored_funcs_array = ignored_funcs_data.as_array(); const toml::array* ignored_funcs_array = ignored_funcs_data.as_array();
// Make room for all the ignored funcs in the array. // Make room for all the ignored funcs in the array.
ignored_funcs.resize(ignored_funcs_array->size()); ignored_funcs.reserve(ignored_funcs_array->size());
// Gather the stubs and place them into the array. // Gather the stubs and place them into the array.
ignored_funcs_array->for_each([&ignored_funcs](auto&& el) { ignored_funcs_array->for_each([&ignored_funcs](auto&& el) {
@ -89,10 +95,13 @@ std::vector<RecompPort::FunctionArgType> parse_args(const toml::array* args_in)
auto type_find = arg_type_map.find(arg_str); auto type_find = arg_type_map.find(arg_str);
if (type_find == arg_type_map.end()) { if (type_find == arg_type_map.end()) {
// It's not, so throw an error (and make it look like a normal toml one). // It's not, so throw an error (and make it look like a normal toml one).
// TODO: throw error throw toml::parse_error(("Invalid argument type: " + arg_str).c_str(), el.source());
} }
ret.push_back(type_find->second); ret.push_back(type_find->second);
} }
else {
throw toml::parse_error("Invalid function argument entry", el.source());
}
}); });
return ret; return ret;
@ -120,9 +129,12 @@ RecompPort::DeclaredFunctionMap get_declared_funcs(const toml::table* patches_da
const toml::array* args_array = args_in.as_array(); const toml::array* args_array = args_in.as_array();
declared_funcs.emplace(func_name.value(), parse_args(args_array)); declared_funcs.emplace(func_name.value(), parse_args(args_array));
} else { } else {
fmt::print(stderr, "Missing required value in func array\n"); throw toml::parse_error("Missing required value in func array", el.source());
} }
} }
else {
throw toml::parse_error("Invalid declared function entry", el.source());
}
}); });
} }
@ -140,22 +152,28 @@ std::vector<RecompPort::FunctionSize> get_func_sizes(const toml::table* patches_
// Copy all the sizes into the output vector. // Copy all the sizes into the output vector.
sizes_array->for_each([&func_sizes](auto&& el) { sizes_array->for_each([&func_sizes](auto&& el) {
if constexpr (toml::is_table<decltype(el)>) { if constexpr (toml::is_table<decltype(el)>) {
const toml::table* cur_size = el.as_table(); const toml::table& cur_size = *el.as_table();
// Get the function name and size. // Get the function name and size.
std::optional<std::string> func_name = (*cur_size)["name"].value<std::string>(); std::optional<std::string> func_name = cur_size["name"].value<std::string>();
std::optional<uint32_t> func_size = (*cur_size)["size"].value<uint32_t>(); std::optional<uint32_t> func_size = cur_size["size"].value<uint32_t>();
if (func_name.has_value() && func_size.has_value()) { if (func_name.has_value() && func_size.has_value()) {
// Make sure the size is divisible by 4 // Make sure the size is divisible by 4
if (func_size.value() & (4 - 1)) { if (func_size.value() & (4 - 1)) {
// It's not, so throw an error (and make it look like a normal toml one). // It's not, so throw an error (and make it look like a normal toml one).
// TODO: throw error throw toml::parse_error("Function size is not divisible by 4", el.source());
} }
} }
else {
throw toml::parse_error("Manually size function is missing required value(s)", el.source());
}
func_sizes.emplace_back(func_name.value(), func_size.value()); func_sizes.emplace_back(func_name.value(), func_size.value());
} }
else {
throw toml::parse_error("Invalid manually sized function entry", el.source());
}
}); });
} }
@ -170,37 +188,37 @@ std::vector<RecompPort::InstructionPatch> get_instruction_patches(const toml::ta
if (insn_patch_data.is_array()) { if (insn_patch_data.is_array()) {
const toml::array* insn_patch_array = insn_patch_data.as_array(); const toml::array* insn_patch_array = insn_patch_data.as_array();
ret.resize(insn_patch_array->size()); ret.reserve(insn_patch_array->size());
// Copy all the patches into the output vector. // Copy all the patches into the output vector.
insn_patch_array->for_each([&ret](auto&& el) { insn_patch_array->for_each([&ret](auto&& el) {
if constexpr (toml::is_table<decltype(el)>) { if constexpr (toml::is_table<decltype(el)>) {
const toml::table* cur_patch = el.as_table(); const toml::table& cur_patch = *el.as_table();
// Get the vram and make sure it's 4-byte aligned. // Get the vram and make sure it's 4-byte aligned.
std::optional<int32_t> vram = (*cur_patch)["vram"].value<int32_t>(); auto testval = cur_patch["vram"];
std::optional<uint32_t> vram = cur_patch["vram"].value<uint32_t>();
std::optional<std::string> func_name = cur_patch["func"].value<std::string>();
std::optional<uint32_t> value = cur_patch["value"].value<uint32_t>();
if (!vram.has_value() || vram.value() & 0b11) { if (!vram.has_value() || !func_name.has_value() || !value.has_value()) {
// Not properly aligned, so throw an error (and make it look like a normal toml one). throw toml::parse_error("Instruction patch is missing required value(s)", el.source());
// TODO: throw error
return;
} }
std::optional<std::string> func_name = (*cur_patch)["func"].value<std::string>(); if (vram.value() & 0b11) {
std::optional<uint32_t> value = (*cur_patch)["value"].value<uint32_t>(); // Not properly aligned, so throw an error (and make it look like a normal toml one).
throw toml::parse_error("Instruction patch is not word-aligned", el.source());
if (!func_name.has_value() || !value.has_value()) {
// Missing required value in instruction patch array
// TODO: throw error
return;
} }
ret.push_back(RecompPort::InstructionPatch{ ret.push_back(RecompPort::InstructionPatch{
.func_name = func_name.value(), .func_name = func_name.value(),
.vram = vram.value(), .vram = (int32_t)vram.value(),
.value = value.value(), .value = value.value(),
}); });
} }
else {
throw toml::parse_error("Invalid instruction patch entry", el.source());
}
}); });
} }
@ -218,18 +236,27 @@ RecompPort::Config::Config(const char* path) {
// Start this config out as bad so that it has to finish parsing without errors to be good. // Start this config out as bad so that it has to finish parsing without errors to be good.
entrypoint = 0; entrypoint = 0;
bad = true; bad = true;
toml::table config_data{};
try { try {
const toml::table config_data = toml::parse_file(path); config_data = toml::parse_file(path);
std::filesystem::path basedir = std::filesystem::path{ path }.parent_path(); std::filesystem::path basedir = std::filesystem::path{ path }.parent_path();
// Input section (required) // Input section (required)
const auto& input_data = config_data["input"]; const auto input_data = config_data["input"];
const auto entrypoint_data = input_data["entrypoint"];
if (config_data.contains("entrypoint")) { if (entrypoint_data) {
entrypoint = config_data["entrypoint"].value<int32_t>().value(); const auto entrypoint_value = entrypoint_data.value<uint32_t>();
if (entrypoint_value.has_value()) {
entrypoint = (int32_t)entrypoint_value.value();
has_entrypoint = true; has_entrypoint = true;
} else { }
else {
throw toml::parse_error { "Invalid entrypoint", entrypoint_data.node()->source() };
}
}
else {
has_entrypoint = false; has_entrypoint = false;
} }
@ -251,7 +278,8 @@ RecompPort::Config::Config(const char* path) {
std::optional<std::string> output_func_path_opt = input_data["output_func_path"].value<std::string>(); std::optional<std::string> output_func_path_opt = input_data["output_func_path"].value<std::string>();
if (output_func_path_opt.has_value()) { if (output_func_path_opt.has_value()) {
output_func_path = concat_if_not_empty(basedir, output_func_path_opt.value()); output_func_path = concat_if_not_empty(basedir, output_func_path_opt.value());
} else { }
else {
fmt::print(stderr, "Missing value in config file:\n{}\n", "output_func_path"); fmt::print(stderr, "Missing value in config file:\n{}\n", "output_func_path");
return; return;
} }
@ -259,35 +287,40 @@ RecompPort::Config::Config(const char* path) {
std::optional<std::string> relocatable_sections_path_opt = input_data["relocatable_sections_path"].value<std::string>(); std::optional<std::string> relocatable_sections_path_opt = input_data["relocatable_sections_path"].value<std::string>();
if (relocatable_sections_path_opt.has_value()) { if (relocatable_sections_path_opt.has_value()) {
relocatable_sections_path = concat_if_not_empty(basedir, relocatable_sections_path_opt.value()); relocatable_sections_path = concat_if_not_empty(basedir, relocatable_sections_path_opt.value());
} else { }
else {
relocatable_sections_path = ""; relocatable_sections_path = "";
} }
std::optional<bool> uses_mips3_float_mode_opt = input_data["uses_mips3_float_mode"].value<bool>(); std::optional<bool> uses_mips3_float_mode_opt = input_data["uses_mips3_float_mode"].value<bool>();
if (uses_mips3_float_mode_opt.has_value()) { if (uses_mips3_float_mode_opt.has_value()) {
uses_mips3_float_mode = uses_mips3_float_mode_opt.value(); uses_mips3_float_mode = uses_mips3_float_mode_opt.value();
} else { }
else {
uses_mips3_float_mode = false; uses_mips3_float_mode = false;
} }
std::optional<std::string> bss_section_suffix_opt = input_data["bss_section_suffix"].value<std::string>(); std::optional<std::string> bss_section_suffix_opt = input_data["bss_section_suffix"].value<std::string>();
if (bss_section_suffix_opt.has_value()) { if (bss_section_suffix_opt.has_value()) {
bss_section_suffix = bss_section_suffix_opt.value(); bss_section_suffix = bss_section_suffix_opt.value();
} else { }
else {
bss_section_suffix = ".bss"; bss_section_suffix = ".bss";
} }
std::optional<bool> single_file_output_opt = input_data["single_file_output"].value<bool>(); std::optional<bool> single_file_output_opt = input_data["single_file_output"].value<bool>();
if (single_file_output_opt.has_value()) { if (single_file_output_opt.has_value()) {
single_file_output = single_file_output_opt.value(); single_file_output = single_file_output_opt.value();
} else { }
else {
single_file_output = false; single_file_output = false;
} }
std::optional<bool> use_absolute_symbols_opt = input_data["use_absolute_symbols"].value<bool>(); std::optional<bool> use_absolute_symbols_opt = input_data["use_absolute_symbols"].value<bool>();
if (use_absolute_symbols_opt.has_value()) { if (use_absolute_symbols_opt.has_value()) {
use_absolute_symbols = use_absolute_symbols_opt.value(); use_absolute_symbols = use_absolute_symbols_opt.value();
} else { }
else {
use_absolute_symbols = false; use_absolute_symbols = false;
} }
@ -300,7 +333,7 @@ RecompPort::Config::Config(const char* path) {
// Patches section (optional) // Patches section (optional)
toml::node_view patches_data = config_data["patches"]; toml::node_view patches_data = config_data["patches"];
if (manual_functions_data.is_table()) { if (patches_data.is_table()) {
const toml::table* table = patches_data.as_table(); const toml::table* table = patches_data.as_table();
// Stubs array (optional) // Stubs array (optional)
@ -320,7 +353,7 @@ RecompPort::Config::Config(const char* path) {
} }
} }
catch (const toml::parse_error& err) { catch (const toml::parse_error& err) {
fmt::print(stderr, "Syntax parsing file {} ({}), full error:\n{}\n", *err.source().path, err.source().begin, err.description()); std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
return; return;
} }
@ -362,75 +395,65 @@ bool RecompPort::Context::from_symbol_file(const std::filesystem::path& symbol_f
ret.section_functions.resize(config_sections->size()); ret.section_functions.resize(config_sections->size());
config_sections->for_each([&ret, &rom](auto&& el) { config_sections->for_each([&ret, &rom](auto&& el) {
size_t section_index = ret.sections.size();
if constexpr (toml::is_table<decltype(el)>) { if constexpr (toml::is_table<decltype(el)>) {
std::optional<uint32_t> rom_addr = el["rom"].template value<uint32_t>(); std::optional<uint32_t> rom_addr = el["rom"].template value<uint32_t>();
std::optional<uint32_t> vram_addr = el["vram"].template value<uint32_t>(); std::optional<uint32_t> vram_addr = el["vram"].template value<uint32_t>();
std::optional<uint32_t> size = el["size"].template value<uint32_t>(); std::optional<uint32_t> size = el["size"].template value<uint32_t>();
std::optional<std::string> name = el["name"].template value<std::string>(); std::optional<std::string> name = el["name"].template value<std::string>();
if (rom_addr.has_value() && vram_addr.has_value() && size.has_value() && name.has_value()) { if (!rom_addr.has_value() || !vram_addr.has_value() || !size.has_value() || !name.has_value()) {
throw toml::parse_error("Section entry missing required field(s)", el.source());
}
size_t section_index = ret.sections.size();
Section& section = ret.sections.emplace_back(Section{}); Section& section = ret.sections.emplace_back(Section{});
section.rom_addr = rom_addr.value(); section.rom_addr = rom_addr.value();
section.ram_addr = vram_addr.value(); section.ram_addr = vram_addr.value();
section.size = size.value(); section.size = size.value();
section.name = name.value(); section.name = name.value();
section.executable = true; section.executable = true;
} else {
// TODO: throw error, we're expecting a table
}
} else {
// TODO: throw error, we're expecting a table
}
});
for (const toml::value& section_value : config_sections) {
size_t section_index = ret.sections.size();
Section& section = ret.sections.emplace_back(Section{});
section.rom_addr = toml::find<uint32_t>(section_value, "rom");
section.ram_addr = toml::find<uint32_t>(section_value, "vram");
section.size = toml::find<uint32_t>(section_value, "size");
section.name = toml::find<toml::string>(section_value, "name");
section.executable = true;
const toml::array& functions = toml::find<toml::array>(section_value, "functions");
// Read functions for the section. // Read functions for the section.
for (const toml::value& function_value : functions) { const toml::node_view cur_functions_value = el["functions"];
if (!cur_functions_value.is_array()) {
throw toml::parse_error("Invalid functions array", cur_functions_value.node()->source());
}
const toml::array* cur_functions = cur_functions_value.as_array();
cur_functions->for_each([&ret, &rom, &section, section_index](auto&& func_el) {
size_t function_index = ret.functions.size(); size_t function_index = ret.functions.size();
if constexpr (toml::is_table<decltype(func_el)>) {
std::optional<std::string> name = func_el["name"].template value<std::string>();
std::optional<uint32_t> vram_addr = func_el["vram"].template value<uint32_t>();
std::optional<uint32_t> func_size_ = func_el["size"].template value<uint32_t>();
if (!name.has_value() || !vram_addr.has_value() || !func_size_.has_value()) {
throw toml::parse_error("Function symbol entry is missing required field(s)", func_el.source());
}
uint32_t func_size = func_size_.value();
Function cur_func{}; Function cur_func{};
cur_func.name = toml::find<std::string>(function_value, "name"); cur_func.name = name.value();
cur_func.vram = toml::find<uint32_t>(function_value, "vram"); cur_func.vram = vram_addr.value();
cur_func.rom = cur_func.vram - section.ram_addr + section.rom_addr; cur_func.rom = cur_func.vram - section.ram_addr + section.rom_addr;
cur_func.section_index = section_index; cur_func.section_index = section_index;
uint32_t func_size = toml::find<uint32_t>(function_value, "size");
if (cur_func.vram & 0b11) { if (cur_func.vram & 0b11) {
// Function isn't word aligned in vram. // Function isn't word aligned in vram.
throw value_error(toml::detail::format_underline( throw toml::parse_error("Function's vram address isn't word aligned", func_el.source());
std::string{ std::source_location::current().function_name() } + ": function's vram address isn't word aligned!", {
{function_value.location(), ""}
}), function_value.location());
} }
if (cur_func.rom & 0b11) { if (cur_func.rom & 0b11) {
// Function isn't word aligned in rom. // Function isn't word aligned in rom.
throw value_error(toml::detail::format_underline( throw toml::parse_error("Function's rom address isn't word aligned", func_el.source());
std::string{ std::source_location::current().function_name() } + ": function's rom address isn't word aligned!", {
{function_value.location(), ""}
}), function_value.location());
} }
if (cur_func.rom + func_size > rom.size()) { if (cur_func.rom + func_size > rom.size()) {
// Function is out of bounds of the provided rom. // Function is out of bounds of the provided rom.
throw value_error(toml::detail::format_underline( throw toml::parse_error("Functio is out of bounds of the provided rom", func_el.source());
std::string{ std::source_location::current().function_name() } + ": function is out of bounds of the provided rom!", {
{function_value.location(), ""}
}), function_value.location());
} }
// Get the function's words from the rom. // Get the function's words from the rom.
@ -446,48 +469,62 @@ bool RecompPort::Context::from_symbol_file(const std::filesystem::path& symbol_f
ret.functions.emplace_back(std::move(cur_func)); ret.functions.emplace_back(std::move(cur_func));
} }
else {
throw toml::parse_error("Invalid function symbol entry", func_el.source());
}
});
// Check if relocs exist for the section and read them if so. // Check if relocs exist for the section and read them if so.
const toml::value& relocs_value = toml::find_or<toml::value>(section_value, "relocs", toml::value{}); const toml::node_view relocs_value = el["relocs"];
if (relocs_value.type() == toml::value_t::array) { if (relocs_value.is_array()) {
// Mark the section as relocatable, since it has relocs. // Mark the section as relocatable, since it has relocs.
section.relocatable = true; section.relocatable = true;
// Read relocs for the section. // Read relocs for the section.
for (const toml::value& reloc_value : relocs_value.as_array()) { const toml::array* relocs_array = relocs_value.as_array();
relocs_array->for_each([&ret, &rom, &section, section_index](auto&& reloc_el) {
if constexpr (toml::is_table<decltype(reloc_el)>) {
size_t reloc_index = ret.functions.size(); size_t reloc_index = ret.functions.size();
std::optional<uint32_t> vram = reloc_el["vram"].template value<uint32_t>();
std::optional<uint32_t> target_vram = reloc_el["target_vram"].template value<uint32_t>();
std::optional<std::string> type_string = reloc_el["type"].template value<std::string>();
if (!vram.has_value() || !target_vram.has_value() || !type_string.has_value()) {
throw toml::parse_error("Reloc entry missing required field(s)", reloc_el.source());
}
RelocType reloc_type = reloc_type_from_name(type_string.value());
// TODO also accept MIPS32 for TLB relocations.
if (reloc_type != RelocType::R_MIPS_HI16 && reloc_type != RelocType::R_MIPS_LO16) {
throw toml::parse_error("Invalid reloc entry type", reloc_el.source());
}
Reloc cur_reloc{}; Reloc cur_reloc{};
cur_reloc.address = toml::find<uint32_t>(reloc_value, "vram"); cur_reloc.address = vram.value();
cur_reloc.target_address = toml::find<uint32_t>(reloc_value, "target_vram"); cur_reloc.target_address = target_vram.value();
cur_reloc.symbol_index = (uint32_t)-1; cur_reloc.symbol_index = (uint32_t)-1;
cur_reloc.target_section = section_index; cur_reloc.target_section = section_index;
const std::string& reloc_type = toml::find<std::string>(reloc_value, "type"); cur_reloc.type = reloc_type;
cur_reloc.type = reloc_type_from_name(reloc_type);
section.relocs.emplace_back(std::move(cur_reloc)); section.relocs.emplace_back(std::move(cur_reloc));
} }
else {
throw toml::parse_error("Invalid reloc entry", reloc_el.source());
}
});
} }
else { else {
section.relocatable = false; section.relocatable = false;
} }
} else {
throw toml::parse_error("Invalid section entry", el.source());
} }
});
} }
catch (const toml::syntax_error& err) { catch (const toml::parse_error& err) {
fmt::print(stderr, "Syntax error in config file on line {}, full error:\n{}\n", err.location().line(), err.what()); std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
return false;
}
catch (const toml::type_error& err) {
fmt::print(stderr, "Incorrect type in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
return false;
}
catch (const value_error& err) {
fmt::print(stderr, "Invalid value in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
return false;
}
catch (const std::out_of_range& err) {
fmt::print(stderr, "Missing value in config file, full error:\n{}\n", err.what());
return false; return false;
} }

View file

@ -1379,19 +1379,47 @@ int main(int argc, char** argv) {
exit_failure("Failed to load symbols file\n"); exit_failure("Failed to load symbols file\n");
} }
for (RecompPort::Function& func : context.functions) { auto rename_function = [&context](size_t func_index, const std::string& new_name) {
RecompPort::Function& func = context.functions[func_index];
context.functions_by_name.erase(func.name);
func.name = new_name;
context.functions_by_name[func.name] = func_index;
};
for (size_t func_index = 0; func_index < context.functions.size(); func_index++) {
RecompPort::Function& func = context.functions[func_index];
if (reimplemented_funcs.contains(func.name)) { if (reimplemented_funcs.contains(func.name)) {
rename_function(func_index, func.name + "_recomp");
func.reimplemented = true; func.reimplemented = true;
func.name = func.name + "_recomp";
func.ignored = true; func.ignored = true;
} else if (ignored_funcs.contains(func.name)) { } else if (ignored_funcs.contains(func.name)) {
func.name = func.name + "_recomp"; rename_function(func_index, func.name + "_recomp");
func.ignored = true; func.ignored = true;
} else if (renamed_funcs.contains(func.name)) { } else if (renamed_funcs.contains(func.name)) {
func.name = func.name + "_recomp"; rename_function(func_index, func.name + "_recomp");
func.ignored = false; func.ignored = false;
} }
} }
if (config.has_entrypoint) {
bool found_entrypoint = false;
for (uint32_t func_index : context.functions_by_vram[config.entrypoint]) {
auto& func = context.functions[func_index];
if (func.rom == 0x1000) {
rename_function(func_index, "recomp_entrypoint");
found_entrypoint = true;
break;
}
}
if (!found_entrypoint) {
exit_failure("No entrypoint provided in symbol file\n");
}
}
} }
else { else {
exit_failure("Config file must provide either an elf or a symbols file\n"); exit_failure("Config file must provide either an elf or a symbols file\n");