From 1ad2c4744464d73ddabbed4eed6646a1c43d6d5d Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 24 Apr 2025 18:58:20 -0700 Subject: [PATCH] make $exit work without a quest loaded on most versions --- src/ChatCommands.cc | 41 +++++++++++++------ .../ExitAnywhere/ExitAnywhere.2OEF.patch.s | 26 ++++++++++++ .../ExitAnywhere/ExitAnywhere.2OJ5.patch.s | 26 ++++++++++++ .../ExitAnywhere/ExitAnywhere.2OJF.patch.s | 26 ++++++++++++ .../ExitAnywhere/ExitAnywhere.2OPF.patch.s | 26 ++++++++++++ .../ExitAnywhere/ExitAnywhere.3OE0.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OE1.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OE2.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OJ2.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OJ3.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OJ4.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OJ5.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.3OP0.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OED.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OEU.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OJB.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OJD.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OJU.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OPD.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.4OPU.patch.s | 17 ++++++++ .../ExitAnywhere/ExitAnywhere.59NL.patch.s | 17 ++++++++ 21 files changed, 405 insertions(+), 12 deletions(-) create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.2OEF.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.2OJ5.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.2OJF.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.2OPF.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OE0.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OE1.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OE2.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OJ2.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OJ3.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OJ4.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OJ5.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.3OP0.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OED.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OEU.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OJB.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OJD.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OJU.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OPD.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.4OPU.patch.s create mode 100644 system/client-functions/ExitAnywhere/ExitAnywhere.59NL.patch.s diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 9a5f49a4..de32d6ee 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -908,24 +908,41 @@ ChatCommandDefinition cc_exit( {"$exit"}, +[](const ServerArgs& a) { auto l = a.c->require_lobby(); - if (l->is_game()) { - if (l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) || l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { - G_UnusedHeader cmd = {0x73, 0x01, 0x0000}; - a.c->channel.send(0x60, 0x00, cmd); - a.c->floor = 0; - } else if (is_ep3(a.c->version())) { - a.c->channel.send(0xED, 0x00); - } else { - throw precondition_failed("$C6You must return to\nthe lobby first"); - } - } else { + if (!l->is_game()) { + // Client is in the lobby; send them to the login server (main menu) send_self_leave_notification(a.c); if (!a.c->config.check_flag(Client::Flag::NO_D6)) { send_message_box(a.c, ""); } - send_client_to_login_server(a.c); + return; } + if (is_ep3(a.c->version())) { + // Client is on Ep3; command ED triggers game exit + a.c->channel.send(0xED, 0x00); + return; + } + if (l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) || l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) { + // Client is in a quest; command 6x73 triggers game exit + G_UnusedHeader cmd = {0x73, 0x01, 0x0000}; + a.c->channel.send(0x60, 0x00, cmd); + a.c->floor = 0; + return; + } + if (a.c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) && + a.c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE)) { + auto s = a.c->require_server_state(); + shared_ptr fn; + try { + fn = s->function_code_index->get_patch("ExitAnywhere", a.c->config.specific_version); + } catch (const out_of_range&) { + } + if (fn) { + send_function_call(a.c, fn); + return; + } + } + throw precondition_failed("$C6You must return to\nthe lobby first"); }, +[](const ProxyArgs& a) { if (a.ses->is_in_game) { diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.2OEF.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.2OEF.patch.s new file mode 100644 index 00000000..cbd5c14c --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.2OEF.patch.s @@ -0,0 +1,26 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + mov r2, 0 + mova r0, [addrs] + mov.l r1, [r0] + mov.l [r1], r2 + mov.l r1, [r0 + 4] + mov.l [r1], r2 + mov r2, 1 + mov.l r1, [r0 + 8] + rets + mov.w [r1], r2 + .align 4 +addrs: + .data 0x8C4ED300 + .data 0x8C4ED344 + .data 0x8C4E8D88 diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.2OJ5.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.2OJ5.patch.s new file mode 100644 index 00000000..cbd5c14c --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.2OJ5.patch.s @@ -0,0 +1,26 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + mov r2, 0 + mova r0, [addrs] + mov.l r1, [r0] + mov.l [r1], r2 + mov.l r1, [r0 + 4] + mov.l [r1], r2 + mov r2, 1 + mov.l r1, [r0 + 8] + rets + mov.w [r1], r2 + .align 4 +addrs: + .data 0x8C4ED300 + .data 0x8C4ED344 + .data 0x8C4E8D88 diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.2OJF.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.2OJF.patch.s new file mode 100644 index 00000000..ebbf67fc --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.2OJF.patch.s @@ -0,0 +1,26 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + mov r2, 0 + mova r0, [addrs] + mov.l r1, [r0] + mov.l [r1], r2 + mov.l r1, [r0 + 4] + mov.l [r1], r2 + mov r2, 1 + mov.l r1, [r0 + 8] + rets + mov.w [r1], r2 + .align 4 +addrs: + .data 0x8C4E6DA0 + .data 0x8C4E6DE4 + .data 0x8C4E2828 diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.2OPF.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.2OPF.patch.s new file mode 100644 index 00000000..417e9fe9 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.2OPF.patch.s @@ -0,0 +1,26 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + mov r2, 0 + mova r0, [addrs] + mov.l r1, [r0] + mov.l [r1], r2 + mov.l r1, [r0 + 4] + mov.l [r1], r2 + mov r2, 1 + mov.l r1, [r0 + 8] + rets + mov.w [r1], r2 + .align 4 +addrs: + .data 0x8C4DC800 + .data 0x8C4DC844 + .data 0x8C4D8288 diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OE0.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE0.patch.s new file mode 100644 index 00000000..c76f5b51 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE0.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4748], r0 + stw [r13 - 0x4744], r0 + li r0, 1 + sth [r13 - 0x4938], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OE1.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE1.patch.s new file mode 100644 index 00000000..c76f5b51 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE1.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4748], r0 + stw [r13 - 0x4744], r0 + li r0, 1 + sth [r13 - 0x4938], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OE2.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE2.patch.s new file mode 100644 index 00000000..dd036e11 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OE2.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4728], r0 + stw [r13 - 0x4724], r0 + li r0, 1 + sth [r13 - 0x4918], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ2.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ2.patch.s new file mode 100644 index 00000000..31d0865b --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ2.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4760], r0 + stw [r13 - 0x475C], r0 + li r0, 1 + sth [r13 - 0x4950], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ3.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ3.patch.s new file mode 100644 index 00000000..5f582bb5 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ3.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4758], r0 + stw [r13 - 0x4754], r0 + li r0, 1 + sth [r13 - 0x4948], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ4.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ4.patch.s new file mode 100644 index 00000000..e81f26b0 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ4.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4738], r0 + stw [r13 - 0x4734], r0 + li r0, 1 + sth [r13 - 0x4928], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ5.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ5.patch.s new file mode 100644 index 00000000..e81f26b0 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OJ5.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x4738], r0 + stw [r13 - 0x4734], r0 + li r0, 1 + sth [r13 - 0x4928], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.3OP0.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.3OP0.patch.s new file mode 100644 index 00000000..67dab124 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.3OP0.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + li r0, 0 + stw [r13 - 0x46E8], r0 + stw [r13 - 0x46E4], r0 + li r0, 1 + sth [r13 - 0x48D8], r0 + blr diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OED.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OED.patch.s new file mode 100644 index 00000000..7360aa20 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OED.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x00632934], eax # is_in_quest = false + mov [0x00632930], eax # dat_source_type = NONE + inc eax + mov [0x00723F88], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OEU.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OEU.patch.s new file mode 100644 index 00000000..11589c3c --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OEU.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x006321CC], eax # is_in_quest = false + mov [0x006321C8], eax # dat_source_type = NONE + inc eax + mov [0x00723808], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OJB.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJB.patch.s new file mode 100644 index 00000000..3a26df80 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJB.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x0062D374], eax # is_in_quest = false + mov [0x0062D370], eax # dat_source_type = NONE + inc eax + mov [0x0071E8E8], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OJD.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJD.patch.s new file mode 100644 index 00000000..0a46e991 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJD.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x0062D914], eax # is_in_quest = false + mov [0x0062D910], eax # dat_source_type = NONE + inc eax + mov [0x0071EF48], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OJU.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJU.patch.s new file mode 100644 index 00000000..1ba76beb --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OJU.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x0063544C], eax # is_in_quest = false + mov [0x00635448], eax # dat_source_type = NONE + inc eax + mov [0x00726A88], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OPD.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OPD.patch.s new file mode 100644 index 00000000..7360aa20 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OPD.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x00632934], eax # is_in_quest = false + mov [0x00632930], eax # dat_source_type = NONE + inc eax + mov [0x00723F88], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.4OPU.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.4OPU.patch.s new file mode 100644 index 00000000..82542961 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.4OPU.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x00632CCC], eax # is_in_quest = false + mov [0x00632CC8], eax # dat_source_type = NONE + inc eax + mov [0x00724308], ax # should_leave_game = true + ret diff --git a/system/client-functions/ExitAnywhere/ExitAnywhere.59NL.patch.s b/system/client-functions/ExitAnywhere/ExitAnywhere.59NL.patch.s new file mode 100644 index 00000000..8e65eaa9 --- /dev/null +++ b/system/client-functions/ExitAnywhere/ExitAnywhere.59NL.patch.s @@ -0,0 +1,17 @@ +# This function implements $exit in a game when no quest is loaded. + +.meta name="Exit anywhere" +.meta description="" +.meta hide_from_patches_menu + +entry_ptr: +reloc0: + .offsetof start + +start: + xor eax, eax + mov [0x00A95624], eax # is_in_quest = false + mov [0x00A955E0], eax # dat_source_type = NONE + inc eax + mov [0x00AAE6D4], ax # should_leave_game = true + ret