From f333a88aaf32a03c154711715f86e4f80ecd51c3 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 18 Jun 2023 22:43:53 -0700 Subject: [PATCH] clean up check_size --- src/CatSession.cc | 6 +- src/Episode3/BattleRecord.cc | 6 +- src/Episode3/Server.cc | 3 +- src/PSOProtocol.hh | 84 +++++---- src/ProxyCommands.cc | 60 +++--- src/ProxyServer.cc | 9 +- src/ReceiveCommands.cc | 37 ++-- src/ReceiveSubcommands.cc | 355 ++++++++++++++++------------------- src/ReplaySession.cc | 72 +++---- 9 files changed, 294 insertions(+), 338 deletions(-) diff --git a/src/CatSession.cc b/src/CatSession.cc index e49b4a94..b8d9927e 100644 --- a/src/CatSession.cc +++ b/src/CatSession.cc @@ -75,8 +75,7 @@ void CatSession::on_channel_input( uint16_t command, uint32_t flag, std::string& data) { if (this->channel.version != GameVersion::BB) { if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) { - const auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); if ((this->channel.version == GameVersion::GC) || (this->channel.version == GameVersion::XB)) { this->channel.crypt_in.reset(new PSOV3Encryption(cmd.server_key)); @@ -95,8 +94,7 @@ void CatSession::on_channel_input( if (!this->bb_key_file) { throw runtime_error("BB encryption requires a key file"); } - const auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); this->channel.crypt_in.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key))); this->channel.crypt_out.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key))); this->log.info("Enabled BB encryption"); diff --git a/src/Episode3/BattleRecord.cc b/src/Episode3/BattleRecord.cc index 9eb9d319..82a514c7 100644 --- a/src/Episode3/BattleRecord.cc +++ b/src/Episode3/BattleRecord.cc @@ -187,11 +187,9 @@ void BattleRecord::add_chat_message( bool BattleRecord::is_map_definition_event(const Event& ev) { if (ev.type == Event::Type::BATTLE_COMMAND) { - auto& header = check_size_t( - ev.data, sizeof(G_CardBattleCommandHeader), 0xFFFF); + auto& header = check_size_t(ev.data, 0xFFFF); if (header.subcommand == 0xB6) { - auto& header = check_size_t( - ev.data, sizeof(G_MapSubsubcommand_GC_Ep3_6xB6), 0xFFFF); + auto& header = check_size_t(ev.data, 0xFFFF); if (header.subsubcommand == 0x41) { return true; } diff --git a/src/Episode3/Server.cc b/src/Episode3/Server.cc index 30efe5cb..fd519f9c 100644 --- a/src/Episode3/Server.cc +++ b/src/Episode3/Server.cc @@ -1532,8 +1532,7 @@ const unordered_map Server::subcommand_handlers({ }); void Server::on_server_data_input(const string& data) { - auto header = check_size_t( - data, sizeof(G_CardBattleCommandHeader), 0xFFFF); + auto header = check_size_t(data, 0xFFFF); if (header.size * 4 < data.size()) { throw runtime_error("command is incomplete"); } diff --git a/src/PSOProtocol.hh b/src/PSOProtocol.hh index d2d99290..0de741a0 100644 --- a/src/PSOProtocol.hh +++ b/src/PSOProtocol.hh @@ -49,12 +49,12 @@ union PSOCommandHeader { // This function is used in a lot of places to check received command sizes and // cast them to the appropriate type -template -const T& check_size_t( - const void* data, +template +RetT& check_size_generic( + PtrT data, size_t size, - size_t min_size = sizeof(T), - size_t max_size = sizeof(T)) { + size_t min_size, + size_t max_size) { if (size < min_size) { throw std::runtime_error(string_printf( "command too small (expected at least 0x%zX bytes, received 0x%zX bytes)", @@ -65,39 +65,59 @@ const T& check_size_t( "command too large (expected at most 0x%zX bytes, received 0x%zX bytes)", max_size, size)); } - return *reinterpret_cast(data); + return *reinterpret_cast(data); +} + +template +const T& check_size_t(const std::string& data, size_t min_size, size_t max_size) { + return check_size_generic(data.data(), data.size(), min_size, max_size); } template -const T& check_size_t( - const std::string& data, - size_t min_size = sizeof(T), - size_t max_size = sizeof(T)) { - return check_size_t(data.data(), data.size(), min_size, max_size); +const T& check_size_t(const std::string& data, size_t max_size) { + return check_size_generic(data.data(), data.size(), sizeof(T), max_size); } template -T& check_size_t( - void* data, - size_t size, - size_t min_size = sizeof(T), - size_t max_size = sizeof(T)) { - if (size < min_size) { - throw std::runtime_error(string_printf( - "command too small (expected at least 0x%zX bytes, received 0x%zX bytes)", - min_size, size)); - } - if (size > max_size) { - throw std::runtime_error(string_printf( - "command too large (expected at most 0x%zX bytes, received 0x%zX bytes)", - max_size, size)); - } - return *reinterpret_cast(data); +const T& check_size_t(const std::string& data) { + return check_size_generic(data.data(), data.size(), sizeof(T), sizeof(T)); +} + +template +T& check_size_t(std::string& data, size_t min_size, size_t max_size) { + return check_size_generic(data.data(), data.size(), min_size, max_size); } template -T& check_size_t( - std::string& data, - size_t min_size = sizeof(T), - size_t max_size = sizeof(T)) { - return check_size_t(data.data(), data.size(), min_size, max_size); +T& check_size_t(std::string& data, size_t max_size) { + return check_size_generic(data.data(), data.size(), sizeof(T), max_size); +} +template +T& check_size_t(std::string& data) { + return check_size_generic(data.data(), data.size(), sizeof(T), sizeof(T)); +} + +template +const T& check_size_t(const void* data, size_t size, size_t min_size, size_t max_size) { + return check_size_generic(data, size, min_size, max_size); +} +template +const T& check_size_t(const void* data, size_t size, size_t max_size) { + return check_size_generic(data, size, sizeof(T), max_size); +} +template +const T& check_size_t(const void* data, size_t size) { + return check_size_generic(data, size, sizeof(T), sizeof(T)); +} + +template +T& check_size_t(void* data, size_t size, size_t min_size, size_t max_size) { + return check_size_generic(data, size, min_size, max_size); +} +template +T& check_size_t(void* data, size_t size, size_t max_size) { + return check_size_generic(data, size, sizeof(T), max_size); +} +template +T& check_size_t(void* data, size_t size) { + return check_size_generic(data, size, sizeof(T), sizeof(T)); } void check_size_v(size_t size, size_t min_size, size_t max_size = 0); diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 0b5d402a..79c9c719 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -224,8 +224,7 @@ static HandlerResult S_V123P_02_17( // Most servers don't include after_message or have a shorter // after_message than newserv does, so don't require it - const auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); if (!session.license) { session.log.info("No license in linked session"); @@ -431,8 +430,7 @@ static HandlerResult S_B_03(shared_ptr s, ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { // Most servers don't include after_message or have a shorter after_message // than newserv does, so don't require it - const auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); // If the session has a detector crypt, then it was resumed from an unlinked // session, during which we already sent an 03 command. @@ -545,8 +543,7 @@ static HandlerResult S_V123_04(shared_ptr, static HandlerResult S_V123_06(shared_ptr, ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { if (session.license) { - auto& cmd = check_size_t(data, - sizeof(SC_TextHeader_01_06_11_B0_EE), 0xFFFF); + auto& cmd = check_size_t(data, 0xFFFF); if (cmd.guild_card_number == session.remote_guild_card_number) { cmd.guild_card_number = session.license->serial_number; return HandlerResult::Type::MODIFIED; @@ -604,8 +601,8 @@ static HandlerResult S_88(shared_ptr, bool modified = false; if (session.license) { size_t expected_size = sizeof(S_ArrowUpdateEntry_88) * flag; - auto* entries = &check_size_t(data, - expected_size, expected_size); + auto* entries = &check_size_t( + data, expected_size, expected_size); for (size_t x = 0; x < flag; x++) { if (entries[x].guild_card_number == session.remote_guild_card_number) { entries[x].guild_card_number = session.license->serial_number; @@ -626,7 +623,7 @@ static HandlerResult S_B1(shared_ptr, static HandlerResult S_B2(shared_ptr, ProxyServer::LinkedSession& session, uint16_t, uint32_t flag, string& data) { - const auto& cmd = check_size_t(data, sizeof(S_ExecuteCode_B2), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); if (cmd.code_size && session.options.save_files) { uint64_t filename_timestamp = now(); @@ -843,7 +840,7 @@ static HandlerResult S_19_P_14(shared_ptr, } else { // This weird maximum size is here to properly handle the version-split // command that some servers (including newserv) use on port 9100 - auto& cmd = check_size_t(data, sizeof(S_Reconnect_19), 0xFFFF); + auto& cmd = check_size_t(data, 0xFFFF); sin->sin_addr.s_addr = cmd.address.load_raw(); // Already big-endian sin->sin_port = htons(cmd.port); } @@ -869,7 +866,7 @@ static HandlerResult S_19_P_14(shared_ptr, if (sin->sin_family != AF_INET) { throw logic_error("existing connection is not ipv4"); } - auto& cmd = check_size_t(data, sizeof(S_Reconnect_19), 0xFFFF); + auto& cmd = check_size_t(data, 0xFFFF); cmd.address.store_raw(sin->sin_addr.s_addr); cmd.port = ntohs(sin->sin_port); return HandlerResult::Type::MODIFIED; @@ -906,11 +903,9 @@ static HandlerResult S_6x(shared_ptr, if (session.options.save_files) { if ((session.version == GameVersion::GC) && (data.size() >= 0x14)) { if (static_cast(data[0]) == 0xB6) { - const auto& header = check_size_t( - data, sizeof(G_MapSubsubcommand_GC_Ep3_6xB6), 0xFFFF); + const auto& header = check_size_t(data, 0xFFFF); if (header.subsubcommand == 0x00000041) { - const auto& cmd = check_size_t( - data, sizeof(G_MapData_GC_Ep3_6xB6x41), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); string filename = string_printf("map%08" PRIX32 ".%" PRIu64 ".mnmd", cmd.map_number.load(), now()); string map_data = prs_decompress( @@ -934,8 +929,7 @@ static HandlerResult S_6x(shared_ptr, ((static_cast(data[0]) == 0xB3) || (static_cast(data[0]) == 0xB4) || (static_cast(data[0]) == 0xB5))) { - const auto& header = check_size_t( - data, sizeof(G_CardBattleCommandHeader), 0xFFFF); + const auto& header = check_size_t(data, 0xFFFF); if (header.mask_key) { set_mask_for_ep3_game_command(data.data(), data.size(), 0); modified = true; @@ -944,7 +938,7 @@ static HandlerResult S_6x(shared_ptr, if (data[0] == 0x46) { const auto& cmd = check_size_t(data, - offsetof(G_AttackFinished_6x46, entries), + offsetof(G_AttackFinished_6x46, targets), sizeof(G_AttackFinished_6x46)); size_t allowed_count = min(cmd.header.size - 2, 11); if (cmd.count > allowed_count) { @@ -973,14 +967,13 @@ static HandlerResult S_6x(shared_ptr, } else if ((data[0] == 0x60) && session.next_drop_item.data.data1d[0] && (session.version != GameVersion::BB)) { - const auto& cmd = check_size_t(data, - sizeof(G_EnemyDropItemRequest_DC_6x60), - sizeof(G_EnemyDropItemRequest_PC_V3_BB_6x60)); + const auto& cmd = check_size_t( + data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60)); session.next_drop_item.data.id = session.next_item_id++; send_drop_item(session.server_channel, session.next_drop_item.data, - true, cmd.area, cmd.x, cmd.z, cmd.enemy_id); + true, cmd.area, cmd.x, cmd.z, cmd.entity_id); send_drop_item(session.client_channel, session.next_drop_item.data, - true, cmd.area, cmd.x, cmd.z, cmd.enemy_id); + true, cmd.area, cmd.x, cmd.z, cmd.entity_id); session.next_drop_item.clear(); return HandlerResult::Type::SUPPRESS; @@ -991,12 +984,12 @@ static HandlerResult S_6x(shared_ptr, } else if ((static_cast(data[0]) == 0xA2) && session.next_drop_item.data.data1d[0] && (session.version != GameVersion::BB)) { - const auto& cmd = check_size_t(data); + const auto& cmd = check_size_t(data); session.next_drop_item.data.id = session.next_item_id++; send_drop_item(session.server_channel, session.next_drop_item.data, - false, cmd.area, cmd.x, cmd.z, cmd.request_id); + false, cmd.area, cmd.x, cmd.z, cmd.entity_id); send_drop_item(session.client_channel, session.next_drop_item.data, - false, cmd.area, cmd.x, cmd.z, cmd.request_id); + false, cmd.area, cmd.x, cmd.z, cmd.entity_id); session.next_drop_item.clear(); return HandlerResult::Type::SUPPRESS; @@ -1024,7 +1017,7 @@ static HandlerResult C_GXB_61(shared_ptr, // return MODIFIED if so. if (session.version == GameVersion::BB) { - auto& pd = check_size_t(data, sizeof(PSOPlayerDataBB), 0xFFFF); + auto& pd = check_size_t(data, 0xFFFF); if (session.options.enable_chat_filter) { add_color_inplace(pd.info_board.data(), pd.info_board.size()); } @@ -1055,7 +1048,7 @@ static HandlerResult C_GXB_61(shared_ptr, } pd = reinterpret_cast(&ep3_pd); } else { - pd = &check_size_t(data, sizeof(PSOPlayerDataV3), 0xFFFF); + pd = &check_size_t(data, 0xFFFF); } if (session.options.enable_chat_filter) { add_color_inplace(pd->info_board.data(), pd->info_board.size()); @@ -1242,8 +1235,7 @@ static HandlerResult S_G_B9(shared_ptr, if (session.options.save_files) { try { - const auto& header = check_size_t(data, - sizeof(S_UpdateMediaHeader_GC_Ep3_B9), 0xFFFF); + const auto& header = check_size_t(data, 0xFFFF); if (data.size() - sizeof(header) < header.size) { throw runtime_error("Media data size extends beyond end of command; not saving file"); @@ -1394,7 +1386,7 @@ static HandlerResult S_64(shared_ptr, CmdT* cmd; S_JoinGame_GC_Ep3_64* cmd_ep3 = nullptr; if (session.sub_version >= 0x40) { - cmd = &check_size_t(data, sizeof(S_JoinGame_GC_Ep3_64), sizeof(S_JoinGame_GC_Ep3_64)); + cmd = &check_size_t(data, sizeof(S_JoinGame_GC_Ep3_64)); cmd_ep3 = &check_size_t(data); } else { cmd = &check_size_t(data); @@ -1549,7 +1541,7 @@ static HandlerResult C_98(shared_ptr s, static HandlerResult C_06(shared_ptr s, ProxyServer::LinkedSession& session, uint16_t, uint32_t, string& data) { if (data.size() >= 12) { - const auto& cmd = check_size_t(data, sizeof(C_Chat_06), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); u16string text; uint8_t private_flags = 0; @@ -1668,6 +1660,8 @@ static HandlerResult C_6x(shared_ptr s, if (session.options.infinite_hp) { send_player_stats_change(session.client_channel, session.lobby_client_id, PlayerStatsChange::ADD_HP, 2550); + send_player_stats_change(session.server_channel, + session.lobby_client_id, PlayerStatsChange::ADD_HP, 2550); } } else if (data[0] == 0x3E) { C_6x_movement(session, data); @@ -1681,6 +1675,8 @@ static HandlerResult C_6x(shared_ptr s, if (session.options.infinite_tp) { send_player_stats_change(session.client_channel, session.lobby_client_id, PlayerStatsChange::ADD_TP, 255); + send_player_stats_change(session.server_channel, + session.lobby_client_id, PlayerStatsChange::ADD_TP, 255); } } } diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index 039f6b9a..3aa4eda1 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -266,8 +266,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 hardware_id = cmd.hardware_id; client_config.cfg.flags |= Client::Flag::IS_DC_V1; } else if (command == 0x9D) { - const auto& cmd = check_size_t( - data, sizeof(C_Login_DC_PC_GC_9D), sizeof(C_LoginExtended_DC_GC_9D)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtended_DC_GC_9D)); license = session->server->state->license_manager->verify_pc( stoul(cmd.serial_number, nullptr, 16), cmd.access_key); sub_version = cmd.sub_version; @@ -283,8 +282,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 if (command != 0x9D) { throw runtime_error("command is not 9D"); } - const auto& cmd = check_size_t( - data, sizeof(C_Login_DC_PC_GC_9D), sizeof(C_LoginExtended_PC_9D)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtended_PC_9D)); license = session->server->state->license_manager->verify_pc( stoul(cmd.serial_number, nullptr, 16), cmd.access_key); sub_version = cmd.sub_version; @@ -298,8 +296,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 if (command != 0x9E) { throw runtime_error("command is not 9E"); } - const auto& cmd = check_size_t( - data, sizeof(C_Login_GC_9E), sizeof(C_LoginExtended_GC_9E)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtended_GC_9E)); license = session->server->state->license_manager->verify_gc( stoul(cmd.serial_number, nullptr, 16), cmd.access_key); sub_version = cmd.sub_version; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 38439e66..ad940f60 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -391,8 +391,7 @@ static void on_88_DCNTE(shared_ptr s, shared_ptr c, static void on_8B_DCNTE(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { - const auto& cmd = check_size_t(data, - sizeof(C_Login_DCNTE_8B), sizeof(C_LoginExtended_DCNTE_8B)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtended_DCNTE_8B)); c->channel.version = GameVersion::DC; c->flags |= flags_for_version(c->version(), -1); c->flags |= Client::Flag::IS_DC_V1 | Client::Flag::IS_TRIAL_EDITION; @@ -436,7 +435,7 @@ static void on_8B_DCNTE(shared_ptr s, shared_ptr c, static void on_90_DC(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { - const auto& cmd = check_size_t(data, sizeof(C_LoginV1_DC_PC_V3_90), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); c->channel.version = GameVersion::DC; c->flags |= flags_for_version(c->version(), -1); c->flags |= Client::Flag::IS_DC_V1; @@ -478,8 +477,7 @@ static void on_92_DC(shared_ptr, shared_ptr c, static void on_93_DC(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { - const auto& cmd = check_size_t(data, - sizeof(C_LoginV1_DC_93), sizeof(C_LoginExtendedV1_DC_93)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtendedV1_DC_93)); set_console_client_flags(c, cmd.sub_version); uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16); @@ -650,8 +648,7 @@ static void on_9D_9E(shared_ptr s, shared_ptr c, uint16_t command, uint32_t, const string& data) { const C_Login_DC_PC_GC_9D* base_cmd; if (command == 0x9D) { - base_cmd = &check_size_t(data, - sizeof(C_Login_DC_PC_GC_9D), sizeof(C_LoginExtended_PC_9D)); + base_cmd = &check_size_t(data, sizeof(C_LoginExtended_PC_9D)); if (base_cmd->is_extended) { if (c->version() == GameVersion::PC) { const auto& cmd = check_size_t(data); @@ -669,8 +666,7 @@ static void on_9D_9E(shared_ptr s, shared_ptr c, } else if (command == 0x9E) { // GC and XB send different amounts of data in this command. This is how // newserv determines if a V3 client is GC or XB. - const auto& cmd = check_size_t(data, - sizeof(C_Login_GC_9E), sizeof(C_LoginExtended_XB_9E)); + const auto& cmd = check_size_t(data, sizeof(C_LoginExtended_XB_9E)); switch (data.size()) { case sizeof(C_Login_GC_9E): case sizeof(C_LoginExtended_GC_9E): @@ -1310,8 +1306,7 @@ static void on_CA_Ep3(shared_ptr s, shared_ptr c, throw runtime_error("Episode 3 server data request sent outside of Episode 3 game"); } - const auto& header = check_size_t( - data, sizeof(G_CardServerDataCommandHeader), 0xFFFF); + const auto& header = check_size_t(data, 0xFFFF); if (header.subcommand != 0xB3) { throw runtime_error("unknown Episode 3 server data request"); } @@ -2448,8 +2443,7 @@ static void on_61_98(shared_ptr s, shared_ptr c, switch (c->version()) { case GameVersion::DC: case GameVersion::PC: { - const auto& pd = check_size_t(data, - sizeof(PSOPlayerDataDCPC), 0xFFFF); + const auto& pd = check_size_t(data, 0xFFFF); c->game_data.import_player(pd); break; } @@ -2464,14 +2458,14 @@ static void on_61_98(shared_ptr s, shared_ptr c, c->game_data.ep3_config.reset(new Episode3::PlayerConfig(pd3->ep3_config)); pd = reinterpret_cast(pd3); } else { - pd = &check_size_t(data, sizeof(PSOPlayerDataV3), + pd = &check_size_t(data, sizeof(PSOPlayerDataV3) + c->game_data.player()->auto_reply.bytes()); } c->game_data.import_player(*pd); break; } case GameVersion::BB: { - const auto& pd = check_size_t(data, sizeof(PSOPlayerDataBB), + const auto& pd = check_size_t(data, sizeof(PSOPlayerDataBB) + c->game_data.player()->auto_reply.bytes()); c->game_data.import_player(pd); break; @@ -2599,7 +2593,7 @@ static void on_chat_generic(shared_ptr s, shared_ptr c, static void on_06_PC_BB(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { - const auto& cmd = check_size_t(data, sizeof(C_Chat_06), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); u16string text(cmd.text.pcbb, (data.size() - sizeof(C_Chat_06)) / sizeof(char16_t)); strip_trailing_zeroes(text); on_chat_generic(s, c, text); @@ -2607,7 +2601,7 @@ static void on_06_PC_BB(shared_ptr s, shared_ptr c, static void on_06_DC_V3(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { - const auto& cmd = check_size_t(data, sizeof(C_Chat_06), 0xFFFF); + const auto& cmd = check_size_t(data, 0xFFFF); u16string decoded_s = decode_sjis(cmd.text.dcv3, data.size() - sizeof(C_Chat_06)); on_chat_generic(s, c, decoded_s); } @@ -4197,21 +4191,18 @@ void on_command_with_header(shared_ptr s, shared_ptr c, case GameVersion::DC: case GameVersion::GC: case GameVersion::XB: { - auto& header = check_size_t(data, - sizeof(PSOCommandHeaderDCV3), 0xFFFF); + auto& header = check_size_t(data, 0xFFFF); on_command(s, c, header.command, header.flag, data.substr(sizeof(header))); break; } case GameVersion::PC: case GameVersion::PATCH: { - auto& header = check_size_t(data, - sizeof(PSOCommandHeaderPC), 0xFFFF); + auto& header = check_size_t(data, 0xFFFF); on_command(s, c, header.command, header.flag, data.substr(sizeof(header))); break; } case GameVersion::BB: { - auto& header = check_size_t(data, - sizeof(PSOCommandHeaderBB), 0xFFFF); + auto& header = check_size_t(data, 0xFFFF); on_command(s, c, header.command, header.flag, data.substr(sizeof(header))); break; } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index be55431c..95e496c9 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -27,38 +27,6 @@ bool command_is_private(uint8_t command) { return (command == 0x62) || (command == 0x6D); } -template -const CmdT& check_size_sc( - const string& data, - size_t min_size = sizeof(CmdT), - size_t max_size = sizeof(CmdT), - bool check_size_field = true) { - if (max_size < min_size) { - max_size = min_size; - } - const auto& cmd = check_size_t(data, min_size, max_size); - if (check_size_field) { - if (data.size() < 4) { - throw runtime_error("subcommand is too short for header"); - } - const auto* header = reinterpret_cast(data.data()); - if (header->size == 0) { - if (data.size() < 8) { - throw runtime_error("subcommand has extended size but is shorter than 8 bytes"); - } - const auto* ext_header = reinterpret_cast*>(data.data()); - if (ext_header->size != data.size()) { - throw runtime_error("invalid subcommand extended size field"); - } - } else { - if ((header->size * 4) != data.size()) { - throw runtime_error("invalid subcommand size field"); - } - } - } - return cmd; -} - static const unordered_set watcher_subcommands({ 0x07, // Symbol chat 0x74, // Word select @@ -120,16 +88,10 @@ static void forward_subcommand(shared_ptr l, shared_ptr c, } } -static void forward_subcommand(shared_ptr l, shared_ptr c, - uint8_t command, uint8_t flag, const string& data) { - forward_subcommand(l, c, command, flag, data.data(), data.size()); -} - static void on_invalid(shared_ptr, shared_ptr, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc( - data, sizeof(G_UnusedHeader), 0xFFFF); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, 0xFFFF); if (command_is_private(command)) { c->log.error("Invalid subcommand: %02hhX (private to %hhu)", cmd.subcommand, flag); @@ -140,9 +102,8 @@ static void on_invalid(shared_ptr, static void on_unimplemented(shared_ptr, shared_ptr, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc( - data, sizeof(G_UnusedHeader), 0xFFFF); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, 0xFFFF); if (command_is_private(command)) { c->log.warning("Unknown subcommand: %02hhX (private to %hhu)", cmd.subcommand, flag); @@ -156,30 +117,29 @@ static void on_unimplemented(shared_ptr, static void on_forward_check_size(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - check_size_sc(data, sizeof(G_UnusedHeader), 0xFFFF); - forward_subcommand(l, c, command, flag, data); + const void* data, size_t size) { + check_size_t(data, size, 0xFFFF); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_game(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (!l->is_game()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_sync_game_state(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (!l->is_game() || !l->any_client_loading()) { return; } - const auto& cmd = check_size_sc( - data, sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E), 0xFFFF); - if (cmd.compressed_size > data.size() - sizeof(cmd)) { + const auto& cmd = check_size_t(data, size, 0xFFFF); + if (cmd.compressed_size > size - sizeof(cmd)) { throw runtime_error("compressed end offset is beyond end of command"); } @@ -190,57 +150,56 @@ static void on_forward_sync_game_state(shared_ptr, print_data(stderr, decompressed); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_game_loading(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (!l->is_game() || !l->any_client_loading()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_size_client(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc( - data, sizeof(G_ClientIDHeader), 0xFFFF); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, 0xFFFF); if (cmd.client_id != c->lobby_client_id) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_size_game(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - check_size_sc(data, sizeof(G_UnusedHeader), 0xFFFF); + const void* data, size_t size) { + check_size_t(data, size, 0xFFFF); if (!l->is_game()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_size_ep3_lobby(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - check_size_sc(data, sizeof(G_UnusedHeader), 0xFFFF); + const void* data, size_t size) { + check_size_t(data, size, 0xFFFF); if (l->is_game() || !l->is_ep3()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_forward_check_size_ep3_game(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - check_size_sc(data, sizeof(G_UnusedHeader), 0xFFFF); + const void* data, size_t size) { + check_size_t(data, size, 0xFFFF); if (!l->is_game() || !l->is_ep3()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } //////////////////////////////////////////////////////////////////////////////// @@ -248,21 +207,20 @@ static void on_forward_check_size_ep3_game(shared_ptr, static void on_ep3_battle_subs(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& orig_data) { - const auto& header = check_size_sc( - orig_data, sizeof(G_CardBattleCommandHeader), 0xFFFF); + const void* orig_data, size_t size) { + const auto& header = check_size_t(orig_data, size, 0xFFFF); if (!l->is_game() || !l->is_ep3()) { return; } - string data = orig_data; + string data(reinterpret_cast(orig_data), size); set_mask_for_ep3_game_command(data.data(), data.size(), 0); if (header.subcommand == 0xB5) { if (header.subsubcommand == 0x1A) { return; } else if (header.subsubcommand == 0x36) { - const auto& cmd = check_size_t(data); + const auto& cmd = check_size_t(data, size); if (l->is_game() && (cmd.unknown_a1 >= 4)) { return; } @@ -277,7 +235,7 @@ static void on_ep3_battle_subs(shared_ptr s, set_mask_for_ep3_game_command(data.data(), data.size(), mask_key); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data.data(), data.size()); } //////////////////////////////////////////////////////////////////////////////// @@ -285,7 +243,7 @@ static void on_ep3_battle_subs(shared_ptr s, static void on_send_guild_card(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (!command_is_private(command) || !l || (flag >= l->max_clients) || (!l->clients[flag])) { return; @@ -293,18 +251,18 @@ static void on_send_guild_card(shared_ptr, switch (c->version()) { case GameVersion::DC: { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); c->game_data.player()->guild_card_description = cmd.description; break; } case GameVersion::PC: { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); c->game_data.player()->guild_card_description = cmd.description; break; } case GameVersion::GC: case GameVersion::XB: { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); c->game_data.player()->guild_card_description = cmd.description; break; } @@ -322,39 +280,39 @@ static void on_send_guild_card(shared_ptr, // client sends a symbol chat static void on_symbol_chat(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!c->can_chat || (cmd.client_id != c->lobby_client_id)) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } // client sends a word select chat static void on_word_select(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!c->can_chat || (cmd.header.client_id != c->lobby_client_id)) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } // client is done loading into a lobby (we use this to trigger arrow updates) static void on_set_player_visibility(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (cmd.header.client_id != c->lobby_client_id) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); if (!l->is_game() && !(c->flags & Client::Flag::IS_DC_V1)) { send_arrow_update(l); @@ -366,38 +324,38 @@ static void on_set_player_visibility(shared_ptr, static void on_change_area(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; } c->area = cmd.area; - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } // when a player is hit by an enemy, heal them if infinite HP is enabled static void on_hit_by_enemy(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, sizeof(G_ClientIDHeader), 0xFFFF); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, 0xFFFF); if (!l->is_game() || (cmd.client_id != c->lobby_client_id)) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.infinite_hp) { send_player_stats_change(l, c, PlayerStatsChange::ADD_HP, 2550); } } -// when a player casts a tech, restore TP if infinite TP is enabled +// When a player casts a tech, restore TP if infinite TP is enabled static void on_cast_technique_finished(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.infinite_tp) { send_player_stats_change(l, c, PlayerStatsChange::ADD_TP, 255); } @@ -405,48 +363,48 @@ static void on_cast_technique_finished(shared_ptr, static void on_attack_finished(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, - offsetof(G_AttackFinished_6x46, entries), sizeof(G_AttackFinished_6x46)); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, + offsetof(G_AttackFinished_6x46, targets), sizeof(G_AttackFinished_6x46)); size_t allowed_count = min(cmd.header.size - 2, 11); if (cmd.count > allowed_count) { throw runtime_error("invalid attack finished command"); } - on_forward_check_size_client(s, l, c, command, flag, data); + on_forward_check_size_client(s, l, c, command, flag, data, size); } static void on_cast_technique(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, offsetof(G_CastTechnique_6x47, targets), sizeof(G_CastTechnique_6x47)); size_t allowed_count = min(cmd.header.size - 2, 10); if (cmd.target_count > allowed_count) { throw runtime_error("invalid cast technique command"); } - on_forward_check_size_client(s, l, c, command, flag, data); + on_forward_check_size_client(s, l, c, command, flag, data, size); } static void on_subtract_pb_energy(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size, offsetof(G_SubtractPBEnergy_6x49, entries), sizeof(G_SubtractPBEnergy_6x49)); size_t allowed_count = min(cmd.header.size - 3, 14); if (cmd.entry_count > allowed_count) { throw runtime_error("invalid subtract PB energy command"); } - on_forward_check_size_client(s, l, c, command, flag, data); + on_forward_check_size_client(s, l, c, command, flag, data, size); } static void on_switch_state_changed(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - auto& cmd = check_size_t(data); + const void* data, size_t size) { + auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); if (cmd.flags && cmd.header.object_id != 0xFFFF) { if ((l->flags & Lobby::Flag::CHEATS_ENABLED) && c->options.switch_assist && (c->last_switch_enabled_command.header.subcommand == 0x05)) { @@ -467,9 +425,8 @@ static void on_switch_state_changed(shared_ptr, template void on_movement(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); - + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (cmd.header.client_id != c->lobby_client_id) { return; } @@ -477,7 +434,23 @@ void on_movement(shared_ptr, c->x = cmd.x; c->z = cmd.z; - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); +} + +template +void on_movement_with_area(shared_ptr, + shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); + if (cmd.header.client_id != c->lobby_client_id) { + return; + } + + c->x = cmd.x; + c->z = cmd.z; + c->area = cmd.area; + + forward_subcommand(l, c, command, flag, data, size); } //////////////////////////////////////////////////////////////////////////////// @@ -485,8 +458,8 @@ void on_movement(shared_ptr, static void on_player_drop_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if ((cmd.header.client_id != c->lobby_client_id)) { return; @@ -508,14 +481,14 @@ static void on_player_drop_item(shared_ptr, c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_create_inventory_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, - sizeof(G_CreateInventoryItem_DC_6x2B), sizeof(G_CreateInventoryItem_PC_V3_BB_6x2B)); + const void* data, size_t size) { + const auto& cmd = check_size_t( + data, size, sizeof(G_CreateInventoryItem_PC_V3_BB_6x2B)); if ((cmd.header.client_id != c->lobby_client_id)) { return; @@ -544,14 +517,14 @@ static void on_create_inventory_item(shared_ptr, c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_drop_partial_stack(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, - sizeof(G_DropStackedItem_DC_6x5D), sizeof(G_DropStackedItem_PC_V3_BB_6x5D)); + const void* data, size_t size) { + const auto& cmd = check_size_t( + data, size, sizeof(G_DropStackedItem_PC_V3_BB_6x5D)); // TODO: Should we check the client ID here too? if (!l->is_game()) { @@ -582,14 +555,14 @@ static void on_drop_partial_stack(shared_ptr, c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_drop_partial_stack_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; @@ -629,14 +602,14 @@ static void on_drop_partial_stack_bb(shared_ptr, send_drop_stacked_item(l, item.data, cmd.area, cmd.x, cmd.z); } else { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_buy_shop_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; @@ -663,14 +636,14 @@ static void on_buy_shop_item(shared_ptr, c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_box_or_enemy_item_drop(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data, - sizeof(G_DropItem_DC_6x5F), sizeof(G_DropItem_PC_V3_BB_6x5F)); + const void* data, size_t size) { + const auto& cmd = check_size_t( + data, size, sizeof(G_DropItem_PC_V3_BB_6x5F)); if (!l->is_game() || (c->lobby_client_id != l->leader_id)) { return; @@ -696,13 +669,13 @@ static void on_box_or_enemy_item_drop(shared_ptr, } } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_pick_up_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - auto& cmd = check_size_sc(data); + const void* data, size_t size) { + auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; @@ -732,15 +705,15 @@ static void on_pick_up_item(shared_ptr, effective_c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_pick_up_item_request(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { // This is handled by the server on BB, and by the leader on other versions if (l->version == GameVersion::BB) { - auto& cmd = check_size_sc(data); + auto& cmd = check_size_t(data, size); if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) { return; @@ -766,14 +739,14 @@ static void on_pick_up_item_request(shared_ptr, send_pick_up_item(l, c, cmd.item_id, cmd.area); } else { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_equip_unequip_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (cmd.header.client_id != c->lobby_client_id) { return; @@ -792,7 +765,7 @@ static void on_equip_unequip_item(shared_ptr, // TODO: Should we forward this command on BB? The old version of newserv // didn't, but that seems wrong. - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_use_item( @@ -801,8 +774,8 @@ static void on_use_item( shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (cmd.header.client_id != c->lobby_client_id) { return; @@ -829,7 +802,7 @@ static void on_use_item( c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_feed_mag( @@ -838,8 +811,8 @@ static void on_feed_mag( shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (cmd.header.client_id != c->lobby_client_id) { return; @@ -880,20 +853,20 @@ static void on_feed_mag( c->game_data.player()->print_inventory(stderr); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (l->is_ep3()) { - on_ep3_battle_subs(s, l, c, command, flag, data); + on_ep3_battle_subs(s, l, c, command, flag, data, size); } else if (!l->item_creator.get()) { throw runtime_error("received shop subcommand without item creator present"); } else { - const auto& cmd = check_size_sc(data, 0x08); + const auto& cmd = check_size_t(data, size); if ((l->version == GameVersion::BB) && l->is_game()) { if (!l->item_creator) { throw logic_error("item creator missing from BB game"); @@ -924,18 +897,18 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr s, } static void on_open_bank_bb_or_card_trade_counter_ep3(shared_ptr, - shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { + shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { if ((l->version == GameVersion::BB) && l->is_game()) { send_bank(c); } else if ((l->version == GameVersion::GC) && l->is_ep3()) { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_bank_action_bb(shared_ptr, - shared_ptr l, shared_ptr c, uint8_t, uint8_t, const string& data) { + shared_ptr l, shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; @@ -984,9 +957,9 @@ static void on_bank_action_bb(shared_ptr, static void on_sort_inventory_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t, uint8_t, - const string& data) { + const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); @@ -1016,7 +989,7 @@ static void on_sort_inventory_bb(shared_ptr, static void on_entity_drop_item_request( shared_ptr, shared_ptr l, shared_ptr c, - uint8_t command, uint8_t flag, const string& data) { + uint8_t command, uint8_t flag, const void* data, size_t size) { if (!l->is_game()) { return; } @@ -1025,17 +998,17 @@ static void on_entity_drop_item_request( // enabled, or just ignore it) instead of generating the item drop command if (l->version != GameVersion::BB) { if (l->flags & Lobby::Flag::DROPS_ENABLED) { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } return; } G_SpecializableItemDropRequest_6xA2 cmd; - if (data.size() == sizeof(G_SpecializableItemDropRequest_6xA2)) { - cmd = check_size_sc(data); + if (size == sizeof(G_SpecializableItemDropRequest_6xA2)) { + cmd = check_size_t(data, size); } else { - const auto& in_cmd = check_size_sc( - data, sizeof(G_StandardDropItemRequest_DC_6x60), 0xFFFF); + const auto& in_cmd = check_size_t( + data, size, 0xFFFF); cmd.entity_id = in_cmd.entity_id; cmd.area = in_cmd.area; cmd.rt_index = in_cmd.rt_index; @@ -1074,19 +1047,19 @@ static void on_entity_drop_item_request( static void on_set_quest_flag(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (!l->is_game()) { return; } uint16_t flag_index, difficulty, action; if (c->version() == GameVersion::DC || c->version() == GameVersion::PC) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); flag_index = cmd.flag; action = cmd.action; difficulty = l->difficulty; } else { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); flag_index = cmd.basic_cmd.flag; action = cmd.basic_cmd.action; difficulty = cmd.difficulty; @@ -1097,7 +1070,7 @@ static void on_set_quest_flag(shared_ptr, } // The client explicitly checks for both 0 and 1 - any other value means no // operation is performed. - size_t bit_index = (l->difficulty << 10) + flag_index; + size_t bit_index = (difficulty << 10) + flag_index; size_t byte_index = bit_index >> 3; uint8_t mask = 0x80 >> (bit_index & 7); if (action == 0) { @@ -1106,7 +1079,7 @@ static void on_set_quest_flag(shared_ptr, c->game_data.player()->quest_data1[byte_index] &= (~mask); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); if (c->version() == GameVersion::GC) { bool should_send_boss_drop_req = false; @@ -1115,8 +1088,8 @@ static void on_set_quest_flag(shared_ptr, // On Normal, Dark Falz does not have a third phase, so send the drop // request after the end of the second phase. On all other difficulty // levels, send it after the third phase. - if (((l->difficulty == 0) && (flag_index == 0x0035)) || - ((l->difficulty != 0) && (flag_index == 0x0037))) { + if (((difficulty == 0) && (flag_index == 0x0035)) || + ((difficulty != 0) && (flag_index == 0x0037))) { should_send_boss_drop_req = true; } } else if (is_ep2 && (flag_index == 0x0057) && (c->area == 0x0D)) { @@ -1253,8 +1226,8 @@ static void on_enemy_killed(shared_ptr s, static void on_destroy_inventory_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; } @@ -1273,14 +1246,14 @@ static void on_destroy_inventory_item(shared_ptr, cmd.item_id.load(), name.c_str()); } c->game_data.player()->print_inventory(stderr); - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_destroy_ground_item(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { - const auto& cmd = check_size_sc(data); + const void* data, size_t size) { + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; } @@ -1294,15 +1267,15 @@ static void on_destroy_ground_item(shared_ptr, send_text_message_printf(c, "$C5DESTROY/GND %08" PRIX32 "\n%s", cmd.item_id.load(), name.c_str()); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_identify_item_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!l->is_game()) { return; } @@ -1328,16 +1301,16 @@ static void on_identify_item_bb(shared_ptr, send_command_t(l, 0x60, 0x00, res); } else { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_accept_identify_item_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data) { + const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); @@ -1354,15 +1327,15 @@ static void on_accept_identify_item_bb(shared_ptr, c->game_data.identify_result.clear(); } else { - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_sell_item_at_shop_bb(shared_ptr s, - shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const string& data) { + shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); @@ -1384,14 +1357,14 @@ static void on_sell_item_at_shop_bb(shared_ptr s, cmd.item_id.load(), price, name.c_str()); } - forward_subcommand(l, c, command, flag, data); + forward_subcommand(l, c, command, flag, data, size); } } static void on_buy_shop_item_bb(shared_ptr, - shared_ptr l, shared_ptr c, uint8_t, uint8_t, const string& data) { + shared_ptr l, shared_ptr c, uint8_t, uint8_t, const void* data, size_t size) { if (l->version == GameVersion::BB) { - const auto& cmd = check_size_sc(data); + const auto& cmd = check_size_t(data, size); if (!(l->flags & Lobby::Flag::ITEM_TRACKING_ENABLED)) { throw logic_error("item tracking not enabled in BB game"); } @@ -1428,7 +1401,7 @@ static void on_buy_shop_item_bb(shared_ptr, } static void on_medical_center_bb(shared_ptr, - shared_ptr l, shared_ptr c, uint8_t, uint8_t, const string&) { + shared_ptr l, shared_ptr c, uint8_t, uint8_t, const void*, size_t) { if (l->version == GameVersion::BB) { if (c->game_data.player()->disp.meseta < 10) { @@ -1445,7 +1418,7 @@ static void on_medical_center_bb(shared_ptr, // for more information on flags. The maximum size is not enforced if it's zero. typedef void (*subcommand_handler_t)(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, - const string& data); + const void* data, size_t size); subcommand_handler_t subcommand_handlers[0x100] = { /* 6x00 */ on_invalid, diff --git a/src/ReplaySession.cc b/src/ReplaySession.cc index 81abf214..b9805616 100644 --- a/src/ReplaySession.cc +++ b/src/ReplaySession.cc @@ -107,16 +107,14 @@ void ReplaySession::check_for_password(shared_ptr ev) const { switch (version) { case GameVersion::PATCH: { - const auto& header = check_size_t( - ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF); + const auto& header = check_size_t(ev->data, 0xFFFF); if (header.command == 0x04) { check_either(check_size_t(cmd_data, cmd_size).password); } break; } case GameVersion::PC: { - const auto& header = check_size_t( - ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF); + const auto& header = check_size_t(ev->data, 0xFFFF); if (header.command == 0x03) { check_ak(check_size_t(cmd_data, cmd_size).access_key2); } else if (header.command == 0x04) { @@ -131,8 +129,8 @@ void ReplaySession::check_for_password(shared_ptr ev) const { check_ak(cmd.access_key); check_pw(cmd.password); } else if (header.command == 0x9D) { - const auto& cmd = check_size_t(cmd_data, cmd_size, - sizeof(C_Login_DC_PC_GC_9D), sizeof(C_LoginExtended_PC_9D)); + const auto& cmd = check_size_t( + cmd_data, cmd_size, sizeof(C_LoginExtended_PC_9D)); check_ak(cmd.v1_access_key); check_ak(cmd.access_key); check_ak(cmd.access_key2); @@ -142,17 +140,16 @@ void ReplaySession::check_for_password(shared_ptr ev) const { case GameVersion::DC: case GameVersion::GC: case GameVersion::XB: { - const auto& header = check_size_t( - ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF); + const auto& header = check_size_t(ev->data, 0xFFFF); if (header.command == 0x03) { check_ak(check_size_t(cmd_data, cmd_size).access_key2); } else if (header.command == 0x04) { check_ak(check_size_t(cmd_data, cmd_size).access_key); } else if (header.command == 0x90) { - check_ak(check_size_t(cmd_data, cmd_size, sizeof(C_LoginV1_DC_PC_V3_90), 0xFFFF).access_key); + check_ak(check_size_t(cmd_data, cmd_size, 0xFFFF).access_key); } else if (header.command == 0x93) { - const auto& cmd = check_size_t(cmd_data, cmd_size, - sizeof(C_LoginV1_DC_93), sizeof(C_LoginExtendedV1_DC_93)); + const auto& cmd = check_size_t( + cmd_data, cmd_size, sizeof(C_LoginExtendedV1_DC_93)); check_ak(cmd.access_key); } else if (header.command == 0x9A) { const auto& cmd = check_size_t(cmd_data, cmd_size); @@ -164,20 +161,20 @@ void ReplaySession::check_for_password(shared_ptr ev) const { check_ak(cmd.access_key); check_pw(cmd.password); } else if (header.command == 0x9D) { - const auto& cmd = check_size_t(cmd_data, cmd_size, - sizeof(C_Login_DC_PC_GC_9D), sizeof(C_LoginExtended_DC_GC_9D)); + const auto& cmd = check_size_t( + cmd_data, cmd_size, sizeof(C_LoginExtended_DC_GC_9D)); check_ak(cmd.v1_access_key); check_ak(cmd.access_key); check_ak(cmd.access_key2); } else if (header.command == 0x9E) { if (version == GameVersion::GC) { - const auto& cmd = check_size_t(cmd_data, cmd_size, - sizeof(C_Login_GC_9E), sizeof(C_LoginExtended_GC_9E)); + const auto& cmd = check_size_t( + cmd_data, cmd_size, sizeof(C_LoginExtended_GC_9E)); check_ak(cmd.access_key); check_ak(cmd.access_key2); } else { // XB - const auto& cmd = check_size_t(cmd_data, cmd_size, - sizeof(C_Login_XB_9E), sizeof(C_LoginExtended_XB_9E)); + const auto& cmd = check_size_t( + cmd_data, cmd_size, sizeof(C_LoginExtended_XB_9E)); check_ak(cmd.access_key); check_ak(cmd.access_key2); } @@ -190,8 +187,7 @@ void ReplaySession::check_for_password(shared_ptr ev) const { break; } case GameVersion::BB: { - const auto& header = check_size_t( - ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF); + const auto& header = check_size_t(ev->data, 0xFFFF); if (header.command == 0x04) { check_pw(check_size_t(cmd_data, cmd_size).password); } else if (header.command == 0x93) { @@ -220,8 +216,7 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { switch (version) { case GameVersion::PATCH: { - const auto& header = check_size_t( - ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF); + const auto& header = check_size_t(ev->data, 0xFFFF); if (header.command == 0x02) { auto& cmd_mask = check_size_t(mask_data, mask_size); cmd_mask.server_key = 0; @@ -235,13 +230,9 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { case GameVersion::XB: { uint8_t command; if (version == GameVersion::PC) { - command = check_size_t( - ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF) - .command; + command = check_size_t(ev->data, 0xFFFF).command; } else { // V3 - command = check_size_t( - ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF) - .command; + command = check_size_t(ev->data, 0xFFFF).command; } switch (command) { case 0x02: @@ -249,7 +240,7 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { case 0x91: case 0x9B: { auto& mask = check_size_t( - mask_data, mask_size, sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF); + mask_data, mask_size, 0xFFFF); mask.server_key = 0; mask.client_key = 0; break; @@ -284,8 +275,8 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { mask.variations.clear(0); mask.rare_seed = 0; } else { // V3 - auto& mask = check_size_t(mask_data, mask_size, - sizeof(S_JoinGame_DC_GC_64), sizeof(S_JoinGame_GC_Ep3_64)); + auto& mask = check_size_t( + mask_data, mask_size, sizeof(S_JoinGame_GC_Ep3_64)); mask.variations.clear(0); mask.rare_seed = 0; } @@ -309,13 +300,11 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { } case 0x6C: { if (version == GameVersion::GC && mask_size >= 0x14) { - const auto& cmd = check_size_t( - cmd_data, cmd_size, sizeof(G_MapList_GC_Ep3_6xB6x40), 0xFFFF); + const auto& cmd = check_size_t(cmd_data, cmd_size, 0xFFFF); if ((cmd.header.header.basic_header.subcommand == 0xB6) && (cmd.header.subsubcommand == 0x40)) { - check_size_t(ev->mask, sizeof(PSOCommandHeaderDCV3), 0xFFFF).size = 0; - auto& mask = check_size_t( - mask_data, mask_size, sizeof(G_MapList_GC_Ep3_6xB6x40), 0xFFFF); + check_size_t(ev->mask, 0xFFFF).size = 0; + auto& mask = check_size_t(mask_data, mask_size, 0xFFFF); mask.header.header.size = 0; mask.compressed_data_size = 0; ev->allow_size_disparity = true; @@ -330,13 +319,10 @@ void ReplaySession::apply_default_mask(shared_ptr ev) { break; } case GameVersion::BB: { - uint16_t command = check_size_t( - ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF) - .command; + uint16_t command = check_size_t(ev->data, 0xFFFF).command; switch (command) { case 0x0003: { - auto& mask = check_size_t( - mask_data, mask_size, sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF); + auto& mask = check_size_t(mask_data, mask_size, 0xFFFF); mask.server_key.clear(0); mask.client_key.clear(0); break; @@ -675,8 +661,7 @@ void ReplaySession::on_command_received( case GameVersion::GC: case GameVersion::XB: if (command == 0x02 || command == 0x17 || command == 0x91 || command == 0x9B) { - auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_DC_PC_V3_02_17_91_9B), 0xFFFF); + auto& cmd = check_size_t(data, 0xFFFF); if ((c->version == GameVersion::DC) || (c->version == GameVersion::PC)) { c->channel.crypt_in.reset(new PSOV2Encryption(cmd.server_key)); c->channel.crypt_out.reset(new PSOV2Encryption(cmd.client_key)); @@ -688,8 +673,7 @@ void ReplaySession::on_command_received( break; case GameVersion::BB: if (command == 0x03 || command == 0x9B) { - auto& cmd = check_size_t(data, - sizeof(S_ServerInitDefault_BB_03_9B), 0xFFFF); + auto& cmd = check_size_t(data, 0xFFFF); // TODO: At some point it may matter which BB private key file we use. // Don't just blindly use the first one here. c->channel.crypt_in.reset(new PSOBBEncryption(