From 8594e5af3c9349f3fdb7046a9181ed961e285688 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 10 Mar 2024 12:06:32 -0700 Subject: [PATCH] add condition clearing and auto-revive to infinite hp mode --- src/ChatCommands.cc | 10 ++- src/CommandFormats.hh | 13 ++-- src/FunctionCompiler.cc | 6 ++ src/FunctionCompiler.hh | 2 + src/ProxyCommands.cc | 2 +- src/ReceiveSubcommands.cc | 57 ++++++++++++++-- src/SendCommands.cc | 68 +++++++++++++++++-- src/SendCommands.hh | 1 + src/ServerState.cc | 1 + src/ServerState.hh | 1 + .../CallProtectedHandler.3OE0.patch.s | 14 ++++ .../CallProtectedHandler.3OE1.patch.s | 14 ++++ .../CallProtectedHandler.3OE2.patch.s | 14 ++++ .../CallProtectedHandler.3OJ2.patch.s | 14 ++++ .../CallProtectedHandler.3OJ3.patch.s | 14 ++++ .../CallProtectedHandler.3OJ4.patch.s | 14 ++++ .../CallProtectedHandler.3OJ5.patch.s | 14 ++++ .../CallProtectedHandler.3OP0.patch.s | 14 ++++ .../CallProtectedHandler.4OED.patch.s | 14 ++++ .../CallProtectedHandler.4OEU.patch.s | 14 ++++ .../CallProtectedHandler.4OJB.patch.s | 14 ++++ .../CallProtectedHandler.4OJD.patch.s | 14 ++++ .../CallProtectedHandler.4OJU.patch.s | 14 ++++ .../CallProtectedHandler.4OPD.patch.s | 14 ++++ .../CallProtectedHandler.4OPU.patch.s | 14 ++++ .../CallProtectedHandlerGC.ppc.inc.s | 32 +++++++++ .../CallProtectedHandlerXB.x86.inc.s | 20 ++++++ system/config.example.json | 8 ++- tests/config.json | 2 + 29 files changed, 407 insertions(+), 26 deletions(-) create mode 100644 system/client-functions/CallProtectedHandler.3OE0.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OE1.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OE2.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OJ2.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OJ3.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OJ4.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OJ5.patch.s create mode 100644 system/client-functions/CallProtectedHandler.3OP0.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OED.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OEU.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OJB.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OJD.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OJU.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OPD.patch.s create mode 100644 system/client-functions/CallProtectedHandler.4OPU.patch.s create mode 100644 system/client-functions/CallProtectedHandlerGC.ppc.inc.s create mode 100644 system/client-functions/CallProtectedHandlerXB.x86.inc.s diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 7832353f..ff7f98ee 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -597,8 +597,7 @@ static void server_command_patch(shared_ptr c, const std::string& args) auto s = c->require_server_state(); // Note: We can't look this up outside of the closure because // c->specific_version can change during prepare_client_for_patches - auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at( - string_printf("%s-%08" PRIX32, args.c_str(), c->config.specific_version)); + auto fn = s->function_code_index->get_patch(args, c->config.specific_version); send_function_call(c, fn); c->function_call_response_queue.emplace_back(empty_function_call_response_handler); } catch (const out_of_range&) { @@ -617,8 +616,7 @@ static void proxy_command_patch(shared_ptr ses, cons ses->log.info("Version detected as %08" PRIX32, ses->config.specific_version); } auto s = ses->require_server_state(); - auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at( - string_printf("%s-%08" PRIX32, args.c_str(), ses->config.specific_version)); + auto fn = s->function_code_index->get_patch(args, ses->config.specific_version); send_function_call(ses->client_channel, ses->config, fn); // Don't forward the patch response to the server ses->function_call_return_handler_queue.emplace_back(empty_patch_return_handler); @@ -1638,7 +1636,7 @@ static void server_command_infinite_hp(shared_ptr c, const std::string&) c->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED); bool enabled = c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED); send_text_message_printf(c, "$C6Infinite HP %s", enabled ? "enabled" : "disabled"); - if (enabled && l->is_game() && is_v1_or_v2(c->version())) { + if (enabled && l->is_game()) { send_remove_conditions(c); } } @@ -1649,7 +1647,7 @@ static void proxy_command_infinite_hp(shared_ptr ses ses->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED); bool enabled = ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED); send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", enabled ? "enabled" : "disabled"); - if (enabled && ses->is_in_game && is_v1_or_v2(ses->version())) { + if (enabled && ses->is_in_game) { send_remove_conditions(ses->client_channel, ses->lobby_client_id); send_remove_conditions(ses->server_channel, ses->lobby_client_id); } diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 656b6998..264bb9ce 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4422,15 +4422,15 @@ struct G_PlayerDied_6x4D { le_uint32_t unknown_a1 = 0; } __packed__; -// 6x4E: Player died (protected on V3/V4) +// 6x4E: Player is dead can be revived (protected on V3/V4) -struct G_PlayerDied_6x4E { +struct G_PlayerRevivable_6x4E { G_ClientIDHeader header; } __packed__; -// 6x4F: Player resurrected (via Scape Doll) (protected on V3/V4) +// 6x4F: Player revived (protected on V3/V4) -struct G_PlayerUsedScapeDoll_6x4F { +struct G_PlayerRevived_6x4F { G_ClientIDHeader header; } __packed__; @@ -5362,10 +5362,9 @@ struct G_GalGryphonBossActions_6xA0 { parray unknown_a4; } __packed__; -// 6xA1: Unknown (not valid on pre-V3) (protected on V3/V4) -// Part of revive process. Occurs right after revive command; function unclear. +// 6xA1: Revive player (not valid on pre-V3) (protected on V3/V4) -struct G_Unknown_6xA1 { +struct G_RevivePlayer_V3_BB_6xA1 { G_ClientIDHeader header; } __packed__; diff --git a/src/FunctionCompiler.cc b/src/FunctionCompiler.cc index 70d829b2..369012b8 100644 --- a/src/FunctionCompiler.cc +++ b/src/FunctionCompiler.cc @@ -345,6 +345,12 @@ bool FunctionCodeIndex::patch_menu_empty(uint32_t specific_version) const { return true; } +std::shared_ptr FunctionCodeIndex::get_patch( + const std::string& name, uint32_t specific_version) const { + return this->name_and_specific_version_to_patch_function.at( + string_printf("%s-%08" PRIX32, name.c_str(), specific_version)); +} + DOLFileIndex::DOLFileIndex(const string& directory) { if (!function_compiler_available()) { function_compiler_log.info("Function compiler is not available"); diff --git a/src/FunctionCompiler.hh b/src/FunctionCompiler.hh index 0cfe866a..ee77ee7a 100644 --- a/src/FunctionCompiler.hh +++ b/src/FunctionCompiler.hh @@ -70,6 +70,8 @@ struct FunctionCodeIndex { std::shared_ptr patch_menu(uint32_t specific_version) const; bool patch_menu_empty(uint32_t specific_version) const; + + std::shared_ptr get_patch(const std::string& name, uint32_t specific_version) const; }; struct DOLFileIndex { diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index ce9b328a..9eab67ca 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -1962,7 +1962,7 @@ HandlerResult C_6x(shared_ptr ses, uint16_t, u ses->floor = cmd.floor; } else if (data[0] == 0x0C) { - if (is_v1_or_v2(ses->version()) && ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { + if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { send_remove_conditions(ses->client_channel, ses->lobby_client_id); send_remove_conditions(ses->server_channel, ses->lobby_client_id); } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index d642bf6e..29858979 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1350,15 +1350,15 @@ static void on_change_floor_6x21(shared_ptr c, uint8_t command, uint8_t forward_subcommand(c, command, flag, data, size); } -// When a player dies, decrease their mag's synchro static void on_player_died(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { - const auto& cmd = check_size_t(data, size, 0xFFFF); + const auto& cmd = check_size_t(data, size, 0xFFFF); auto l = c->require_lobby(); - if (!l->is_game() || (cmd.client_id != c->lobby_client_id)) { + if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; } + // Decrease MAG's synchro try { auto& inventory = c->character()->inventory; size_t mag_index = inventory.find_equipped_item(EquipSlot::MAG); @@ -1370,13 +1370,58 @@ static void on_player_died(shared_ptr c, uint8_t command, uint8_t flag, forward_subcommand(c, command, flag, data, size); } +static void on_player_revivable(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + const auto& cmd = check_size_t(data, size, 0xFFFF); + + auto l = c->require_lobby(); + if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { + return; + } + + forward_subcommand(c, command, flag, data, size); + + // Revive if infinite HP is enabled + bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE)); + if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { + G_UseMedicalCenter_6x31 v2_cmd = {0x31, 0x01, c->lobby_client_id}; + G_RevivePlayer_V3_BB_6xA1 v3_cmd = {0xA1, 0x01, c->lobby_client_id}; + for (auto lc : l->clients) { + if (!lc) { + continue; + } + bool use_v3 = !is_v1_or_v2(lc->version()) || (lc->version() == Version::GC_NTE); + const void* data = use_v3 ? static_cast(&v3_cmd) : static_cast(&v2_cmd); + size_t size = use_v3 ? sizeof(v3_cmd) : sizeof(v2_cmd); + if (lc == c) { + send_protected_command(lc, data, size, false); + } else { + send_command(lc, 0x60, 0x00, data, size); + } + } + } +} + +void on_player_revived(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + check_size_t(data, size, 0xFFFF); + + auto l = c->require_lobby(); + if (l->is_game()) { + forward_subcommand(c, command, flag, data, size); + bool player_cheats_enabled = !is_v1(c->version()) && + (l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE))); + if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { + send_player_stats_change(c, PlayerStatsChange::ADD_HP, 2550); + } + } +} + static void on_received_condition(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { const auto& cmd = check_size_t(data, size, 0xFFFF); auto l = c->require_lobby(); if (l->is_game()) { forward_subcommand(c, command, flag, data, size); - if (is_v1_or_v2(c->version()) && (cmd.client_id == c->lobby_client_id)) { + if (cmd.client_id == c->lobby_client_id) { bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE)); if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) { send_remove_conditions(c); @@ -4126,8 +4171,8 @@ const SubcommandDefinition subcommand_definitions[0x100] = { /* 6x4B */ {0x40, 0x46, 0x4B, on_change_hp}, /* 6x4C */ {0x41, 0x47, 0x4C, on_change_hp}, /* 6x4D */ {0x42, 0x48, 0x4D, on_player_died}, - /* 6x4E */ {0x00, 0x00, 0x4E, on_forward_check_game_client}, - /* 6x4F */ {0x43, 0x49, 0x4F, on_forward_check_game_client}, + /* 6x4E */ {0x00, 0x00, 0x4E, on_player_revivable}, + /* 6x4F */ {0x43, 0x49, 0x4F, on_player_revived}, /* 6x50 */ {0x44, 0x4A, 0x50, on_forward_check_game_client}, /* 6x51 */ {0x00, 0x00, 0x51, on_invalid}, /* 6x52 */ {0x46, 0x4C, 0x52, on_set_animation_state}, diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 5f3f8319..2c9f152a 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -477,6 +477,62 @@ void send_function_call( ch.send(0xB2, code ? code->index : 0x00, data); } +bool send_protected_command(std::shared_ptr c, const void* data, size_t size, bool echo_to_lobby) { + switch (c->version()) { + case Version::DC_NTE: + case Version::DC_V1_11_2000_PROTOTYPE: + case Version::DC_V1: + case Version::DC_V2: + case Version::PC_NTE: + case Version::PC_V2: + case Version::GC_NTE: + if (echo_to_lobby) { + send_command(c->require_lobby(), 0x60, 0x00, data, size); + } else { + send_command(c, 0x60, 0x00, data, size); + } + return true; + + case Version::GC_V3: + case Version::XB_V3: + case Version::GC_EP3_NTE: + case Version::GC_EP3: { + auto s = c->require_server_state(); + if (!s->enable_v3_v4_protected_subcommands || + c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) || + c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) { + return false; + } + + prepare_client_for_patches(c, [wc = weak_ptr(c), data = string(reinterpret_cast(data), size), echo_to_lobby]() { + auto c = wc.lock(); + if (!c) { + return; + } + try { + auto s = c->require_server_state(); + auto fn = s->function_code_index->get_patch("CallProtectedHandler", c->config.specific_version); + uint32_t size_label_value = is_big_endian(c->version()) ? data.size() : bswap32(data.size()); + send_function_call(c, fn, {{"size", size_label_value}}, data); + c->function_call_response_queue.emplace_back(empty_function_call_response_handler); + if (echo_to_lobby) { + auto l = c->lobby.lock(); + if (l) { + send_command_excluding_client(l, c, 0x60, 0x00, data.data(), data.size()); + } + } + } catch (const exception& e) { + c->log.warning("Failed to send protected command: %s", e.what()); + } + }); + return true; + } + + default: + return false; + } +} + void send_reconnect(shared_ptr c, uint32_t address, uint16_t port) { S_Reconnect_19 cmd = {{address, port, 0}}; send_command_t(c, is_patch(c->version()) ? 0x14 : 0x19, 0x00, cmd); @@ -2354,12 +2410,14 @@ void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange } void send_remove_conditions(shared_ptr c) { - auto l = c->require_lobby(); - for (auto& lc : l->clients) { - if (lc) { - send_remove_conditions(lc->channel, c->lobby_client_id); - } + parray cmds; + for (size_t z = 0; z < 4; z++) { + auto& cmd = cmds[z]; + cmd.header = {0x0D, sizeof(G_AddOrRemoveCondition_6x0C_6x0D) >> 2, c->lobby_client_id}; + cmd.unknown_a1 = z; + cmd.unknown_a2 = 0; } + send_protected_command(c, &cmds, sizeof(cmds), true); } void send_remove_conditions(Channel& ch, uint16_t client_id) { diff --git a/src/SendCommands.hh b/src/SendCommands.hh index e402fe14..c22a8896 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -159,6 +159,7 @@ void send_function_call( uint32_t checksum_addr = 0, uint32_t checksum_size = 0, uint32_t override_relocations_offset = 0); +bool send_protected_command(std::shared_ptr c, const void* data, size_t size, bool echo_to_lobby); void send_reconnect(std::shared_ptr c, uint32_t address, uint16_t port); void send_pc_console_split_reconnect( diff --git a/src/ServerState.cc b/src/ServerState.cc index 14aaa79c..2d507474 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -734,6 +734,7 @@ void ServerState::load_config_early() { this->default_rare_notifs_enabled_v1_v2 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV1V2", this->default_rare_notifs_enabled_v1_v2); this->default_rare_notifs_enabled_v3_v4 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV3V4", this->default_rare_notifs_enabled_v3_v4); this->ep3_send_function_call_enabled = this->config_json->get_bool("EnableEpisode3SendFunctionCall", false); + this->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false); this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true); auto parse_int_list = +[](const JSON& json) -> vector { diff --git a/src/ServerState.hh b/src/ServerState.hh index 52227c67..d1f8b9cb 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -113,6 +113,7 @@ struct ServerState : public std::enable_shared_from_this { QuestFlagsForDifficulty quest_flag_persist_mask; uint64_t persistent_game_idle_timeout_usecs = 0; bool ep3_send_function_call_enabled = false; + bool enable_v3_v4_protected_subcommands = false; bool catch_handler_exceptions = true; bool ep3_infinite_meseta = false; std::vector ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800}; diff --git a/system/client-functions/CallProtectedHandler.3OE0.patch.s b/system/client-functions/CallProtectedHandler.3OE0.patch.s new file mode 100644 index 00000000..47d6a71a --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OE0.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805C5650 + .data 0x801E3F9C +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OE1.patch.s b/system/client-functions/CallProtectedHandler.3OE1.patch.s new file mode 100644 index 00000000..99b30969 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OE1.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805CC630 + .data 0x801E3F9C +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OE2.patch.s b/system/client-functions/CallProtectedHandler.3OE2.patch.s new file mode 100644 index 00000000..a9cdd563 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OE2.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805D5E50 + .data 0x801E405C +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OJ2.patch.s b/system/client-functions/CallProtectedHandler.3OJ2.patch.s new file mode 100644 index 00000000..fe10948a --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OJ2.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805C4D58 + .data 0x801E3B38 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OJ3.patch.s b/system/client-functions/CallProtectedHandler.3OJ3.patch.s new file mode 100644 index 00000000..934f9d5c --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OJ3.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805CF320 + .data 0x801E40BC +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OJ4.patch.s b/system/client-functions/CallProtectedHandler.3OJ4.patch.s new file mode 100644 index 00000000..99617000 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OJ4.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805D67A0 + .data 0x801E4290 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OJ5.patch.s b/system/client-functions/CallProtectedHandler.3OJ5.patch.s new file mode 100644 index 00000000..23da6d5b --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OJ5.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805D6540 + .data 0x801E4008 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.3OP0.patch.s b/system/client-functions/CallProtectedHandler.3OP0.patch.s new file mode 100644 index 00000000..9cb290b2 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.3OP0.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerGC + .data 0x805D2090 + .data 0x801E4698 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OED.patch.s b/system/client-functions/CallProtectedHandler.4OED.patch.s new file mode 100644 index 00000000..e000cd66 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OED.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x00723F68 + .data 0x002DDB00 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OEU.patch.s b/system/client-functions/CallProtectedHandler.4OEU.patch.s new file mode 100644 index 00000000..40d11b07 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OEU.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x007237E8 + .data 0x002DE000 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OJB.patch.s b/system/client-functions/CallProtectedHandler.4OJB.patch.s new file mode 100644 index 00000000..069b619d --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OJB.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x0071E8C8 + .data 0x002DBBA0 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OJD.patch.s b/system/client-functions/CallProtectedHandler.4OJD.patch.s new file mode 100644 index 00000000..ab23e427 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OJD.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x0071EF28 + .data 0x002DC720 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OJU.patch.s b/system/client-functions/CallProtectedHandler.4OJU.patch.s new file mode 100644 index 00000000..f1af4f3a --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OJU.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x00726A68 + .data 0x002DDFE0 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OPD.patch.s b/system/client-functions/CallProtectedHandler.4OPD.patch.s new file mode 100644 index 00000000..57af46bc --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OPD.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x00723F68 + .data 0x002DDB30 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandler.4OPU.patch.s b/system/client-functions/CallProtectedHandler.4OPU.patch.s new file mode 100644 index 00000000..2f0fb949 --- /dev/null +++ b/system/client-functions/CallProtectedHandler.4OPU.patch.s @@ -0,0 +1,14 @@ +.meta hide_from_patches_menu +.meta name="CallProtectedHandler" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + .include CallProtectedHandlerXB + .data 0x007242E8 + .data 0x002DE030 +size: + .data 0x00000000 +data: diff --git a/system/client-functions/CallProtectedHandlerGC.ppc.inc.s b/system/client-functions/CallProtectedHandlerGC.ppc.inc.s new file mode 100644 index 00000000..ab405124 --- /dev/null +++ b/system/client-functions/CallProtectedHandlerGC.ppc.inc.s @@ -0,0 +1,32 @@ + stwu [r1 - 0x10], r1 + mflr r0 + stw [r1 + 0x14], r0 + stw [r1 + 0x08], r31 + stw [r1 + 0x0C], r30 + + b get_data_addr +resume: + mflr r31 + + lwz r30, [r31] + li r0, 1 + stw [r30], r0 + + addi r3, r31, 0x0C + lwz r4, [r31 + 8] + lwz r0, [r31 + 4] + mtctr r0 + bctrl + + li r0, 0 + stw [r30], r0 + + lwz r30, [r1 + 0x0C] + lwz r31, [r1 + 0x08] + lwz r0, [r1 + 0x14] + mtlr r0 + addi r1, r1, 0x10 + blr + +get_data_addr: + bl resume diff --git a/system/client-functions/CallProtectedHandlerXB.x86.inc.s b/system/client-functions/CallProtectedHandlerXB.x86.inc.s new file mode 100644 index 00000000..a9e773a9 --- /dev/null +++ b/system/client-functions/CallProtectedHandlerXB.x86.inc.s @@ -0,0 +1,20 @@ + jmp get_data_addr +resume: + xchg ebx, [esp] + + mov edx, [ebx] + mov dword [edx], 1 + + mov edx, [ebx + 4] + lea ecx, [ebx + 0x0C] + mov eax, [ebx + 8] + call edx + + mov edx, [ebx] + mov dword [edx], 0 + + pop ebx + ret + +get_data_addr: + call resume diff --git a/system/config.example.json b/system/config.example.json index 48cab806..9e005e48 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -966,7 +966,13 @@ // exploiting a bug in Episode 3, and while it seems to work reliably on // Dolphin, it hasn't been tested on a real GameCube. So, newserv doesn't // enable Episode 3 USA patches by default; it only does if this option is on. - // "EnableEpisode3SendFunctionCall": true, + "EnableEpisode3SendFunctionCall": false, + + // Whether to enable protected subcommands on GC and Xbox. This enables the + // infinite HP cheat to also automatically revive players and clear conditions + // like poison and freeze. (On v1 and v2, those functions are always enabled + // in infinite HP mode.) + "EnableV3V4ProtectedSubcommands": false, // Whether to allow cross-play for various game versions. DCv1 and DCv2 are // always allowed to join each other's games (though DCv2 can deny permission diff --git a/tests/config.json b/tests/config.json index ffa90e28..55ce4a08 100644 --- a/tests/config.json +++ b/tests/config.json @@ -40,6 +40,8 @@ "PPPStackListen": [], "HTTPListen": [], "Episode3BehaviorFlags": 0xFA, + "EnableEpisode3SendFunctionCall": false, + "EnableV3V4ProtectedSubcommands": false, "Episode3InfiniteMeseta": false, "Episode3DefeatPlayerMeseta": [400, 500, 600, 700, 800],