fix patch cache clear behavior on GC versions that need it
This commit is contained in:
@@ -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,
|
||||
|
||||
+22
-11
@@ -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 <typename FooterT>
|
||||
string CompiledFunctionCode::generate_client_command_t(
|
||||
const unordered_map<string, uint32_t>& 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<typename FooterT::U16T>(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<typename FooterT::U16T>(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<string, uint32_t>& 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<S_ExecuteCode_Footer_GC_B2>(
|
||||
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<S_ExecuteCode_Footer_DC_PC_XB_BB_B2>(
|
||||
label_writes, suffix);
|
||||
label_writes, suffix, override_relocations_offset);
|
||||
} else {
|
||||
throw logic_error("invalid architecture");
|
||||
}
|
||||
|
||||
@@ -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 <typename FooterT>
|
||||
std::string generate_client_command_t(
|
||||
const std::unordered_map<std::string, uint32_t>& 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<std::string, uint32_t>& 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);
|
||||
|
||||
@@ -1194,6 +1194,10 @@ int main(int argc, char** argv) {
|
||||
use_terminal_colors = true;
|
||||
}
|
||||
|
||||
if (is_replay) {
|
||||
set_function_compiler_available(false);
|
||||
}
|
||||
|
||||
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
||||
shared_ptr<ServerState> state(new ServerState(config_filename, is_replay));
|
||||
|
||||
|
||||
+11
-1
@@ -156,7 +156,8 @@ static void send_proxy_destinations_menu(shared_ptr<ServerState> s, shared_ptr<C
|
||||
|
||||
static bool send_enable_send_function_call_if_applicable(
|
||||
shared_ptr<ServerState> s, shared_ptr<Client> 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<ServerState> s, shared_ptr<Client> 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)) {
|
||||
|
||||
+58
-55
@@ -58,12 +58,12 @@ const unordered_set<string> bb_crypt_initial_client_commands({
|
||||
});
|
||||
|
||||
void send_command(shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> c,
|
||||
}
|
||||
|
||||
void send_command_if_not_loading(shared_ptr<Lobby> 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<Lobby> l,
|
||||
}
|
||||
|
||||
void send_command(shared_ptr<Lobby> 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<ServerState> 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<ServerState> s, shared_ptr<Client> c,
|
||||
uint8_t flags) {
|
||||
uint8_t flags) {
|
||||
bool use_secondary_message = (flags & SendServerInitFlag::USE_SECONDARY_MESSAGE);
|
||||
parray<uint8_t, 0x30> server_key;
|
||||
parray<uint8_t, 0x30> client_key;
|
||||
@@ -316,7 +316,8 @@ void send_function_call(
|
||||
const unordered_map<string, uint32_t>& 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<string, uint32_t>& 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<Client> c, uint32_t address, uint16_t port) {
|
||||
}
|
||||
|
||||
void send_pc_console_split_reconnect(shared_ptr<Client> 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<Client> c) {
|
||||
}
|
||||
|
||||
void send_player_preview_bb(shared_ptr<Client> 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<Client> c, shared_ptr<PatchFileIndex::File> 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<Client> c, const u16string& text) {
|
||||
}
|
||||
|
||||
void send_quest_info(shared_ptr<Client> 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<Client> 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<Lobby> 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<Lobby> l, uint32_t from_guild_card_number,
|
||||
}
|
||||
|
||||
void send_chat_message(shared_ptr<Client> 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<Client> 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<G_SendGuildCard_PC_6x06>(
|
||||
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<G_SendGuildCard_V3_6x06>(
|
||||
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<Client> c, const u16string& menu_name,
|
||||
uint32_t menu_id, const vector<MenuItem>& items, bool is_info_menu) {
|
||||
uint32_t menu_id, const vector<MenuItem>& 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<Client> c, uint32_t menu_id,
|
||||
const vector<shared_ptr<const Quest>>& quests, bool is_download_menu) {
|
||||
const vector<shared_ptr<const Quest>>& quests, bool is_download_menu) {
|
||||
switch (c->version()) {
|
||||
case GameVersion::PC:
|
||||
send_quest_menu_t<S_QuestMenuEntry_PC_A2_A4>(c, menu_id, quests, is_download_menu);
|
||||
@@ -1244,7 +1247,7 @@ void send_quest_menu(shared_ptr<Client> c, uint32_t menu_id,
|
||||
}
|
||||
|
||||
void send_quest_menu(shared_ptr<Client> c, uint32_t menu_id,
|
||||
const vector<MenuItem>& items, bool is_download_menu) {
|
||||
const vector<MenuItem>& items, bool is_download_menu) {
|
||||
switch (c->version()) {
|
||||
case GameVersion::PC:
|
||||
send_quest_menu_t<S_QuestMenuEntry_PC_A2_A4>(c, menu_id, items, is_download_menu);
|
||||
@@ -1468,7 +1471,7 @@ void send_join_game_t(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
|
||||
template <typename LobbyDataT, typename DispDataT>
|
||||
void send_join_lobby_t(shared_ptr<Client> c, shared_ptr<Lobby> l,
|
||||
shared_ptr<Client> joining_client = nullptr) {
|
||||
shared_ptr<Client> joining_client = nullptr) {
|
||||
uint8_t command;
|
||||
if (l->is_game()) {
|
||||
if (joining_client) {
|
||||
@@ -1586,7 +1589,7 @@ void send_join_lobby(shared_ptr<Client> c, shared_ptr<Lobby> l) {
|
||||
}
|
||||
|
||||
void send_player_join_notification(shared_ptr<Client> c,
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> joining_client) {
|
||||
shared_ptr<Lobby> l, shared_ptr<Client> joining_client) {
|
||||
switch (c->version()) {
|
||||
case GameVersion::PC:
|
||||
send_join_lobby_t<PlayerLobbyDataPC, PlayerDispDataDCPCV3>(c, l, joining_client);
|
||||
@@ -1638,7 +1641,7 @@ void send_get_player_info(shared_ptr<Client> c) {
|
||||
// Trade window
|
||||
|
||||
void send_execute_item_trade(shared_ptr<Client> c,
|
||||
const vector<ItemData>& items) {
|
||||
const vector<ItemData>& 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<Client> c,
|
||||
}
|
||||
|
||||
void send_execute_card_trade(shared_ptr<Client> c,
|
||||
const vector<pair<uint32_t, uint32_t>>& card_to_count) {
|
||||
const vector<pair<uint32_t, uint32_t>>& 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<G_UpdatePlayerStat_6x9A> generate_stats_change_subcommands(
|
||||
}
|
||||
|
||||
void send_player_stats_change(shared_ptr<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> 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<Lobby> 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<Lobby> 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<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> c,
|
||||
// Creates an item in a player's inventory (used for withdrawing items from the
|
||||
// bank)
|
||||
void send_create_inventory_item(shared_ptr<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> c,
|
||||
|
||||
// destroys an item
|
||||
void send_destroy_item(shared_ptr<Lobby> l, shared_ptr<Client> 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<Lobby> l, shared_ptr<Client> c,
|
||||
// sends the player their bank data
|
||||
void send_bank(shared_ptr<Client> c) {
|
||||
vector<PlayerBankItem> 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<uint32_t>();
|
||||
G_BankContentsHeader_BB_6xBC cmd = {
|
||||
@@ -1878,7 +1881,7 @@ void send_level_up(shared_ptr<Lobby> l, shared_ptr<Client> c) {
|
||||
|
||||
// gives a player EXP
|
||||
void send_give_experience(shared_ptr<Lobby> l, shared_ptr<Client> 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<Client> c, shared_ptr<Lobby> 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<Client> c, const string& quest_name,
|
||||
const string& basename, shared_ptr<const string> contents, QuestFileType type) {
|
||||
const string& basename, shared_ptr<const string> contents, QuestFileType type) {
|
||||
|
||||
switch (c->version()) {
|
||||
case GameVersion::DC:
|
||||
@@ -2423,7 +2426,7 @@ void send_open_quest_file(shared_ptr<Client> 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<uint16_t>() % (s->ep3_card_auction_max_size - s->ep3_card_auction_min_size + 1));
|
||||
(random_object<uint16_t>() % (s->ep3_card_auction_max_size - s->ep3_card_auction_min_size + 1));
|
||||
}
|
||||
num_cards = min<uint16_t>(num_cards, 0x14);
|
||||
|
||||
@@ -2534,7 +2537,7 @@ void send_server_time(shared_ptr<Client> 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");
|
||||
}
|
||||
|
||||
+4
-2
@@ -139,14 +139,16 @@ void send_function_call(
|
||||
const std::unordered_map<std::string, uint32_t>& 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<Client> c,
|
||||
std::shared_ptr<CompiledFunctionCode> code,
|
||||
const std::unordered_map<std::string, uint32_t>& 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<Client> c, uint32_t address, uint16_t port);
|
||||
void send_pc_console_split_reconnect(
|
||||
|
||||
+21
-19
@@ -15,7 +15,7 @@ const vector<string> version_to_lobby_port_name = {
|
||||
const vector<string> 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;
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ extern const std::vector<std::string> version_to_login_port_name;
|
||||
extern const std::vector<std::string> version_to_lobby_port_name;
|
||||
extern const std::vector<std::string> 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);
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
entry_ptr:
|
||||
.data 0x8000C274
|
||||
start:
|
||||
.include CacheClearFix
|
||||
@@ -0,0 +1,5 @@
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CacheClearFix
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user