add renaming and re mode

This commit is contained in:
LittleCube 2024-12-23 16:11:22 -05:00
parent 2632d435b0
commit 0cc8edcf40
7 changed files with 79 additions and 9 deletions

View file

@ -216,6 +216,9 @@ namespace N64Recomp {
// List of symbols from events, which contains the names of events that this context provides. // List of symbols from events, which contains the names of events that this context provides.
std::vector<EventSymbol> event_symbols; std::vector<EventSymbol> event_symbols;
// Causes functions to print their name to the console the first time they're called.
bool re_mode;
// Imports sections and function symbols from a provided context into this context's reference sections and reference functions. // 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); bool import_reference_context(const Context& reference_context);
// Reads a data symbol file and adds its contents into this context's reference data symbols. // Reads a data symbol file and adds its contents into this context's reference data symbols.

View file

@ -93,7 +93,7 @@ std::vector<std::string> get_ignored_funcs(const toml::table* patches_data) {
// Make room for all the ignored funcs in the array. // Make room for all the ignored funcs in the array.
ignored_funcs.reserve(ignored_funcs_array->size()); ignored_funcs.reserve(ignored_funcs_array->size());
// Gather the stubs and place them into the array. // Gather the ignored and place them into the array.
ignored_funcs_array->for_each([&ignored_funcs](auto&& el) { ignored_funcs_array->for_each([&ignored_funcs](auto&& el) {
if constexpr (toml::is_string<decltype(el)>) { if constexpr (toml::is_string<decltype(el)>) {
ignored_funcs.push_back(*el); ignored_funcs.push_back(*el);
@ -104,6 +104,29 @@ std::vector<std::string> get_ignored_funcs(const toml::table* patches_data) {
return ignored_funcs; return ignored_funcs;
} }
std::vector<std::string> get_renamed_funcs(const toml::table* patches_data) {
std::vector<std::string> renamed_funcs{};
// Check if the renamed funcs array exists.
const toml::node_view renamed_funcs_data = (*patches_data)["renamed"];
if (renamed_funcs_data.is_array()) {
const toml::array* renamed_funcs_array = renamed_funcs_data.as_array();
// Make room for all the renamed funcs in the array.
renamed_funcs.reserve(renamed_funcs_array->size());
// Gather the renamed and place them into the array.
renamed_funcs_array->for_each([&renamed_funcs](auto&& el) {
if constexpr (toml::is_string<decltype(el)>) {
renamed_funcs.push_back(*el);
}
});
}
return renamed_funcs;
}
std::vector<N64Recomp::FunctionSize> get_func_sizes(const toml::table* patches_data) { std::vector<N64Recomp::FunctionSize> get_func_sizes(const toml::table* patches_data) {
std::vector<N64Recomp::FunctionSize> func_sizes{}; std::vector<N64Recomp::FunctionSize> func_sizes{};
@ -377,6 +400,9 @@ N64Recomp::Config::Config(const char* path) {
// Ignored funcs array (optional) // Ignored funcs array (optional)
ignored_funcs = get_ignored_funcs(table); ignored_funcs = get_ignored_funcs(table);
// Renamed funcs array (optional)
renamed_funcs = get_renamed_funcs(table);
// Single-instruction patches (optional) // Single-instruction patches (optional)
instruction_patches = get_instruction_patches(table); instruction_patches = get_instruction_patches(table);
@ -387,6 +413,18 @@ N64Recomp::Config::Config(const char* path) {
function_hooks = get_function_hooks(table); function_hooks = get_function_hooks(table);
} }
// Use RE mode if enabled (optional)
std::optional<bool> re_mode_opt = input_data["re_mode"].value<bool>();
if (re_mode_opt.has_value()) {
re_mode = re_mode_opt.value();
if (re_mode) {
recomp_include += "\n#include <stdio.h>\n#include <stdlib.h>";
}
}
else {
re_mode = false;
}
// Function reference symbols file (optional) // Function reference symbols file (optional)
std::optional<std::string> func_reference_syms_file_opt = input_data["func_reference_syms_file"].value<std::string>(); std::optional<std::string> func_reference_syms_file_opt = input_data["func_reference_syms_file"].value<std::string>();
if (func_reference_syms_file_opt.has_value()) { if (func_reference_syms_file_opt.has_value()) {

View file

@ -42,6 +42,7 @@ namespace N64Recomp {
bool single_file_output; bool single_file_output;
bool use_absolute_symbols; bool use_absolute_symbols;
bool unpaired_lo16_warnings; bool unpaired_lo16_warnings;
bool re_mode;
bool allow_exports; bool allow_exports;
bool strict_patch_mode; bool strict_patch_mode;
std::filesystem::path elf_path; std::filesystem::path elf_path;
@ -54,6 +55,7 @@ namespace N64Recomp {
std::filesystem::path output_binary_path; std::filesystem::path output_binary_path;
std::vector<std::string> stubbed_funcs; std::vector<std::string> stubbed_funcs;
std::vector<std::string> ignored_funcs; std::vector<std::string> ignored_funcs;
std::vector<std::string> renamed_funcs;
std::vector<InstructionPatch> instruction_patches; std::vector<InstructionPatch> instruction_patches;
std::vector<FunctionHook> function_hooks; std::vector<FunctionHook> function_hooks;
std::vector<FunctionSize> manual_func_sizes; std::vector<FunctionSize> manual_func_sizes;

View file

@ -60,7 +60,7 @@ bool read_symbols(N64Recomp::Context& context, const ELFIO::elfio& elf_file, ELF
if (section_index < context.sections.size()) { if (section_index < context.sections.size()) {
// Check if this symbol is the entrypoint // Check if this symbol is the entrypoint
if (elf_config.has_entrypoint && value == elf_config.entrypoint_address && type == ELFIO::STT_FUNC) { if (elf_config.has_entrypoint && (uint32_t) value == (uint32_t) elf_config.entrypoint_address && (type == ELFIO::STT_FUNC || type == ELFIO::STT_NOTYPE)) {
if (found_entrypoint_func) { if (found_entrypoint_func) {
fmt::print(stderr, "Ambiguous entrypoint: {}\n", name); fmt::print(stderr, "Ambiguous entrypoint: {}\n", name);
return false; return false;
@ -105,7 +105,7 @@ bool read_symbols(N64Recomp::Context& context, const ELFIO::elfio& elf_file, ELF
auto section_offset = value - elf_file.sections[section_index]->get_address(); auto section_offset = value - elf_file.sections[section_index]->get_address();
const uint32_t* words = reinterpret_cast<const uint32_t*>(elf_file.sections[section_index]->get_data() + section_offset); const uint32_t* words = reinterpret_cast<const uint32_t*>(elf_file.sections[section_index]->get_data() + section_offset);
uint32_t vram = static_cast<uint32_t>(value); uint32_t vram = static_cast<uint32_t>(value);
uint32_t num_instructions = type == ELFIO::STT_FUNC ? size / 4 : 0; uint32_t num_instructions = (type == ELFIO::STT_FUNC || type == ELFIO::STT_NOTYPE) ? size / 4 : 0;
uint32_t rom_address = static_cast<uint32_t>(section_offset + section.rom_addr); uint32_t rom_address = static_cast<uint32_t>(section_offset + section.rom_addr);
section.function_addrs.push_back(vram); section.function_addrs.push_back(vram);

View file

@ -273,10 +273,10 @@ int main(int argc, char** argv) {
}; };
// TODO expose a way to dump the context from the command line. // TODO expose a way to dump the context from the command line.
bool dumping_context = false; bool dumping_context = argc > 2;
if (argc != 2) { if (argc > 3) {
fmt::print("Usage: {} [config file]\n", argv[0]); fmt::print("Usage: {} [config file] (should-dump)\n", argv[0]);
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
} }
@ -485,10 +485,27 @@ int main(int argc, char** argv) {
// This helps prevent typos in the config file or functions renamed between versions from causing issues. // This helps prevent typos in the config file or functions renamed between versions from causing issues.
exit_failure(fmt::format("Function {} is set as ignored in the config file but does not exist!", ignored_func)); exit_failure(fmt::format("Function {} is set as ignored in the config file but does not exist!", ignored_func));
} }
// Mark the function as . // Mark the function as ignored.
context.functions[func_find->second].ignored = true; context.functions[func_find->second].ignored = true;
} }
// Rename any functions specified in the config file.
for (const std::string& renamed_func : config.renamed_funcs) {
// Check if the specified function exists.
auto func_find = context.functions_by_name.find(renamed_func);
if (func_find == context.functions_by_name.end()) {
// Function doesn't exist, present an error to the user instead of silently failing to mark it as ignored.
// This helps prevent typos in the config file or functions renamed between versions from causing issues.
exit_failure(fmt::format("Function {} is set as renamed in the config file but does not exist!", renamed_func));
}
// Rename the function.
N64Recomp::Function* func = &context.functions[func_find->second];
func->name = func->name + "_recomp";
}
// Propogate the re_mode parameter.
context.re_mode = config.re_mode;
// Apply any single-instruction patches. // Apply any single-instruction patches.
for (const N64Recomp::InstructionPatch& patch : config.instruction_patches) { for (const N64Recomp::InstructionPatch& patch : config.instruction_patches) {
// Check if the specified function exists. // Check if the specified function exists.

View file

@ -745,6 +745,16 @@ bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64R
" int c1cs = 0;\n", // cop1 conditional signal " int c1cs = 0;\n", // cop1 conditional signal
func.name); func.name);
if (context.re_mode) {
fmt::print(output_file,
" static int {0}_was_called = 0;\n"
" if ({0}_was_called == 0) {{\n"
" fprintf(stderr, \"new function: {0}\\n\");\n"
" {0}_was_called = 1;\n"
" }}\n",
func.name);
}
// Skip analysis and recompilation of this function is stubbed. // Skip analysis and recompilation of this function is stubbed.
if (!func.stubbed) { if (!func.stubbed) {
// Use a set to sort and deduplicate labels // Use a set to sort and deduplicate labels

View file

@ -1,6 +1,6 @@
#include "n64recomp.h" #include "n64recomp.h"
const std::unordered_set<std::string> N64Recomp::reimplemented_funcs{ const std::unordered_set<std::string> N64Recomp::reimplemented_funcs {
// OS initialize functions // OS initialize functions
"__osInitialize_common", "__osInitialize_common",
"osInitialize", "osInitialize",
@ -557,7 +557,7 @@ const std::unordered_set<std::string> N64Recomp::ignored_funcs {
"kdebugserver", "kdebugserver",
}; };
const std::unordered_set<std::string> N64Recomp::renamed_funcs{ const std::unordered_set<std::string> N64Recomp::renamed_funcs {
// Math // Math
"sincosf", "sincosf",
"sinf", "sinf",