diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index f0a1aa0a..f3d62ad7 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -24,6 +24,7 @@ #include #ifdef HAVE_RESOURCE_FILE #include +#include #endif #include "ChatCommands.hh" @@ -666,6 +667,7 @@ static HandlerResult S_B1(shared_ptr ses, uint16_t, return HandlerResult::Type::SUPPRESS; } +template static HandlerResult S_B2(shared_ptr ses, uint16_t, uint32_t flag, string& data) { const auto& cmd = check_size_t(data, 0xFFFF); @@ -711,33 +713,51 @@ static HandlerResult S_B2(shared_ptr ses, uint16_t, ses->log.info("Wrote code from server to file %s", output_filename.c_str()); #ifdef HAVE_RESOURCE_FILE - if (is_gc(ses->version())) { + using FooterT = S_ExecuteCode_Footer_B2; + using U16T = typename std::conditional::type; + using U32T = typename std::conditional::type; + // TODO: Support SH-4 disassembly too + bool is_ppc = ::is_ppc(ses->version()); + bool is_x86 = ::is_x86(ses->version()); + if (is_ppc || is_x86) { try { - if (code.size() < sizeof(S_ExecuteCode_Footer_GC_B2)) { + if (code.size() < sizeof(FooterT)) { throw runtime_error("code section is too small"); } - size_t footer_offset = code.size() - sizeof(S_ExecuteCode_Footer_GC_B2); + size_t footer_offset = code.size() - sizeof(FooterT); StringReader r(code.data(), code.size()); - const auto& footer = r.pget(footer_offset); + const auto& footer = r.pget(footer_offset); multimap labels; r.go(footer.relocations_offset); uint32_t reloc_offset = 0; for (size_t x = 0; x < footer.num_relocations; x++) { - reloc_offset += (r.get_u16b() * 4); + reloc_offset += (r.get() * 4); labels.emplace(reloc_offset, string_printf("reloc%zu", x)); } labels.emplace(footer.entrypoint_addr_offset.load(), "entry_ptr"); labels.emplace(footer_offset, "footer"); - labels.emplace(r.pget_u32b(footer.entrypoint_addr_offset), "start"); + labels.emplace(r.pget(footer.entrypoint_addr_offset), "start"); - string disassembly = PPC32Emulator::disassemble( - &r.pget(0, code.size()), - code.size(), - 0, - &labels); + string disassembly; + if (is_ppc) { + disassembly = PPC32Emulator::disassemble( + &r.pget(0, code.size()), + code.size(), + 0, + &labels); + } else if (is_x86) { + disassembly = X86Emulator::disassemble( + &r.pget(0, code.size()), + code.size(), + 0, + &labels); + } else { + // We shouldn't have entered the outer if statement if this happens + throw logic_error("unsupported architecture"); + } output_filename = string_printf("code.%" PRId64 ".txt", filename_timestamp); { @@ -1247,7 +1267,8 @@ static HandlerResult S_13_A7(shared_ptr ses, uint16_ save_file(sf->output_filename, data); if (ends_with(sf->basename, ".bin")) { try { - auto disassembly = disassemble_quest_script(data.data(), data.size(), ses->version(), ses->language(), false); + string decompressed = prs_decompress(data); + auto disassembly = disassemble_quest_script(decompressed.data(), decompressed.size(), ses->version(), ses->language(), false); save_file(sf->output_filename + ".txt", disassembly); } catch (const exception& e) { ses->log.warning("Failed to disassemble quest file: %s", e.what()); @@ -1992,9 +2013,9 @@ static on_command_t handlers[0x100][NUM_VERSIONS][2] = { /* AF */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, // CMD S_PC_PATCH C S_BB_PATCH C S_DC_NTE C S_DC_V1_12_2000_PROTO C S_DC_V1 C S_DC_V2 C S_PC_NTE C S_PC_V2 C S_GC_NTE C S_GC_V3 C S_GC_EP3_NTE C S_GC_EP3 C S_XB_V3 C S_BB_V4 C /* B0 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}}, -/* B1 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}}, -/* B2 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}}, -/* B3 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}}, +/* B1 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}, {S_B1, nullptr}}, +/* B2 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}, {S_B2, nullptr}}, +/* B3 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}, {S_invalid, C_B3}}, /* B4 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* B5 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* B6 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 98e38e3e..2616d24d 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -2795,7 +2795,7 @@ static void on_B3(shared_ptr c, uint16_t, uint32_t flag, string& data) { throw logic_error("unknown function called during DOL loading"); } } else { - throw runtime_error("function call response queue is empty, and no program is being sent"); + c->log.warning("Received patch response but function call response queue is empty and no program is being sent"); } } diff --git a/src/Version.hh b/src/Version.hh index 23aeafef..7243afd1 100644 --- a/src/Version.hh +++ b/src/Version.hh @@ -83,6 +83,27 @@ inline bool is_gc(Version version) { (version == Version::GC_EP3); } +inline bool is_sh4(Version version) { + return (version == Version::DC_NTE) || + (version == Version::DC_V1_11_2000_PROTOTYPE) || + (version == Version::DC_V1) || + (version == Version::DC_V2); +} +inline bool is_x86(Version version) { + return (version == Version::PC_PATCH) || + (version == Version::BB_PATCH) || + (version == Version::PC_NTE) || + (version == Version::PC_V2) || + (version == Version::XB_V3) || + (version == Version::BB_V4); +} +inline bool is_ppc(Version version) { + return (version == Version::GC_NTE) || + (version == Version::GC_V3) || + (version == Version::GC_EP3_NTE) || + (version == Version::GC_EP3); +} + inline bool is_big_endian(Version version) { return (version == Version::GC_NTE) || (version == Version::GC_V3) ||