diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index ccd371e9ed..2cd3748dc5 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -311,7 +311,7 @@ static void ReceiveSyncIdentifier(sf::Packet& spac, SyncIdentifier& sync_identif } // called from ---NETPLAY--- thread -unsigned int NetPlayClient::OnData(sf::Packet& packet) +u32 NetPlayClient::OnData(sf::Packet& packet) { MessageId mid; packet >> mid; @@ -321,1016 +321,137 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) switch (mid) { case NP_MSG_PLAYER_JOIN: - { - Player player; - packet >> player.pid; - packet >> player.name; - packet >> player.revision; - - INFO_LOG_FMT(NETPLAY, "Player {} ({}) using {} joined", player.name, player.pid, - player.revision); - - { - std::lock_guard lkp(m_crit.players); - m_players[player.pid] = player; - } - - m_dialog->OnPlayerConnect(player.name); - - m_dialog->Update(); - } - break; + OnPlayerJoin(packet); + break; case NP_MSG_PLAYER_LEAVE: - { - PlayerId pid; - packet >> pid; - - { - std::lock_guard lkp(m_crit.players); - const auto it = m_players.find(pid); - if (it == m_players.end()) - break; - - const auto& player = it->second; - INFO_LOG_FMT(NETPLAY, "Player {} ({}) left", player.name, pid); - m_dialog->OnPlayerDisconnect(player.name); - m_players.erase(m_players.find(pid)); - } - - m_dialog->Update(); - } - break; + OnPlayerLeave(packet); + break; case NP_MSG_CHAT_MESSAGE: - { - PlayerId pid; - packet >> pid; - std::string msg; - packet >> msg; - - // don't need lock to read in this thread - const Player& player = m_players[pid]; - - INFO_LOG_FMT(NETPLAY, "Player {} ({}) wrote: {}", player.name, player.pid, msg); - - // add to gui - std::ostringstream ss; - ss << player.name << '[' << (char)(pid + '0') << "]: " << msg; - - m_dialog->AppendChat(ss.str()); - } - break; + OnChatMessage(packet); + break; case NP_MSG_CHUNKED_DATA_START: - { - u32 cid; - packet >> cid; - std::string title; - packet >> title; - u64 data_size = Common::PacketReadU64(packet); - - m_chunked_data_receive_queue.emplace(cid, sf::Packet{}); - - std::vector players; - players.push_back(m_local_player->pid); - m_dialog->ShowChunkedProgressDialog(title, data_size, players); - } - break; + OnChunkedDataStart(packet); + break; case NP_MSG_CHUNKED_DATA_END: - { - u32 cid; - packet >> cid; - - if (m_chunked_data_receive_queue.count(cid)) - { - OnData(m_chunked_data_receive_queue[cid]); - m_chunked_data_receive_queue.erase(cid); - m_dialog->HideChunkedProgressDialog(); - - sf::Packet complete_packet; - complete_packet << static_cast(NP_MSG_CHUNKED_DATA_COMPLETE); - complete_packet << cid; - Send(complete_packet, CHUNKED_DATA_CHANNEL); - } - } - break; + OnChunkedDataEnd(packet); + break; case NP_MSG_CHUNKED_DATA_PAYLOAD: - { - u32 cid; - packet >> cid; - - if (m_chunked_data_receive_queue.count(cid)) - { - while (!packet.endOfPacket()) - { - u8 byte; - packet >> byte; - m_chunked_data_receive_queue[cid] << byte; - } - - m_dialog->SetChunkedProgress(m_local_player->pid, - m_chunked_data_receive_queue[cid].getDataSize()); - - sf::Packet progress_packet; - progress_packet << static_cast(NP_MSG_CHUNKED_DATA_PROGRESS); - progress_packet << cid; - progress_packet << sf::Uint64{m_chunked_data_receive_queue[cid].getDataSize()}; - Send(progress_packet, CHUNKED_DATA_CHANNEL); - } - } - break; + OnChunkedDataPayload(packet); + break; case NP_MSG_CHUNKED_DATA_ABORT: - { - u32 cid; - packet >> cid; - - if (m_chunked_data_receive_queue.count(cid)) - { - m_chunked_data_receive_queue.erase(cid); - m_dialog->HideChunkedProgressDialog(); - } - } - break; + OnChunkedDataAbort(packet); + break; case NP_MSG_PAD_MAPPING: - { - for (PlayerId& mapping : m_pad_map) - { - packet >> mapping; - } - - UpdateDevices(); - - m_dialog->Update(); - } - break; + OnPadMapping(packet); + break; case NP_MSG_GBA_CONFIG: - { - for (size_t i = 0; i < m_gba_config.size(); ++i) - { - auto& config = m_gba_config[i]; - const auto old_config = config; - - packet >> config.enabled >> config.has_rom >> config.title; - for (auto& data : config.hash) - packet >> data; - - if (std::tie(config.has_rom, config.title, config.hash) != - std::tie(old_config.has_rom, old_config.title, old_config.hash)) - { - m_dialog->OnMsgChangeGBARom(static_cast(i), config); - m_net_settings.m_GBARomPaths[i] = - config.has_rom ? - m_dialog->FindGBARomPath(config.hash, config.title, static_cast(i)) : - ""; - } - } - - SendGameStatus(); - UpdateDevices(); - - m_dialog->Update(); - } - break; + OnGBAConfig(packet); + break; case NP_MSG_WIIMOTE_MAPPING: - { - for (PlayerId& mapping : m_wiimote_map) - { - packet >> mapping; - } - - m_dialog->Update(); - } - break; + OnWiimoteMapping(packet); + break; case NP_MSG_PAD_DATA: - { - while (!packet.endOfPacket()) - { - PadIndex map; - packet >> map; - - GCPadStatus pad; - packet >> pad.button; - if (!m_gba_config.at(map).enabled) - { - packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> - pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; - } - - // Trusting server for good map value (>=0 && <4) - // add to pad buffer - m_pad_buffer.at(map).Push(pad); - m_gc_pad_event.Set(); - } - } - break; + OnPadData(packet); + break; case NP_MSG_PAD_HOST_DATA: - { - while (!packet.endOfPacket()) - { - PadIndex map; - packet >> map; - - GCPadStatus pad; - packet >> pad.button; - if (!m_gba_config.at(map).enabled) - { - packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> - pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; - } - - // Trusting server for good map value (>=0 && <4) - // write to last status - m_last_pad_status[map] = pad; - - if (!m_first_pad_status_received[map]) - { - m_first_pad_status_received[map] = true; - m_first_pad_status_received_event.Set(); - } - } - } - break; + OnPadHostData(packet); + break; case NP_MSG_WIIMOTE_DATA: - { - PadIndex map; - WiimoteInput nw; - u8 size; - packet >> map >> nw.report_id >> size; - nw.data.resize(size); - for (auto& byte : nw.data) - packet >> byte; - - // Trusting server for good map value (>=0 && <4) - // add to Wiimote buffer - m_wiimote_buffer.at(map).Push(nw); - m_wii_pad_event.Set(); - } - break; + OnWiimoteData(packet); + break; case NP_MSG_PAD_BUFFER: - { - u32 size = 0; - packet >> size; - - m_target_buffer_size = size; - m_dialog->OnPadBufferChanged(size); - } - break; + OnPadBuffer(packet); + break; case NP_MSG_HOST_INPUT_AUTHORITY: - { - packet >> m_host_input_authority; - m_dialog->OnHostInputAuthorityChanged(m_host_input_authority); - } - break; + OnHostInputAuthority(packet); + break; case NP_MSG_GOLF_SWITCH: - { - PlayerId pid; - packet >> pid; - - const PlayerId previous_golfer = m_current_golfer; - m_current_golfer = pid; - m_dialog->OnGolferChanged(m_local_player->pid == pid, pid != 0 ? m_players[pid].name : ""); - - if (m_local_player->pid == previous_golfer) - { - sf::Packet spac; - spac << static_cast(NP_MSG_GOLF_RELEASE); - Send(spac); - } - else if (m_local_player->pid == pid) - { - sf::Packet spac; - spac << static_cast(NP_MSG_GOLF_ACQUIRE); - Send(spac); - - // Pads are already calibrated so we can just ignore this - m_first_pad_status_received.fill(true); - - m_wait_on_input = false; - m_wait_on_input_event.Set(); - } - } - break; + OnGolfSwitch(packet); + break; case NP_MSG_GOLF_PREPARE: - { - m_wait_on_input_received = true; - m_wait_on_input = true; - } - break; + OnGolfPrepare(packet); + break; case NP_MSG_CHANGE_GAME: - { - std::string netplay_name; - { - std::lock_guard lkg(m_crit.game); - ReceiveSyncIdentifier(packet, m_selected_game); - packet >> netplay_name; - } - - INFO_LOG_FMT(NETPLAY, "Game changed to {}", netplay_name); - - // update gui - m_dialog->OnMsgChangeGame(m_selected_game, netplay_name); - - SendGameStatus(); - - sf::Packet client_capabilities_packet; - client_capabilities_packet << static_cast(NP_MSG_CLIENT_CAPABILITIES); - client_capabilities_packet << ExpansionInterface::CEXIIPL::HasIPLDump(); - client_capabilities_packet << Config::Get(Config::SESSION_USE_FMA); - Send(client_capabilities_packet); - } - break; + OnChangeGame(packet); + break; case NP_MSG_GAME_STATUS: - { - PlayerId pid; - packet >> pid; - - { - std::lock_guard lkp(m_crit.players); - Player& player = m_players[pid]; - u32 status; - packet >> status; - player.game_status = static_cast(status); - } - - m_dialog->Update(); - } - break; + OnGameStatus(packet); + break; case NP_MSG_START_GAME: - { - { - std::lock_guard lkg(m_crit.game); - packet >> m_current_game; - packet >> m_net_settings.m_CPUthread; - - INFO_LOG_FMT(NETPLAY, "Start of game {}", m_selected_game.game_id); - - { - std::underlying_type_t core; - if (packet >> core) - m_net_settings.m_CPUcore = static_cast(core); - else - m_net_settings.m_CPUcore = PowerPC::CPUCore::CachedInterpreter; - } - - packet >> m_net_settings.m_EnableCheats; - packet >> m_net_settings.m_SelectedLanguage; - packet >> m_net_settings.m_OverrideRegionSettings; - packet >> m_net_settings.m_DSPEnableJIT; - packet >> m_net_settings.m_DSPHLE; - packet >> m_net_settings.m_WriteToMemcard; - packet >> m_net_settings.m_RAMOverrideEnable; - packet >> m_net_settings.m_Mem1Size; - packet >> m_net_settings.m_Mem2Size; - - { - std::underlying_type_t tmp; - packet >> tmp; - m_net_settings.m_FallbackRegion = static_cast(tmp); - } - - packet >> m_net_settings.m_AllowSDWrites; - packet >> m_net_settings.m_CopyWiiSave; - packet >> m_net_settings.m_OCEnable; - packet >> m_net_settings.m_OCFactor; - - for (auto& device : m_net_settings.m_EXIDevice) - { - int tmp; - packet >> tmp; - device = static_cast(tmp); - } - - for (u32& value : m_net_settings.m_SYSCONFSettings) - packet >> value; - - packet >> m_net_settings.m_EFBAccessEnable; - packet >> m_net_settings.m_BBoxEnable; - packet >> m_net_settings.m_ForceProgressive; - packet >> m_net_settings.m_EFBToTextureEnable; - packet >> m_net_settings.m_XFBToTextureEnable; - packet >> m_net_settings.m_DisableCopyToVRAM; - packet >> m_net_settings.m_ImmediateXFBEnable; - packet >> m_net_settings.m_EFBEmulateFormatChanges; - packet >> m_net_settings.m_SafeTextureCacheColorSamples; - packet >> m_net_settings.m_PerfQueriesEnable; - packet >> m_net_settings.m_FPRF; - packet >> m_net_settings.m_AccurateNaNs; - packet >> m_net_settings.m_DisableICache; - packet >> m_net_settings.m_SyncOnSkipIdle; - packet >> m_net_settings.m_SyncGPU; - packet >> m_net_settings.m_SyncGpuMaxDistance; - packet >> m_net_settings.m_SyncGpuMinDistance; - packet >> m_net_settings.m_SyncGpuOverclock; - packet >> m_net_settings.m_JITFollowBranch; - packet >> m_net_settings.m_FastDiscSpeed; - packet >> m_net_settings.m_MMU; - packet >> m_net_settings.m_Fastmem; - packet >> m_net_settings.m_SkipIPL; - packet >> m_net_settings.m_LoadIPLDump; - packet >> m_net_settings.m_VertexRounding; - packet >> m_net_settings.m_InternalResolution; - packet >> m_net_settings.m_EFBScaledCopy; - packet >> m_net_settings.m_FastDepthCalc; - packet >> m_net_settings.m_EnablePixelLighting; - packet >> m_net_settings.m_WidescreenHack; - packet >> m_net_settings.m_ForceFiltering; - packet >> m_net_settings.m_MaxAnisotropy; - packet >> m_net_settings.m_ForceTrueColor; - packet >> m_net_settings.m_DisableCopyFilter; - packet >> m_net_settings.m_DisableFog; - packet >> m_net_settings.m_ArbitraryMipmapDetection; - packet >> m_net_settings.m_ArbitraryMipmapDetectionThreshold; - packet >> m_net_settings.m_EnableGPUTextureDecoding; - packet >> m_net_settings.m_DeferEFBCopies; - packet >> m_net_settings.m_EFBAccessTileSize; - packet >> m_net_settings.m_EFBAccessDeferInvalidation; - packet >> m_net_settings.m_StrictSettingsSync; - - m_initial_rtc = Common::PacketReadU64(packet); - - packet >> m_net_settings.m_SyncSaveData; - packet >> m_net_settings.m_SaveDataRegion; - packet >> m_net_settings.m_SyncCodes; - packet >> m_net_settings.m_SyncAllWiiSaves; - - for (int& extension : m_net_settings.m_WiimoteExtension) - packet >> extension; - - packet >> m_net_settings.m_GolfMode; - packet >> m_net_settings.m_UseFMA; - packet >> m_net_settings.m_HideRemoteGBAs; - - m_net_settings.m_IsHosting = m_local_player->IsHost(); - m_net_settings.m_HostInputAuthority = m_host_input_authority; - } - - m_dialog->OnMsgStartGame(); - } - break; + OnStartGame(packet); + break; case NP_MSG_STOP_GAME: case NP_MSG_DISABLE_GAME: - { - INFO_LOG_FMT(NETPLAY, "Game stopped"); - - StopGame(); - m_dialog->OnMsgStopGame(); - } - break; + OnStopGame(packet); + break; case NP_MSG_POWER_BUTTON: - { - m_dialog->OnMsgPowerButton(); - } - break; + OnPowerButton(); + break; case NP_MSG_PING: - { - u32 ping_key = 0; - packet >> ping_key; - - sf::Packet response_packet; - response_packet << static_cast(NP_MSG_PONG); - response_packet << ping_key; - - Send(response_packet); - } - break; + OnPing(packet); + break; case NP_MSG_PLAYER_PING_DATA: - { - PlayerId pid; - packet >> pid; - - { - std::lock_guard lkp(m_crit.players); - Player& player = m_players[pid]; - packet >> player.ping; - } - - DisplayPlayersPing(); - m_dialog->Update(); - } - break; + OnPlayerPingData(packet); + break; case NP_MSG_DESYNC_DETECTED: - { - int pid_to_blame; - u32 frame; - packet >> pid_to_blame; - packet >> frame; - - std::string player = "??"; - std::lock_guard lkp(m_crit.players); - { - auto it = m_players.find(pid_to_blame); - if (it != m_players.end()) - player = it->second.name; - } - - INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame); - - m_dialog->OnDesync(frame, player); - } - break; + OnDesyncDetected(packet); + break; case NP_MSG_SYNC_GC_SRAM: - { - const size_t sram_settings_len = sizeof(g_SRAM) - offsetof(Sram, settings); - u8 sram[sram_settings_len]; - for (u8& cell : sram) - { - packet >> cell; - } - - { - std::lock_guard lkg(m_crit.game); - memcpy(&g_SRAM.settings, sram, sram_settings_len); - g_SRAM_netplay_initialized = true; - } - } - break; + OnSyncGCSRAM(packet); + break; case NP_MSG_SYNC_SAVE_DATA: - { - MessageId sub_id; - packet >> sub_id; - - switch (sub_id) - { - case SYNC_SAVE_DATA_NOTIFY: - { - if (m_local_player->IsHost()) - return 0; - - packet >> m_sync_save_data_count; - m_sync_save_data_success_count = 0; - - if (m_sync_save_data_count == 0) - SyncSaveDataResponse(true); - else - m_dialog->AppendChat(Common::GetStringT("Synchronizing save data...")); - } + OnSyncSaveData(packet); break; - case SYNC_SAVE_DATA_RAW: - { - if (m_local_player->IsHost()) - return 0; - - bool is_slot_a; - std::string region; - int size_override; - packet >> is_slot_a >> region >> size_override; - - // This check is mainly intended to filter out characters which have special meanings in paths - if (region != JAP_DIR && region != USA_DIR && region != EUR_DIR) - { - SyncSaveDataResponse(false); - return 0; - } - - std::string size_suffix; - if (size_override >= 0 && size_override <= 4) - { - size_suffix = fmt::format( - ".{}", Memcard::MbitToFreeBlocks(Memcard::MBIT_SIZE_MEMORY_CARD_59 << size_override)); - } - - const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY + - (is_slot_a ? "A." : "B.") + region + size_suffix + ".raw"; - if (File::Exists(path) && !File::Delete(path)) - { - PanicAlertFmtT("Failed to delete NetPlay memory card. Verify your write permissions."); - SyncSaveDataResponse(false); - return 0; - } - - const bool success = DecompressPacketIntoFile(packet, path); - SyncSaveDataResponse(success); - } - break; - - case SYNC_SAVE_DATA_GCI: - { - if (m_local_player->IsHost()) - return 0; - - bool is_slot_a; - u8 file_count; - packet >> is_slot_a >> file_count; - - const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP + - fmt::format("Card {}", is_slot_a ? 'A' : 'B'); - - if ((File::Exists(path) && !File::DeleteDirRecursively(path + DIR_SEP)) || - !File::CreateFullPath(path + DIR_SEP)) - { - PanicAlertFmtT("Failed to reset NetPlay GCI folder. Verify your write permissions."); - SyncSaveDataResponse(false); - return 0; - } - - for (u8 i = 0; i < file_count; i++) - { - std::string file_name; - packet >> file_name; - - if (!Common::IsFileNameSafe(file_name) || - !DecompressPacketIntoFile(packet, path + DIR_SEP + file_name)) - { - SyncSaveDataResponse(false); - return 0; - } - } - - SyncSaveDataResponse(true); - } - break; - - case SYNC_SAVE_DATA_WII: - { - if (m_local_player->IsHost()) - return 0; - - const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; - - if (File::Exists(path) && !File::DeleteDirRecursively(path)) - { - PanicAlertFmtT("Failed to reset NetPlay NAND folder. Verify your write permissions."); - SyncSaveDataResponse(false); - return 0; - } - - auto temp_fs = std::make_unique(path); - std::vector titles; - - const IOS::HLE::FS::Modes fs_modes = {IOS::HLE::FS::Mode::ReadWrite, - IOS::HLE::FS::Mode::ReadWrite, - IOS::HLE::FS::Mode::ReadWrite}; - - // Read the Mii data - bool mii_data; - packet >> mii_data; - if (mii_data) - { - auto buffer = DecompressPacketIntoBuffer(packet); - - temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, "/shared2/menu/FaceLib/", 0, - fs_modes); - auto file = temp_fs->CreateAndOpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL, - Common::GetMiiDatabasePath(), fs_modes); - - if (!buffer || !file || !file->Write(buffer->data(), buffer->size())) - { - PanicAlertFmtT("Failed to write Mii data."); - SyncSaveDataResponse(false); - return 0; - } - } - - // Read the saves - u32 save_count; - packet >> save_count; - for (u32 n = 0; n < save_count; n++) - { - u64 title_id = Common::PacketReadU64(packet); - titles.push_back(title_id); - temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, - Common::GetTitleDataPath(title_id) + '/', 0, fs_modes); - auto save = WiiSave::MakeNandStorage(temp_fs.get(), title_id); - - bool exists; - packet >> exists; - if (!exists) - continue; - - // Header - WiiSave::Header header; - packet >> header.tid; - packet >> header.banner_size; - packet >> header.permissions; - packet >> header.unk1; - for (u8& byte : header.md5) - packet >> byte; - packet >> header.unk2; - for (size_t i = 0; i < header.banner_size; i++) - packet >> header.banner[i]; - - // BkHeader - WiiSave::BkHeader bk_header; - packet >> bk_header.size; - packet >> bk_header.magic; - packet >> bk_header.ngid; - packet >> bk_header.number_of_files; - packet >> bk_header.size_of_files; - packet >> bk_header.unk1; - packet >> bk_header.unk2; - packet >> bk_header.total_size; - for (u8& byte : bk_header.unk3) - packet >> byte; - packet >> bk_header.tid; - for (u8& byte : bk_header.mac_address) - packet >> byte; - - // Files - std::vector files; - for (u32 i = 0; i < bk_header.number_of_files; i++) - { - WiiSave::Storage::SaveFile file; - packet >> file.mode >> file.attributes; - { - u8 tmp; - packet >> tmp; - file.type = static_cast(tmp); - } - packet >> file.path; - - if (file.type == WiiSave::Storage::SaveFile::Type::File) - { - auto buffer = DecompressPacketIntoBuffer(packet); - if (!buffer) - { - SyncSaveDataResponse(false); - return 0; - } - - file.data = std::move(*buffer); - } - - files.push_back(std::move(file)); - } - - if (!save->WriteHeader(header) || !save->WriteBkHeader(bk_header) || - !save->WriteFiles(files)) - { - PanicAlertFmtT("Failed to write Wii save."); - SyncSaveDataResponse(false); - return 0; - } - } - - SetWiiSyncData(std::move(temp_fs), titles); - SyncSaveDataResponse(true); - } - break; - - case SYNC_SAVE_DATA_GBA: - { - if (m_local_player->IsHost()) - return 0; - - u8 slot; - packet >> slot; - - const std::string path = - fmt::format("{}{}{}.sav", File::GetUserPath(D_GBAUSER_IDX), GBA_SAVE_NETPLAY, slot + 1); - if (File::Exists(path) && !File::Delete(path)) - { - PanicAlertFmtT("Failed to delete NetPlay GBA{0} save file. Verify your write permissions.", - slot + 1); - SyncSaveDataResponse(false); - return 0; - } - - const bool success = DecompressPacketIntoFile(packet, path); - SyncSaveDataResponse(success); - } - break; - - default: - PanicAlertFmtT("Unknown SYNC_SAVE_DATA message received with id: {0}", sub_id); - break; - } - } - break; - case NP_MSG_SYNC_CODES: - { - // Recieve Data Packet - MessageId sub_id; - packet >> sub_id; - - // Check Which Operation to Perform with This Packet - switch (sub_id) - { - case SYNC_CODES_NOTIFY: - { - // Set both codes as unsynced - m_sync_gecko_codes_complete = false; - m_sync_ar_codes_complete = false; - } + OnSyncCodes(packet); break; - case SYNC_CODES_NOTIFY_GECKO: - { - // Return if this is the host - if (m_local_player->IsHost()) - return 0; - - // Receive Number of Codelines to Receive - packet >> m_sync_gecko_codes_count; - - m_sync_gecko_codes_success_count = 0; - - NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} Gecko codelines", m_sync_gecko_codes_count); - - // Check if no codes to sync, if so return as finished - if (m_sync_gecko_codes_count == 0) - { - m_sync_gecko_codes_complete = true; - SyncCodeResponse(true); - } - else - { - m_dialog->AppendChat(Common::GetStringT("Synchronizing Gecko codes...")); - } - } - break; - - case SYNC_CODES_DATA_GECKO: - { - // Return if this is the host - if (m_local_player->IsHost()) - return 0; - - // Create a synced code vector - std::vector synced_codes; - // Create a GeckoCode - Gecko::GeckoCode gcode; - gcode = Gecko::GeckoCode(); - // Initialize gcode - gcode.name = "Synced Codes"; - gcode.enabled = true; - - // Receive code contents from packet - for (int i = 0; i < m_sync_gecko_codes_count; i++) - { - Gecko::GeckoCode::Code new_code; - packet >> new_code.address; - packet >> new_code.data; - - NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.address, new_code.data); - - gcode.codes.push_back(std::move(new_code)); - - if (++m_sync_gecko_codes_success_count >= m_sync_gecko_codes_count) - { - m_sync_gecko_codes_complete = true; - SyncCodeResponse(true); - } - } - - // Add gcode containing all codes to Gecko Code vector - synced_codes.push_back(std::move(gcode)); - - // Clear Vector if received 0 codes (match host's end when using no codes) - if (m_sync_gecko_codes_count == 0) - synced_codes.clear(); - - // Copy this to the vector located in GeckoCode.cpp - Gecko::UpdateSyncedCodes(synced_codes); - } - break; - - case SYNC_CODES_NOTIFY_AR: - { - // Return if this is the host - if (m_local_player->IsHost()) - return 0; - - // Receive Number of Codelines to Receive - packet >> m_sync_ar_codes_count; - - m_sync_ar_codes_success_count = 0; - - NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} AR codelines", m_sync_ar_codes_count); - - // Check if no codes to sync, if so return as finished - if (m_sync_ar_codes_count == 0) - { - m_sync_ar_codes_complete = true; - SyncCodeResponse(true); - } - else - { - m_dialog->AppendChat(Common::GetStringT("Synchronizing AR codes...")); - } - } - break; - - case SYNC_CODES_DATA_AR: - { - // Return if this is the host - if (m_local_player->IsHost()) - return 0; - - // Create a synced code vector - std::vector synced_codes; - // Create an ARCode - ActionReplay::ARCode arcode; - arcode = ActionReplay::ARCode(); - // Initialize arcode - arcode.name = "Synced Codes"; - arcode.enabled = true; - - // Receive code contents from packet - for (int i = 0; i < m_sync_ar_codes_count; i++) - { - ActionReplay::AREntry new_code; - packet >> new_code.cmd_addr; - packet >> new_code.value; - - NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.cmd_addr, new_code.value); - arcode.ops.push_back(new_code); - - if (++m_sync_ar_codes_success_count >= m_sync_ar_codes_count) - { - m_sync_ar_codes_complete = true; - SyncCodeResponse(true); - } - } - - // Add arcode containing all codes to AR Code vector - synced_codes.push_back(std::move(arcode)); - - // Clear Vector if received 0 codes (match host's end when using no codes) - if (m_sync_ar_codes_count == 0) - synced_codes.clear(); - - // Copy this to the vector located in ActionReplay.cpp - ActionReplay::UpdateSyncedCodes(synced_codes); - } - break; - } - } - break; - case NP_MSG_COMPUTE_MD5: - { - SyncIdentifier sync_identifier; - ReceiveSyncIdentifier(packet, sync_identifier); - - ComputeMD5(sync_identifier); - } - break; + OnComputeMD5(packet); + break; case NP_MSG_MD5_PROGRESS: - { - PlayerId pid; - int progress; - packet >> pid; - packet >> progress; - - m_dialog->SetMD5Progress(pid, progress); - } - break; + OnMD5Progress(packet); + break; case NP_MSG_MD5_RESULT: - { - PlayerId pid; - std::string result; - packet >> pid; - packet >> result; - - m_dialog->SetMD5Result(pid, result); - } - break; + OnMD5Result(packet); + break; case NP_MSG_MD5_ERROR: - { - PlayerId pid; - std::string error; - packet >> pid; - packet >> error; - - m_dialog->SetMD5Result(pid, error); - } - break; + OnMD5Error(packet); + break; case NP_MSG_MD5_ABORT: - { - m_should_compute_MD5 = false; - m_dialog->AbortMD5(); - } - break; + OnMD5Abort(); + break; default: PanicAlertFmtT("Unknown message received with id : {0}", mid); @@ -1340,6 +461,1002 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) return 0; } +void NetPlayClient::OnPlayerJoin(sf::Packet& packet) +{ + Player player{}; + packet >> player.pid; + packet >> player.name; + packet >> player.revision; + + INFO_LOG_FMT(NETPLAY, "Player {} ({}) using {} joined", player.name, player.pid, player.revision); + + { + std::lock_guard lkp(m_crit.players); + m_players[player.pid] = player; + } + + m_dialog->OnPlayerConnect(player.name); + + m_dialog->Update(); +} + +void NetPlayClient::OnPlayerLeave(sf::Packet& packet) +{ + PlayerId pid; + packet >> pid; + + { + std::lock_guard lkp(m_crit.players); + const auto it = m_players.find(pid); + if (it == m_players.end()) + return; + + const auto& player = it->second; + INFO_LOG_FMT(NETPLAY, "Player {} ({}) left", player.name, pid); + m_dialog->OnPlayerDisconnect(player.name); + m_players.erase(m_players.find(pid)); + } + + m_dialog->Update(); +} + +void NetPlayClient::OnChatMessage(sf::Packet& packet) +{ + PlayerId pid; + packet >> pid; + std::string msg; + packet >> msg; + + // don't need lock to read in this thread + const Player& player = m_players[pid]; + + INFO_LOG_FMT(NETPLAY, "Player {} ({}) wrote: {}", player.name, player.pid, msg); + + // add to gui + std::ostringstream ss; + ss << player.name << '[' << char(pid + '0') << "]: " << msg; + + m_dialog->AppendChat(ss.str()); +} + +void NetPlayClient::OnChunkedDataStart(sf::Packet& packet) +{ + u32 cid; + packet >> cid; + std::string title; + packet >> title; + const u64 data_size = Common::PacketReadU64(packet); + + m_chunked_data_receive_queue.emplace(cid, sf::Packet{}); + + std::vector players; + players.push_back(m_local_player->pid); + m_dialog->ShowChunkedProgressDialog(title, data_size, players); +} + +void NetPlayClient::OnChunkedDataEnd(sf::Packet& packet) +{ + u32 cid; + packet >> cid; + + const auto data_packet_iter = m_chunked_data_receive_queue.find(cid); + if (data_packet_iter == m_chunked_data_receive_queue.end()) + return; + + auto& data_packet = data_packet_iter->second; + OnData(data_packet); + m_chunked_data_receive_queue.erase(data_packet_iter); + m_dialog->HideChunkedProgressDialog(); + + sf::Packet complete_packet; + complete_packet << static_cast(NP_MSG_CHUNKED_DATA_COMPLETE); + complete_packet << cid; + Send(complete_packet, CHUNKED_DATA_CHANNEL); +} + +void NetPlayClient::OnChunkedDataPayload(sf::Packet& packet) +{ + u32 cid; + packet >> cid; + + const auto data_packet_iter = m_chunked_data_receive_queue.find(cid); + if (data_packet_iter == m_chunked_data_receive_queue.end()) + return; + + auto& data_packet = data_packet_iter->second; + while (!packet.endOfPacket()) + { + u8 byte; + packet >> byte; + data_packet << byte; + } + + m_dialog->SetChunkedProgress(m_local_player->pid, data_packet.getDataSize()); + + sf::Packet progress_packet; + progress_packet << static_cast(NP_MSG_CHUNKED_DATA_PROGRESS); + progress_packet << cid; + progress_packet << sf::Uint64{data_packet.getDataSize()}; + Send(progress_packet, CHUNKED_DATA_CHANNEL); +} + +void NetPlayClient::OnChunkedDataAbort(sf::Packet& packet) +{ + u32 cid; + packet >> cid; + + const auto iter = m_chunked_data_receive_queue.find(cid); + if (iter == m_chunked_data_receive_queue.end()) + return; + + m_chunked_data_receive_queue.erase(iter); + m_dialog->HideChunkedProgressDialog(); +} + +void NetPlayClient::OnPadMapping(sf::Packet& packet) +{ + for (PlayerId& mapping : m_pad_map) + packet >> mapping; + + UpdateDevices(); + + m_dialog->Update(); +} + +void NetPlayClient::OnWiimoteMapping(sf::Packet& packet) +{ + for (PlayerId& mapping : m_wiimote_map) + packet >> mapping; + + m_dialog->Update(); +} + +void NetPlayClient::OnGBAConfig(sf::Packet& packet) +{ + for (size_t i = 0; i < m_gba_config.size(); ++i) + { + auto& config = m_gba_config[i]; + const auto old_config = config; + + packet >> config.enabled >> config.has_rom >> config.title; + for (auto& data : config.hash) + packet >> data; + + if (std::tie(config.has_rom, config.title, config.hash) != + std::tie(old_config.has_rom, old_config.title, old_config.hash)) + { + m_dialog->OnMsgChangeGBARom(static_cast(i), config); + m_net_settings.m_GBARomPaths[i] = + config.has_rom ? + m_dialog->FindGBARomPath(config.hash, config.title, static_cast(i)) : + ""; + } + } + + SendGameStatus(); + UpdateDevices(); + + m_dialog->Update(); +} + +void NetPlayClient::OnPadData(sf::Packet& packet) +{ + while (!packet.endOfPacket()) + { + PadIndex map; + packet >> map; + + GCPadStatus pad; + packet >> pad.button; + if (!m_gba_config.at(map).enabled) + { + packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> + pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; + } + + // Trusting server for good map value (>=0 && <4) + // add to pad buffer + m_pad_buffer.at(map).Push(pad); + m_gc_pad_event.Set(); + } +} + +void NetPlayClient::OnPadHostData(sf::Packet& packet) +{ + while (!packet.endOfPacket()) + { + PadIndex map; + packet >> map; + + GCPadStatus pad; + packet >> pad.button; + if (!m_gba_config.at(map).enabled) + { + packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >> + pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected; + } + + // Trusting server for good map value (>=0 && <4) + // write to last status + m_last_pad_status[map] = pad; + + if (!m_first_pad_status_received[map]) + { + m_first_pad_status_received[map] = true; + m_first_pad_status_received_event.Set(); + } + } +} + +void NetPlayClient::OnWiimoteData(sf::Packet& packet) +{ + PadIndex map; + WiimoteInput nw; + u8 size; + + packet >> map >> nw.report_id >> size; + + nw.data.resize(size); + for (auto& byte : nw.data) + packet >> byte; + + // Trusting server for good map value (>=0 && <4) + // add to Wiimote buffer + m_wiimote_buffer.at(map).Push(nw); + m_wii_pad_event.Set(); +} + +void NetPlayClient::OnPadBuffer(sf::Packet& packet) +{ + u32 size = 0; + packet >> size; + + m_target_buffer_size = size; + m_dialog->OnPadBufferChanged(size); +} + +void NetPlayClient::OnHostInputAuthority(sf::Packet& packet) +{ + packet >> m_host_input_authority; + m_dialog->OnHostInputAuthorityChanged(m_host_input_authority); +} + +void NetPlayClient::OnGolfSwitch(sf::Packet& packet) +{ + PlayerId pid; + packet >> pid; + + const PlayerId previous_golfer = m_current_golfer; + m_current_golfer = pid; + m_dialog->OnGolferChanged(m_local_player->pid == pid, pid != 0 ? m_players[pid].name : ""); + + if (m_local_player->pid == previous_golfer) + { + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_RELEASE); + Send(spac); + } + else if (m_local_player->pid == pid) + { + sf::Packet spac; + spac << static_cast(NP_MSG_GOLF_ACQUIRE); + Send(spac); + + // Pads are already calibrated so we can just ignore this + m_first_pad_status_received.fill(true); + + m_wait_on_input = false; + m_wait_on_input_event.Set(); + } +} + +void NetPlayClient::OnGolfPrepare(sf::Packet& packet) +{ + m_wait_on_input_received = true; + m_wait_on_input = true; +} + +void NetPlayClient::OnChangeGame(sf::Packet& packet) +{ + std::string netplay_name; + { + std::lock_guard lkg(m_crit.game); + ReceiveSyncIdentifier(packet, m_selected_game); + packet >> netplay_name; + } + + INFO_LOG_FMT(NETPLAY, "Game changed to {}", netplay_name); + + // update gui + m_dialog->OnMsgChangeGame(m_selected_game, netplay_name); + + SendGameStatus(); + + sf::Packet client_capabilities_packet; + client_capabilities_packet << static_cast(NP_MSG_CLIENT_CAPABILITIES); + client_capabilities_packet << ExpansionInterface::CEXIIPL::HasIPLDump(); + client_capabilities_packet << Config::Get(Config::SESSION_USE_FMA); + Send(client_capabilities_packet); +} + +void NetPlayClient::OnGameStatus(sf::Packet& packet) +{ + PlayerId pid; + packet >> pid; + + { + std::lock_guard lkp(m_crit.players); + Player& player = m_players[pid]; + u32 status; + packet >> status; + player.game_status = static_cast(status); + } + + m_dialog->Update(); +} + +void NetPlayClient::OnStartGame(sf::Packet& packet) +{ + { + std::lock_guard lkg(m_crit.game); + packet >> m_current_game; + packet >> m_net_settings.m_CPUthread; + + INFO_LOG_FMT(NETPLAY, "Start of game {}", m_selected_game.game_id); + + { + std::underlying_type_t core; + if (packet >> core) + m_net_settings.m_CPUcore = static_cast(core); + else + m_net_settings.m_CPUcore = PowerPC::CPUCore::CachedInterpreter; + } + + packet >> m_net_settings.m_EnableCheats; + packet >> m_net_settings.m_SelectedLanguage; + packet >> m_net_settings.m_OverrideRegionSettings; + packet >> m_net_settings.m_DSPEnableJIT; + packet >> m_net_settings.m_DSPHLE; + packet >> m_net_settings.m_WriteToMemcard; + packet >> m_net_settings.m_RAMOverrideEnable; + packet >> m_net_settings.m_Mem1Size; + packet >> m_net_settings.m_Mem2Size; + + { + std::underlying_type_t tmp; + packet >> tmp; + m_net_settings.m_FallbackRegion = static_cast(tmp); + } + + packet >> m_net_settings.m_AllowSDWrites; + packet >> m_net_settings.m_CopyWiiSave; + packet >> m_net_settings.m_OCEnable; + packet >> m_net_settings.m_OCFactor; + + for (auto& device : m_net_settings.m_EXIDevice) + { + int tmp; + packet >> tmp; + device = static_cast(tmp); + } + + for (u32& value : m_net_settings.m_SYSCONFSettings) + packet >> value; + + packet >> m_net_settings.m_EFBAccessEnable; + packet >> m_net_settings.m_BBoxEnable; + packet >> m_net_settings.m_ForceProgressive; + packet >> m_net_settings.m_EFBToTextureEnable; + packet >> m_net_settings.m_XFBToTextureEnable; + packet >> m_net_settings.m_DisableCopyToVRAM; + packet >> m_net_settings.m_ImmediateXFBEnable; + packet >> m_net_settings.m_EFBEmulateFormatChanges; + packet >> m_net_settings.m_SafeTextureCacheColorSamples; + packet >> m_net_settings.m_PerfQueriesEnable; + packet >> m_net_settings.m_FPRF; + packet >> m_net_settings.m_AccurateNaNs; + packet >> m_net_settings.m_DisableICache; + packet >> m_net_settings.m_SyncOnSkipIdle; + packet >> m_net_settings.m_SyncGPU; + packet >> m_net_settings.m_SyncGpuMaxDistance; + packet >> m_net_settings.m_SyncGpuMinDistance; + packet >> m_net_settings.m_SyncGpuOverclock; + packet >> m_net_settings.m_JITFollowBranch; + packet >> m_net_settings.m_FastDiscSpeed; + packet >> m_net_settings.m_MMU; + packet >> m_net_settings.m_Fastmem; + packet >> m_net_settings.m_SkipIPL; + packet >> m_net_settings.m_LoadIPLDump; + packet >> m_net_settings.m_VertexRounding; + packet >> m_net_settings.m_InternalResolution; + packet >> m_net_settings.m_EFBScaledCopy; + packet >> m_net_settings.m_FastDepthCalc; + packet >> m_net_settings.m_EnablePixelLighting; + packet >> m_net_settings.m_WidescreenHack; + packet >> m_net_settings.m_ForceFiltering; + packet >> m_net_settings.m_MaxAnisotropy; + packet >> m_net_settings.m_ForceTrueColor; + packet >> m_net_settings.m_DisableCopyFilter; + packet >> m_net_settings.m_DisableFog; + packet >> m_net_settings.m_ArbitraryMipmapDetection; + packet >> m_net_settings.m_ArbitraryMipmapDetectionThreshold; + packet >> m_net_settings.m_EnableGPUTextureDecoding; + packet >> m_net_settings.m_DeferEFBCopies; + packet >> m_net_settings.m_EFBAccessTileSize; + packet >> m_net_settings.m_EFBAccessDeferInvalidation; + packet >> m_net_settings.m_StrictSettingsSync; + + m_initial_rtc = Common::PacketReadU64(packet); + + packet >> m_net_settings.m_SyncSaveData; + packet >> m_net_settings.m_SaveDataRegion; + packet >> m_net_settings.m_SyncCodes; + packet >> m_net_settings.m_SyncAllWiiSaves; + + for (int& extension : m_net_settings.m_WiimoteExtension) + packet >> extension; + + packet >> m_net_settings.m_GolfMode; + packet >> m_net_settings.m_UseFMA; + packet >> m_net_settings.m_HideRemoteGBAs; + + m_net_settings.m_IsHosting = m_local_player->IsHost(); + m_net_settings.m_HostInputAuthority = m_host_input_authority; + } + + m_dialog->OnMsgStartGame(); +} + +void NetPlayClient::OnStopGame(sf::Packet& packet) +{ + INFO_LOG_FMT(NETPLAY, "Game stopped"); + + StopGame(); + m_dialog->OnMsgStopGame(); +} + +void NetPlayClient::OnPowerButton() +{ + m_dialog->OnMsgPowerButton(); +} + +void NetPlayClient::OnPing(sf::Packet& packet) +{ + u32 ping_key = 0; + packet >> ping_key; + + sf::Packet response_packet; + response_packet << static_cast(NP_MSG_PONG); + response_packet << ping_key; + + Send(response_packet); +} + +void NetPlayClient::OnPlayerPingData(sf::Packet& packet) +{ + PlayerId pid; + packet >> pid; + + { + std::lock_guard lkp(m_crit.players); + Player& player = m_players[pid]; + packet >> player.ping; + } + + DisplayPlayersPing(); + m_dialog->Update(); +} + +void NetPlayClient::OnDesyncDetected(sf::Packet& packet) +{ + int pid_to_blame; + u32 frame; + packet >> pid_to_blame; + packet >> frame; + + std::string player = "??"; + std::lock_guard lkp(m_crit.players); + { + const auto it = m_players.find(pid_to_blame); + if (it != m_players.end()) + player = it->second.name; + } + + INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame); + + m_dialog->OnDesync(frame, player); +} + +void NetPlayClient::OnSyncGCSRAM(sf::Packet& packet) +{ + const size_t sram_settings_len = sizeof(g_SRAM) - offsetof(Sram, settings); + u8 sram[sram_settings_len]; + for (u8& cell : sram) + packet >> cell; + + { + std::lock_guard lkg(m_crit.game); + memcpy(&g_SRAM.settings, sram, sram_settings_len); + g_SRAM_netplay_initialized = true; + } +} + +void NetPlayClient::OnSyncSaveData(sf::Packet& packet) +{ + MessageId sub_id; + packet >> sub_id; + + if (m_local_player->IsHost()) + return; + + switch (sub_id) + { + case SYNC_SAVE_DATA_NOTIFY: + OnSyncSaveDataNotify(packet); + break; + + case SYNC_SAVE_DATA_RAW: + OnSyncSaveDataRaw(packet); + break; + + case SYNC_SAVE_DATA_GCI: + OnSyncSaveDataGCI(packet); + break; + + case SYNC_SAVE_DATA_WII: + OnSyncSaveDataWii(packet); + break; + + case SYNC_SAVE_DATA_GBA: + OnSyncSaveDataGBA(packet); + break; + + default: + PanicAlertFmtT("Unknown SYNC_SAVE_DATA message received with id: {0}", sub_id); + break; + } +} + +void NetPlayClient::OnSyncSaveDataNotify(sf::Packet& packet) +{ + packet >> m_sync_save_data_count; + m_sync_save_data_success_count = 0; + + if (m_sync_save_data_count == 0) + SyncSaveDataResponse(true); + else + m_dialog->AppendChat(Common::GetStringT("Synchronizing save data...")); +} + +void NetPlayClient::OnSyncSaveDataRaw(sf::Packet& packet) +{ + bool is_slot_a; + std::string region; + int size_override; + packet >> is_slot_a >> region >> size_override; + + // This check is mainly intended to filter out characters which have special meanings in paths + if (region != JAP_DIR && region != USA_DIR && region != EUR_DIR) + { + SyncSaveDataResponse(false); + return; + } + + std::string size_suffix; + if (size_override >= 0 && size_override <= 4) + { + size_suffix = fmt::format( + ".{}", Memcard::MbitToFreeBlocks(Memcard::MBIT_SIZE_MEMORY_CARD_59 << size_override)); + } + + const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY + + (is_slot_a ? "A." : "B.") + region + size_suffix + ".raw"; + if (File::Exists(path) && !File::Delete(path)) + { + PanicAlertFmtT("Failed to delete NetPlay memory card. Verify your write permissions."); + SyncSaveDataResponse(false); + return; + } + + const bool success = DecompressPacketIntoFile(packet, path); + SyncSaveDataResponse(success); +} + +void NetPlayClient::OnSyncSaveDataGCI(sf::Packet& packet) +{ + bool is_slot_a; + u8 file_count; + packet >> is_slot_a >> file_count; + + const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP + + fmt::format("Card {}", is_slot_a ? 'A' : 'B'); + + if ((File::Exists(path) && !File::DeleteDirRecursively(path + DIR_SEP)) || + !File::CreateFullPath(path + DIR_SEP)) + { + PanicAlertFmtT("Failed to reset NetPlay GCI folder. Verify your write permissions."); + SyncSaveDataResponse(false); + return; + } + + for (u8 i = 0; i < file_count; i++) + { + std::string file_name; + packet >> file_name; + + if (!Common::IsFileNameSafe(file_name) || + !DecompressPacketIntoFile(packet, path + DIR_SEP + file_name)) + { + SyncSaveDataResponse(false); + return; + } + } + + SyncSaveDataResponse(true); +} + +void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet) +{ + const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP; + + if (File::Exists(path) && !File::DeleteDirRecursively(path)) + { + PanicAlertFmtT("Failed to reset NetPlay NAND folder. Verify your write permissions."); + SyncSaveDataResponse(false); + return; + } + + auto temp_fs = std::make_unique(path); + std::vector titles; + + constexpr IOS::HLE::FS::Modes fs_modes{ + IOS::HLE::FS::Mode::ReadWrite, + IOS::HLE::FS::Mode::ReadWrite, + IOS::HLE::FS::Mode::ReadWrite, + }; + + // Read the Mii data + bool mii_data; + packet >> mii_data; + if (mii_data) + { + auto buffer = DecompressPacketIntoBuffer(packet); + + temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, "/shared2/menu/FaceLib/", 0, + fs_modes); + auto file = temp_fs->CreateAndOpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL, + Common::GetMiiDatabasePath(), fs_modes); + + if (!buffer || !file || !file->Write(buffer->data(), buffer->size())) + { + PanicAlertFmtT("Failed to write Mii data."); + SyncSaveDataResponse(false); + return; + } + } + + // Read the saves + u32 save_count; + packet >> save_count; + for (u32 n = 0; n < save_count; n++) + { + u64 title_id = Common::PacketReadU64(packet); + titles.push_back(title_id); + temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, + Common::GetTitleDataPath(title_id) + '/', 0, fs_modes); + auto save = WiiSave::MakeNandStorage(temp_fs.get(), title_id); + + bool exists; + packet >> exists; + if (!exists) + continue; + + // Header + WiiSave::Header header; + packet >> header.tid; + packet >> header.banner_size; + packet >> header.permissions; + packet >> header.unk1; + for (u8& byte : header.md5) + packet >> byte; + packet >> header.unk2; + for (size_t i = 0; i < header.banner_size; i++) + packet >> header.banner[i]; + + // BkHeader + WiiSave::BkHeader bk_header; + packet >> bk_header.size; + packet >> bk_header.magic; + packet >> bk_header.ngid; + packet >> bk_header.number_of_files; + packet >> bk_header.size_of_files; + packet >> bk_header.unk1; + packet >> bk_header.unk2; + packet >> bk_header.total_size; + for (u8& byte : bk_header.unk3) + packet >> byte; + packet >> bk_header.tid; + for (u8& byte : bk_header.mac_address) + packet >> byte; + + // Files + std::vector files; + for (u32 i = 0; i < bk_header.number_of_files; i++) + { + WiiSave::Storage::SaveFile file; + packet >> file.mode >> file.attributes; + { + u8 tmp; + packet >> tmp; + file.type = static_cast(tmp); + } + packet >> file.path; + + if (file.type == WiiSave::Storage::SaveFile::Type::File) + { + auto buffer = DecompressPacketIntoBuffer(packet); + if (!buffer) + { + SyncSaveDataResponse(false); + return; + } + + file.data = std::move(*buffer); + } + + files.push_back(std::move(file)); + } + + if (!save->WriteHeader(header) || !save->WriteBkHeader(bk_header) || !save->WriteFiles(files)) + { + PanicAlertFmtT("Failed to write Wii save."); + SyncSaveDataResponse(false); + return; + } + } + + SetWiiSyncData(std::move(temp_fs), titles); + SyncSaveDataResponse(true); +} + +void NetPlayClient::OnSyncSaveDataGBA(sf::Packet& packet) +{ + u8 slot; + packet >> slot; + + const std::string path = + fmt::format("{}{}{}.sav", File::GetUserPath(D_GBAUSER_IDX), GBA_SAVE_NETPLAY, slot + 1); + if (File::Exists(path) && !File::Delete(path)) + { + PanicAlertFmtT("Failed to delete NetPlay GBA{0} save file. Verify your write permissions.", + slot + 1); + SyncSaveDataResponse(false); + return; + } + + const bool success = DecompressPacketIntoFile(packet, path); + SyncSaveDataResponse(success); +} + +void NetPlayClient::OnSyncCodes(sf::Packet& packet) +{ + // Recieve Data Packet + MessageId sub_id; + packet >> sub_id; + + // Check Which Operation to Perform with This Packet + switch (sub_id) + { + case SYNC_CODES_NOTIFY: + OnSyncCodesNotify(); + break; + + case SYNC_CODES_NOTIFY_GECKO: + OnSyncCodesNotifyGecko(packet); + break; + + case SYNC_CODES_DATA_GECKO: + OnSyncCodesDataGecko(packet); + break; + + case SYNC_CODES_NOTIFY_AR: + OnSyncCodesNotifyAR(packet); + break; + + case SYNC_CODES_DATA_AR: + OnSyncCodesDataAR(packet); + break; + + default: + PanicAlertFmtT("Unknown SYNC_CODES message received with id: {0}", sub_id); + break; + } +} + +void NetPlayClient::OnSyncCodesNotify() +{ + // Set both codes as unsynced + m_sync_gecko_codes_complete = false; + m_sync_ar_codes_complete = false; +} + +void NetPlayClient::OnSyncCodesNotifyGecko(sf::Packet& packet) +{ + // Return if this is the host + if (m_local_player->IsHost()) + return; + + // Receive Number of Codelines to Receive + packet >> m_sync_gecko_codes_count; + + m_sync_gecko_codes_success_count = 0; + + NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} Gecko codelines", m_sync_gecko_codes_count); + + // Check if no codes to sync, if so return as finished + if (m_sync_gecko_codes_count == 0) + { + m_sync_gecko_codes_complete = true; + SyncCodeResponse(true); + } + else + { + m_dialog->AppendChat(Common::GetStringT("Synchronizing Gecko codes...")); + } +} + +void NetPlayClient::OnSyncCodesDataGecko(sf::Packet& packet) +{ + // Return if this is the host + if (m_local_player->IsHost()) + return; + + std::vector synced_codes; + synced_codes.reserve(m_sync_gecko_codes_count); + + Gecko::GeckoCode gcode{}; + gcode.name = "Synced Codes"; + gcode.enabled = true; + + // Receive code contents from packet + for (u32 i = 0; i < m_sync_gecko_codes_count; i++) + { + Gecko::GeckoCode::Code new_code; + packet >> new_code.address; + packet >> new_code.data; + + NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.address, new_code.data); + + gcode.codes.push_back(std::move(new_code)); + + if (++m_sync_gecko_codes_success_count >= m_sync_gecko_codes_count) + { + m_sync_gecko_codes_complete = true; + SyncCodeResponse(true); + } + } + + // Add gcode containing all codes to Gecko Code vector + synced_codes.push_back(std::move(gcode)); + + // Clear Vector if received 0 codes (match host's end when using no codes) + if (m_sync_gecko_codes_count == 0) + synced_codes.clear(); + + // Copy this to the vector located in GeckoCode.cpp + Gecko::UpdateSyncedCodes(synced_codes); +} + +void NetPlayClient::OnSyncCodesNotifyAR(sf::Packet& packet) +{ + // Return if this is the host + if (m_local_player->IsHost()) + return; + + // Receive Number of Codelines to Receive + packet >> m_sync_ar_codes_count; + + m_sync_ar_codes_success_count = 0; + + NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} AR codelines", m_sync_ar_codes_count); + + // Check if no codes to sync, if so return as finished + if (m_sync_ar_codes_count == 0) + { + m_sync_ar_codes_complete = true; + SyncCodeResponse(true); + } + else + { + m_dialog->AppendChat(Common::GetStringT("Synchronizing AR codes...")); + } +} + +void NetPlayClient::OnSyncCodesDataAR(sf::Packet& packet) +{ + // Return if this is the host + if (m_local_player->IsHost()) + return; + + std::vector synced_codes; + synced_codes.reserve(m_sync_ar_codes_count); + + ActionReplay::ARCode arcode{}; + arcode.name = "Synced Codes"; + arcode.enabled = true; + + // Receive code contents from packet + for (u32 i = 0; i < m_sync_ar_codes_count; i++) + { + ActionReplay::AREntry new_code; + packet >> new_code.cmd_addr; + packet >> new_code.value; + + NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.cmd_addr, new_code.value); + arcode.ops.push_back(new_code); + + if (++m_sync_ar_codes_success_count >= m_sync_ar_codes_count) + { + m_sync_ar_codes_complete = true; + SyncCodeResponse(true); + } + } + + // Add arcode containing all codes to AR Code vector + synced_codes.push_back(std::move(arcode)); + + // Clear Vector if received 0 codes (match host's end when using no codes) + if (m_sync_ar_codes_count == 0) + synced_codes.clear(); + + // Copy this to the vector located in ActionReplay.cpp + ActionReplay::UpdateSyncedCodes(synced_codes); +} + +void NetPlayClient::OnComputeMD5(sf::Packet& packet) +{ + SyncIdentifier sync_identifier; + ReceiveSyncIdentifier(packet, sync_identifier); + + ComputeMD5(sync_identifier); +} + +void NetPlayClient::OnMD5Progress(sf::Packet& packet) +{ + PlayerId pid; + int progress; + packet >> pid; + packet >> progress; + + m_dialog->SetMD5Progress(pid, progress); +} + +void NetPlayClient::OnMD5Result(sf::Packet& packet) +{ + PlayerId pid; + std::string result; + packet >> pid; + packet >> result; + + m_dialog->SetMD5Result(pid, result); +} + +void NetPlayClient::OnMD5Error(sf::Packet& packet) +{ + PlayerId pid; + std::string error; + packet >> pid; + packet >> error; + + m_dialog->SetMD5Result(pid, error); +} + +void NetPlayClient::OnMD5Abort() +{ + m_should_compute_MD5 = false; + m_dialog->AbortMD5(); +} + void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id) { ENetPacket* epac = diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 1be8702b20..d9ca1d1798 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -232,7 +232,6 @@ private: void UpdateDevices(); void AddPadStateToPacket(int in_game_pad, const GCPadStatus& np, sf::Packet& packet); void SendWiimoteState(int in_game_pad, const WiimoteInput& nw); - unsigned int OnData(sf::Packet& packet); void Send(const sf::Packet& packet, u8 channel_id = DEFAULT_CHANNEL); void Disconnect(); bool Connect(); @@ -241,6 +240,51 @@ private: void DisplayPlayersPing(); u32 GetPlayersMaxPing() const; + u32 OnData(sf::Packet& packet); + void OnPlayerJoin(sf::Packet& packet); + void OnPlayerLeave(sf::Packet& packet); + void OnChatMessage(sf::Packet& packet); + void OnChunkedDataStart(sf::Packet& packet); + void OnChunkedDataEnd(sf::Packet& packet); + void OnChunkedDataPayload(sf::Packet& packet); + void OnChunkedDataAbort(sf::Packet& packet); + void OnPadMapping(sf::Packet& packet); + void OnWiimoteMapping(sf::Packet& packet); + void OnGBAConfig(sf::Packet& packet); + void OnPadData(sf::Packet& packet); + void OnPadHostData(sf::Packet& packet); + void OnWiimoteData(sf::Packet& packet); + void OnPadBuffer(sf::Packet& packet); + void OnHostInputAuthority(sf::Packet& packet); + void OnGolfSwitch(sf::Packet& packet); + void OnGolfPrepare(sf::Packet& packet); + void OnChangeGame(sf::Packet& packet); + void OnGameStatus(sf::Packet& packet); + void OnStartGame(sf::Packet& packet); + void OnStopGame(sf::Packet& packet); + void OnPowerButton(); + void OnPing(sf::Packet& packet); + void OnPlayerPingData(sf::Packet& packet); + void OnDesyncDetected(sf::Packet& packet); + void OnSyncGCSRAM(sf::Packet& packet); + void OnSyncSaveData(sf::Packet& packet); + void OnSyncSaveDataNotify(sf::Packet& packet); + void OnSyncSaveDataRaw(sf::Packet& packet); + void OnSyncSaveDataGCI(sf::Packet& packet); + void OnSyncSaveDataWii(sf::Packet& packet); + void OnSyncSaveDataGBA(sf::Packet& packet); + void OnSyncCodes(sf::Packet& packet); + void OnSyncCodesNotify(); + void OnSyncCodesNotifyGecko(sf::Packet& packet); + void OnSyncCodesDataGecko(sf::Packet& packet); + void OnSyncCodesNotifyAR(sf::Packet& packet); + void OnSyncCodesDataAR(sf::Packet& packet); + void OnComputeMD5(sf::Packet& packet); + void OnMD5Progress(sf::Packet& packet); + void OnMD5Result(sf::Packet& packet); + void OnMD5Error(sf::Packet& packet); + void OnMD5Abort(); + bool m_is_connected = false; ConnectionState m_connection_state = ConnectionState::Failure;