add B2 patch support on PSO Plus

This commit is contained in:
Martin Michelsen
2024-06-22 21:38:24 -07:00
parent 998664d2fb
commit 862b3d27da
21 changed files with 465 additions and 96 deletions
+1 -1
View File
@@ -1538,7 +1538,7 @@ static void server_command_loadchar(shared_ptr<Client> c, const std::string& arg
(c->version() == Version::XB_V3)) {
// TODO: Support extended player info on other versions
auto s = c->require_server_state();
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) ||
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
send_text_message_printf(c, "Can\'t load character\ndata on this game\nversion");
return;
+22 -7
View File
@@ -37,36 +37,47 @@ 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:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_NTE:
case Version::DC_V1_11_2000_PROTOTYPE:
case Version::DC_V1:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case Version::DC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::PC_NTE:
case Version::PC_V2:
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case Version::GC_NTE:
case Version::GC_V3:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case Version::GC_EP3_NTE:
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
break;
case Version::GC_V3:
case Version::GC_EP3:
// Some of these versions have send_function_call and some don't; we
// have to set these flags later when we get sub_version
break;
case Version::XB_V3:
// TODO: Do all versions of XB need this flag? US does, at least.
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
default:
@@ -77,35 +88,38 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x20: // DCNTE, possibly also DCv1 JP
case 0x21: // DCv1 US
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x22: // DCv1 EU 50Hz (presumably)
case 0x23: // DCv1 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
case 0x25: // DCv2 JP
case 0x26: // DCv2 US
case 0x27: // DCv2 EU 50Hz (presumably)
case 0x28: // DCv2 EU 60Hz (presumably)
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x29: // PC
this->set_flag(Flag::NO_D6);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
case 0x30: // GC Ep1&2 GameJam demo, GC Ep1&2 Trial Edition, GC Ep1&2 JP v1.2, at least one version of XB
case 0x31: // GC Ep1&2 US v1.0, GC US v1.1, XB US
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x32: // GC Ep1&2 EU 50Hz
case 0x33: // GC Ep1&2 EU 60Hz
case 0x34: // GC Ep1&2 JP v1.3
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
break;
case 0x35: // GC Ep1&2 JP v1.4 (Plus)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
break;
@@ -113,12 +127,14 @@ 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::NO_SEND_FUNCTION_CALL);
break;
case 0x40: // GC Ep3 JP and Trial Edition (and BB)
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::HAS_SEND_FUNCTION_CALL);
this->set_flag(Flag::ENCRYPTED_SEND_FUNCTION_CALL);
this->set_flag(Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH);
// sub_version can't be used to tell JP final and Trial Edition apart; we
@@ -132,7 +148,6 @@ void Client::Config::set_flags_for_version(Version version, int64_t sub_version)
case 0x42: // GC Ep3 EU 50Hz
case 0x43: // GC Ep3 EU 60Hz
this->set_flag(Flag::NO_D6_AFTER_LOBBY);
this->set_flag(Flag::NO_SEND_FUNCTION_CALL);
break;
default:
throw runtime_error(string_printf("unknown sub_version %" PRIX64, sub_version));
+4 -2
View File
@@ -35,7 +35,7 @@ public:
// TODO: It'd be nice to use a pattern here (e.g. all server-side flags are
// in the high bits) but that would require re-recording or manually
// rewriting all the tests
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB,
CLIENT_SIDE_MASK = 0xFF3CFFFF7C0BFFFB,
// Version-related flags
CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002,
@@ -44,11 +44,13 @@ public:
FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400,
// Flags describing the behavior for send_function_call
NO_SEND_FUNCTION_CALL = 0x0000000000001000,
HAS_SEND_FUNCTION_CALL = 0x0000000000001000,
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
// State flags
LOADING = 0x0000000000100000, // Server-side only
+1 -2
View File
@@ -430,8 +430,7 @@ struct C_LegacyLogin_BB_04 {
// Any other nonzero value = Generic failure (101)
// The client config field in this command is ignored by pre-V3 clients as well
// as Episodes 1&2 Trial Edition. All other V3 clients save it as opaque data to
// be returned in a 9E or 9F command later. newserv sends the client config
// anyway to clients that ignore it.
// be returned in a 9E or 9F command later.
// The client will respond with a 96 command, but only the first time it
// receives this command - for later 04 commands, the client will still update
// its client config but will not respond. Changing the security data at any
-2
View File
@@ -588,8 +588,6 @@ QuestIndex::QuestIndex(
throw runtime_error("qst file contains unsupported file type: " + it.first);
}
}
} else {
static_game_data_log.warning("(%s) Skipping file (unsupported format)", filename.c_str());
}
} catch (const exception& e) {
+65 -26
View File
@@ -128,7 +128,7 @@ void send_first_pre_lobby_commands(shared_ptr<Client> c, std::function<void()> o
if (function_compiler_available() &&
!c->config.check_flag(Client::Flag::HAS_AUTO_PATCHES) &&
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c), on_complete = std::move(on_complete)]() -> void {
auto c = wc.lock();
if (!c) {
@@ -242,7 +242,7 @@ static bool send_enable_send_function_call_if_applicable(shared_ptr<Client> c) {
if (s->ep3_send_function_call_enabled) {
send_quest_buffer_overflow(c);
} else {
c->config.set_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
c->config.clear_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
}
c->config.clear_flag(Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL);
return true;
@@ -363,6 +363,30 @@ static void send_main_menu(shared_ptr<Client> c) {
send_menu(c, main_menu);
}
void on_login_server_login_complete(shared_ptr<Client> c) {
auto s = c->require_server_state();
if (s->pre_lobby_event && (!is_ep3(c->version()) || s->ep3_menu_song < 0)) {
send_change_event(c, s->pre_lobby_event);
}
if (is_ep3(c->version())) {
send_ep3_rank_update(c);
send_get_player_info(c);
}
if (s->welcome_message.empty() ||
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 {
send_message_box(c, s->welcome_message.c_str());
}
}
void on_login_complete(shared_ptr<Client> c) {
c->convert_account_to_temporary_if_nte();
@@ -372,24 +396,32 @@ void on_login_complete(shared_ptr<Client> c) {
case ServerBehavior::LOGIN_SERVER: {
auto s = c->require_server_state();
if (s->pre_lobby_event && (!is_ep3(c->version()) || s->ep3_menu_song < 0)) {
send_change_event(c, s->pre_lobby_event);
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);
if (q) {
auto vq = q->version(c->version(), (c->sub_version == 0x39 ? 0 : 1));
if (vq) {
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::AWAITING_ENABLE_B2_QUEST);
send_update_client_config(c, false);
c->log.info("Sending %c version of quest \"%s\"", char_for_language_code(vq->language), vq->name.c_str());
string bin_filename = vq->bin_filename();
string dat_filename = vq->dat_filename();
string xb_filename = vq->xb_filename();
send_open_quest_file(c, bin_filename, bin_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->bin_contents);
send_open_quest_file(c, dat_filename, dat_filename, xb_filename, vq->quest_number, QuestFileType::ONLINE, vq->dat_contents);
send_command(c, 0xAC, 0x00);
}
}
}
if (is_ep3(c->version())) {
send_ep3_rank_update(c);
send_get_player_info(c);
}
if (s->welcome_message.empty() ||
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 {
send_message_box(c, s->welcome_message.c_str());
if (!c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
on_login_server_login_complete(c);
}
break;
}
@@ -882,10 +914,10 @@ static void on_9D_9E(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
// 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.clear_flag(Client::Flag::NO_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.set_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
c->config.clear_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
}
try {
@@ -2195,7 +2227,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
prepare_client_for_patches(c, [c]() -> void {
@@ -2207,7 +2239,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
// We have to prepare the client for patches here, even though we
@@ -2222,7 +2254,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
if (!function_compiler_available()) {
throw runtime_error("function compiler not available");
}
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
prepare_client_for_patches(c, [c]() -> void {
@@ -2577,7 +2609,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2595,7 +2627,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2615,7 +2647,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
send_main_menu(c);
} else {
if (c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw runtime_error("client does not support send_function_call");
}
@@ -2700,6 +2732,13 @@ static void on_84(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
const auto& cmd = check_size_t<C_LobbySelection_84>(data);
auto s = c->require_server_state();
if ((c->server_behavior == ServerBehavior::LOGIN_SERVER) &&
c->config.check_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST)) {
on_login_server_login_complete(c);
c->config.clear_flag(Client::Flag::AWAITING_ENABLE_B2_QUEST);
return;
}
if (cmd.menu_id != MenuID::LOBBY) {
send_message_box(c, "Incorrect menu ID");
return;
+4 -4
View File
@@ -474,7 +474,7 @@ void send_function_call(
uint32_t checksum_addr,
uint32_t checksum_size,
uint32_t override_relocations_offset) {
if (client_config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL)) {
if (!client_config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL)) {
throw logic_error("client does not support function calls");
}
if (code.get() && client_config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
@@ -511,7 +511,7 @@ bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t
case Version::BB_V4: {
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::HAS_SEND_FUNCTION_CALL) ||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
return false;
}
@@ -1429,7 +1429,7 @@ void send_menu_t(shared_ptr<Client> c, shared_ptr<const Menu> menu, bool is_info
is_visible &= !c->config.check_flag(Client::Flag::NO_D6);
}
if (item.flags & MenuItem::Flag::REQUIRES_SEND_FUNCTION_CALL) {
is_visible &= !c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL);
is_visible &= c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
}
if (item.flags & MenuItem::Flag::REQUIRES_SAVE_DISABLED) {
is_visible &= !c->config.check_flag(Client::Flag::SAVE_ENABLED);
@@ -2407,7 +2407,7 @@ void send_get_player_info(shared_ptr<Client> c, bool request_extended) {
}
if (request_extended &&
!c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) &&
c->config.check_flag(Client::Flag::HAS_SEND_FUNCTION_CALL) &&
!c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
auto s = c->require_server_state();
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c)]() {
+1
View File
@@ -793,6 +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_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false);
this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true);
+1
View File
@@ -120,6 +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;
bool enable_v3_v4_protected_subcommands = false;
bool catch_handler_exceptions = true;