diff --git a/src/Client.hh b/src/Client.hh index d07cc413..65796c49 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -75,6 +75,9 @@ struct Client { ENCRYPTED_SEND_FUNCTION_CALL = 0x00000800, // Client supports send_function_call but does not actually run the code SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x00001000, + // Client supports send_function_call but doesn't clear its caches properly + // before calling the function (so we need to patch it) + SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH = 0x00020000, // Client is vulnerable to a buffer overflow that we can use to enable // send_function_call USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x00008000, diff --git a/src/FunctionCompiler.cc b/src/FunctionCompiler.cc index 8719e8e4..b1aa9cfa 100644 --- a/src/FunctionCompiler.cc +++ b/src/FunctionCompiler.cc @@ -15,14 +15,20 @@ using namespace std; +static bool is_function_compiler_available = true; + bool function_compiler_available() { #ifndef HAVE_RESOURCE_FILE return false; #else - return true; + return is_function_compiler_available; #endif } +void set_function_compiler_available(bool is_available) { + is_function_compiler_available = is_available; +} + const char* name_for_architecture(CompiledFunctionCode::Architecture arch) { switch (arch) { case CompiledFunctionCode::Architecture::POWERPC: @@ -37,7 +43,8 @@ const char* name_for_architecture(CompiledFunctionCode::Architecture arch) { template string CompiledFunctionCode::generate_client_command_t( const unordered_map& label_writes, - const string& suffix) const { + const string& suffix, + uint32_t override_relocations_offset) const { FooterT footer; footer.num_relocations = this->relocation_deltas.size(); footer.unused1.clear(0); @@ -63,12 +70,15 @@ string CompiledFunctionCode::generate_client_command_t( w.put_u8(0); } - footer.relocations_offset = w.size(); - for (uint16_t delta : this->relocation_deltas) { - w.put(delta); - } - if (this->relocation_deltas.size() & 1) { - w.put_u16(0); + if (override_relocations_offset) { + footer.relocations_offset = override_relocations_offset; + } else { + for (uint16_t delta : this->relocation_deltas) { + w.put(delta); + } + if (this->relocation_deltas.size() & 1) { + w.put_u16(0); + } } w.put(footer); @@ -77,13 +87,14 @@ string CompiledFunctionCode::generate_client_command_t( string CompiledFunctionCode::generate_client_command( const unordered_map& label_writes, - const string& suffix) const { + const string& suffix, + uint32_t override_relocations_offset) const { if (this->arch == Architecture::POWERPC) { return this->generate_client_command_t( - label_writes, suffix); + label_writes, suffix, override_relocations_offset); } else if ((this->arch == Architecture::X86) || (this->arch == Architecture::SH4)) { return this->generate_client_command_t( - label_writes, suffix); + label_writes, suffix, override_relocations_offset); } else { throw logic_error("invalid architecture"); } diff --git a/src/FunctionCompiler.hh b/src/FunctionCompiler.hh index eb8cb981..b0b2c579 100644 --- a/src/FunctionCompiler.hh +++ b/src/FunctionCompiler.hh @@ -11,6 +11,7 @@ #include "Menu.hh" bool function_compiler_available(); +void set_function_compiler_available(bool is_available); // TODO: Support x86 and SH4 function calls in the future. Currently we only // support PPC32 because I haven't written an appropriate x86 assembler yet. @@ -36,10 +37,12 @@ struct CompiledFunctionCode { template std::string generate_client_command_t( const std::unordered_map& label_writes, - const std::string& suffix) const; + const std::string& suffix, + uint32_t override_relocations_offset = 0) const; std::string generate_client_command( const std::unordered_map& label_writes = {}, - const std::string& suffix = "") const; + const std::string& suffix = "", + uint32_t override_relocations_offset = 0) const; }; const char* name_for_architecture(CompiledFunctionCode::Architecture arch); diff --git a/src/Main.cc b/src/Main.cc index c10987cd..fe25c759 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1194,6 +1194,10 @@ int main(int argc, char** argv) { use_terminal_colors = true; } + if (is_replay) { + set_function_compiler_available(false); + } + shared_ptr base(event_base_new(), event_base_free); shared_ptr state(new ServerState(config_filename, is_replay)); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 0c66b2a2..cfd21be1 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -156,7 +156,8 @@ static void send_proxy_destinations_menu(shared_ptr s, shared_ptr s, shared_ptr c) { - if (c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL) { + if (function_compiler_available() && + (c->flags & Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL)) { if (s->episode_3_send_function_call_enabled) { send_quest_buffer_overflow(s, c); } else { @@ -231,6 +232,15 @@ void on_login_complete(shared_ptr s, shared_ptr c) { send_change_event(c, s->pre_lobby_event); } + if (function_compiler_available() && (c->flags & Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH)) { + send_function_call( + c, s->function_code_index->name_to_function.at("CacheClearFix-Phase1"), {}, "", 0, 0, 0x7F2634EC); + send_function_call( + c, s->function_code_index->name_to_function.at("CacheClearFix-Phase2")); + c->flags &= ~Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH; + send_update_client_config(c); + } + if (s->welcome_message.empty() || (c->flags & Client::Flag::NO_D6) || !(c->flags & Client::Flag::AT_WELCOME_MESSAGE)) { diff --git a/src/SendCommands.cc b/src/SendCommands.cc index fedd99a5..9fc4467a 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -58,12 +58,12 @@ const unordered_set bb_crypt_initial_client_commands({ }); void send_command(shared_ptr c, uint16_t command, uint32_t flag, - const void* data, size_t size) { + const void* data, size_t size) { c->channel.send(command, flag, data, size); } void send_command_excluding_client(shared_ptr l, shared_ptr c, - uint16_t command, uint32_t flag, const void* data, size_t size) { + uint16_t command, uint32_t flag, const void* data, size_t size) { for (auto& client : l->clients) { if (!client || (client == c)) { continue; @@ -73,7 +73,7 @@ void send_command_excluding_client(shared_ptr l, shared_ptr c, } void send_command_if_not_loading(shared_ptr l, - uint16_t command, uint32_t flag, const void* data, size_t size) { + uint16_t command, uint32_t flag, const void* data, size_t size) { for (auto& client : l->clients) { if (!client || (client->flags & Client::Flag::LOADING)) { continue; @@ -83,12 +83,12 @@ void send_command_if_not_loading(shared_ptr l, } void send_command(shared_ptr l, uint16_t command, uint32_t flag, - const void* data, size_t size) { + const void* data, size_t size) { send_command_excluding_client(l, nullptr, command, flag, data, size); } void send_command(shared_ptr s, uint16_t command, uint32_t flag, - const void* data, size_t size) { + const void* data, size_t size) { for (auto& l : s->all_lobbies()) { send_command(l, command, flag, data, size); } @@ -132,8 +132,8 @@ prepare_server_init_contents_console( bool initial_connection = (flags & SendServerInitFlag::IS_INITIAL_CONNECTION); S_ServerInitWithAfterMessage_DC_PC_V3_02_17_91_9B<0xB4> cmd; cmd.basic_cmd.copyright = initial_connection - ? dc_port_map_copyright - : dc_lobby_server_copyright; + ? dc_port_map_copyright + : dc_lobby_server_copyright; cmd.basic_cmd.server_key = server_key; cmd.basic_cmd.client_key = client_key; cmd.after_message = anti_copyright; @@ -184,7 +184,7 @@ prepare_server_init_contents_bb( } void send_server_init_bb(shared_ptr s, shared_ptr c, - uint8_t flags) { + uint8_t flags) { bool use_secondary_message = (flags & SendServerInitFlag::USE_SECONDARY_MESSAGE); parray server_key; parray client_key; @@ -316,7 +316,8 @@ void send_function_call( const unordered_map& label_writes, const string& suffix, uint32_t checksum_addr, - uint32_t checksum_size) { + uint32_t checksum_size, + uint32_t override_relocations_offset) { return send_function_call( c->channel, c->flags, @@ -324,7 +325,8 @@ void send_function_call( label_writes, suffix, checksum_addr, - checksum_size); + checksum_size, + override_relocations_offset); } void send_function_call( @@ -334,7 +336,8 @@ void send_function_call( const unordered_map& label_writes, const string& suffix, uint32_t checksum_addr, - uint32_t checksum_size) { + uint32_t checksum_size, + uint32_t override_relocations_offset) { if (client_flags & Client::Flag::NO_SEND_FUNCTION_CALL) { throw logic_error("client does not support function calls"); } @@ -345,7 +348,7 @@ void send_function_call( string data; uint32_t index = 0; if (code.get()) { - data = code->generate_client_command(label_writes, suffix); + data = code->generate_client_command(label_writes, suffix, override_relocations_offset); index = code->index; if (client_flags & Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL) { @@ -393,7 +396,7 @@ void send_reconnect(shared_ptr c, uint32_t address, uint16_t port) { } void send_pc_console_split_reconnect(shared_ptr c, uint32_t address, - uint16_t pc_port, uint16_t console_port) { + uint16_t pc_port, uint16_t console_port) { S_ReconnectSplit_19 cmd; cmd.pc_address = address; cmd.pc_port = pc_port; @@ -421,7 +424,7 @@ void send_team_and_key_config_bb(shared_ptr c) { } void send_player_preview_bb(shared_ptr c, uint8_t player_index, - const PlayerDispDataBBPreview* preview) { + const PlayerDispDataBBPreview* preview) { if (!preview) { // no player exists @@ -586,7 +589,7 @@ void send_patch_file(shared_ptr c, shared_ptr f) { // message functions void send_text(Channel& ch, StringWriter& w, uint16_t command, - const u16string& text, bool should_add_color) { + const u16string& text, bool should_add_color) { if ((ch.version == GameVersion::DC) || (ch.version == GameVersion::GC) || (ch.version == GameVersion::XB)) { @@ -617,7 +620,7 @@ void send_text(Channel& ch, uint16_t command, const u16string& text, bool should } void send_header_text(Channel& ch, uint16_t command, - uint32_t guild_card_number, const u16string& text, bool should_add_color) { + uint32_t guild_card_number, const u16string& text, bool should_add_color) { StringWriter w; w.put(SC_TextHeader_01_06_11_B0_EE({0, guild_card_number})); send_text(ch, w, command, text, should_add_color); @@ -660,7 +663,7 @@ void send_lobby_name(shared_ptr c, const u16string& text) { } void send_quest_info(shared_ptr c, const u16string& text, - bool is_download_quest) { + bool is_download_quest) { send_text(c->channel, is_download_quest ? 0xA5 : 0xA3, text, true); } @@ -750,12 +753,12 @@ void send_chat_message(Channel& ch, const u16string& text, char private_flags) { } void send_chat_message(shared_ptr c, uint32_t from_guild_card_number, - const u16string& prepared_data) { + const u16string& prepared_data) { send_header_text(c->channel, 0x06, from_guild_card_number, prepared_data, false); } void send_chat_message(shared_ptr l, uint32_t from_guild_card_number, - const u16string& prepared_data) { + const u16string& prepared_data) { for (auto c : l->clients) { if (c) { send_header_text(c->channel, 0x06, from_guild_card_number, prepared_data, false); @@ -764,7 +767,7 @@ void send_chat_message(shared_ptr l, uint32_t from_guild_card_number, } void send_chat_message(shared_ptr c, uint32_t from_guild_card_number, - const u16string& from_name, const u16string& text, char private_flags) { + const u16string& from_name, const u16string& text, char private_flags) { auto data = prepare_chat_message(c->version(), from_name, text, private_flags); send_chat_message(c, from_guild_card_number, data); } @@ -800,7 +803,7 @@ void send_simple_mail_bb( } void send_simple_mail(shared_ptr c, uint32_t from_guild_card_number, - const u16string& from_name, const u16string& text) { + const u16string& from_name, const u16string& text) { switch (c->version()) { case GameVersion::DC: case GameVersion::GC: @@ -872,13 +875,13 @@ void send_card_search_result_t( if (result_lobby->is_game()) { string encoded_lobby_name = encode_sjis(result_lobby->name); location_string = string_printf("%s,BLOCK01,%s", - encoded_lobby_name.c_str(), encoded_server_name.c_str()); + encoded_lobby_name.c_str(), encoded_server_name.c_str()); } else if (result_lobby->is_ep3()) { location_string = string_printf("BLOCK01-C%02" PRIu32 ",BLOCK01,%s", - result_lobby->lobby_id - 15, encoded_server_name.c_str()); + result_lobby->lobby_id - 15, encoded_server_name.c_str()); } else { location_string = string_printf("BLOCK01-%02" PRIu32 ",BLOCK01,%s", - result_lobby->lobby_id, encoded_server_name.c_str()); + result_lobby->lobby_id, encoded_server_name.c_str()); } cmd.location_string = location_string; cmd.extension.lobby_refs[0].menu_id = MenuID::LOBBY; @@ -971,7 +974,7 @@ void send_guild_card( send_guild_card_dc_pc_v3_t( ch, guild_card_number, name, description, section_id, char_class); } else if ((ch.version == GameVersion::GC) || - (ch.version == GameVersion::XB)) { + (ch.version == GameVersion::XB)) { send_guild_card_dc_pc_v3_t( ch, guild_card_number, name, description, section_id, char_class); } else if (ch.version == GameVersion::BB) { @@ -1067,7 +1070,7 @@ void send_menu_t( } void send_menu(shared_ptr c, const u16string& menu_name, - uint32_t menu_id, const vector& items, bool is_info_menu) { + uint32_t menu_id, const vector& items, bool is_info_menu) { if (c->version() == GameVersion::PC || c->version() == GameVersion::PATCH || c->version() == GameVersion::BB) { @@ -1223,7 +1226,7 @@ void send_quest_menu_t( } void send_quest_menu(shared_ptr c, uint32_t menu_id, - const vector>& quests, bool is_download_menu) { + const vector>& quests, bool is_download_menu) { switch (c->version()) { case GameVersion::PC: send_quest_menu_t(c, menu_id, quests, is_download_menu); @@ -1244,7 +1247,7 @@ void send_quest_menu(shared_ptr c, uint32_t menu_id, } void send_quest_menu(shared_ptr c, uint32_t menu_id, - const vector& items, bool is_download_menu) { + const vector& items, bool is_download_menu) { switch (c->version()) { case GameVersion::PC: send_quest_menu_t(c, menu_id, items, is_download_menu); @@ -1468,7 +1471,7 @@ void send_join_game_t(shared_ptr c, shared_ptr l) { template void send_join_lobby_t(shared_ptr c, shared_ptr l, - shared_ptr joining_client = nullptr) { + shared_ptr joining_client = nullptr) { uint8_t command; if (l->is_game()) { if (joining_client) { @@ -1586,7 +1589,7 @@ void send_join_lobby(shared_ptr c, shared_ptr l) { } void send_player_join_notification(shared_ptr c, - shared_ptr l, shared_ptr joining_client) { + shared_ptr l, shared_ptr joining_client) { switch (c->version()) { case GameVersion::PC: send_join_lobby_t(c, l, joining_client); @@ -1638,7 +1641,7 @@ void send_get_player_info(shared_ptr c) { // Trade window void send_execute_item_trade(shared_ptr c, - const vector& items) { + const vector& items) { SC_TradeItems_D0_D3 cmd; if (items.size() > sizeof(cmd.items) / sizeof(cmd.items[0])) { throw logic_error("too many items in execute trade command"); @@ -1652,7 +1655,7 @@ void send_execute_item_trade(shared_ptr c, } void send_execute_card_trade(shared_ptr c, - const vector>& card_to_count) { + const vector>& card_to_count) { if (!(c->flags & Client::Flag::IS_EPISODE_3)) { throw logic_error("cannot send trade cards command to non-Ep3 client"); } @@ -1727,7 +1730,7 @@ static vector generate_stats_change_subcommands( } void send_player_stats_change(shared_ptr l, shared_ptr c, - PlayerStatsChange stat, uint32_t amount) { + PlayerStatsChange stat, uint32_t amount) { auto subs = generate_stats_change_subcommands(c->lobby_client_id, stat, amount); send_command_vt(l, (subs.size() > 0x400 / sizeof(G_UpdatePlayerStat_6x9A)) ? 0x6C : 0x60, 0x00, subs); } @@ -1753,7 +1756,7 @@ void send_ep3_change_music(Channel& ch, uint32_t song) { } void send_set_player_visibility(shared_ptr l, shared_ptr c, - bool visible) { + bool visible) { uint8_t subcmd = visible ? 0x23 : 0x22; uint16_t client_id = c->lobby_client_id; G_SetPlayerVisibility_6x22_6x23 cmd = {{subcmd, 0x01, client_id}}; @@ -1764,35 +1767,35 @@ void send_set_player_visibility(shared_ptr l, shared_ptr c, // BB game commands void send_drop_item(Channel& ch, const ItemData& item, - bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) { + bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) { G_DropItem_PC_V3_BB_6x5F cmd = { {{0x5F, 0x0B, 0x0000}, area, from_enemy, request_id, x, z, 0, 0, item}, 0}; ch.send(0x60, 0x00, &cmd, sizeof(cmd)); } void send_drop_item(shared_ptr l, const ItemData& item, - bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) { + bool from_enemy, uint8_t area, float x, float z, uint16_t request_id) { G_DropItem_PC_V3_BB_6x5F cmd = { {{0x5F, 0x0B, 0x0000}, area, from_enemy, request_id, x, z, 0, 0, item}, 0}; send_command_t(l, 0x60, 0x00, cmd); } void send_drop_stacked_item(Channel& ch, const ItemData& item, - uint8_t area, float x, float z) { + uint8_t area, float x, float z) { G_DropStackedItem_PC_V3_BB_6x5D cmd = { {{0x5D, 0x0A, 0x0000}, area, 0, x, z, item}, 0}; ch.send(0x60, 0x00, &cmd, sizeof(cmd)); } void send_drop_stacked_item(shared_ptr l, const ItemData& item, - uint8_t area, float x, float z) { + uint8_t area, float x, float z) { G_DropStackedItem_PC_V3_BB_6x5D cmd = { {{0x5D, 0x0A, 0x0000}, area, 0, x, z, item}, 0}; send_command_t(l, 0x60, 0x00, cmd); } void send_pick_up_item(shared_ptr l, shared_ptr c, - uint32_t item_id, uint8_t area) { + uint32_t item_id, uint8_t area) { uint16_t client_id = c->lobby_client_id; G_PickUpItem_6x59 cmd = { {0x59, 0x03, client_id}, client_id, area, item_id}; @@ -1802,7 +1805,7 @@ void send_pick_up_item(shared_ptr l, shared_ptr c, // Creates an item in a player's inventory (used for withdrawing items from the // bank) void send_create_inventory_item(shared_ptr l, shared_ptr c, - const ItemData& item) { + const ItemData& item) { uint16_t client_id = c->lobby_client_id; G_CreateInventoryItem_BB_6xBE cmd = {{0xBE, 0x07, client_id}, item, 0}; send_command_t(l, 0x60, 0x00, cmd); @@ -1810,7 +1813,7 @@ void send_create_inventory_item(shared_ptr l, shared_ptr c, // destroys an item void send_destroy_item(shared_ptr l, shared_ptr c, - uint32_t item_id, uint32_t amount) { + uint32_t item_id, uint32_t amount) { uint16_t client_id = c->lobby_client_id; G_DeleteInventoryItem_6x29 cmd = {{0x29, 0x03, client_id}, item_id, amount}; send_command_t(l, 0x60, 0x00, cmd); @@ -1819,7 +1822,7 @@ void send_destroy_item(shared_ptr l, shared_ptr c, // sends the player their bank data void send_bank(shared_ptr c) { vector items(c->game_data.player()->bank.items, - &c->game_data.player()->bank.items[c->game_data.player()->bank.num_items]); + &c->game_data.player()->bank.items[c->game_data.player()->bank.num_items]); uint32_t checksum = random_object(); G_BankContentsHeader_BB_6xBC cmd = { @@ -1878,7 +1881,7 @@ void send_level_up(shared_ptr l, shared_ptr c) { // gives a player EXP void send_give_experience(shared_ptr l, shared_ptr c, - uint32_t amount) { + uint32_t amount) { uint16_t client_id = c->lobby_client_id; G_GiveExperience_BB_6xBF cmd = { {0xBF, sizeof(G_GiveExperience_BB_6xBF) / 4, client_id}, amount}; @@ -1993,8 +1996,8 @@ void send_ep3_tournament_list( } auto& entry = cmd.entries[z]; entry.menu_id = is_for_spectator_team_create - ? MenuID::TOURNAMENTS_FOR_SPEC - : MenuID::TOURNAMENTS; + ? MenuID::TOURNAMENTS_FOR_SPEC + : MenuID::TOURNAMENTS; entry.item_id = tourn->get_number(); // TODO: What does it mean for a tournament to be locked? Should we support // that? @@ -2004,8 +2007,8 @@ void send_ep3_tournament_list( // as long as the winners of the preceding matches have been determined. entry.state = (tourn->get_state() == Episode3::Tournament::State::REGISTRATION) - ? 0x00 - : 0x05; + ? 0x00 + : 0x05; // TODO: Fill in cmd.start_time here when we implement scheduled starts. entry.name = tourn->get_name(); const auto& teams = tourn->all_teams(); @@ -2178,8 +2181,8 @@ void send_ep3_game_details(shared_ptr c, shared_ptr l) { flag = 0x04; } else if (primary_lobby && - primary_lobby->ep3_server_base && - primary_lobby->ep3_server_base->server->get_setup_phase() != Episode3::SetupPhase::REGISTRATION) { + primary_lobby->ep3_server_base && + primary_lobby->ep3_server_base->server->get_setup_phase() != Episode3::SetupPhase::REGISTRATION) { cmd.rules = primary_lobby->ep3_server_base->map_and_rules1->rules; flag = 0x01; @@ -2275,8 +2278,8 @@ void send_ep3_tournament_match_result( G_TournamentMatchResult_GC_Ep3_6xB4x51 cmd; cmd.match_description = (match == tourn->get_final_match()) - ? string_printf("(%s) Final match", tourn->get_name().c_str()) - : string_printf("(%s) Round %zu", tourn->get_name().c_str(), match->round_num); + ? string_printf("(%s) Final match", tourn->get_name().c_str()) + : string_printf("(%s) Round %zu", tourn->get_name().c_str(), match->round_num); cmd.names_entries[0].team_name = match->preceding_a->winner_team->name; write_player_names(cmd.names_entries[0], match->preceding_a->winner_team); cmd.names_entries[1].team_name = match->preceding_b->winner_team->name; @@ -2393,7 +2396,7 @@ void send_quest_file_chunk( } void send_open_quest_file(shared_ptr c, const string& quest_name, - const string& basename, shared_ptr contents, QuestFileType type) { + const string& basename, shared_ptr contents, QuestFileType type) { switch (c->version()) { case GameVersion::DC: @@ -2423,7 +2426,7 @@ void send_open_quest_file(shared_ptr c, const string& quest_name, chunk_bytes = 0x400; } send_quest_file_chunk(c, basename.c_str(), offset / 0x400, - contents->data() + offset, chunk_bytes, (type != QuestFileType::ONLINE)); + contents->data() + offset, chunk_bytes, (type != QuestFileType::ONLINE)); } } else { c->sending_files.emplace(basename, contents); @@ -2494,7 +2497,7 @@ void send_card_auction_if_all_clients_ready( num_cards = s->ep3_card_auction_min_size; } else { num_cards = s->ep3_card_auction_min_size + - (random_object() % (s->ep3_card_auction_max_size - s->ep3_card_auction_min_size + 1)); + (random_object() % (s->ep3_card_auction_max_size - s->ep3_card_auction_min_size + 1)); } num_cards = min(num_cards, 0x14); @@ -2534,7 +2537,7 @@ void send_server_time(shared_ptr c) { string time_str(128, 0); size_t len = strftime(time_str.data(), time_str.size(), - "%Y:%m:%d: %H:%M:%S.000", &t_parsed); + "%Y:%m:%d: %H:%M:%S.000", &t_parsed); if (len == 0) { throw runtime_error("format_time buffer too short"); } diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 80283cf1..9271f2c3 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -139,14 +139,16 @@ void send_function_call( const std::unordered_map& label_writes = {}, const std::string& suffix = "", uint32_t checksum_addr = 0, - uint32_t checksum_size = 0); + uint32_t checksum_size = 0, + uint32_t override_relocations_offset = 0); void send_function_call( std::shared_ptr c, std::shared_ptr code, const std::unordered_map& label_writes = {}, const std::string& suffix = "", uint32_t checksum_addr = 0, - uint32_t checksum_size = 0); + uint32_t checksum_size = 0, + uint32_t override_relocations_offset = 0); void send_reconnect(std::shared_ptr c, uint32_t address, uint16_t port); void send_pc_console_split_reconnect( diff --git a/src/Version.cc b/src/Version.cc index 77a8d687..ef804d9e 100644 --- a/src/Version.cc +++ b/src/Version.cc @@ -15,7 +15,7 @@ const vector version_to_lobby_port_name = { const vector version_to_proxy_port_name = { "", "dc-proxy", "pc-proxy", "gc-proxy", "xb-proxy", "bb-proxy"}; -uint16_t flags_for_version(GameVersion version, int64_t sub_version) { +uint32_t flags_for_version(GameVersion version, int64_t sub_version) { switch (sub_version) { case -1: // Initial check (before sub_version recognition) switch (version) { @@ -26,13 +26,13 @@ uint16_t flags_for_version(GameVersion version, int64_t sub_version) { return 0; case GameVersion::PC: return Client::Flag::NO_D6 | - Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY; + Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY; case GameVersion::PATCH: return Client::Flag::NO_D6 | - Client::Flag::NO_SEND_FUNCTION_CALL; + Client::Flag::NO_SEND_FUNCTION_CALL; case GameVersion::BB: return Client::Flag::NO_D6 | - Client::Flag::SAVE_ENABLED; + Client::Flag::SAVE_ENABLED; } break; @@ -42,47 +42,49 @@ uint16_t flags_for_version(GameVersion version, int64_t sub_version) { // get here, so the remaining flags are the same as DCv1 case 0x21: // DCv1 US return Client::Flag::IS_DC_V1 | - Client::Flag::NO_D6 | - Client::Flag::NO_SEND_FUNCTION_CALL; + Client::Flag::NO_D6 | + Client::Flag::NO_SEND_FUNCTION_CALL; case 0x26: // DCv2 US return Client::Flag::NO_D6; case 0x29: // PC return Client::Flag::NO_D6 | - Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY; + Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY; case 0x30: // GC Ep1&2 JP v1.02, at least one version of PSO XB case 0x31: // GC Ep1&2 US v1.00, GC US v1.01, GC EU v1.00, GC JP v1.00 case 0x34: // GC Ep1&2 JP v1.03 - return 0; + return ((version == GameVersion::GC) && function_compiler_available()) + ? Client::Flag::SEND_FUNCTION_CALL_NEEDS_CACHE_PATCH + : 0; case 0x32: // GC Ep1&2 EU 50Hz case 0x33: // GC Ep1&2 EU 60Hz return Client::Flag::NO_D6_AFTER_LOBBY; case 0x35: // GC Ep1&2 JP v1.04 (Plus) return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; + Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; case 0x36: // GC Ep1&2 US v1.02 (Plus) case 0x39: // GC Ep1&2 JP v1.05 (Plus) return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::NO_SEND_FUNCTION_CALL; + Client::Flag::NO_SEND_FUNCTION_CALL; case 0x40: // GC Ep3 trial return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::IS_EPISODE_3 | - Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; + Client::Flag::IS_EPISODE_3 | + Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; case 0x42: // GC Ep3 JP return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::IS_EPISODE_3 | - Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; + Client::Flag::IS_EPISODE_3 | + Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL; case 0x41: // GC Ep3 US return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::IS_EPISODE_3 | - Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL; + Client::Flag::IS_EPISODE_3 | + Client::Flag::USE_OVERFLOW_FOR_SEND_FUNCTION_CALL; case 0x43: // GC Ep3 EU return Client::Flag::NO_D6_AFTER_LOBBY | - Client::Flag::IS_EPISODE_3 | - Client::Flag::NO_SEND_FUNCTION_CALL; + Client::Flag::IS_EPISODE_3 | + Client::Flag::NO_SEND_FUNCTION_CALL; } throw runtime_error("unknown sub_version"); } @@ -116,7 +118,7 @@ GameVersion version_for_name(const char* name) { } else if (!strcasecmp(name, "XB") || !strcasecmp(name, "Xbox")) { return GameVersion::XB; } else if (!strcasecmp(name, "BB") || !strcasecmp(name, "BlueBurst") || - !strcasecmp(name, "Blue Burst")) { + !strcasecmp(name, "Blue Burst")) { return GameVersion::BB; } else if (!strcasecmp(name, "Patch")) { return GameVersion::PATCH; diff --git a/src/Version.hh b/src/Version.hh index 8fb49cd7..2a6da44e 100644 --- a/src/Version.hh +++ b/src/Version.hh @@ -28,7 +28,7 @@ extern const std::vector version_to_login_port_name; extern const std::vector version_to_lobby_port_name; extern const std::vector version_to_proxy_port_name; -uint16_t flags_for_version(GameVersion version, int64_t sub_version); +uint32_t flags_for_version(GameVersion version, int64_t sub_version); const char* name_for_version(GameVersion version); GameVersion version_for_name(const char* name); diff --git a/system/ppc/CacheClearFix-Phase1.s b/system/ppc/CacheClearFix-Phase1.s new file mode 100644 index 00000000..412b1db8 --- /dev/null +++ b/system/ppc/CacheClearFix-Phase1.s @@ -0,0 +1,4 @@ +entry_ptr: + .data 0x8000C274 +start: + .include CacheClearFix diff --git a/system/ppc/CacheClearFix-Phase2.s b/system/ppc/CacheClearFix-Phase2.s new file mode 100644 index 00000000..bcc07bdc --- /dev/null +++ b/system/ppc/CacheClearFix-Phase2.s @@ -0,0 +1,5 @@ +entry_ptr: +reloc0: + .offsetof start +start: + .include CacheClearFix diff --git a/system/ppc/CacheClearFix.inc.s b/system/ppc/CacheClearFix.inc.s new file mode 100644 index 00000000..0426da16 --- /dev/null +++ b/system/ppc/CacheClearFix.inc.s @@ -0,0 +1,89 @@ +start: + mflr r7 + + # If this patch has already been run, then the opcode that led here will + # not be bctrl (4E800421). In that case, do nothing. + lis r3, 0x4E80 + ori r3, r3, 0x0421 + lwz r4, [r7 - 4] + cmp r3, r4 + beq apply_patch + blr +apply_patch: + + bl patch_end + .offsetof patch + .offsetof patch_end +patch: + mfctr r6 + mr r3, r6 + li r4, 0x7C00 + .include FlushCachedCode + mtctr r6 + bctr +patch_end: + mflr r4 + + addi r4, r4, 8 + lwz r3, [r4 - 8] + lwz r5, [r4 - 4] + sub r5, r5, r3 + + # At this point: + # r4 = address of patch label + # r5 = patch size in bytes + # r7 = saved LR + + # Find a spot in the interrupt handlers with enough memory for the patch + lis r3, 0x8000 + ori r3, r3, 0x0200 + sub r3, r3, r5 + +check_location: + rlwinm r0, r5, 30, 2, 31 + mtctr r0 # ctr = patch size in words + subi r8, r3, 4 +check_location_next_word: + lwzu r0, [r8 + 4] + cmpwi r0, 0 + beq check_location_word_ok + addi r3, r3, 0x0100 + rlwinm r0, r3, 0, 16, 31 + cmpwi r0, 0x1800 + blt check_location + # No suitable location was found - return null + li r3, 0 + mtlr r7 + blr + +check_location_word_ok: + bdnz check_location_next_word + +location_ok: + mr r6, r3 + # Now: + # r3 = destination location + # r4 = patch src data + # r5 = patch size in bytes + # r6 = destination location + # r7 = saved LR + .include CopyCode + +setup_branch: + # Replace the bctrl opcode that led to this call with a bl opcode that + # leads to the copied patch code + subi r3, r7, 4 + sub r4, r6, r3 + rlwinm r4, r4, 0, 6, 31 + oris r4, r4, 0x4800 + ori r4, r4, 0x0001 + stw [r3], r4 + dcbst r0, r3 + sync + icbi r0, r3 + isync + + # Return the address that the patch was copied to + mr r3, r6 + mtlr r7 + blr diff --git a/system/ppc/CopyCode.inc.s b/system/ppc/CopyCode.inc.s index 3a7c7b58..dcf90b79 100644 --- a/system/ppc/CopyCode.inc.s +++ b/system/ppc/CopyCode.inc.s @@ -1,6 +1,7 @@ # r3 = dest ptr # r4 = src ptr # r5 = size + # Clobbers r0, r3, r4, r5 addi r5, r5, 3 rlwinm r5, r5, 30, 2, 31 # r5 = number of words to copy mtctr r5