mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-14 08:12:19 +00:00
Finished moving N64Recomp from toml11 to tomlplusplus
This commit is contained in:
parent
6c0b3015b1
commit
a67337c7c3
7 changed files with 263 additions and 202 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||||
|
|
|
@ -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,25 +72,20 @@ 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)
|
||||||
|
|
||||||
# RSP recompiler
|
# RSP recompiler
|
||||||
project(RSPRecomp)
|
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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
2
lib/fmt
2
lib/fmt
|
@ -1 +1 @@
|
||||||
Subproject commit d2e89c8b080394e996d449371267365c223ca76b
|
Subproject commit 8e728044f673774160f43b44a07c6b185352310f
|
1
lib/tomlplusplus
Submodule
1
lib/tomlplusplus
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 1f7884e59165e517462f922e7b6de131bd9844f3
|
387
src/config.cpp
387
src/config.cpp
|
@ -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,20 +236,29 @@ 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>();
|
||||||
has_entrypoint = true;
|
if (entrypoint_value.has_value()) {
|
||||||
} else {
|
entrypoint = (int32_t)entrypoint_value.value();
|
||||||
has_entrypoint = false;
|
has_entrypoint = true;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
throw toml::parse_error { "Invalid entrypoint", entrypoint_data.node()->source() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
has_entrypoint = false;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> elf_path_opt = input_data["elf_path"].value<std::string>();
|
std::optional<std::string> elf_path_opt = input_data["elf_path"].value<std::string>();
|
||||||
if (elf_path_opt.has_value()) {
|
if (elf_path_opt.has_value()) {
|
||||||
|
@ -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,70 +287,75 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual functions (optional)
|
// Manual functions (optional)
|
||||||
toml::node_view manual_functions_data = input_data["manual_funcs"];
|
toml::node_view manual_functions_data = input_data["manual_funcs"];
|
||||||
if (manual_functions_data.is_array()) {
|
if (manual_functions_data.is_array()) {
|
||||||
const toml::array* array = manual_functions_data.as_array();
|
const toml::array* array = manual_functions_data.as_array();
|
||||||
get_manual_funcs(array);
|
get_manual_funcs(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
stubbed_funcs = get_stubbed_funcs(table);
|
stubbed_funcs = get_stubbed_funcs(table);
|
||||||
|
|
||||||
// Ignored funcs array (optional)
|
// Ignored funcs array (optional)
|
||||||
ignored_funcs = get_ignored_funcs(table);
|
ignored_funcs = get_ignored_funcs(table);
|
||||||
|
|
||||||
// Functions (optional)
|
// Functions (optional)
|
||||||
declared_funcs = get_declared_funcs(table);
|
declared_funcs = get_declared_funcs(table);
|
||||||
|
|
||||||
// Single-instruction patches (optional)
|
// Single-instruction patches (optional)
|
||||||
instruction_patches = get_instruction_patches(table);
|
instruction_patches = get_instruction_patches(table);
|
||||||
|
|
||||||
// Manual function sizes (optional)
|
// Manual function sizes (optional)
|
||||||
manual_func_sizes = get_func_sizes(table);
|
manual_func_sizes = get_func_sizes(table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No errors occured, so mark this config file as good.
|
// No errors occured, so mark this config file as good.
|
||||||
bad = false;
|
bad = false;
|
||||||
|
@ -362,134 +395,138 @@ 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()) {
|
||||||
Section& section = ret.sections.emplace_back(Section{});
|
throw toml::parse_error("Section entry missing required field(s)", el.source());
|
||||||
section.rom_addr = rom_addr.value();
|
}
|
||||||
section.ram_addr = vram_addr.value();
|
|
||||||
section.size = size.value();
|
size_t section_index = ret.sections.size();
|
||||||
section.name = name.value();
|
|
||||||
section.executable = true;
|
Section& section = ret.sections.emplace_back(Section{});
|
||||||
} else {
|
section.rom_addr = rom_addr.value();
|
||||||
// TODO: throw error, we're expecting a table
|
section.ram_addr = vram_addr.value();
|
||||||
|
section.size = size.value();
|
||||||
|
section.name = name.value();
|
||||||
|
section.executable = true;
|
||||||
|
|
||||||
|
// Read functions for the section.
|
||||||
|
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, §ion, section_index](auto&& func_el) {
|
||||||
|
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{};
|
||||||
|
cur_func.name = name.value();
|
||||||
|
cur_func.vram = vram_addr.value();
|
||||||
|
cur_func.rom = cur_func.vram - section.ram_addr + section.rom_addr;
|
||||||
|
cur_func.section_index = section_index;
|
||||||
|
|
||||||
|
if (cur_func.vram & 0b11) {
|
||||||
|
// Function isn't word aligned in vram.
|
||||||
|
throw toml::parse_error("Function's vram address isn't word aligned", func_el.source());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_func.rom & 0b11) {
|
||||||
|
// Function isn't word aligned in rom.
|
||||||
|
throw toml::parse_error("Function's rom address isn't word aligned", func_el.source());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur_func.rom + func_size > rom.size()) {
|
||||||
|
// Function is out of bounds of the provided rom.
|
||||||
|
throw toml::parse_error("Functio is out of bounds of the provided rom", func_el.source());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the function's words from the rom.
|
||||||
|
cur_func.words.reserve(func_size / sizeof(uint32_t));
|
||||||
|
for (size_t rom_addr = cur_func.rom; rom_addr < cur_func.rom + func_size; rom_addr += sizeof(uint32_t)) {
|
||||||
|
cur_func.words.push_back(*reinterpret_cast<const uint32_t*>(rom.data() + rom_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
section.function_addrs.push_back(cur_func.vram);
|
||||||
|
ret.functions_by_name[cur_func.name] = function_index;
|
||||||
|
ret.functions_by_vram[cur_func.vram].push_back(function_index);
|
||||||
|
ret.section_functions[section_index].push_back(function_index);
|
||||||
|
|
||||||
|
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.
|
||||||
|
const toml::node_view relocs_value = el["relocs"];
|
||||||
|
if (relocs_value.is_array()) {
|
||||||
|
// Mark the section as relocatable, since it has relocs.
|
||||||
|
section.relocatable = true;
|
||||||
|
|
||||||
|
// Read relocs for the section.
|
||||||
|
const toml::array* relocs_array = relocs_value.as_array();
|
||||||
|
relocs_array->for_each([&ret, &rom, §ion, section_index](auto&& reloc_el) {
|
||||||
|
if constexpr (toml::is_table<decltype(reloc_el)>) {
|
||||||
|
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{};
|
||||||
|
cur_reloc.address = vram.value();
|
||||||
|
cur_reloc.target_address = target_vram.value();
|
||||||
|
cur_reloc.symbol_index = (uint32_t)-1;
|
||||||
|
cur_reloc.target_section = section_index;
|
||||||
|
cur_reloc.type = reloc_type;
|
||||||
|
|
||||||
|
section.relocs.emplace_back(std::move(cur_reloc));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw toml::parse_error("Invalid reloc entry", reloc_el.source());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
section.relocatable = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: throw error, we're expecting a table
|
throw toml::parse_error("Invalid section entry", el.source());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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.
|
|
||||||
for (const toml::value& function_value : functions) {
|
|
||||||
size_t function_index = ret.functions.size();
|
|
||||||
|
|
||||||
Function cur_func{};
|
|
||||||
cur_func.name = toml::find<std::string>(function_value, "name");
|
|
||||||
cur_func.vram = toml::find<uint32_t>(function_value, "vram");
|
|
||||||
cur_func.rom = cur_func.vram - section.ram_addr + section.rom_addr;
|
|
||||||
cur_func.section_index = section_index;
|
|
||||||
|
|
||||||
uint32_t func_size = toml::find<uint32_t>(function_value, "size");
|
|
||||||
|
|
||||||
if (cur_func.vram & 0b11) {
|
|
||||||
// Function isn't word aligned in vram.
|
|
||||||
throw value_error(toml::detail::format_underline(
|
|
||||||
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) {
|
|
||||||
// Function isn't word aligned in rom.
|
|
||||||
throw value_error(toml::detail::format_underline(
|
|
||||||
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()) {
|
|
||||||
// Function is out of bounds of the provided rom.
|
|
||||||
throw value_error(toml::detail::format_underline(
|
|
||||||
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.
|
|
||||||
cur_func.words.reserve(func_size / sizeof(uint32_t));
|
|
||||||
for (size_t rom_addr = cur_func.rom; rom_addr < cur_func.rom + func_size; rom_addr += sizeof(uint32_t)) {
|
|
||||||
cur_func.words.push_back(*reinterpret_cast<const uint32_t*>(rom.data() + rom_addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
section.function_addrs.push_back(cur_func.vram);
|
|
||||||
ret.functions_by_name[cur_func.name] = function_index;
|
|
||||||
ret.functions_by_vram[cur_func.vram].push_back(function_index);
|
|
||||||
ret.section_functions[section_index].push_back(function_index);
|
|
||||||
|
|
||||||
ret.functions.emplace_back(std::move(cur_func));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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{});
|
|
||||||
if (relocs_value.type() == toml::value_t::array) {
|
|
||||||
// Mark the section as relocatable, since it has relocs.
|
|
||||||
section.relocatable = true;
|
|
||||||
|
|
||||||
// Read relocs for the section.
|
|
||||||
for (const toml::value& reloc_value : relocs_value.as_array()) {
|
|
||||||
size_t reloc_index = ret.functions.size();
|
|
||||||
|
|
||||||
Reloc cur_reloc{};
|
|
||||||
cur_reloc.address = toml::find<uint32_t>(reloc_value, "vram");
|
|
||||||
cur_reloc.target_address = toml::find<uint32_t>(reloc_value, "target_vram");
|
|
||||||
cur_reloc.symbol_index = (uint32_t)-1;
|
|
||||||
cur_reloc.target_section = section_index;
|
|
||||||
const std::string& reloc_type = toml::find<std::string>(reloc_value, "type");
|
|
||||||
cur_reloc.type = reloc_type_from_name(reloc_type);
|
|
||||||
|
|
||||||
section.relocs.emplace_back(std::move(cur_reloc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
section.relocatable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const toml::syntax_error& err) {
|
|
||||||
fmt::print(stderr, "Syntax error in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
catch (const toml::parse_error& err) {
|
||||||
|
std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ret.rom = std::move(rom);
|
ret.rom = std::move(rom);
|
||||||
out = std::move(ret);
|
out = std::move(ret);
|
||||||
|
|
36
src/main.cpp
36
src/main.cpp
|
@ -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");
|
||||||
|
|
Loading…
Add table
Reference in a new issue