diff --git a/src/ARCodeTranslator-Stub.hh b/src/ARCodeTranslator-Stub.hh index 73b47178..fbebff15 100644 --- a/src/ARCodeTranslator-Stub.hh +++ b/src/ARCodeTranslator-Stub.hh @@ -2,7 +2,13 @@ #include #include +#include +#include inline void run_ar_code_translator(const std::string&, const std::string&, const std::string&) { throw std::runtime_error("resource_file is not available; install it and rebuild newserv"); } + +inline std::vector> diff_dol_files(const std::string&, const std::string&) { + throw std::runtime_error("resource_file is not available; install it and rebuild newserv"); +} diff --git a/src/ARCodeTranslator.cc b/src/ARCodeTranslator.cc index 77a9e8ec..82ff3234 100644 --- a/src/ARCodeTranslator.cc +++ b/src/ARCodeTranslator.cc @@ -11,10 +11,35 @@ class ARCodeTranslator { public: enum class ExpandMethod { FORWARD = 0, + FORWARD_WITH_BARRIER, BACKWARD, + BACKWARD_WITH_BARRIER, BOTH, + BOTH_WITH_BARRIER, + BOTH_IGNORE_ORIGIN, }; + static const char* name_for_expand_method(ExpandMethod method) { + switch (method) { + case ExpandMethod::FORWARD: + return "FORWARD"; + case ExpandMethod::FORWARD_WITH_BARRIER: + return "FORWARD_WITH_BARRIER"; + case ExpandMethod::BACKWARD: + return "BACKWARD"; + case ExpandMethod::BACKWARD_WITH_BARRIER: + return "BACKWARD_WITH_BARRIER"; + case ExpandMethod::BOTH: + return "BOTH"; + case ExpandMethod::BOTH_WITH_BARRIER: + return "BOTH_WITH_BARRIER"; + case ExpandMethod::BOTH_IGNORE_ORIGIN: + return "BOTH_IGNORE_ORIGIN"; + default: + throw logic_error("invalid expand method"); + } + } + ARCodeTranslator(const string& directory) : log("[ar-trans] "), directory(directory) { @@ -110,11 +135,14 @@ public: if (!src_section) { throw runtime_error("source address not within any section"); } + + const char* method_token = this->name_for_expand_method(expand_method); + size_t src_offset = src_address - src_section->address; - size_t src_bytes_available_before = src_section->data.size() - src_offset; - size_t src_bytes_available_after = src_section->data.size() - src_offset; - this->log.info("(find_match) Source offset = %08zX with %zX/%zX bytes available before/after", - src_offset, src_bytes_available_before, src_bytes_available_after); + size_t src_bytes_available_before = src_offset; + size_t src_bytes_available_after = src_section->data.size() - src_offset - 4; + this->log.info("(find_match/%s) Source offset = %08zX with %zX/%zX bytes available before/after", + method_token, src_offset, src_bytes_available_before, src_bytes_available_after); size_t match_bytes_before = 0; size_t match_bytes_after = 0; @@ -131,7 +159,10 @@ public: StringReader dest_r(dest_section.data.data() + dest_match_offset, match_length); size_t z; for (z = 0; z < match_length; z += 4) { - if (src_section->is_text) { + if (expand_method == ExpandMethod::BOTH_IGNORE_ORIGIN && z == match_bytes_before) { + src_r.skip(4); + dest_r.skip(4); + } else if (src_section->is_text) { uint32_t src_opcode = src_r.get_u32b(); uint32_t dest_opcode = dest_r.get_u32b(); uint32_t src_class = src_opcode & 0xFC000000; @@ -142,12 +173,8 @@ public: // b +-offset src_opcode &= 0xFC000003; dest_opcode &= 0xFC000003; - } else if (src_class == 0x40000000) { - // bc +- offset - src_opcode &= 0xFFFF0003; - dest_opcode &= 0xFFFF0003; - } else if (((src_opcode & 0xEC1F0000) == 0x800D0000) || ((src_opcode & 0xEC1F0000) == 0x80020000)) { - // lwz rXX, [r2/r13 +- offset] OR stw [r2/r13 +- offset], rXX + } else if (((src_opcode & 0xAC1F0000) == 0x800D0000) || ((src_opcode & 0xAC1F0000) == 0x80020000)) { + // lwz/lfs rXX/fXX, [r2/r13 +- offset] OR stw/stfs [r2/r13 +- offset], rXX/fXX src_opcode &= 0xFFFF0000; dest_opcode &= 0xFFFF0000; } @@ -174,39 +201,52 @@ public: } } } - this->log.info("(find_match) For match length %zX, %zu matches found", match_length, num_matches); + this->log.info("(find_match/%s) For match length %zX, %zu matches found", method_token, match_length, num_matches); if (num_matches == 1) { return last_match_address; } else if (num_matches == 0) { throw runtime_error("did not find exactly one match"); } + bool can_expand_backward = false; + bool can_expand_forward = false; switch (expand_method) { + case ExpandMethod::BACKWARD_WITH_BARRIER: + can_expand_backward = (src_r.pget_u32b(0) != 0x4E800020) && + (src_bytes_available_before >= match_bytes_before + 4); + break; case ExpandMethod::BACKWARD: - match_bytes_before += 4; - if (src_bytes_available_before < match_bytes_before) { - throw runtime_error("no more source data to match"); - } + can_expand_backward = (src_bytes_available_before >= match_bytes_before + 4); + break; + case ExpandMethod::FORWARD_WITH_BARRIER: + can_expand_forward = (src_r.pget_u32b(src_r.size() - 4) != 0x4E800020) && + (src_bytes_available_after >= match_bytes_after + 4); break; case ExpandMethod::FORWARD: - match_bytes_after += 4; - if (src_bytes_available_after < match_bytes_after) { - throw runtime_error("no more source data to match"); - } + can_expand_forward = (src_bytes_available_after >= match_bytes_after + 4); + break; + case ExpandMethod::BOTH_WITH_BARRIER: + case ExpandMethod::BOTH_IGNORE_ORIGIN: + can_expand_backward = (src_r.pget_u32b(0) != 0x4E800020) && + (src_bytes_available_before >= match_bytes_before + 4); + can_expand_forward = (src_r.pget_u32b(src_r.size() - 4) != 0x4E800020) && + (src_bytes_available_after >= match_bytes_after + 4); break; case ExpandMethod::BOTH: - match_bytes_before += 4; - match_bytes_after += 4; - if ((src_bytes_available_before < match_bytes_before) && (src_bytes_available_after < match_bytes_after)) { - throw runtime_error("no more source data to match"); - } else if (src_bytes_available_before < match_bytes_before) { - match_bytes_before -= 4; - } else if (src_bytes_available_after < match_bytes_after) { - match_bytes_after -= 4; - } + can_expand_backward = (src_bytes_available_before >= match_bytes_before + 4); + can_expand_forward = (src_bytes_available_after >= match_bytes_after + 4); break; default: throw logic_error("invalid expand method"); } + if (!can_expand_backward && !can_expand_forward) { + throw runtime_error("no further expansion is allowed"); + } + if (can_expand_backward) { + match_bytes_before += 4; + } + if (can_expand_forward) { + match_bytes_after += 4; + } } throw runtime_error("scan field too long; too many matches"); } @@ -223,41 +263,38 @@ public: results.emplace(it.first, src_addr); } else { - auto f_forward = async(&ARCodeTranslator::find_match, this, it.second, src_addr, ExpandMethod::FORWARD); - auto f_backward = async(&ARCodeTranslator::find_match, this, it.second, src_addr, ExpandMethod::BACKWARD); - auto f_both = async(&ARCodeTranslator::find_match, this, it.second, src_addr, ExpandMethod::BOTH); - - uint32_t match_addr_forward = 0; - uint32_t match_addr_backward = 0; - uint32_t match_addr_both = 0; - try { - match_addr_forward = f_forward.get(); - log.info("(%s) (forward) %08" PRIX32, it.first.c_str(), match_addr_forward); - } catch (const exception& e) { - log.error("(%s) (forward) failed: %s", it.first.c_str(), e.what()); - } - try { - match_addr_backward = f_backward.get(); - log.info("(%s) (backward) %08" PRIX32, it.first.c_str(), match_addr_backward); - } catch (const exception& e) { - log.error("(%s) (backward) failed: %s", it.first.c_str(), e.what()); - } - try { - match_addr_both = f_both.get(); - log.info("(%s) (both) %08" PRIX32, it.first.c_str(), match_addr_both); - } catch (const exception& e) { - log.error("(%s) (both) failed: %s", it.first.c_str(), e.what()); + array, 7> futures; + static const array methods = { + ExpandMethod::FORWARD, + ExpandMethod::FORWARD_WITH_BARRIER, + ExpandMethod::BACKWARD, + ExpandMethod::BACKWARD_WITH_BARRIER, + ExpandMethod::BOTH, + ExpandMethod::BOTH_WITH_BARRIER, + ExpandMethod::BOTH_IGNORE_ORIGIN, + }; + for (size_t z = 0; z < methods.size(); z++) { + futures[z] = async(&ARCodeTranslator::find_match, this, it.second, src_addr, methods[z]); } - uint32_t all_addrs = match_addr_backward | match_addr_forward | match_addr_both; - if (!all_addrs) { + unordered_set match_addrs; + for (size_t z = 0; z < futures.size(); z++) { + const char* method_name = this->name_for_expand_method(methods[z]); + try { + uint32_t ret = futures[z].get(); + log.info("(%s) (%s) %08" PRIX32, it.first.c_str(), method_name, ret); + match_addrs.emplace(ret); + } catch (const exception& e) { + log.error("(%s) (%s) failed: %s", it.first.c_str(), method_name, e.what()); + } + } + + if (match_addrs.empty()) { log.error("(%s) no match found", it.first.c_str()); - } else if ((match_addr_forward && (match_addr_forward != all_addrs)) || - (match_addr_backward && (match_addr_backward != all_addrs)) || - (match_addr_both && (match_addr_both != all_addrs))) { + } else if (match_addrs.size() > 1) { log.error("(%s) different matches found by different methods", it.first.c_str()); } else { - results.emplace(it.first, all_addrs); + results.emplace(it.first, *match_addrs.begin()); } } } @@ -323,3 +360,41 @@ void run_ar_code_translator(const std::string& directory, const std::string& use trans.run_shell(); } } + +vector> diff_dol_files(const string& a_filename, const string& b_filename) { + DOLFile a(a_filename.c_str()); + DOLFile b(b_filename.c_str()); + auto a_mem = make_shared(); + auto b_mem = make_shared(); + a.load_into(a_mem); + b.load_into(b_mem); + + uint32_t min_addr = 0xFFFFFFFF; + uint32_t max_addr = 0x00000000; + for (const auto& sec : a.sections) { + min_addr = min(min_addr, sec.address); + max_addr = max(max_addr, sec.address + sec.data.size()); + } + for (const auto& sec : b.sections) { + min_addr = min(min_addr, sec.address); + max_addr = max(max_addr, sec.address + sec.data.size()); + } + + vector> ret; + for (uint32_t addr = min_addr; addr < max_addr; addr += 4) { + bool a_exists = a_mem->exists(addr, 4); + bool b_exists = b_mem->exists(addr, 4); + if (a_exists && b_exists) { + string a_value = a_mem->read(addr, 4); + string b_value = b_mem->read(addr, 4); + if (a_value != b_value) { + if (!ret.empty() && (ret.back().first + ret.back().second.size() == addr)) { + ret.back().second += b_value; + } else { + ret.emplace_back(make_pair(addr, b_value)); + } + } + } + } + return ret; +} diff --git a/src/ARCodeTranslator.hh b/src/ARCodeTranslator.hh index ba632940..27f727fe 100644 --- a/src/ARCodeTranslator.hh +++ b/src/ARCodeTranslator.hh @@ -1,5 +1,8 @@ #pragma once #include +#include +#include void run_ar_code_translator(const std::string& initial_directory, const std::string& use_file, const std::string& command); +std::vector> diff_dol_files(const std::string& a_filename, const std::string& b_filename); diff --git a/src/Main.cc b/src/Main.cc index 6798609d..f970b94d 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1722,6 +1722,17 @@ Action a_ar_code_translator( run_ar_code_translator(dir, args.get(2, false), args.get(3, false)); }); +Action a_diff_dol_files( + "diff-dol-files", nullptr, +[](Arguments& args) { + const string& a_filename = args.get(1); + const string& b_filename = args.get(2); + auto result = diff_dol_files(a_filename, b_filename); + for (const auto& it : result) { + string data = format_data_string(it.second, nullptr, FormatDataFlags::HEX_ONLY); + fprintf(stdout, "%08" PRIX32 " %s\n", it.first, data.c_str()); + } + }); + Action a_run_server_replay_log( "", nullptr, +[](Arguments& args) { string config_filename = args.get("config");