diff --git a/CMakeLists.txt b/CMakeLists.txt index 0db7a7a..2733666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,3 +154,13 @@ target_include_directories(RecompModTool PRIVATE target_link_libraries(RecompModTool fmt tomlplusplus::tomlplusplus N64RecompElf) +# Offline mod recompiler +project(OfflineModRecomp) +add_executable(OfflineModRecomp) + +target_sources(OfflineModRecomp PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src/config.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/OfflineModRecomp/main.cpp +) + +target_link_libraries(OfflineModRecomp fmt rabbitizer tomlplusplus::tomlplusplus N64Recomp) diff --git a/OfflineModRecomp/main.cpp b/OfflineModRecomp/main.cpp new file mode 100644 index 0000000..88a8c60 --- /dev/null +++ b/OfflineModRecomp/main.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include + +#include "n64recomp.h" +#include "rabbitizer.hpp" + +static std::vector read_file(const std::filesystem::path& path, bool& found) { + std::vector ret; + found = false; + + std::ifstream file{ path, std::ios::binary}; + + if (file.good()) { + file.seekg(0, std::ios::end); + ret.resize(file.tellg()); + file.seekg(0, std::ios::beg); + + file.read(reinterpret_cast(ret.data()), ret.size()); + found = true; + } + + return ret; +} + +const std::filesystem::path func_reference_syms_file_path { + "C:/n64/MMRecompTestMod/Zelda64RecompSyms/mm.us.rev1.syms.toml" +}; +const std::vector data_reference_syms_file_paths { + "C:/n64/MMRecompTestMod/Zelda64RecompSyms/mm.us.rev1.datasyms.toml", + "C:/n64/MMRecompTestMod/Zelda64RecompSyms/mm.us.rev1.datasyms_static.toml" +}; + +int main(int argc, const char** argv) { + if (argc != 4) { + printf("Usage: %s [mod symbol file] [ROM] [output C file]\n", argv[0]); + return EXIT_SUCCESS; + } + bool found; + std::vector symbol_data = read_file(argv[1], found); + if (!found) { + fprintf(stderr, "Failed to open symbol file\n"); + return EXIT_FAILURE; + } + + std::vector rom_data = read_file(argv[2], found); + if (!found) { + fprintf(stderr, "Failed to open ROM\n"); + return EXIT_FAILURE; + } + + N64Recomp::ModContext mod_context{}; + + std::span symbol_data_span { reinterpret_cast(symbol_data.data()), symbol_data.size() }; + + std::vector dummy_rom{}; + N64Recomp::Context reference_context{}; + if (!N64Recomp::Context::from_symbol_file(func_reference_syms_file_path, std::move(dummy_rom), reference_context, false)) { + printf("Failed to load provided function reference symbol file\n"); + return EXIT_FAILURE; + } + + //for (const std::filesystem::path& cur_data_sym_path : data_reference_syms_file_paths) { + // if (!reference_context.read_data_reference_syms(cur_data_sym_path)) { + // printf("Failed to load provided data reference symbol file\n"); + // return EXIT_FAILURE; + // } + //} + + std::unordered_map sections_by_vrom{}; + for (uint16_t section_index = 0; section_index < reference_context.sections.size(); section_index++) { + sections_by_vrom[reference_context.sections[section_index].rom_addr] = section_index; + } + + N64Recomp::ModSymbolsError error = N64Recomp::parse_mod_symbols(symbol_data_span, rom_data, sections_by_vrom, reference_context, mod_context); + if (error != N64Recomp::ModSymbolsError::Good) { + fprintf(stderr, "Error parsing mod symbols: %d\n", (int)error); + return EXIT_FAILURE; + } + + // Populate R_MIPS_26 reloc symbol indices. Start by building a map of vram address to matching reference symbols. + std::unordered_map> reference_symbols_by_vram{}; + for (size_t reference_symbol_index = 0; reference_symbol_index < mod_context.base_context.reference_symbols.size(); reference_symbol_index++) { + const auto& sym = mod_context.base_context.reference_symbols[reference_symbol_index]; + uint16_t section_index = sym.section_index; + if (section_index != N64Recomp::SectionAbsolute && section_index != N64Recomp::SectionSelf && section_index != N64Recomp::SectionImport) { + const auto& section = mod_context.base_context.reference_sections[section_index]; + + uint32_t vram = section.ram_addr + sym.section_offset; + reference_symbols_by_vram[vram].push_back(reference_symbol_index); + } + } + + size_t import_symbols_start = mod_context.base_context.reference_symbols.size() - mod_context.import_symbol_dependency_indices.size(); + + // Use the mapping to populate the symbol index for every R_MIPS_26 reference symbol reloc. + for (auto& section : mod_context.base_context.sections) { + for (auto& reloc : section.relocs) { + if (reloc.type == N64Recomp::RelocType::R_MIPS_26 && reloc.reference_symbol) { + if (reloc.target_section == N64Recomp::SectionImport) { + reloc.symbol_index = reloc.target_section_offset + import_symbols_start; + } + else { + const auto& target_section = mod_context.base_context.reference_sections[reloc.target_section]; + uint32_t target_vram = target_section.ram_addr + reloc.target_section_offset; + + auto find_funcs_it = reference_symbols_by_vram.find(target_vram); + bool found = false; + if (find_funcs_it != reference_symbols_by_vram.end()) { + for (size_t symbol_index : find_funcs_it->second) { + const auto& cur_symbol = mod_context.base_context.reference_symbols[symbol_index]; + if (cur_symbol.section_index == reloc.target_section) { + reloc.symbol_index = symbol_index; + found = true; + break; + } + } + } + if (!found) { + fprintf(stderr, "Failed to find R_MIPS_26 relocation target in section %d with vram 0x%08X\n", reloc.target_section, target_vram); + return EXIT_FAILURE; + } + } + } + } + } + + mod_context.base_context.rom = std::move(rom_data); + + std::vector> static_funcs_by_section{}; + static_funcs_by_section.resize(mod_context.base_context.sections.size()); + + std::ofstream output_file { argv[3] }; + + RabbitizerConfig_Cfg.pseudos.pseudoMove = false; + RabbitizerConfig_Cfg.pseudos.pseudoBeqz = false; + RabbitizerConfig_Cfg.pseudos.pseudoBnez = false; + RabbitizerConfig_Cfg.pseudos.pseudoNot = false; + RabbitizerConfig_Cfg.pseudos.pseudoBal = false; + + bool should_write_header = true; + for (const auto& func : mod_context.base_context.functions) { + N64Recomp::recompile_function(mod_context.base_context, func, output_file, static_funcs_by_section, should_write_header); + should_write_header = false; + } + + return EXIT_SUCCESS; +}