add Ep3 codepaths to B2 enabler

This commit is contained in:
Martin Michelsen
2024-06-23 00:24:01 -07:00
parent 862b3d27da
commit 3a22a5c489
14 changed files with 166 additions and 80 deletions
+2 -9
View File
@@ -37,9 +37,6 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
switch (sub_version) { switch (sub_version) {
case -1: // Initial check (before sub_version recognition) 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) { switch (version) {
case Version::PC_PATCH: case Version::PC_PATCH:
case Version::BB_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); this->set_flag(Flag::IS_CLIENT_CUSTOMIZATION);
[[fallthrough]]; [[fallthrough]];
case 0x36: // GC Ep1&2 US v1.2 (Plus) 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) case 0x39: // GC Ep1&2 JP v1.5 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY); this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break; break;
case 0x40: // GC Ep3 JP and Trial Edition (and BB) case 0x40: // GC Ep3 JP and Trial Edition (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY); 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. // instead look at header.flag in the 61 command and set the version then.
break; break;
case 0x41: // GC Ep3 US (and BB) 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 0x42: // GC Ep3 EU 50Hz
case 0x43: // GC Ep3 EU 60Hz case 0x43: // GC Ep3 EU 60Hz
this->set_flag(Flag::NO_D6_AFTER_LOBBY); this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::CAN_RECEIVE_ENABLE_B2_QUEST);
break; break;
default: default:
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version)); throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
-1
View File
@@ -48,7 +48,6 @@ public:
ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000, ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000,
SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000, SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000,
SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000, SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000,
USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000,
CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000, CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000,
AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, // Server-side only
+20 -31
View File
@@ -236,20 +236,6 @@ static void send_proxy_destinations_menu(shared_ptr<Client> c) {
send_menu(c, s->proxy_destinations_menu(c->version())); send_menu(c, s->proxy_destinations_menu(c->version()));
} }
static bool send_enable_send_function_call_if_applicable(shared_ptr<Client> 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<Client> c) { void on_connect(std::shared_ptr<Client> c) {
@@ -379,7 +365,6 @@ void on_login_server_login_complete(shared_ptr<Client> c) {
c->config.check_flag(Client::Flag::NO_D6) || c->config.check_flag(Client::Flag::NO_D6) ||
!c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) { !c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
c->config.clear_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_update_client_config(c, false);
send_main_menu(c); send_main_menu(c);
} else { } else {
@@ -398,10 +383,27 @@ void on_login_complete(shared_ptr<Client> c) {
if (c->config.check_flag(Client::Flag::CAN_RECEIVE_ENABLE_B2_QUEST) && if (c->config.check_flag(Client::Flag::CAN_RECEIVE_ENABLE_B2_QUEST) &&
!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) && !c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) &&
(s->ep12_plus_send_function_call_quest_num >= 0)) { (s->enable_send_function_call_quest_num >= 0)) {
auto q = s->quest_index(c->version())->get(s->ep12_plus_send_function_call_quest_num); auto q = s->default_quest_index->get(s->enable_send_function_call_quest_num);
if (q) { 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) { if (vq) {
c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL); c->config.set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH); c->config.set_flag(Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
@@ -908,18 +910,6 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
c->channel.language = base_cmd->language; c->channel.language = base_cmd->language;
set_console_client_flags(c, base_cmd->sub_version); 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 { try {
switch (c->version()) { switch (c->version()) {
case Version::DC_V2: { case Version::DC_V2: {
@@ -1775,7 +1765,6 @@ static void on_D6_V3(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_menu(c, s->information_menu(c->version())); send_menu(c, s->information_menu(c->version()));
} else if (c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) { } else if (c->config.check_flag(Client::Flag::AT_WELCOME_MESSAGE)) {
c->config.clear_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_update_client_config(c, false);
send_main_menu(c); send_main_menu(c);
} }
+1 -2
View File
@@ -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_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_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->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->enable_send_function_call_quest_num = this->config_json->get_int("EnableSendFunctionCallQuestNumber", -1);
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->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false);
this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true); this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true);
+1 -2
View File
@@ -120,8 +120,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4; std::unordered_map<uint16_t, IntegralExpression> quest_flag_rewrites_v4;
std::unordered_map<std::string, std::pair<uint8_t, uint32_t>> quest_counter_fields; // For $qfread command std::unordered_map<std::string, std::pair<uint8_t, uint32_t>> quest_counter_fields; // For $qfread command
uint64_t persistent_game_idle_timeout_usecs = 0; uint64_t persistent_game_idle_timeout_usecs = 0;
int64_t ep12_plus_send_function_call_quest_num = -1; int64_t enable_send_function_call_quest_num = -1;
bool ep3_send_function_call_enabled = false;
bool enable_v3_v4_protected_subcommands = false; bool enable_v3_v4_protected_subcommands = false;
bool catch_handler_exceptions = true; bool catch_handler_exceptions = true;
bool ep3_infinite_meseta = false; bool ep3_infinite_meseta = false;
@@ -1,10 +1,6 @@
# This patch gives you the maximum number of each card. It only works if used # 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`. # 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 hide_from_patches_menu
.meta name="Get all cards" .meta name="Get all cards"
.meta description="This patch gives you\nthe maximum number\nof each card." .meta description="This patch gives you\nthe maximum number\nof each card."
@@ -4,13 +4,9 @@
# present in PSO PC and PSOX as well, but not in GC Episodes 1 & 2. There are # 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. # 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 # This patch must not be run from the Patches menu - it should only be run with
# EnableEpisode3SendFunctionCall option to be enabled in config.json. If that # the $patch command, since the client will likely crash if the player is not
# option is disabled, the Patches menu won't appear for the client. If this # in a game or lobby when the patch runs.
# 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.
.meta hide_from_patches_menu .meta hide_from_patches_menu
.meta name="Editors" .meta name="Editors"
@@ -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 name="Get VIP card"
.meta description="Gives you a VIP card" .meta description="Gives you a VIP card"
@@ -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 # 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 # 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 # of PSO, but the code here is specific to the USA version of Episode 3.) This
+7 -9
View File
@@ -1055,15 +1055,13 @@
// load, then wait for the client to leave the "game", before even getting to // load, then wait for the client to leave the "game", before even getting to
// the welcome message. // the welcome message.
// This quest is not intended to be localized since it should not contain any // 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 // user-visible text, so the server uses the language field to determine
// v1.2, and the Japanese version for PSO JP v1.5, regardless of the client's // which quest to send based on the client's version:
// language setting. The quest is not used on any other PSO version. // - US Plus v1.2 + customizations: English
"PSOPlusSendFunctionCallQuestNumber": -1, // - JP Plus v1.5: Japanese
// Whether to enable patches on Episode 3 USA. This functionality depends on // - US Ep3: Spanish
// exploiting a bug in Episode 3, and while it seems to work reliably on // - EU Ep3: German
// Dolphin, it hasn't been tested on a real GameCube. So, newserv doesn't "EnableSendFunctionCallQuestNumber": -1,
// enable Episode 3 USA patches by default; it only does if this option is on.
"EnableEpisode3SendFunctionCall": false,
// Whether to enable protected subcommands on GC and Xbox. This enables the // Whether to enable protected subcommands on GC and Xbox. This enables the
// infinite HP cheat to also automatically revive players and clear conditions // infinite HP cheat to also automatically revive players and clear conditions
+47
View File
@@ -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:
+47
View File
@@ -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:
+33 -8
View File
@@ -79,8 +79,7 @@ handle_B2_skip_relocations:
ori r0, r0, 0xC274 ori r0, r0, 0xC274
mr r3, r6 mr r3, r6
mr r4, r5 mr r4, r5
mtctr r0 bl call_flush_code # flush_code(code_base_addr, code_section_size)
bctrl # flush_code(code_base_addr, code_section_size)
# Call the code section and put the return value (byteswapped) on the stack # 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 # 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 xori r3, r3, 0xFFFF
blr # return (result ^ 0xFFFFFFFF) 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: get_handle_B2_ptr:
mflr r9 # r9 = &handle_B2 mflr r9 # r9 = &handle_B2
@@ -170,20 +179,36 @@ copy_handle_B2_word_again:
bdnz copy_handle_B2_word_again bdnz copy_handle_B2_word_again
# Invalidate the caches appropriately for the newly-copied code # Invalidate the caches appropriately for the newly-copied code
lis r9, 0x8000
ori r9, r9, 0xC274
mtctr r9
mr r3, r12 mr r3, r12
rlwinm r4, r7, 30, 2, 31 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 # Replace the command handler table entry for command 0E (which is an unused
# legacy command and has very broken behavior) with our B2 implementation # legacy command and has very broken behavior) with our B2 implementation
lis r5, 0x804C
ori r5, r5, 0x4E08
li r0, 0x00B2 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], r0
stw [r5 + 0x0C], r12 stw [r5 + 0x0C], r12
done:
mtlr r11 mtlr r11
blr blr
+1 -2
View File
@@ -49,8 +49,7 @@
"HTTPListen": [], "HTTPListen": [],
"BannedIPV4Ranges": [], "BannedIPV4Ranges": [],
"Episode3BehaviorFlags": 0xFA, "Episode3BehaviorFlags": 0xFA,
"PSOPlusSendFunctionCallQuestNumber": -1, "EnableSendFunctionCallQuestNumber": -1,
"EnableEpisode3SendFunctionCall": false,
"EnableV3V4ProtectedSubcommands": false, "EnableV3V4ProtectedSubcommands": false,
"Episode3InfiniteMeseta": false, "Episode3InfiniteMeseta": false,