diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index e35b7cd746..6a20a37c6e 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -30,6 +30,7 @@ set(SRCS Analytics.cpp Version.cpp x64ABI.cpp x64Emitter.cpp + MD5.cpp Crypto/bn.cpp Crypto/ec.cpp Logging/LogManager.cpp) diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 20edab8a65..07daa25fb8 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -114,6 +114,7 @@ + @@ -158,6 +159,7 @@ + diff --git a/Source/Core/Common/MD5.cpp b/Source/Core/Common/MD5.cpp new file mode 100644 index 0000000000..4746dbdebc --- /dev/null +++ b/Source/Core/Common/MD5.cpp @@ -0,0 +1,52 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/MD5.h" +#include "Common/StringUtil.h" +#include "DiscIO/Blob.h" + +namespace MD5 +{ +std::string MD5Sum(const std::string& file_path, std::function report_progress) +{ + std::string output_string; + std::vector data(8 * 1024 * 1024); + u64 read_offset = 0; + mbedtls_md5_context ctx; + + std::unique_ptr file(DiscIO::CreateBlobReader(file_path)); + u64 game_size = file->GetDataSize(); + + mbedtls_md5_starts(&ctx); + + while (read_offset < game_size) + { + size_t read_size = std::min(static_cast(data.size()), game_size - read_offset); + if (!file->Read(read_offset, read_size, data.data())) + return output_string; + + mbedtls_md5_update(&ctx, data.data(), read_size); + read_offset += read_size; + + int progress = + static_cast(static_cast(read_offset) / static_cast(game_size) * 100); + if (!report_progress(progress)) + return output_string; + } + + std::array output; + mbedtls_md5_finish(&ctx, output.data()); + + // Convert to hex + for (u8 n : output) + output_string += StringFromFormat("%02x", n); + + return output_string; +} +} \ No newline at end of file diff --git a/Source/Core/Common/MD5.h b/Source/Core/Common/MD5.h new file mode 100644 index 0000000000..3d23783ec2 --- /dev/null +++ b/Source/Core/Common/MD5.h @@ -0,0 +1,13 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace MD5 +{ +std::string MD5Sum(const std::string& file_name, std::function progress); +} \ No newline at end of file diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 952fc60ae6..4f8f088988 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -4,10 +4,14 @@ #include "Core/NetPlayClient.h" #include +#include +#include #include +#include #include "Common/Common.h" #include "Common/CommonTypes.h" #include "Common/ENetUtil.h" +#include "Common/MD5.h" #include "Common/MsgHandler.h" #include "Common/Timer.h" #include "Core/ConfigManager.h" @@ -19,6 +23,8 @@ #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h" #include "Core/Movie.h" +#include "Core/Movie.h" +#include "Core/NetPlayClient.h" #include "InputCommon/GCAdapter.h" static std::mutex crit_netplay_client; @@ -500,6 +506,55 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) } break; + case NP_MSG_COMPUTE_MD5: + { + std::string file_identifier; + packet >> file_identifier; + + ComputeMD5(file_identifier); + } + break; + + case NP_MSG_MD5_PROGRESS: + { + PlayerId pid; + int progress; + packet >> pid; + packet >> progress; + + m_dialog->SetMD5Progress(pid, progress); + } + break; + + case NP_MSG_MD5_RESULT: + { + PlayerId pid; + std::string result; + packet >> pid; + packet >> result; + + m_dialog->SetMD5Result(pid, result); + } + break; + + case NP_MSG_MD5_ERROR: + { + PlayerId pid; + std::string error; + packet >> pid; + packet >> error; + + m_dialog->SetMD5Result(pid, error); + } + break; + + case NP_MSG_MD5_ABORT: + { + m_should_compute_MD5 = false; + m_dialog->AbortMD5(); + } + break; + default: PanicAlertT("Unknown message received with id : %d", mid); break; @@ -1197,6 +1252,47 @@ bool NetPlayClient::DoAllPlayersHaveGame() [](auto entry) { return entry.second.game_status == PlayerGameStatus::Ok; }); } +void NetPlayClient::ComputeMD5(const std::string& file_identifier) +{ + if (m_should_compute_MD5) + return; + + m_dialog->ShowMD5Dialog(file_identifier); + m_should_compute_MD5 = true; + + std::string file; + if (file_identifier == "sd.raw") + file = File::GetUserPath(D_WIIROOT_IDX) + "/sd.raw"; + else + file = m_dialog->FindGame(file_identifier); + + if (file.empty() || !File::Exists(file)) + { + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_ERROR); + spac << "file not found"; + Send(spac); + return; + } + + m_MD5_thread = std::thread([this, file]() { + std::string sum = MD5::MD5Sum(file, [&](int progress) { + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_PROGRESS); + spac << progress; + Send(spac); + + return m_should_compute_MD5; + }); + + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_RESULT); + spac << sum; + Send(spac); + }); + m_MD5_thread.detach(); +} + // stuff hacked into dolphin // called from ---CPU--- thread diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 8004d13d0e..f8e2a6c6c1 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -34,6 +34,10 @@ public: virtual void OnMsgStopGame() = 0; virtual bool IsRecording() = 0; virtual std::string FindGame(const std::string& game) = 0; + virtual void ShowMD5Dialog(const std::string& file_identifier) = 0; + virtual void SetMD5Progress(int pid, int progress) = 0; + virtual void SetMD5Result(int pid, const std::string& result) = 0; + virtual void AbortMD5() = 0; }; enum class PlayerGameStatus @@ -155,6 +159,7 @@ private: void Send(sf::Packet& packet); void Disconnect(); bool Connect(); + void ComputeMD5(const std::string& file_identifier); bool m_is_connected = false; ConnectionState m_connection_state = ConnectionState::Failure; @@ -165,6 +170,8 @@ private: std::string m_player_name; bool m_connecting = false; TraversalClient* m_traversal_client = nullptr; + std::thread m_MD5_thread; + bool m_should_compute_MD5 = false; u32 m_timebase_frame = 0; }; diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h index 1454fb3678..ffaf47a2bd 100644 --- a/Source/Core/Core/NetPlayProto.h +++ b/Source/Core/Core/NetPlayProto.h @@ -58,6 +58,12 @@ enum NP_MSG_TIMEBASE = 0xB0, NP_MSG_DESYNC_DETECTED = 0xB1, + NP_MSG_COMPUTE_MD5 = 0xC0, + NP_MSG_MD5_PROGRESS = 0xC1, + NP_MSG_MD5_RESULT = 0xC2, + NP_MSG_MD5_ABORT = 0xC3, + NP_MSG_MD5_ERROR = 0xC4, + NP_MSG_READY = 0xD0, NP_MSG_NOT_READY = 0xD1, diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index d51074d66f..44224ce588 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -664,6 +664,49 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player) } } break; + + case NP_MSG_MD5_PROGRESS: + { + int progress; + packet >> progress; + + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_PROGRESS); + spac << player.pid; + spac << progress; + + SendToClients(spac); + } + break; + + case NP_MSG_MD5_RESULT: + { + std::string result; + packet >> result; + + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_RESULT); + spac << player.pid; + spac << result; + + SendToClients(spac); + } + break; + + case NP_MSG_MD5_ERROR: + { + std::string error; + packet >> error; + + sf::Packet spac; + spac << static_cast(NP_MSG_MD5_ERROR); + spac << player.pid; + spac << error; + + SendToClients(spac); + } + break; + default: PanicAlertT("Unknown message with id:%d received from player:%d Kicking player!", mid, player.pid); @@ -685,8 +728,8 @@ void NetPlayServer::OnTraversalStateChanged() void NetPlayServer::SendChatMessage(const std::string& msg) { auto spac = std::make_unique(); - *spac << (MessageId)NP_MSG_CHAT_MESSAGE; - *spac << (PlayerId)0; // server id always 0 + *spac << static_cast(NP_MSG_CHAT_MESSAGE); + *spac << static_cast(0); // server id always 0 *spac << msg; SendAsyncToClients(std::move(spac)); @@ -701,7 +744,7 @@ bool NetPlayServer::ChangeGame(const std::string& game) // send changed game to clients auto spac = std::make_unique(); - *spac << (MessageId)NP_MSG_CHANGE_GAME; + *spac << static_cast(NP_MSG_CHANGE_GAME); *spac << game; SendAsyncToClients(std::move(spac)); @@ -709,6 +752,29 @@ bool NetPlayServer::ChangeGame(const std::string& game) return true; } +// called from ---GUI--- thread +bool NetPlayServer::ComputeMD5(const std::string& file_identifier) +{ + auto spac = std::make_unique(); + *spac << static_cast(NP_MSG_COMPUTE_MD5); + *spac << file_identifier; + + SendAsyncToClients(std::move(spac)); + + return true; +} + +// called from ---GUI--- thread +bool NetPlayServer::AbortMD5() +{ + auto spac = std::make_unique(); + *spac << static_cast(NP_MSG_MD5_ABORT); + + SendAsyncToClients(std::move(spac)); + + return true; +} + // called from ---GUI--- thread void NetPlayServer::SetNetSettings(const NetSettings& settings) { diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 3fd72e38bf..f75431fcfa 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -20,6 +20,7 @@ enum class PlayerGameStatus; class NetPlayUI; +enum class PlayerGameStatus; class NetPlayServer : public TraversalClientClient { @@ -31,6 +32,8 @@ public: ~NetPlayServer(); bool ChangeGame(const std::string& game); + bool ComputeMD5(const std::string& file_identifier); + bool AbortMD5(); void SendChatMessage(const std::string& msg); void SetNetSettings(const NetSettings& settings); diff --git a/Source/Core/DolphinWX/CMakeLists.txt b/Source/Core/DolphinWX/CMakeLists.txt index c67834889b..65a8719432 100644 --- a/Source/Core/DolphinWX/CMakeLists.txt +++ b/Source/Core/DolphinWX/CMakeLists.txt @@ -34,6 +34,7 @@ set(GUI_SRCS Debugger/WatchView.cpp Debugger/WatchWindow.cpp NetPlay/ChangeGameDialog.cpp + NetPlay/MD5Dialog.cpp NetPlay/NetPlaySetupFrame.cpp NetPlay/NetWindow.cpp NetPlay/PadMapDialog.cpp diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj b/Source/Core/DolphinWX/DolphinWX.vcxproj index 56a434ae64..56b5ed630a 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj @@ -89,6 +89,7 @@ + @@ -127,6 +128,7 @@ + diff --git a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters index 5534ec41f8..c65d43ef1b 100644 --- a/Source/Core/DolphinWX/DolphinWX.vcxproj.filters +++ b/Source/Core/DolphinWX/DolphinWX.vcxproj.filters @@ -360,6 +360,9 @@ GUI\NetPlay + + GUI\NetPlay + GUI\NetPlay diff --git a/Source/Core/DolphinWX/ISOProperties.cpp b/Source/Core/DolphinWX/ISOProperties.cpp index b939b1db3c..74352fd5ca 100644 --- a/Source/Core/DolphinWX/ISOProperties.cpp +++ b/Source/Core/DolphinWX/ISOProperties.cpp @@ -51,6 +51,7 @@ #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" +#include "Common/MD5.h" #include "Common/StringUtil.h" #include "Common/SysConf.h" #include "Core/ActionReplay.h" @@ -1304,42 +1305,13 @@ void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED(event)) void CISOProperties::OnComputeMD5Sum(wxCommandEvent& WXUNUSED(event)) { - u8 output[16]; - std::string output_string; - std::vector data(8 * 1024 * 1024); - u64 read_offset = 0; - mbedtls_md5_context ctx; - - std::unique_ptr file( - DiscIO::CreateBlobReader(OpenGameListItem.GetFileName())); - u64 game_size = file->GetDataSize(); - - wxProgressDialog progressDialog(_("Computing MD5 checksum"), _("Working..."), 1000, this, + wxProgressDialog progressDialog(_("Computing MD5 checksum"), _("Working..."), 100, this, wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME | wxPD_SMOOTH); - mbedtls_md5_starts(&ctx); - - while (read_offset < game_size) - { - if (!progressDialog.Update((int)((double)read_offset / (double)game_size * 1000))) - return; - - size_t read_size = std::min((u64)data.size(), game_size - read_offset); - if (!file->Read(read_offset, read_size, data.data())) - return; - - mbedtls_md5_update(&ctx, data.data(), read_size); - read_offset += read_size; - } - - mbedtls_md5_finish(&ctx, output); - - // Convert to hex - for (int a = 0; a < 16; ++a) - output_string += StringFromFormat("%02x", output[a]); - - m_MD5Sum->SetValue(output_string); + m_MD5Sum->SetValue(MD5::MD5Sum(OpenGameListItem.GetFileName(), [&progressDialog](int progress) { + return progressDialog.Update(progress); + })); } // Opens all pre-defined INIs for the game. If there are multiple ones, diff --git a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp index 4c315ee44b..be1173e12e 100644 --- a/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp +++ b/Source/Core/DolphinWX/NetPlay/ChangeGameDialog.cpp @@ -10,7 +10,7 @@ #include "DolphinWX/NetPlay/NetWindow.h" ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const game_list) - : wxDialog(parent, wxID_ANY, _("Change Game")) + : wxDialog(parent, wxID_ANY, _("Select Game")) { m_game_lbox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SORT); @@ -18,7 +18,7 @@ ChangeGameDialog::ChangeGameDialog(wxWindow* parent, const CGameListCtrl* const NetPlayDialog::FillWithGameNames(m_game_lbox, *game_list); - wxButton* const ok_btn = new wxButton(this, wxID_OK, _("Change")); + wxButton* const ok_btn = new wxButton(this, wxID_OK, _("Select")); ok_btn->Bind(wxEVT_BUTTON, &ChangeGameDialog::OnPick, this); wxBoxSizer* const szr = new wxBoxSizer(wxVERTICAL); diff --git a/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp b/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp new file mode 100644 index 0000000000..1d0f7a0817 --- /dev/null +++ b/Source/Core/DolphinWX/NetPlay/MD5Dialog.cpp @@ -0,0 +1,100 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include + +#include "Common/StringUtil.h" +#include "DolphinWX/NetPlay/MD5Dialog.h" +#include "DolphinWX/NetPlay/NetWindow.h" + +MD5Dialog::MD5Dialog(wxWindow* parent, NetPlayServer* server, std::vector players, + const std::string& game) + : wxDialog(parent, wxID_ANY, _("MD5 Checksum")), m_parent(parent), m_netplay_server(server) +{ + Bind(wxEVT_CLOSE_WINDOW, &MD5Dialog::OnClose, this); + wxBoxSizer* const main_sizer = new wxBoxSizer(wxVERTICAL); + + main_sizer->Add(new wxStaticText(this, wxID_ANY, _("Computing MD5 Checksum for:") + "\n" + game, + wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL), + 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5); + + for (const Player* player : players) + { + wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer( + wxVERTICAL, this, player->name + " (p" + std::to_string(player->pid) + ")"); + + wxGauge* gauge = new wxGauge(this, wxID_ANY, 100); + m_progress_bars[player->pid] = gauge; + player_szr->Add(gauge, 0, wxEXPAND | wxALL, 5); + + m_result_labels[player->pid] = + new wxStaticText(this, wxID_ANY, _("Computing..."), wxDefaultPosition, wxSize(250, 20), + wxALIGN_CENTRE_HORIZONTAL); + + m_result_labels[player->pid]->SetSize(250, 15); + player_szr->Add(m_result_labels[player->pid], 0, wxALL, 5); + + main_sizer->Add(player_szr, 0, wxEXPAND | wxALL, 5); + } + + m_final_result_label = + new wxStaticText(this, wxID_ANY, + " ", // so it takes space + wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL); + main_sizer->Add(m_final_result_label, 1, wxALL, 5); + + wxButton* close_btn = new wxButton(this, wxID_ANY, _("Close")); + close_btn->Bind(wxEVT_BUTTON, &MD5Dialog::OnCloseBtnPressed, this); + main_sizer->Add(close_btn, 0, wxEXPAND | wxALL, 5); + + SetSizerAndFit(main_sizer); + SetFocus(); + Center(); +} + +void MD5Dialog::SetProgress(int pid, int progress) +{ + if (m_progress_bars[pid] == nullptr) + return; + + m_progress_bars[pid]->SetValue(progress); + m_result_labels[pid]->SetLabel(_("Computing: ") + std::to_string(progress) + "%"); + Update(); +} + +void MD5Dialog::SetResult(int pid, const std::string& result) +{ + if (m_result_labels[pid] == nullptr) + return; + + m_result_labels[pid]->SetLabel(result); + m_hashes.push_back(result); + + if (m_hashes.size() <= 1) + return; + + wxString label = AllHashesMatch() ? _("Hashes match!") : _("Hashes do not match."); + m_final_result_label->SetLabel(label); +} + +bool MD5Dialog::AllHashesMatch() const +{ + return std::adjacent_find(m_hashes.begin(), m_hashes.end(), std::not_equal_to<>()) == + m_hashes.end(); +} + +void MD5Dialog::OnClose(wxCloseEvent& event) +{ + m_netplay_server->AbortMD5(); +} + +void MD5Dialog::OnCloseBtnPressed(wxCommandEvent&) +{ + Close(); +} diff --git a/Source/Core/DolphinWX/NetPlay/MD5Dialog.h b/Source/Core/DolphinWX/NetPlay/MD5Dialog.h new file mode 100644 index 0000000000..82b88714e9 --- /dev/null +++ b/Source/Core/DolphinWX/NetPlay/MD5Dialog.h @@ -0,0 +1,40 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +class NetPlayServer; +class Player; +class wxCloseEvent; +class wxGauge; +class wxStaticText; +class wxWindow; + +class MD5Dialog final : public wxDialog +{ +public: + MD5Dialog(wxWindow* parent, NetPlayServer* server, std::vector players, + const std::string& game); + + void SetProgress(int pid, int progress); + void SetResult(int pid, const std::string& result); + +private: + bool AllHashesMatch() const; + void OnClose(wxCloseEvent& event); + void OnCloseBtnPressed(wxCommandEvent& event); + + wxWindow* m_parent; + wxStaticText* m_final_result_label; + NetPlayServer* m_netplay_server; + std::map m_progress_bars; + std::map m_result_labels; + std::vector m_hashes; +}; diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp index c1c5881db2..62597831ef 100644 --- a/Source/Core/DolphinWX/NetPlay/NetWindow.cpp +++ b/Source/Core/DolphinWX/NetPlay/NetWindow.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include "DolphinWX/NetPlay/NetWindow.h" #include "DolphinWX/NetPlay/PadMapDialog.h" #include "DolphinWX/WxUtils.h" +#include "MD5Dialog.h" NetPlayServer* NetPlayDialog::netplay_server = nullptr; NetPlayClient* NetPlayDialog::netplay_client = nullptr; @@ -120,7 +122,8 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const wxPanel* const panel = new wxPanel(this); - // top crap + wxBoxSizer* const top_szr = new wxBoxSizer(wxHORIZONTAL); + m_game_btn = new wxButton(panel, wxID_ANY, StrToWxStr(m_selected_game).Prepend(_(" Game : ")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT); @@ -129,6 +132,21 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const else m_game_btn->Disable(); + top_szr->Add(m_game_btn, 1, wxALL | wxEXPAND); + + if (m_is_hosting) + { + m_MD5_choice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(150, -1)); + m_MD5_choice->Bind(wxEVT_CHOICE, &NetPlayDialog::OnMD5ComputeRequested, this); + m_MD5_choice->Append(_("MD5 check...")); + m_MD5_choice->Append(_("Curent game")); + m_MD5_choice->Append(_("Other game")); + m_MD5_choice->Append(_("SD card")); + m_MD5_choice->SetSelection(0); + + top_szr->Add(m_MD5_choice, 0, wxALL); + } + // middle crap // chat @@ -232,7 +250,7 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const // main sizer wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); - main_szr->Add(m_game_btn, 0, wxEXPAND | wxALL, 5); + main_szr->Add(top_szr, 0, wxEXPAND | wxALL, 5); main_szr->Add(mid_szr, 1, wxEXPAND | wxLEFT | wxRIGHT, 5); main_szr->Add(bottom_szr, 0, wxEXPAND | wxALL, 5); @@ -471,6 +489,31 @@ void NetPlayDialog::OnThread(wxThreadEvent& event) netplay_client->StopGame(); } break; + case NP_GUI_EVT_DISPLAY_MD5_DIALOG: + { + m_MD5_dialog = new MD5Dialog(this, netplay_server, netplay_client->GetPlayers(), + event.GetString().ToStdString()); + m_MD5_dialog->Show(); + } + break; + case NP_GUI_EVT_MD5_PROGRESS: + { + if (m_MD5_dialog == nullptr || m_MD5_dialog->IsBeingDeleted()) + break; + + std::pair payload = event.GetPayload>(); + m_MD5_dialog->SetProgress(payload.first, payload.second); + } + break; + case NP_GUI_EVT_MD5_RESULT: + { + if (m_MD5_dialog == nullptr || m_MD5_dialog->IsBeingDeleted()) + break; + + std::pair payload = event.GetPayload>(); + m_MD5_dialog->SetResult(payload.first, payload.second); + } + break; } // chat messages @@ -485,10 +528,10 @@ void NetPlayDialog::OnThread(wxThreadEvent& event) void NetPlayDialog::OnChangeGame(wxCommandEvent&) { - ChangeGameDialog cgd(this, m_game_list); - cgd.ShowModal(); + ChangeGameDialog change_game_dialog(this, m_game_list); + change_game_dialog.ShowModal(); - wxString game_name = cgd.GetChosenGameName(); + wxString game_name = change_game_dialog.GetChosenGameName(); if (game_name.empty()) return; @@ -497,6 +540,66 @@ void NetPlayDialog::OnChangeGame(wxCommandEvent&) m_game_btn->SetLabel(game_name.Prepend(_(" Game : "))); } +void NetPlayDialog::OnMD5ComputeRequested(wxCommandEvent&) +{ + MD5Target selection = static_cast(m_MD5_choice->GetSelection()); + std::string file_identifier; + ChangeGameDialog change_game_dialog(this, m_game_list); + + m_MD5_choice->SetSelection(0); + + switch (selection) + { + case MD5Target::CurrentGame: + file_identifier = m_selected_game; + break; + + case MD5Target::OtherGame: + change_game_dialog.ShowModal(); + + file_identifier = WxStrToStr(change_game_dialog.GetChosenGameName()); + if (file_identifier.empty()) + return; + break; + + case MD5Target::SdCard: + file_identifier = "sd.raw"; + break; + + default: + return; + } + + netplay_server->ComputeMD5(file_identifier); +} + +void NetPlayDialog::ShowMD5Dialog(const std::string& file_identifier) +{ + wxThreadEvent evt(wxEVT_THREAD, NP_GUI_EVT_DISPLAY_MD5_DIALOG); + evt.SetString(file_identifier); + GetEventHandler()->AddPendingEvent(evt); +} + +void NetPlayDialog::SetMD5Progress(int pid, int progress) +{ + wxThreadEvent evt(wxEVT_THREAD, NP_GUI_EVT_MD5_PROGRESS); + evt.SetPayload(std::pair(pid, progress)); + GetEventHandler()->AddPendingEvent(evt); +} + +void NetPlayDialog::SetMD5Result(int pid, const std::string& result) +{ + wxThreadEvent evt(wxEVT_THREAD, NP_GUI_EVT_MD5_RESULT); + evt.SetPayload(std::pair(pid, result)); + GetEventHandler()->AddPendingEvent(evt); +} + +void NetPlayDialog::AbortMD5() +{ + if (m_MD5_dialog != nullptr && !m_MD5_dialog->IsBeingDeleted()) + m_MD5_dialog->Destroy(); +} + void NetPlayDialog::OnAssignPads(wxCommandEvent&) { PadMapDialog pmd(this, netplay_server, netplay_client); diff --git a/Source/Core/DolphinWX/NetPlay/NetWindow.h b/Source/Core/DolphinWX/NetPlay/NetWindow.h index 51ff07b06c..6894f69442 100644 --- a/Source/Core/DolphinWX/NetPlay/NetWindow.h +++ b/Source/Core/DolphinWX/NetPlay/NetWindow.h @@ -14,12 +14,13 @@ #include "Core/NetPlayServer.h" class CGameListCtrl; +class MD5Dialog; class wxButton; class wxCheckBox; class wxChoice; class wxListBox; -class wxString; class wxStaticText; +class wxString; class wxTextCtrl; enum @@ -27,6 +28,9 @@ enum NP_GUI_EVT_CHANGE_GAME = 45, NP_GUI_EVT_START_GAME, NP_GUI_EVT_STOP_GAME, + NP_GUI_EVT_DISPLAY_MD5_DIALOG, + NP_GUI_EVT_MD5_PROGRESS, + NP_GUI_EVT_MD5_RESULT, }; enum @@ -34,6 +38,14 @@ enum INITIAL_PAD_BUFFER_SIZE = 5 }; +// IDs are UI-dependent here +enum class MD5Target +{ + CurrentGame = 1, + OtherGame = 2, + SdCard = 3 +}; + class NetPlayDialog : public wxFrame, public NetPlayUI { public: @@ -52,6 +64,11 @@ public: void Update() override; void AppendChat(const std::string& msg) override; + void ShowMD5Dialog(const std::string& file_identifier) override; + void SetMD5Progress(int pid, int progress) override; + void SetMD5Result(int pid, const std::string& result) override; + void AbortMD5() override; + void OnMsgChangeGame(const std::string& filename) override; void OnMsgStartGame() override; void OnMsgStopGame() override; @@ -68,6 +85,7 @@ private: void OnQuit(wxCommandEvent& event); void OnThread(wxThreadEvent& event); void OnChangeGame(wxCommandEvent& event); + void OnMD5ComputeRequested(wxCommandEvent& event); void OnAdjustBuffer(wxCommandEvent& event); void OnAssignPads(wxCommandEvent& event); void OnKick(wxCommandEvent& event); @@ -94,6 +112,8 @@ private: wxStaticText* m_host_label; wxChoice* m_host_type_choice; wxButton* m_host_copy_btn; + wxChoice* m_MD5_choice; + MD5Dialog* m_MD5_dialog = nullptr; bool m_host_copy_btn_is_retry; bool m_is_hosting;