diff --git a/.gitmodules b/.gitmodules index cdaa6ae..86a551c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/toml11"] path = lib/toml11 url = https://github.com/ToruNiina/toml11 +[submodule "lib/tomlplusplus"] + path = lib/tomlplusplus + url = https://github.com/marzer/tomlplusplus diff --git a/CMakeLists.txt b/CMakeLists.txt index d029deb..264990e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # set(CMAKE_CXX_VISIBILITY_PRESET hidden) -# fmtlib -add_subdirectory(lib/fmt) - # Rabbitizer project(rabbitizer) 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/RabbitizerRegisterDescriptor.c") -target_include_directories(rabbitizer PRIVATE +target_include_directories(rabbitizer PUBLIC "${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") -# toml++ -include(FetchContent) -FetchContent_Declare( - tomlplusplus - GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git - GIT_TAG v3.4.0 -) -FetchContent_MakeAvailable(tomlplusplus) +# fmtlib +add_subdirectory(lib/fmt) + +# tomlplusplus +add_subdirectory(lib/tomlplusplus) # N64 recompiler project(N64Recomp) @@ -76,25 +72,20 @@ target_sources(N64Recomp PRIVATE ${CMAKE_SOURCE_DIR}/src/recompilation.cpp) 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/fmt/include" "${CMAKE_SOURCE_DIR}/include") - target_link_libraries(N64Recomp fmt rabbitizer tomlplusplus::tomlplusplus) +target_link_libraries(N64Recomp fmt rabbitizer tomlplusplus::tomlplusplus) # RSP recompiler project(RSPRecomp) add_executable(RSPRecomp) target_include_directories(RSPRecomp PRIVATE - "${CMAKE_SOURCE_DIR}/lib/rabbitizer/include" - "${CMAKE_SOURCE_DIR}/lib/rabbitizer/cplusplus/include" - "${CMAKE_SOURCE_DIR}/lib/fmt/include" - "${CMAKE_SOURCE_DIR}/include") + "${CMAKE_SOURCE_DIR}/include" + "${CMAKE_SOURCE_DIR}/lib/toml11") -target_link_libraries(RSPRecomp fmt rabbitizer tomlplusplus::tomlplusplus) +target_link_libraries(RSPRecomp fmt rabbitizer) target_sources(RSPRecomp PRIVATE ${CMAKE_SOURCE_DIR}/RSPRecomp/src/rsp_recomp.cpp) diff --git a/include/recomp_port.h b/include/recomp_port.h index 22581cc..a74a8b5 100644 --- a/include/recomp_port.h +++ b/include/recomp_port.h @@ -113,6 +113,7 @@ namespace RecompPort { Function(uint32_t vram, uint32_t rom, std::vector 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) {} + Function() = default; }; enum class RelocType : uint8_t { diff --git a/lib/fmt b/lib/fmt index d2e89c8..8e72804 160000 --- a/lib/fmt +++ b/lib/fmt @@ -1 +1 @@ -Subproject commit d2e89c8b080394e996d449371267365c223ca76b +Subproject commit 8e728044f673774160f43b44a07c6b185352310f diff --git a/lib/tomlplusplus b/lib/tomlplusplus new file mode 160000 index 0000000..1f7884e --- /dev/null +++ b/lib/tomlplusplus @@ -0,0 +1 @@ +Subproject commit 1f7884e59165e517462f922e7b6de131bd9844f3 diff --git a/src/config.cpp b/src/config.cpp index e6e2bb9..e8c3369 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -22,6 +22,9 @@ std::vector get_manual_funcs(const toml::array* manu 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; @@ -37,13 +40,16 @@ std::vector get_stubbed_funcs(const toml::table* patches_data) { const toml::array* stubs_array = stubs_data.as_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. stubs_array->for_each([&stubbed_funcs](auto&& el) { if constexpr (toml::is_string) { stubbed_funcs.push_back(*el); } + else { + throw toml::parse_error{ "Invalid stubbed function", el.source()}; + } }); } @@ -60,7 +66,7 @@ std::vector get_ignored_funcs(const toml::table* patches_data) { const toml::array* ignored_funcs_array = ignored_funcs_data.as_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. ignored_funcs_array->for_each([&ignored_funcs](auto&& el) { @@ -89,10 +95,13 @@ std::vector parse_args(const toml::array* args_in) auto type_find = arg_type_map.find(arg_str); if (type_find == arg_type_map.end()) { // 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); } + else { + throw toml::parse_error("Invalid function argument entry", el.source()); + } }); 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(); declared_funcs.emplace(func_name.value(), parse_args(args_array)); } 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 get_func_sizes(const toml::table* patches_ // Copy all the sizes into the output vector. sizes_array->for_each([&func_sizes](auto&& el) { if constexpr (toml::is_table) { - const toml::table* cur_size = el.as_table(); + const toml::table& cur_size = *el.as_table(); // Get the function name and size. - std::optional func_name = (*cur_size)["name"].value(); - std::optional func_size = (*cur_size)["size"].value(); + std::optional func_name = cur_size["name"].value(); + std::optional func_size = cur_size["size"].value(); if (func_name.has_value() && func_size.has_value()) { // Make sure the size is divisible by 4 if (func_size.value() & (4 - 1)) { // 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()); } + else { + throw toml::parse_error("Invalid manually sized function entry", el.source()); + } }); } @@ -170,37 +188,37 @@ std::vector get_instruction_patches(const toml::ta if (insn_patch_data.is_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. insn_patch_array->for_each([&ret](auto&& el) { if constexpr (toml::is_table) { - 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. - std::optional vram = (*cur_patch)["vram"].value(); + auto testval = cur_patch["vram"]; + std::optional vram = cur_patch["vram"].value(); + std::optional func_name = cur_patch["func"].value(); + std::optional value = cur_patch["value"].value(); - if (!vram.has_value() || vram.value() & 0b11) { - // Not properly aligned, so throw an error (and make it look like a normal toml one). - // TODO: throw error - return; + if (!vram.has_value() || !func_name.has_value() || !value.has_value()) { + throw toml::parse_error("Instruction patch is missing required value(s)", el.source()); } - std::optional func_name = (*cur_patch)["func"].value(); - std::optional value = (*cur_patch)["value"].value(); - - if (!func_name.has_value() || !value.has_value()) { - // Missing required value in instruction patch array - // TODO: throw error - return; + if (vram.value() & 0b11) { + // 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()); } ret.push_back(RecompPort::InstructionPatch{ .func_name = func_name.value(), - .vram = vram.value(), + .vram = (int32_t)vram.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. entrypoint = 0; bad = true; + toml::table config_data{}; - try { - const toml::table config_data = toml::parse_file(path); - std::filesystem::path basedir = std::filesystem::path{ path }.parent_path(); + try { + config_data = toml::parse_file(path); + std::filesystem::path basedir = std::filesystem::path{ path }.parent_path(); - // Input section (required) - const auto& input_data = config_data["input"]; + // Input section (required) + const auto input_data = config_data["input"]; + const auto entrypoint_data = input_data["entrypoint"]; - if (config_data.contains("entrypoint")) { - entrypoint = config_data["entrypoint"].value().value(); - has_entrypoint = true; - } else { - has_entrypoint = false; - } + if (entrypoint_data) { + const auto entrypoint_value = entrypoint_data.value(); + if (entrypoint_value.has_value()) { + entrypoint = (int32_t)entrypoint_value.value(); + has_entrypoint = true; + } + else { + throw toml::parse_error { "Invalid entrypoint", entrypoint_data.node()->source() }; + } + } + else { + has_entrypoint = false; + } std::optional elf_path_opt = input_data["elf_path"].value(); if (elf_path_opt.has_value()) { @@ -251,7 +278,8 @@ RecompPort::Config::Config(const char* path) { std::optional output_func_path_opt = input_data["output_func_path"].value(); if (output_func_path_opt.has_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"); return; } @@ -259,70 +287,75 @@ RecompPort::Config::Config(const char* path) { std::optional relocatable_sections_path_opt = input_data["relocatable_sections_path"].value(); if (relocatable_sections_path_opt.has_value()) { relocatable_sections_path = concat_if_not_empty(basedir, relocatable_sections_path_opt.value()); - } else { + } + else { relocatable_sections_path = ""; } std::optional uses_mips3_float_mode_opt = input_data["uses_mips3_float_mode"].value(); if (uses_mips3_float_mode_opt.has_value()) { uses_mips3_float_mode = uses_mips3_float_mode_opt.value(); - } else { + } + else { uses_mips3_float_mode = false; } std::optional bss_section_suffix_opt = input_data["bss_section_suffix"].value(); if (bss_section_suffix_opt.has_value()) { bss_section_suffix = bss_section_suffix_opt.value(); - } else { + } + else { bss_section_suffix = ".bss"; } std::optional single_file_output_opt = input_data["single_file_output"].value(); if (single_file_output_opt.has_value()) { single_file_output = single_file_output_opt.value(); - } else { + } + else { single_file_output = false; } std::optional use_absolute_symbols_opt = input_data["use_absolute_symbols"].value(); if (use_absolute_symbols_opt.has_value()) { use_absolute_symbols = use_absolute_symbols_opt.value(); - } else { + } + else { use_absolute_symbols = false; } - // Manual functions (optional) + // Manual functions (optional) toml::node_view manual_functions_data = input_data["manual_funcs"]; if (manual_functions_data.is_array()) { const toml::array* array = manual_functions_data.as_array(); get_manual_funcs(array); } - // Patches section (optional) + // Patches section (optional) 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(); - // Stubs array (optional) - stubbed_funcs = get_stubbed_funcs(table); + // Stubs array (optional) + stubbed_funcs = get_stubbed_funcs(table); - // Ignored funcs array (optional) - ignored_funcs = get_ignored_funcs(table); + // Ignored funcs array (optional) + ignored_funcs = get_ignored_funcs(table); - // Functions (optional) - declared_funcs = get_declared_funcs(table); + // Functions (optional) + declared_funcs = get_declared_funcs(table); - // Single-instruction patches (optional) - instruction_patches = get_instruction_patches(table); + // Single-instruction patches (optional) + instruction_patches = get_instruction_patches(table); - // Manual function sizes (optional) - manual_func_sizes = get_func_sizes(table); - } - } + // Manual function sizes (optional) + manual_func_sizes = get_func_sizes(table); + } + } catch (const toml::parse_error& err) { - fmt::print(stderr, "Syntax parsing file {} ({}), full error:\n{}\n", *err.source().path, err.source().begin, err.description()); - return; - } + std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl; + return; + } // No errors occured, so mark this config file as good. 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()); config_sections->for_each([&ret, &rom](auto&& el) { - size_t section_index = ret.sections.size(); - if constexpr (toml::is_table) { std::optional rom_addr = el["rom"].template value(); std::optional vram_addr = el["vram"].template value(); std::optional size = el["size"].template value(); std::optional name = el["name"].template value(); - if (rom_addr.has_value() && vram_addr.has_value() && size.has_value() && name.has_value()) { - Section& section = ret.sections.emplace_back(Section{}); - section.rom_addr = rom_addr.value(); - section.ram_addr = vram_addr.value(); - section.size = size.value(); - section.name = name.value(); - section.executable = true; - } else { - // TODO: throw error, we're expecting a table + 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.rom_addr = rom_addr.value(); + 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) { + std::optional name = func_el["name"].template value(); + std::optional vram_addr = func_el["vram"].template value(); + std::optional func_size_ = func_el["size"].template value(); + + 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(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) { + size_t reloc_index = ret.functions.size(); + + std::optional vram = reloc_el["vram"].template value(); + std::optional target_vram = reloc_el["target_vram"].template value(); + std::optional type_string = reloc_el["type"].template value(); + + 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 { - // 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(section_value, "rom"); - section.ram_addr = toml::find(section_value, "vram"); - section.size = toml::find(section_value, "size"); - section.name = toml::find(section_value, "name"); - section.executable = true; - - const toml::array& functions = toml::find(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(function_value, "name"); - cur_func.vram = toml::find(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(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(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(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(reloc_value, "vram"); - cur_reloc.target_address = toml::find(reloc_value, "target_vram"); - cur_reloc.symbol_index = (uint32_t)-1; - cur_reloc.target_section = section_index; - const std::string& reloc_type = toml::find(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); out = std::move(ret); diff --git a/src/main.cpp b/src/main.cpp index f48341c..e83ca66 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1379,19 +1379,47 @@ int main(int argc, char** argv) { 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)) { + rename_function(func_index, func.name + "_recomp"); func.reimplemented = true; - func.name = func.name + "_recomp"; func.ignored = true; } else if (ignored_funcs.contains(func.name)) { - func.name = func.name + "_recomp"; + rename_function(func_index, func.name + "_recomp"); func.ignored = true; } else if (renamed_funcs.contains(func.name)) { - func.name = func.name + "_recomp"; + rename_function(func_index, func.name + "_recomp"); 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 { exit_failure("Config file must provide either an elf or a symbols file\n");