diff --git a/src/Client.cc b/src/Client.cc index 30fb4557..834ded3d 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -37,9 +37,6 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version) switch (sub_version) { case -1: // Initial check (before sub_version recognition) - // Note: BB does not appear here because we always get its sub_version in - // the very first command; there is no way to get here for a BB client - // before we know the client's sub_version. switch (version) { case Version::PC_PATCH: case Version::BB_PATCH: @@ -127,10 +124,9 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version) this->set_flag(Flag::IS_CLIENT_CUSTOMIZATION); [[fallthrough]]; case 0x36: // GC Ep1&2 US v1.2 (Plus) - this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST); - [[fallthrough]]; case 0x39: // GC Ep1&2 JP v1.5 (Plus) this->set_flag(Flag::NO_D6_AFTER_LOBBY); + this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST); break; case 0x40: // GC Ep3 JP and Trial Edition (and BB) this->set_flag(Flag::NO_D6_AFTER_LOBBY); @@ -141,13 +137,10 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version) // instead look at header.flag in the 61 command and set the version then. break; case 0x41: // GC Ep3 US (and BB) - this->set_flag(Flag::NO_D6_AFTER_LOBBY); - this->set_flag(Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL); - this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH); - break; case 0x42: // GC Ep3 EU 50Hz case 0x43: // GC Ep3 EU 60Hz this->set_flag(Flag::NO_D6_AFTER_LOBBY); + this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST); break; default: throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version)); diff --git a/src/Client.hh b/src/Client.hh index 36fe30f5..e90dff44 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -48,7 +48,6 @@ public: ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000, SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000, SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000, - USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000, CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000, AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 71c93e5e..e4667335 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -236,20 +236,6 @@ static void send_proxy_destinations_menu(shared_ptr c) { send_menu(c, s->proxy_destinations_menu(c->version())); } -static bool send_enable_send_function_call_if_applicable(shared_ptr c) { - auto s = c->require_server_state(); - if (function_compiler_available() && c->config.check_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) { - if (s->ep3_send_function_call_enabled) { - send_quest_buffer_overflow(c); - } else { - c->config.clear_flag(Client::Flag::HAS_SEND_FUNCTION_CALL); - } - c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL); - return true; - } - return false; -} - //////////////////////////////////////////////////////////////////////////////// void on_connect(std::shared_ptr c) { @@ -379,7 +365,6 @@ void on_login_server_login_complete(shared_ptr c) { c->config.check_flag(Client::Flag::NO_D6) || !c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) { c->config.clear_flag(Client::Flag::AT_WELCOME_MESSAGE); - send_enable_send_function_call_if_applicable(c); send_update_client_config(c, false); send_main_menu(c); } else { @@ -398,10 +383,27 @@ void on_login_complete(shared_ptr c) { if (c->config.check_flag(Client::Flag::CAN_RECEIVE_ENABLE_B2_QUEST) && !c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) && - (s->ep12_plus_send_function_call_quest_num >= 0)) { - auto q = s->quest_index(c->version())->get(s->ep12_plus_send_function_call_quest_num); + (s->enable_send_function_call_quest_num >= 0)) { + auto q = s->default_quest_index->get(s->enable_send_function_call_quest_num); if (q) { - auto vq = q->version(c->version(), (c->sub_version == 0x39 ? 0 : 1)); + uint8_t q_language; + switch (c->sub_version) { + case 0x39: + q_language = 0; // Japanese (JP Plus v1.5) + break; + case 0x42: + case 0x43: + q_language = 2; // German (EU Ep3) + break; + case 0x41: + q_language = 4; // Spanish (US Ep3) + break; + case 0x36: + case 0x3A: + default: + q_language = 1; // English (US Plus v1.2 + customizations) + } + auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), q_language); if (vq) { c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL); c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH); @@ -908,18 +910,6 @@ static void on_9D_9E(shared_ptr c, uint16_t command, uint32_t, string& d c->channel.language = base_cmd->language; set_console_client_flags(c, base_cmd->sub_version); - // See system/client-functions/Episode3USAQuestBufferOverflow.ppc.s for where - // this value gets set. We use this to determine if the client has already run - // the code or not; sending it again when the client has already run it will - // likely cause the client to crash. - if (base_cmd->unused1 == 0x5F5CA297) { - c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL); - c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL); - } else if (!s->ep3_send_function_call_enabled && c->config.check_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) { - c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL); - c->config.clear_flag(Client::Flag::HAS_SEND_FUNCTION_CALL); - } - try { switch (c->version()) { case Version::DC_V2: { @@ -1775,7 +1765,6 @@ static void on_D6_V3(shared_ptr c, uint16_t, uint32_t, string& data) { send_menu(c, s->information_menu(c->version())); } else if (c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) { c->config.clear_flag(Client::Flag::AT_WELCOME_MESSAGE); - send_enable_send_function_call_if_applicable(c); send_update_client_config(c, false); send_main_menu(c); } diff --git a/src/ServerState.cc b/src/ServerState.cc index 4d62eca9..55989dc3 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -793,8 +793,7 @@ void ServerState::load_config_early() { this->default_rare_notifs_enabled_v3_v4 = this->default_rare_notifs_enabled_v1_v2; 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->ep12_plus_send_function_call_quest_num = this->config_json->get_int("PSOPlusSendFunctionCallQuestNumber", -1); - this->ep3_send_function_call_enabled = this->config_json->get_bool("EnableEpisode3SendFunctionCall", false); + this->enable_send_function_call_quest_num = this->config_json->get_int("EnableSendFunctionCallQuestNumber", -1); this->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false); this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true); diff --git a/src/ServerState.hh b/src/ServerState.hh index b34767a4..c458939e 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -120,8 +120,7 @@ struct ServerState : public std::enable_shared_from_this { std::unordered_map quest_flag_rewrites_v4; std::unordered_map> quest_counter_fields; // For $qfread command uint64_t persistent_game_idle_timeout_usecs = 0; - int64_t ep12_plus_send_function_call_quest_num = -1; - bool ep3_send_function_call_enabled = false; + int64_t enable_send_function_call_quest_num = -1; bool enable_v3_v4_protected_subcommands = false; bool catch_handler_exceptions = true; bool ep3_infinite_meseta = false; diff --git a/system/client-functions/Episode3/AllCards.3SE0.patch.s b/system/client-functions/Episode3/AllCards.3SE0.patch.s index a6e56a1d..7df0335e 100644 --- a/system/client-functions/Episode3/AllCards.3SE0.patch.s +++ b/system/client-functions/Episode3/AllCards.3SE0.patch.s @@ -1,10 +1,6 @@ # This patch gives you the maximum number of each card. It only works if used # in-game, which means it must be used by running `$patch AllCards`. -# This patch is only for PSO Episode 3 USA, which means it requires the -# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that -# option is disabled, the Patches menu won't appear for the client. - .meta hide_from_patches_menu .meta name="Get all cards" .meta description="This patch gives you\nthe maximum number\nof each card." diff --git a/system/client-functions/Episode3/Editors.3SE0.patch.s b/system/client-functions/Episode3/Editors.3SE0.patch.s index 9806cb57..6b028503 100644 --- a/system/client-functions/Episode3/Editors.3SE0.patch.s +++ b/system/client-functions/Episode3/Editors.3SE0.patch.s @@ -4,13 +4,9 @@ # present in PSO PC and PSOX as well, but not in GC Episodes 1 & 2. There are # notes in the below comments that may help get these editors working on PSO PC. -# This patch is only for PSO Episode 3 USA, which means it requires the -# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that -# option is disabled, the Patches menu won't appear for the client. If this -# patch is run on a different client version, it will do nothing. Also, this -# patch must not be run from the Patches menu - it should only be run with the -# $patch command, since the client will likely crash if the player is not in a -# game or lobby when the patch runs. +# This patch must not be run from the Patches menu - it should only be run with +# the $patch command, since the client will likely crash if the player is not +# in a game or lobby when the patch runs. .meta hide_from_patches_menu .meta name="Editors" diff --git a/system/client-functions/Episode3/VIPCard.3SE0.patch.s b/system/client-functions/Episode3/VIPCard.3SE0.patch.s index 96263984..3f919b0b 100644 --- a/system/client-functions/Episode3/VIPCard.3SE0.patch.s +++ b/system/client-functions/Episode3/VIPCard.3SE0.patch.s @@ -1,8 +1,3 @@ -# This patch is only for PSO Episode 3 USA, which means it requires the -# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that -# option is disabled, the Patches menu won't appear for the client. If this -# patch is run on a different client version, it will do nothing. - .meta name="Get VIP card" .meta description="Gives you a VIP card" diff --git a/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s b/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s index e444291b..3fd998c0 100644 --- a/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s +++ b/system/client-functions/System/Episode3USAQuestBufferOverflow.ppc.s @@ -1,3 +1,7 @@ +# This program was an early attempt at restoring B2 patching functionality to +# Episode 3. It is no longer used, since the quest loading method is more +# reliable, but this file remains for documentation purposes. + # There is a buffer overflow bug in PSO Episode 3 that this program uses to # achieve arbitrary code execution. (This bug is likely present in all versions # of PSO, but the code here is specific to the USA version of Episode 3.) This diff --git a/system/config.example.json b/system/config.example.json index 7ab02de9..f8a26141 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -1055,15 +1055,13 @@ // load, then wait for the client to leave the "game", before even getting to // the welcome message. // This quest is not intended to be localized since it should not contain any - // user-visible text, so the server sends the English version for PSO USA - // v1.2, and the Japanese version for PSO JP v1.5, regardless of the client's - // language setting. The quest is not used on any other PSO version. - "PSOPlusSendFunctionCallQuestNumber": -1, - // Whether to enable patches on Episode 3 USA. This functionality depends on - // 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": false, + // user-visible text, so the server uses the language field to determine + // which quest to send based on the client's version: + // - US Plus v1.2 + customizations: English + // - JP Plus v1.5: Japanese + // - US Ep3: Spanish + // - EU Ep3: German + "EnableSendFunctionCallQuestNumber": -1, // Whether to enable protected subcommands on GC and Xbox. This enables the // infinite HP cheat to also automatically revive players and clear conditions diff --git a/system/quests/hidden/q88500-gc-g.bin.txt b/system/quests/hidden/q88500-gc-g.bin.txt new file mode 100644 index 00000000..441adab9 --- /dev/null +++ b/system/quests/hidden/q88500-gc-g.bin.txt @@ -0,0 +1,47 @@ +.version GC_EP3 +.quest_num 88500 +.language 1 +.episode Episode1 +.name "GC Ep3 EU patch enabler" +.short_desc "" +.long_desc "" + +start: + leti r3, 0x80004000 + write4 0x80454E04, 0x80109FB4 + write4 0x80454E08, 0x8000C324 + write4 0x80454E0C, r3 + + read4 r0, 0x8057CA10 + leto r4, code + read4 r4, r4 + add r4, r0 + leto r5, code_end + read4 r5, r5 + add r5, r0 + +copy_byte: + jmp_eq r4, r5, copy_done + read1 r0, r4 + write1 r3, r0 + addi r3, 1 + addi r4, 1 + jmp copy_byte + +copy_done: + .data F9FE00400080 + .data F9FF + + ba_initial_floor 17 + write2 0x8057C930, 1 + + // Clean up quest handler table + write4 0x80454E04, 0 + write4 0x80454E08, 0 + write4 0x80454E0C, 0 + + ret + +code: + .include_native q88500-gc.s +code_end: diff --git a/system/quests/hidden/q88500-gc-s.bin.txt b/system/quests/hidden/q88500-gc-s.bin.txt new file mode 100644 index 00000000..9fe66ef2 --- /dev/null +++ b/system/quests/hidden/q88500-gc-s.bin.txt @@ -0,0 +1,47 @@ +.version GC_EP3 +.quest_num 88500 +.language 1 +.episode Episode1 +.name "GC Ep3 USA patch enabler" +.short_desc "" +.long_desc "" + +start: + leti r3, 0x80004000 + write4 0x80452A4C, 0x80109B28 + write4 0x80452A50, 0x8000C324 + write4 0x80452A54, r3 + + read4 r0, 0x8057A5F0 + leto r4, code + read4 r4, r4 + add r4, r0 + leto r5, code_end + read4 r5, r5 + add r5, r0 + +copy_byte: + jmp_eq r4, r5, copy_done + read1 r0, r4 + write1 r3, r0 + addi r3, 1 + addi r4, 1 + jmp copy_byte + +copy_done: + .data F9FE00400080 + .data F9FF + + ba_initial_floor 17 + write2 0x8057A510, 1 + + // Clean up quest handler table + write4 0x80452A4C, 0 + write4 0x80452A50, 0 + write4 0x80452A54, 0 + + ret + +code: + .include_native q88500-gc.s +code_end: diff --git a/system/quests/hidden/q88500-gc.s b/system/quests/hidden/q88500-gc.s index cd843aec..5295e624 100644 --- a/system/quests/hidden/q88500-gc.s +++ b/system/quests/hidden/q88500-gc.s @@ -79,8 +79,7 @@ handle_B2_skip_relocations: ori r0, r0, 0xC274 mr r3, r6 mr r4, r5 - mtctr r0 - bctrl # flush_code(code_base_addr, code_section_size) + bl call_flush_code # flush_code(code_base_addr, code_section_size) # Call the code section and put the return value (byteswapped) on the stack # Note: flush_code only uses r3, r4, and r5, so we don't need to reload r7 @@ -148,6 +147,16 @@ crc32_done: xori r3, r3, 0xFFFF blr # return (result ^ 0xFFFFFFFF) +call_flush_code: + lis r5, 0x8000 + ori r5, r5, 0xC274 + mtctr r5 + lhz r0, [r5 + 6] + cmplwi r0, 0xFFF1 + beqctr + addi r5, r5, 0xB0 # 8000C324 + mtctr r5 + bctr get_handle_B2_ptr: mflr r9 # r9 = &handle_B2 @@ -170,20 +179,36 @@ copy_handle_B2_word_again: bdnz copy_handle_B2_word_again # Invalidate the caches appropriately for the newly-copied code - lis r9, 0x8000 - ori r9, r9, 0xC274 - mtctr r9 mr r3, r12 rlwinm r4, r7, 30, 2, 31 - bctrl # flush_code(copied_B2_handler, copied_B2_handler_bytes) + bl call_flush_code # flush_code(copied_B2_handler, copied_B2_handler_bytes) # Replace the command handler table entry for command 0E (which is an unused # legacy command and has very broken behavior) with our B2 implementation - lis r5, 0x804C - ori r5, r5, 0x4E08 li r0, 0x00B2 + lis r6, 0x804C + ori r5, r6, 0x4E08 # US v1.2 + lwz r3, [r5] + cmplwi r3, 0x000E + beq patch_main_handlers_write + ori r5, r6, 0x5530 # JP v1.5 + lwz r3, [r5] + cmplwi r3, 0x000E + beq patch_main_handlers_write + lis r6, 0x8045 + subi r5, r6, 0x097C # US Ep3 + lwz r3, [r5] + cmplwi r3, 0x000E + beq patch_main_handlers_write + ori r5, r6, 0x1A3C # EU Ep3 + lwz r3, [r5] + cmplwi r3, 0x000E + bne done + +patch_main_handlers_write: stw [r5], r0 stw [r5 + 0x0C], r12 +done: mtlr r11 blr diff --git a/tests/config.json b/tests/config.json index 3d229031..bf3e9271 100644 --- a/tests/config.json +++ b/tests/config.json @@ -49,8 +49,7 @@ "HTTPListen": [], "BannedIPV4Ranges": [], "Episode3BehaviorFlags": 0xFA, - "PSOPlusSendFunctionCallQuestNumber": -1, - "EnableEpisode3SendFunctionCall": false, + "EnableSendFunctionCallQuestNumber": -1, "EnableV3V4ProtectedSubcommands": false, "Episode3InfiniteMeseta": false,