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) {
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));
-1
View File
@@ -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
+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()));
}
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) {
@@ -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::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<Client> 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<Client> 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<Client> 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);
}
+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_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);
+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<std::string, std::pair<uint8_t, uint32_t>> 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;
@@ -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."
@@ -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"
@@ -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"
@@ -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
+7 -9
View File
@@ -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
+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
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
+1 -2
View File
@@ -49,8 +49,7 @@
"HTTPListen": [],
"BannedIPV4Ranges": [],
"Episode3BehaviorFlags": 0xFA,
"PSOPlusSendFunctionCallQuestNumber": -1,
"EnableEpisode3SendFunctionCall": false,
"EnableSendFunctionCallQuestNumber": -1,
"EnableV3V4ProtectedSubcommands": false,
"Episode3InfiniteMeseta": false,