#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; int enable_v2_features = 1; u32 new_ip_addr; u16 new_port; TPlyMeetUserExtension meet_user_settings; u32 game_variations[16][2]; const char *port_map_copyright = "DreamCast Port Map. Copyright SEGA Enterprises. 1999"; void copy_packet(struct packet *pkt) { } void TProtocol::recv_pso_regist_check(packet &pkt) { packet_header tmp = pkt.pkt.header; tmp.bswap(); m_regist_state = tmp.flags; if (m_recv_handlers.handle_18_90_9A_recv_pso_regist_check != nullptr) { m_recv_handlers.handle_18_90_9A_recv_pso_regist_check(tmp.flags); } } void TProtocol::recv_pso_regist_connect(packet &pkt) { TRecvPsoRegistConnect tmp = as(TRecvPsoRegistConnect &, pkt); tmp.bswap(); if (!strcmp(port_map_copyright, tmp.copyright)) { TSendPsoRegistConnect tmp2; m_recv_crypt.reset(tmp.server_key); m_send_crypt.reset(tmp.client_key); m_is_encrypted = true; memset(&tmp2, 0, sizeof(tmp2)); tmp2.header.command = 0x90; tmp2.header.size = sizeof(tmp2); strncpy(tmp2.serial_number, m_serial_number_v1, sizeof(tmp2.serial_number)-1); strncpy(tmp2.access_key, m_access_key_v1, sizeof(tmp2.access_key)-1); tmp2.bswap(); send(as(u8 *, &tmp2), sizeof(tmp2)); } else { close(); } } void TProtocol::recv_exit_lobby(packet &pkt) { TRecvExit tmp = as(TRecvExit &, pkt); tmp.bswap(); if (m_recv_handlers.handle_69_recv_exit_lobby != nullptr) { m_recv_handlers.handle_69_recv_exit_lobby(tmp.client_id, tmp.leader_id); } } void TProtocol::recv_burst_lobby(packet &pkt) { TPlyJoinLobby tmp = as(TPlyJoinLobby &, pkt); tmp.bswap(); if (m_recv_handlers.handle_68_recv_burst_lobby != nullptr) { m_recv_handlers.handle_68_recv_burst_lobby(tmp.entries, tmp.leader_id); } } void TProtocol::recv_exit_game(packet &pkt) { TRecvExit tmp = as(TRecvExit &, pkt); tmp.bswap(); if (m_recv_handlers.handle_66_recv_exit_game != nullptr) { m_recv_handlers.handle_66_recv_exit_game(tmp.client_id, tmp.leader_id, tmp.disable_udp); } } void TProtocol::recv_burst_game(packet &pkt) { TPlyJoinLobby tmp = as(TPlyJoinLobby &, pkt); tmp.bswap(); if (m_recv_handlers.handle_65_recv_burst_game != nullptr) { m_recv_handlers.handle_65_recv_burst_game(tmp.entries, tmp.leader_id, tmp.disable_udp); } } void TProtocol::send_create_game(char *name, char *password, u8 difficulty, u8 battle_mode, u8 challenge_mode, u8 episode) { if (m_connected) { TCreateGame tmp; tmp.header.command = 0xC1; tmp.header.flags = 3; tmp.header.size = sizeof(tmp); tmp.tag = TPlyGuildCardTag(0, 0); tmp.difficulty = difficulty; tmp.battle_mode = battle_mode; tmp.challenge_mode = challenge_mode; tmp.episode = episode; strncpy(tmp.name, name, sizeof(tmp.name)); strncpy(tmp.password, password, sizeof(tmp.password)); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_find_user(TPlyGuildCardTag &tag, u32 gc_num) { if (m_connected) { TFindUser tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x40; tmp.header.size = sizeof(tmp); tmp.tag = tag; tmp.target_gc = gc_num; tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_room_change(int idx) { if (m_connected) { TRoomChange tmp; tmp.header.command = 0x84; tmp.header.flags = 0; tmp.header.size = sizeof(tmp); tmp.tag = m_lobby_entries[idx].tag; tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_pso_data_long2(char flags, long_game_command &packet, size_t size) { const int header_size = sizeof(packet_header); const int command_size = sizeof(long_game_command); if (m_connected) { TRecvPsoDataLong *tmp = new TRecvPsoDataLong; u8 *pkt = as(u8 *, tmp); int packet_size; int cmd_count; int leftover; int offset; memcpy(tmp->data.bytes, &packet, size); tmp->header.command = 0x6d; tmp->header.flags = flags; tmp->header.size = size + header_size; tmp->bswap(); packet_size = (int)size + header_size; cmd_count = packet_size/command_size; leftover = packet_size - (cmd_count * command_size); for (offset = 0; cmd_count > 0; --cmd_count, offset += command_size) { if (test_connection()) { goto end; } else { send(&pkt[offset], command_size); } } if (!test_connection() && leftover > 0) { send(&pkt[offset], leftover); } end: delete tmp; } } void TProtocol::send_pso_data2(u32 flags, game_command &packet, u32 size) { char tmp_str[128]; sprintf(tmp_str, "SndPsoData2 %d %d %d", packet.header.command, size, flags); if (m_connected) { TRecvPsoData tmp; int packet_size; packet_size = (u16)(size + sizeof(packet_header)); tmp.header.command = 0x62; tmp.header.flags = flags; packet_size = (u16)(size + sizeof(packet_header)); tmp.header.size = packet_size; memcpy(&tmp.data, &packet, size); tmp.bswap(); send(as(u8 *, &tmp), packet_size); } } void TProtocol::send_pso_data(game_command &packet, u32 size) { char tmp_str[128]; sprintf(tmp_str, "SndPsoData %d %d", packet.header.command, size); if (m_connected) { TRecvPsoData tmp; int packet_size; tmp.header.command = 0x60; tmp.header.flags = 0; packet_size = (u16)(size + sizeof(packet_header)); tmp.header.size = packet_size; memcpy(&tmp.data, &packet, (u32)size); tmp.bswap(); send(as(u8 *, &tmp), packet_size); } } void TProtocol::send_update_chara_data_v2(TPlyCharData &char_data, TPlyChallenge &challenge, TPlyChoiceSearchConfig &choice_search_config, TBlockedSenders &blocked_senders, TPlyText<512> &auto_reply, char *info_board) { if (m_connected) { char_data.m_disp_data.m_disp_part2.is_valid(); m_character_data = char_data; TPlyJoinData tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x98; tmp.header.flags = 3; tmp.header.size = sizeof(tmp); tmp.char_data = m_character_data; tmp.challenge = challenge; tmp.choice_search_config = choice_search_config; strncpy(tmp.info_board, info_board, sizeof(tmp.info_board)); for (int i = 0; i < blocked_senders.header.flags; ++i) { tmp.blocked_senders[i] = blocked_senders.blocked_senders[i]; } tmp.auto_reply_enabled = auto_reply.header.flags; strncpy(tmp.auto_reply, auto_reply.text, sizeof(tmp.auto_reply)-1); const int packet_size = strlen(tmp.auto_reply) + offsetof(TPlyJoinData, auto_reply) + sizeof(packet_header); tmp.header.size = packet_size & ~3; tmp.char_data.some_stub(); tmp.bswap(); send(as(u8 *, &tmp), packet_size & ~3); } } void TProtocol::send_chara_data_v2(TPlyCharData &char_data, TPlyChallenge &challenge, TPlyChoiceSearchConfig &choice_search_config, TBlockedSenders &blocked_senders, TPlyText<512> &auto_reply, char *info_board) { if (m_connected) { char_data.m_disp_data.m_disp_part2.is_valid(); m_character_data = char_data; TPlyJoinData tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x61; tmp.header.flags = 3; tmp.header.size = sizeof(tmp); tmp.char_data = m_character_data; tmp.challenge = challenge; tmp.choice_search_config = choice_search_config; strncpy(tmp.info_board, info_board, sizeof(tmp.info_board)); for (int i = 0; i < blocked_senders.header.flags; ++i) { tmp.blocked_senders[i] = blocked_senders.blocked_senders[i]; } tmp.auto_reply_enabled = auto_reply.header.flags; strncpy(tmp.auto_reply, auto_reply.text, sizeof(tmp.auto_reply)-1); const int packet_size = strlen(tmp.auto_reply) + offsetof(TPlyJoinData, auto_reply) + sizeof(packet_header); tmp.header.size = packet_size & ~3; tmp.char_data.some_stub(); tmp.bswap(); send(as(u8 *, &tmp), packet_size & ~3); } } void TProtocol::send_text_list() { if (m_connected) { packet_header tmp; tmp.command = 0x1F; tmp.flags = 0; tmp.size = sizeof(tmp); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_game_list() { if (m_connected) { packet_header tmp; tmp.command = 0x08; tmp.flags = 0; tmp.size = sizeof(tmp); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_info(TPlyGuildCardTag &tag) { if (m_connected) { TSendAction<0> tmp; tmp.header.command = 0x09; tmp.header.flags = 0; tmp.header.size = sizeof(tmp); tmp.tag = tag; tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } } void TProtocol::send_action(GameListEntry &selection, u8 flags, char *action_name, char *password) { if (m_connected) { switch (selection.flags & 3) { case 0: { TSendAction<0> action; action.header.command = 0x10; action.header.flags = flags; action.header.size = sizeof(action); action.tag = selection.tag; action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 1: { TSendAction<1> action; action.header.command = 0x10; action.header.flags = 1; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.name, action_name, sizeof(action.name)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 2: { TSendAction<2> action; action.header.command = 0x10; action.header.flags = 2; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.password, password, sizeof(action.password)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 3: { TSendAction<3> action; action.header.command = 0x10; action.header.flags = 3; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.name, action_name, sizeof(action.name)); strncpy(action.password, password, sizeof(action.password)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } } } } void TProtocol::send_action(GameListEntry &selection, char *action_name, char *password) { if (m_connected) { switch (selection.flags & 3) { case 0: { TSendAction<0> action; action.header.command = 0x10; action.header.flags = 0; action.header.size = sizeof(action); action.tag = selection.tag; action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 1: { TSendAction<1> action; action.header.command = 0x10; action.header.flags = 1; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.name, action_name, sizeof(action.name)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 2: { TSendAction<2> action; action.header.command = 0x10; action.header.flags = 2; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.password, password, sizeof(action.password)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } case 3: { TSendAction<3> action; action.header.command = 0x10; action.header.flags = 3; action.header.size = sizeof(action); action.tag = selection.tag; strncpy(action.name, action_name, sizeof(action.name)); strncpy(action.password, password, sizeof(action.password)); action.bswap(); send(as(u8 *, &action), sizeof(action)); break; } } } } void TProtocol::send_chat(TPlyGuildCardTag &tag, char *mesg) { if (m_connected) { TMessageBox tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x06; tmp.header.flags = 0; tmp.tag = tag; strncpy(tmp.mesg, mesg, sizeof(tmp.mesg) - 1); tmp.header.size = (strlen(tmp.mesg) + 16) & ~3; // NOTE: Both the `const`ness of it, and it's placement // here are required to match. const u16 size = tmp.header.size; tmp.bswap(); send(as(u8 *, &tmp), size); } } void TProtocol::send_logout() { packet_header tmp; tmp.command = 0x05; tmp.flags = 0; tmp.size = sizeof(tmp); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } 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::send_login2(int disable_udp) { m_udp_disabled = disable_udp; TRegister tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x04; tmp.header.flags = m_udp_disabled; tmp.header.size = sizeof(tmp); tmp.smth = m_smth; tmp.sub_version = m_sub_version; tmp.language = m_language; strncpy(tmp.serial_number, m_serial_number, sizeof(tmp.serial_number)); strncpy(tmp.access_key, m_access_key, sizeof(tmp.access_key)); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } void TProtocol::send_regist() { TRegister tmp; memset(&tmp, 0, sizeof(tmp)); tmp.header.command = 0x03; tmp.header.size = sizeof(tmp); tmp.smth = m_smth; tmp.sub_version = m_sub_version; tmp.language = m_language; strncpy(tmp.serial_number, m_serial_number, sizeof(tmp.serial_number)); strncpy(tmp.access_key, m_access_key, sizeof(tmp.access_key)); tmp.bswap(); send(as(u8 *, &tmp), sizeof(tmp)); } void TProtocol::recv_emergency_call(packet &pkt) { TMessageBox tmp = as(TMessageBox &, pkt); tmp.bswap(); if (m_recv_handlers.handle_B0_recv_emergency_call != nullptr) { m_recv_handlers.handle_B0_recv_emergency_call(tmp.mesg); } }; void TProtocol::recv_quest_info(packet &pkt) { TPlyText<288> tmp = as(TPlyText<288> &, pkt); tmp.bswap(); if (m_recv_handlers.handle_A5_recv_quest_info != nullptr) { m_recv_handlers.handle_A5_recv_quest_info(tmp); } }; void TProtocol::recv_quest_list(packet &pkt) { typedef TMenuList quest_list; quest_list tmp = as(quest_list &, pkt); tmp.bswap(); m_entry_count = tmp.header.flags; for (int i = 0; i < m_entry_count; ++i) { m_quest_entries[i] = tmp.entries[i]; } if (m_recv_handlers.handle_A4_recv_quest_list != nullptr) { m_recv_handlers.handle_A4_recv_quest_list(tmp.header.flags, tmp); } }; void TProtocol::recv_quest_menu_info(packet &pkt) { TPlyText<288> tmp = as(TPlyText<288> &, pkt); tmp.bswap(); if (m_recv_handlers.handle_A3_recv_quest_menu_info != nullptr) { m_recv_handlers.handle_A3_recv_quest_menu_info(tmp); } }; void TProtocol::recv_quest_menu_list(packet &pkt) { typedef TMenuList quest_list; quest_list tmp = as(quest_list &, pkt); tmp.bswap(); if (m_recv_handlers.handle_A2_recv_quest_menu_list != nullptr) { m_recv_handlers.handle_A2_recv_quest_menu_list(tmp); } }; 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 (enable_v2_features) { 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) { TRecvPsoDataLong &tmp = as(TRecvPsoDataLong &, pkt); tmp.bswap(); tmp.data.recv_pso_data_buf(tmp.header.size - sizeof(packet_header)); }; void TProtocol::recv_pso_data(packet &pkt) { TRecvPsoData tmp = as(TRecvPsoData &, pkt); tmp.bswap(); tmp.data.recv_pso_data_buf(tmp.header.size - sizeof(packet_header)); }; 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_regist_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, &recv_pso_regist_check }, // 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, &recv_burst_game }, { 0x66, &recv_exit_game }, { 0x68, &recv_burst_lobby }, { 0x69, &recv_exit_lobby }, { 0x90, &recv_pso_regist_check }, { 0x91, &recv_pso_regist_connect }, // 0x92 // 0x95 { 0xA0, &recv_dir_list }, { 0xA1, &recv_dir_list }, { 0xA2, &recv_quest_menu_list }, { 0xA3, &recv_quest_menu_info }, { 0xA4, &recv_quest_list }, { 0xA5, &recv_quest_info }, { 0xA6, &recv_vm_download_head }, { 0xA7, &recv_vm_download }, // 0x97 { 0xB0, &recv_emergency_call }, { 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_regist_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_recv_burst_game = nullptr; m_recv_handlers.handle_66_recv_exit_game = nullptr; m_recv_handlers.handle_68_recv_burst_lobby = nullptr; m_recv_handlers.handle_69_recv_exit_lobby = 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_recv_quest_menu_list = nullptr; m_recv_handlers.handle_A3_recv_quest_menu_info = nullptr; m_recv_handlers.handle_44_recv_download_head = nullptr; m_recv_handlers.handle_13_recv_download = nullptr; m_recv_handlers.handle_A4_recv_quest_list = nullptr; m_recv_handlers.handle_A5_recv_quest_info = nullptr; m_recv_handlers.unused11 = nullptr; m_recv_handlers.handle_97_checksum_reply_packet = nullptr; m_recv_handlers.handle_B0_recv_emergency_call = 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