#include #include #include "pso/forward.h" #include "pso/macros.h" #include "pso/TMainTask.h" #include "pso/TMenuList.h" #include "pso/TPlyCharData.h" #include "pso/TPlyDispData.h" #include "pso/TPlyInventory.h" #include "pso/TObject.h" #include "pso/TProtocol.h" #include "pso/TSocket.h" OBJECT_NAME(TProtocol); packet *server_packet = nullptr; // Seems like this is 1 if it's the full release, and 0 if it's the trial edition. int maybe_release_flag = 1; u32 new_ip_addr; u16 new_port; TPlyMeetUserExtension meet_user_settings; u32 game_variations[16][2]; void copy_packet(struct packet *pkt) { } int TProtocol::send_login3() { int ret; set_ip_address(new_ip_addr); set_port((short)new_port); if (open() >= 0) { // Return 0 if we successfully opened the socket. // NOTE: Using a variable for the return value is needed to match. ret = 0; } else { // Return 1 if we failed to open the socket. // NOTE: This return here is needed to match. return 1; } return ret; } void TProtocol::recv_chat_message(packet &pkt) { TChatMessage tmp = as(TChatMessage &, pkt); tmp.bswap(); if (m_recv_handlers.handle_81_recv_chat_message != nullptr) { m_recv_handlers.handle_81_recv_chat_message(tmp); } }; void TProtocol::recv_ping(packet &pkt) { packet_header tmp; tmp.command = 0x1D; tmp.flags = 0; tmp.size = sizeof(tmp); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); }; void TProtocol::recv_user_ans(packet &pkt) { TUserAns tmp = as(TUserAns &, pkt); tmp.bswap(); if (m_recv_handlers.handle_41_recv_user_ans != nullptr) { m_recv_handlers.handle_41_recv_user_ans(tmp); } }; void TProtocol::recv_room_info(packet &pkt) { typedef TMenuList lobby_list; lobby_list tmp = as(lobby_list &, pkt); tmp.bswap(); m_lobby_list_count = tmp.header.flags; if (maybe_release_flag) { for (int i = 0; i < m_lobby_list_count; ++i) { m_lobby_entries[i] = tmp.entries[i]; } } else { for (int i = 0; i < 10; ++i) { m_lobby_entries[i] = tmp.entries[i]; } } }; void TProtocol::recv_generate_id(packet &pkt) { TRecvGenerateID tmp = as(TRecvGenerateID &, pkt); tmp.bswap(); if (m_recv_handlers.handle_80_recv_generate_id != nullptr) { m_recv_handlers.handle_80_recv_generate_id(tmp); } }; void TProtocol::recv_pso_data_long(packet &pkt) { TRecvPsoData &tmp = as(TRecvPsoData &, pkt); pkt.bswap(); tmp.data.recv_pso_data_buf(tmp.header.size - 4); }; void TProtocol::recv_pso_data(packet &pkt) { TRecvPsoData tmp = as(TRecvPsoData &, pkt); tmp.bswap(); tmp.data.recv_pso_data_buf(tmp.header.size - 4); }; void TProtocol::recv_system_file(packet &pkt) {}; void TProtocol::recv_battle_data(packet &pkt) { packet_header tmp = pkt.pkt.header; tmp.bswap(); }; void TProtocol::seq_jump(u8 *some_struct, TPlyMeetUserExtension &extension) { // TODO: Implement, and match this. } void TProtocol::recv_port(packet &pkt) { TRecvPort tmp = as(TRecvPort &, pkt); tmp.bswap(); new_ip_addr = tmp.ip_addr; new_port = tmp.port; close(); m_connected = false; m_packet_offset = 0; m_packet_size = 0; // Did we fail to reconnect? if (send_login3()) { m_is_invalid_packet = true; } }; void TProtocol::recv_message(packet &pkt) { TMessageBox tmp = as(TMessageBox &, pkt); tmp.bswap(); if (m_recv_handlers.handle_11_recv_message != nullptr) { m_recv_handlers.handle_11_recv_message(tmp.mesg, tmp.header.flags); } }; void TProtocol::recv_upload(packet &pkt) {}; void TProtocol::recv_download(packet &pkt) { TSendDownloadHead tmp2; TRecvDownload tmp = as(TRecvDownload &, pkt); tmp.bswap(); tmp2.header.command = tmp.header.command; tmp2.header.flags = tmp.header.flags; tmp2.header.size = sizeof(tmp2); strncpy(tmp2.filename, tmp.filename, sizeof(tmp2.filename)); tmp2.bswap(); if (m_recv_handlers.handle_13_recv_download != nullptr) { m_recv_handlers.handle_13_recv_download(tmp); } send(reinterpret_cast(&tmp2), sizeof(tmp2)); }; void TProtocol::recv_vm_download(packet &pkt) { TSendDownloadHead tmp2; TRecvDownload tmp = as(TRecvDownload &, pkt); tmp.bswap(); tmp2.header.command = tmp.header.command; tmp2.header.flags = tmp.header.flags; tmp2.header.size = sizeof(tmp2); strncpy(tmp2.filename, tmp.filename, sizeof(tmp2.filename)); tmp2.bswap(); if (m_recv_handlers.handle_A7_recv_vm_download != nullptr) { m_recv_handlers.handle_A7_recv_vm_download(tmp); } send(reinterpret_cast(&tmp2), sizeof(tmp2)); }; void TProtocol::recv_vm_download_head(packet &pkt) { TSendDownloadHead tmp2; TRecvDownloadHead tmp = as(TRecvDownloadHead &, pkt); tmp.bswap(); tmp2.header.command = tmp.header.command; tmp2.header.flags = tmp.header.flags; tmp2.header.size = sizeof(tmp2); strncpy(tmp2.filename, tmp.filename, sizeof(tmp2.filename)); tmp2.bswap(); if (m_recv_handlers.handle_A6_recv_vm_download_head != nullptr) { m_recv_handlers.handle_A6_recv_vm_download_head(tmp); } send(reinterpret_cast(&tmp2), sizeof(tmp2)); }; void TProtocol::recv_download_head(packet &pkt) { TSendDownloadHead tmp2; TRecvDownloadHead tmp = as(TRecvDownloadHead &, pkt); tmp.bswap(); tmp2.header.command = tmp.header.command; tmp2.header.flags = tmp.header.flags; tmp2.header.size = sizeof(tmp2); strncpy(tmp2.filename, tmp.filename, sizeof(tmp2.filename)); tmp2.bswap(); if (m_recv_handlers.handle_44_recv_download_head != nullptr) { m_recv_handlers.handle_44_recv_download_head(tmp); } send(reinterpret_cast(&tmp2), sizeof(tmp2)); }; void TProtocol::recv_banner_head(packet &pkt) {}; void TProtocol::recv_banner(packet &pkt) {}; void TProtocol::recv_start_lobby2(packet &pkt) { TPlyJoinLobby tmp = as(TPlyJoinLobby &, pkt); tmp.bswap(); m_joined_game = true; if (m_recv_handlers.handle_67_recv_start_lobby2 != nullptr) { m_recv_handlers.handle_67_recv_start_lobby2(tmp.entries, tmp.header.flags, tmp.client_id, tmp.leader_id, tmp.lobby_number, tmp.block_number, tmp.unknown1, tmp.event); } }; void TProtocol::recv_start_game3(packet &pkt) { TPlyJoinGame tmp = as(TPlyJoinGame &, pkt); tmp.bswap(); memcpy(game_variations, tmp.variations, sizeof(game_variations)); m_joined_game = true; if (m_recv_handlers.handle_64_recv_start_game3 != nullptr) { m_recv_handlers.handle_64_recv_start_game3(tmp); } }; void TProtocol::recv_start_game(packet &pkt) { int lobby_entry_idx; TPlyStartGame tmp = as(TPlyStartGame &, pkt); tmp.bswap(); meet_user_settings = tmp.extension; lobby_entry_idx = -1; for (int i = 0; i < tmp.header.flags && i < 4; ++i) { if (tmp.lobby_data[i].tag == m_guildcard_tag) { lobby_entry_idx = i; } } // Failed to find our guildcard tag, go into an infinite loop. if (lobby_entry_idx == -1) { for (;;) {} } else { m_joined_game = true; if (m_recv_handlers.handle_0E_recv_start_game != nullptr) { m_recv_handlers.handle_0E_recv_start_game(tmp.lobby_data, tmp.header.flags, lobby_entry_idx); } } }; void TProtocol::recv_text_list(packet &pkt) { typedef TMenuList text_list; text_list tmp = as(text_list &, pkt); tmp.bswap(); m_entry_count = tmp.header.flags; for (int i = 0; i < m_entry_count; ++i) { m_game_entries[i] = tmp.entries[i]; } if (m_recv_handlers.handle_1F_recv_text_list != nullptr) { m_recv_handlers.handle_1F_recv_text_list(m_entry_count, m_game_entries, tmp.pad_entries[0]); } }; void TProtocol::recv_game_list(packet &pkt) { typedef TMenuList game_list; game_list tmp = as(game_list &, pkt); tmp.bswap(); m_entry_count = tmp.header.flags; for (int i = 0; i < m_entry_count; ++i) { m_game_entries[i] = tmp.entries[i]; } if (m_recv_handlers.handle_08_recv_game_list != nullptr) { m_recv_handlers.handle_08_recv_game_list(m_entry_count, m_game_entries, tmp.pad_entries[0]); } }; void TProtocol::recv_dir_list(packet &pkt) { typedef TMenuList dir_list; dir_list tmp = as(dir_list &, pkt); tmp.bswap(); m_entry_count = tmp.header.flags; for (int i = 0; i < m_entry_count; ++i) { m_game_entries[i] = tmp.entries[i]; } if (m_recv_handlers.handle_07_A0_A1_recv_dir_list != nullptr) { m_recv_handlers.handle_07_A0_A1_recv_dir_list(m_entry_count, m_game_entries, tmp.pad_entries[0]); } }; void TProtocol::recv_chat(packet &pkt) { TMessageBox tmp = as(TMessageBox &, pkt); tmp.bswap(); if (m_recv_handlers.handle_06_recv_chat != nullptr) { m_recv_handlers.handle_06_recv_chat(tmp.tag, tmp.mesg); } }; void TProtocol::recv_logout(packet &pkt) { close(); m_is_encrypted = false; m_connected = false; m_packet_offset = 0; m_packet_size = 0; if (m_recv_handlers.handle_05_recv_logout != nullptr) { m_recv_handlers.handle_05_recv_logout(); } }; void TProtocol::recv_login(packet &pkt) { TPlyRecvLogin tmp = as(TPlyRecvLogin &, pkt); tmp.bswap(); if (!tmp.header.flags) { m_guildcard_tag = tmp.tag; m_client_config = tmp.client_config; m_connected = true; } if (m_recv_handlers.handle_04_recv_login != nullptr) { m_recv_handlers.handle_04_recv_login(tmp.header.flags); } }; void TProtocol::recv_regist(packet &pkt) { packet_header tmp = pkt.pkt.header; tmp.bswap(); m_login_response_state = tmp.flags; if (m_recv_handlers.handle_03_recv_regist != nullptr) { m_recv_handlers.handle_03_recv_regist(tmp.flags); } }; void TProtocol::recv_text(packet &pkt) { TPlyText<1024> tmp = as(TPlyText<1024> &, pkt); if (m_recv_handlers.handle_1A_D5_recv_text != nullptr) { m_recv_handlers.handle_1A_D5_recv_text(tmp.text); } }; void TProtocol::recv_error(packet &pkt) { TMessageBox tmp = as(TMessageBox &, pkt); tmp.bswap(); if (m_recv_handlers.handle_01_recv_error != nullptr) { m_recv_handlers.handle_01_recv_error(tmp.mesg); } }; int TProtocol::handle_command(struct packet *pkt) { static const command_handler_entry handlers[] = { { 0x01, &recv_error }, { 0x03, &recv_regist }, { 0x04, &recv_login }, { 0x05, &recv_logout }, { 0x06, &recv_chat }, { 0x07, &recv_dir_list }, { 0x08, &recv_game_list }, { 0x0E, &recv_start_game }, { 0x64, &recv_start_game3 }, { 0x12, &recv_banner }, { 0x16, &recv_banner_head }, { 0x44, &recv_download_head }, { 0x13, &recv_download }, { 0x14, &recv_upload }, { 0x11, &recv_message }, // 0x18 // 0x17 { 0x19, &recv_port }, { 0x1A, &recv_text }, { 0x1B, &recv_battle_data }, { 0x1C, &recv_system_file }, { 0x60, &recv_pso_data }, { 0x62, &recv_pso_data }, { 0x6C, &recv_pso_data_long }, { 0x6D, &recv_pso_data_long }, { 0x81, &recv_chat_message }, { 0x41, &recv_user_ans }, // 0x88 // 0x8A { 0x80, &recv_generate_id }, { 0x67, &recv_start_lobby2 }, { 0x83, &recv_room_info }, { 0x1D, &recv_ping }, // 0x65 // 0x66 // 0x68 // 0x69 // 0x90 // 0x91 // 0x92 // 0x95 { 0xA0, &recv_dir_list }, { 0xA1, &recv_dir_list }, // 0xA2 // 0xA3 // 0xA4 // 0xA5 { 0xA6, &recv_vm_download_head }, { 0xA7, &recv_vm_download }, // 0x97 // 0xB0 { 0x1F, &recv_text_list }, // 0xB1 // 0xC0 // 0xC4 // 0xB2 // 0x02 // 0x9A // 0x9B // 0x9C // 0xD1 // 0xD2 // 0xD3 { 0xD5, &recv_text }, // 0xD7 // 0xD8 // 0xDA // 0x9F // 0xAB // 0xAC { 0, nullptr } }; for (int i = 0; !handlers[i].command; ++i) { if (pkt->pkt.header.command == handlers[i].command) { (this->*handlers[i].handler)(*pkt); return 0; } } m_is_invalid_packet = true; return 1; } void TProtocol::parse_packet() { // This is needed to match, either that or cast the sizeof to an int. const int max_packet_size = sizeof(struct packet); while (!is_empty()) { m_packet.bytes[m_packet_offset++] = next(); if (m_packet_offset == sizeof(packet_header)) { if (m_is_encrypted) { m_recv_crypt.encrypt(&m_packet.pkt.header, sizeof(packet_header)); } } // NOTE: The `const int &` is required here to match. // Convert packet size to native endian. const int &packet_size = m_packet_size = to_be_uint16_t(m_packet.pkt.header.size); // Is the packet size invalid? if (packet_size > max_packet_size || !packet_size) { m_packet_size = 0; m_packet_offset = 0; m_is_invalid_packet = true; break; // Do we have the rest of the packet yet? } else if (m_packet_size > 0 && m_packet_offset >= m_packet_size) { // Decrypt it if necessary. if (m_is_encrypted) { m_recv_crypt.encrypt(&m_packet.pkt.data, m_packet_size-sizeof(packet_header)); } struct packet *pkt = &m_packet; m_packet_size = 0; m_packet_offset = 0; if (m_handle_pings_only) { if (pkt->pkt.header.command == 0x1d) { if (!handle_command(pkt)) { continue; } else { break; } } else { copy_packet(pkt); } } else if (handle_command(pkt)) { break; } } } } short TProtocol::send(u8 *data, size_t size) { const short ret = TTcpSocket::send(data, size); return ret; } void TProtocol::run_task() { TTcpSocket::some_stub(); some_stub(); if (m_is_invalid_packet) { close(); m_is_encrypted = 0; m_connected = 0; m_packet_offset = 0; m_packet_size = 0; } else if (!m_buffer_cleared) { server_packet = &m_packet; parse_packet(); recv(); } } void TProtocol::some_stub() {} TProtocol::~TProtocol() { } #define fill_with(name, value) memset(&m_##name, value, sizeof(m_##name)) #define copy(name) strncpy(m_##name, name, sizeof(m_##name)) TProtocol::TProtocol(TObject *parent, u16 sub_version, int language, char *serial_number, char *access_key, char *password) : TTcpSocket(parent) { u8 thing[6]; m_name = TProtocol_name; memset(thing, 0, sizeof(thing)); { int i = 0; int j = 0; m_smth.m_smth[i++] = 0; m_smth.m_smth[i++] = 0; m_smth.m_smth[i++] = thing[j++]; m_smth.m_smth[i++] = thing[j++]; i = 0; m_smth.m_smth1[i++] = thing[j++]; m_smth.m_smth1[i++] = thing[j++]; m_smth.m_smth1[i++] = thing[j++]; m_smth.m_smth1[i++] = thing[j++]; } m_guildcard_tag = TPlyGuildCardTag(-1, -1); m_login_response_state = 0; m_connected = 0; m_joined_game = 0; m_has_meet_user_settings = 0; m_handle_pings_only = false; m_entry_count = 0; m_unused = 0; m_lobby_list_count = 0; m_unused5[0] = 0; m_unused2 = 0; m_udp_disabled = 0; m_packet_offset = 0; m_packet_size = 0; m_sub_version = sub_version; m_language = language; fill_with(serial_number, 0); fill_with(access_key, 0); fill_with(password, 0); copy(serial_number); copy(access_key); copy(password); fill_with(client_config, 0); m_recv_handlers.handle_unused_login = nullptr; m_recv_handlers.handle_03_recv_regist = nullptr; m_recv_handlers.handle_04_recv_login = nullptr; m_recv_handlers.handle_05_recv_logout = nullptr; m_recv_handlers.handle_07_A0_A1_recv_dir_list = nullptr; m_recv_handlers.handle_01_recv_error = nullptr; m_recv_handlers.handle_06_recv_chat = nullptr; m_recv_handlers.handle_11_recv_message = nullptr; m_recv_handlers.handle_0E_recv_start_game = nullptr; m_recv_handlers.unused7 = nullptr; m_recv_handlers.handle_64_recv_start_game3 = nullptr; m_recv_handlers.unused8 = nullptr; m_recv_handlers.handle_67_recv_start_lobby2 = nullptr; m_recv_handlers.handle_13_recv_download = nullptr; m_recv_handlers.handle_80_recv_generate_id = nullptr; m_recv_handlers.unused9 = nullptr; m_recv_handlers.handle_65_add_player_to_game_packet = nullptr; m_recv_handlers.handle_66_player_left_game_packet = nullptr; m_recv_handlers.handle_68_add_player_to_lobby_packet = nullptr; m_recv_handlers.handle_69_player_left_lobby_packet = nullptr; m_recv_handlers.handle_92_9C_register_response_packet = nullptr; m_recv_handlers.unused10 = nullptr; m_recv_handlers.handle_81_recv_chat_message = nullptr; m_recv_handlers.handle_41_recv_user_ans = nullptr; m_recv_handlers.handle_A2_quest_list_packet = nullptr; m_recv_handlers.handle_A3_quest_info_packet = nullptr; m_recv_handlers.handle_44_recv_download_head = nullptr; m_recv_handlers.handle_13_recv_download = nullptr; m_recv_handlers.handle_A4_downloadable_quest_menu_packet = nullptr; m_recv_handlers.handle_A5_downloadable_quest_info_packet = nullptr; m_recv_handlers.unused11 = nullptr; m_recv_handlers.handle_97_checksum_reply_packet = nullptr; m_recv_handlers.handle_B0_server_message_packet = nullptr; m_recv_handlers.handle_1F_recv_text_list = nullptr; m_recv_handlers.handle_88_player_arrow_color_list_packet = nullptr; m_recv_handlers.handle_8A_lobby_name_packet = nullptr; m_recv_handlers.handle_C5_player_challenge_data_packet = nullptr; m_recv_handlers.handle_D3_execute_trade_packet = nullptr; m_recv_handlers.handle_D4_trade_result_packet = nullptr; m_recv_handlers.handle_D1_advance_trade_state_packet = nullptr; m_recv_handlers.handle_D8_infoboard_packet = nullptr; m_recv_handlers.handle_DA_lobby_event_packet = nullptr; m_recv_handlers.handle_AB_quest_stats_response_packet = nullptr; } #undef fill_with #undef copy