JosJuice 0a15aaaa12 Move DiscIO enums to a new file
At first there weren't many enums in Volume.h, but the number has been
growing, and I'm planning to add one more for regions. To not make
Volume.h too large, and to avoid needing to include Volume.h in code
that doesn't use volume objects, I'm moving the enums to a new file.
I'm also turning them into enum classes while I'm at it.
2016-07-13 17:29:27 +02:00

623 lines
18 KiB
C++

// Copyright 2015 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <sstream>
#include <string>
#include <vector>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/clipbrd.h>
#include <wx/dialog.h>
#include <wx/frame.h>
#include <wx/listbox.h>
#include <wx/msgdlg.h>
#include <wx/notebook.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include "Common/CommonTypes.h"
#include "Common/FifoQueue.h"
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Core/ConfigManager.h"
#include "Core/HW/EXI_Device.h"
#include "Core/NetPlayClient.h"
#include "Core/NetPlayProto.h"
#include "Core/NetPlayServer.h"
#include "DiscIO/Enums.h"
#include "DolphinWX/Frame.h"
#include "DolphinWX/GameListCtrl.h"
#include "DolphinWX/ISOFile.h"
#include "DolphinWX/Main.h"
#include "DolphinWX/NetPlay/ChangeGameDialog.h"
#include "DolphinWX/NetPlay/NetWindow.h"
#include "DolphinWX/NetPlay/PadMapDialog.h"
#include "DolphinWX/WxUtils.h"
NetPlayServer* NetPlayDialog::netplay_server = nullptr;
NetPlayClient* NetPlayDialog::netplay_client = nullptr;
NetPlayDialog* NetPlayDialog::npd = nullptr;
static wxString FailureReasonStringForHostLabel(int reason)
{
switch (reason)
{
case TraversalClient::BadHost:
return _("(Error: Bad host)");
case TraversalClient::VersionTooOld:
return _("(Error: Dolphin too old)");
case TraversalClient::ServerForgotAboutUs:
return _("(Error: Disconnected)");
case TraversalClient::SocketSendError:
return _("(Error: Socket)");
case TraversalClient::ResendTimeout:
return _("(Error: Timeout)");
default:
return _("(Error: Unknown)");
}
}
static std::string BuildGameName(const GameListItem& game)
{
// Lang needs to be consistent
const DiscIO::Language lang = DiscIO::Language::LANGUAGE_ENGLISH;
std::vector<std::string> info;
if (!game.GetUniqueID().empty())
info.push_back(game.GetUniqueID());
if (game.GetRevision() != 0)
{
std::string rev_str = "Revision ";
info.push_back(rev_str + std::to_string((long long)game.GetRevision()));
}
std::string name(game.GetName(lang));
if (name.empty())
name = game.GetName();
int disc_number = game.GetDiscNumber() + 1;
std::string lower_name = name;
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), ::tolower);
if (disc_number > 1 &&
lower_name.find(std::string(wxString::Format("disc %i", disc_number))) == std::string::npos &&
lower_name.find(std::string(wxString::Format("disc%i", disc_number))) == std::string::npos)
{
std::string disc_text = "Disc ";
info.push_back(disc_text + std::to_string(disc_number));
}
if (info.empty())
return name;
std::ostringstream ss;
std::copy(info.begin(), info.end() - 1, std::ostream_iterator<std::string>(ss, ", "));
ss << info.back();
return name + " (" + ss.str() + ")";
}
void NetPlayDialog::FillWithGameNames(wxListBox* game_lbox, const CGameListCtrl& game_list)
{
for (u32 i = 0; auto game = game_list.GetISO(i); ++i)
game_lbox->Append(StrToWxStr(BuildGameName(*game)));
}
NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const game_list,
const std::string& game, const bool is_hosting)
: wxFrame(parent, wxID_ANY, _("Dolphin NetPlay")), m_selected_game(game), m_start_btn(nullptr),
m_host_label(nullptr), m_host_type_choice(nullptr), m_host_copy_btn(nullptr),
m_host_copy_btn_is_retry(false), m_is_hosting(is_hosting), m_game_list(game_list)
{
Bind(wxEVT_THREAD, &NetPlayDialog::OnThread, this);
wxPanel* const panel = new wxPanel(this);
// top crap
m_game_btn = new wxButton(panel, wxID_ANY, StrToWxStr(m_selected_game).Prepend(_(" Game : ")),
wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
if (m_is_hosting)
m_game_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnChangeGame, this);
else
m_game_btn->Disable();
// middle crap
// chat
m_chat_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_READONLY | wxTE_MULTILINE);
m_chat_msg_text = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize(-1, 25), wxTE_PROCESS_ENTER);
m_chat_msg_text->Bind(wxEVT_TEXT_ENTER, &NetPlayDialog::OnChat, this);
m_chat_msg_text->SetMaxLength(2000);
wxButton* const chat_msg_btn =
new wxButton(panel, wxID_ANY, _("Send"), wxDefaultPosition, wxSize(-1, 26));
chat_msg_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnChat, this);
wxBoxSizer* const chat_msg_szr = new wxBoxSizer(wxHORIZONTAL);
chat_msg_szr->Add(m_chat_msg_text, 1);
chat_msg_szr->Add(chat_msg_btn, 0);
wxStaticBoxSizer* const chat_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Chat"));
chat_szr->Add(m_chat_text, 1, wxEXPAND);
chat_szr->Add(chat_msg_szr, 0, wxEXPAND | wxTOP, 5);
m_player_lbox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxSize(256, -1));
wxStaticBoxSizer* const player_szr = new wxStaticBoxSizer(wxVERTICAL, panel, _("Players"));
// player list
if (m_is_hosting && g_TraversalClient)
{
wxBoxSizer* const host_szr = new wxBoxSizer(wxHORIZONTAL);
m_host_type_choice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxSize(76, -1));
m_host_type_choice->Bind(wxEVT_CHOICE, &NetPlayDialog::OnChoice, this);
m_host_type_choice->Append(_("Room ID:"));
host_szr->Add(m_host_type_choice);
m_host_label = new wxStaticText(panel, wxID_ANY, "555.555.555.555:55555", wxDefaultPosition,
wxDefaultSize, wxST_NO_AUTORESIZE | wxALIGN_LEFT);
// Update() should fix this immediately.
m_host_label->SetLabel("");
host_szr->Add(m_host_label, 1, wxLEFT | wxCENTER, 5);
m_host_copy_btn = new wxButton(panel, wxID_ANY, _("Copy"));
m_host_copy_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnCopyIP, this);
m_host_copy_btn->Disable();
host_szr->Add(m_host_copy_btn, 0, wxLEFT | wxCENTER, 5);
player_szr->Add(host_szr, 0, wxEXPAND | wxBOTTOM, 5);
m_host_type_choice->Select(0);
UpdateHostLabel();
}
player_szr->Add(m_player_lbox, 1, wxEXPAND);
if (m_is_hosting)
{
m_player_lbox->Bind(wxEVT_LISTBOX, &NetPlayDialog::OnPlayerSelect, this);
m_kick_btn = new wxButton(panel, wxID_ANY, _("Kick Player"));
m_kick_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnKick, this);
player_szr->Add(m_kick_btn, 0, wxEXPAND | wxTOP, 5);
m_kick_btn->Disable();
m_player_config_btn = new wxButton(panel, wxID_ANY, _("Assign Controller Ports"));
m_player_config_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnAssignPads, this);
player_szr->Add(m_player_config_btn, 0, wxEXPAND | wxTOP, 5);
}
wxBoxSizer* const mid_szr = new wxBoxSizer(wxHORIZONTAL);
mid_szr->Add(chat_szr, 1, wxEXPAND | wxRIGHT, 5);
mid_szr->Add(player_szr, 0, wxEXPAND);
// bottom crap
wxButton* const quit_btn = new wxButton(panel, wxID_ANY, _("Quit Netplay"));
quit_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnQuit, this);
wxBoxSizer* const bottom_szr = new wxBoxSizer(wxHORIZONTAL);
if (is_hosting)
{
m_start_btn = new wxButton(panel, wxID_ANY, _("Start"));
m_start_btn->Bind(wxEVT_BUTTON, &NetPlayDialog::OnStart, this);
bottom_szr->Add(m_start_btn);
bottom_szr->Add(new wxStaticText(panel, wxID_ANY, _("Buffer:")), 0, wxLEFT | wxCENTER, 5);
wxSpinCtrl* const padbuf_spin =
new wxSpinCtrl(panel, wxID_ANY, std::to_string(INITIAL_PAD_BUFFER_SIZE), wxDefaultPosition,
wxSize(64, -1), wxSP_ARROW_KEYS, 0, 200, INITIAL_PAD_BUFFER_SIZE);
padbuf_spin->Bind(wxEVT_SPINCTRL, &NetPlayDialog::OnAdjustBuffer, this);
bottom_szr->AddSpacer(3);
bottom_szr->Add(padbuf_spin, 0, wxCENTER);
bottom_szr->AddSpacer(5);
m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write to memcards/SD"));
bottom_szr->Add(m_memcard_write, 0, wxCENTER);
}
bottom_szr->AddSpacer(5);
m_record_chkbox = new wxCheckBox(panel, wxID_ANY, _("Record inputs"));
bottom_szr->Add(m_record_chkbox, 0, wxCENTER);
bottom_szr->AddStretchSpacer(1);
bottom_szr->Add(quit_btn);
// main sizer
wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL);
main_szr->Add(m_game_btn, 0, wxEXPAND | wxALL, 5);
main_szr->Add(mid_szr, 1, wxEXPAND | wxLEFT | wxRIGHT, 5);
main_szr->Add(bottom_szr, 0, wxEXPAND | wxALL, 5);
panel->SetSizerAndFit(main_szr);
main_szr->SetSizeHints(this);
SetSize(768, 768 - 128);
Center();
}
NetPlayDialog::~NetPlayDialog()
{
if (netplay_client)
{
delete netplay_client;
netplay_client = nullptr;
}
if (netplay_server)
{
delete netplay_server;
netplay_server = nullptr;
}
npd = nullptr;
}
void NetPlayDialog::OnChat(wxCommandEvent&)
{
wxString text = m_chat_msg_text->GetValue();
if (!text.empty())
{
netplay_client->SendChatMessage(WxStrToStr(text));
m_chat_text->AppendText(text.Prepend(" >> ").Append('\n'));
m_chat_msg_text->Clear();
}
}
void NetPlayDialog::GetNetSettings(NetSettings& settings)
{
SConfig& instance = SConfig::GetInstance();
settings.m_CPUthread = instance.bCPUThread;
settings.m_CPUcore = instance.iCPUCore;
settings.m_EnableCheats = instance.bEnableCheats;
settings.m_SelectedLanguage = instance.SelectedLanguage;
settings.m_OverrideGCLanguage = instance.bOverrideGCLanguage;
settings.m_ProgressiveScan = instance.bProgressive;
settings.m_PAL60 = instance.bPAL60;
settings.m_DSPHLE = instance.bDSPHLE;
settings.m_DSPEnableJIT = instance.m_DSPEnableJIT;
settings.m_WriteToMemcard = m_memcard_write->GetValue();
settings.m_OCEnable = instance.m_OCEnable;
settings.m_OCFactor = instance.m_OCFactor;
settings.m_EXIDevice[0] = instance.m_EXIDevice[0];
settings.m_EXIDevice[1] = instance.m_EXIDevice[1];
}
std::string NetPlayDialog::FindGame(const std::string& target_game)
{
// find path for selected game, sloppy..
for (u32 i = 0; auto game = m_game_list->GetISO(i); ++i)
if (target_game == BuildGameName(*game))
return game->GetFileName();
return "";
}
std::string NetPlayDialog::FindCurrentGame()
{
return FindGame(m_selected_game);
}
void NetPlayDialog::OnStart(wxCommandEvent&)
{
bool should_start = true;
if (!netplay_client->DoAllPlayersHaveGame())
{
should_start = wxMessageBox(_("Not all players have the game. Do you really want to start?"),
_("Warning"), wxYES_NO) == wxYES;
}
if (!should_start)
return;
NetSettings settings;
GetNetSettings(settings);
netplay_server->SetNetSettings(settings);
netplay_server->StartGame();
}
void NetPlayDialog::BootGame(const std::string& filename)
{
main_frame->BootGame(filename);
}
void NetPlayDialog::StopGame()
{
main_frame->DoStop();
}
// NetPlayUI methods called from ---NETPLAY--- thread
void NetPlayDialog::Update()
{
wxThreadEvent evt(wxEVT_THREAD, 1);
GetEventHandler()->AddPendingEvent(evt);
}
void NetPlayDialog::AppendChat(const std::string& msg)
{
chat_msgs.Push(msg);
// silly
Update();
}
void NetPlayDialog::OnMsgChangeGame(const std::string& filename)
{
wxThreadEvent* evt = new wxThreadEvent(wxEVT_THREAD, NP_GUI_EVT_CHANGE_GAME);
evt->SetString(StrToWxStr(filename));
GetEventHandler()->QueueEvent(evt);
}
void NetPlayDialog::OnMsgStartGame()
{
wxThreadEvent evt(wxEVT_THREAD, NP_GUI_EVT_START_GAME);
GetEventHandler()->AddPendingEvent(evt);
if (m_is_hosting)
{
m_start_btn->Disable();
m_memcard_write->Disable();
m_game_btn->Disable();
m_player_config_btn->Disable();
}
m_record_chkbox->Disable();
}
void NetPlayDialog::OnMsgStopGame()
{
wxThreadEvent evt(wxEVT_THREAD, NP_GUI_EVT_STOP_GAME);
GetEventHandler()->AddPendingEvent(evt);
if (m_is_hosting)
{
m_start_btn->Enable();
m_memcard_write->Enable();
m_game_btn->Enable();
m_player_config_btn->Enable();
}
m_record_chkbox->Enable();
}
void NetPlayDialog::OnAdjustBuffer(wxCommandEvent& event)
{
const int val = ((wxSpinCtrl*)event.GetEventObject())->GetValue();
netplay_server->AdjustPadBufferSize(val);
std::ostringstream ss;
ss << "< Pad Buffer: " << val << " >";
netplay_client->SendChatMessage(ss.str());
m_chat_text->AppendText(StrToWxStr(ss.str()).Append('\n'));
}
void NetPlayDialog::OnQuit(wxCommandEvent&)
{
Destroy();
}
// update gui
void NetPlayDialog::OnThread(wxThreadEvent& event)
{
if (m_is_hosting && m_host_label && g_TraversalClient)
{
UpdateHostLabel();
}
// player list
m_playerids.clear();
std::string tmps;
netplay_client->GetPlayerList(tmps, m_playerids);
wxString selection;
if (m_player_lbox->GetSelection() != wxNOT_FOUND)
selection = m_player_lbox->GetString(m_player_lbox->GetSelection());
m_player_lbox->Clear();
std::istringstream ss(tmps);
while (std::getline(ss, tmps))
m_player_lbox->Append(StrToWxStr(tmps));
// remove ping from selection string, in case it has changed
selection.erase(selection.rfind('|') + 1);
if (!selection.empty())
{
for (unsigned int i = 0; i < m_player_lbox->GetCount(); ++i)
{
if (selection == m_player_lbox->GetString(i).substr(0, selection.length()))
{
m_player_lbox->SetSelection(i);
break;
}
}
}
// flash window in taskbar when someone joins if window isn't active
static u8 numPlayers = 1;
if (netplay_server != nullptr && numPlayers < m_playerids.size() && !HasFocus())
{
RequestUserAttention();
}
numPlayers = m_playerids.size();
switch (event.GetId())
{
case NP_GUI_EVT_CHANGE_GAME:
// update selected game :/
{
m_selected_game = WxStrToStr(event.GetString());
wxString button_label = event.GetString();
m_game_btn->SetLabel(button_label.Prepend(_(" Game : ")));
}
break;
case NP_GUI_EVT_START_GAME:
// client start game :/
{
std::string game = FindCurrentGame();
if (game.empty())
WxUtils::ShowErrorDialog(_("Game not found!"));
else
netplay_client->StartGame(game);
}
break;
case NP_GUI_EVT_STOP_GAME:
// client stop game
{
netplay_client->StopGame();
}
break;
}
// chat messages
while (chat_msgs.Size())
{
std::string s;
chat_msgs.Pop(s);
// PanicAlert("message: %s", s.c_str());
m_chat_text->AppendText(StrToWxStr(s).Append('\n'));
}
}
void NetPlayDialog::OnChangeGame(wxCommandEvent&)
{
ChangeGameDialog cgd(this, m_game_list);
cgd.ShowModal();
wxString game_name = cgd.GetChosenGameName();
if (game_name.empty())
return;
m_selected_game = WxStrToStr(game_name);
netplay_server->ChangeGame(m_selected_game);
m_game_btn->SetLabel(game_name.Prepend(_(" Game : ")));
}
void NetPlayDialog::OnAssignPads(wxCommandEvent&)
{
PadMapDialog pmd(this, netplay_server, netplay_client);
pmd.ShowModal();
netplay_server->SetPadMapping(pmd.GetModifiedPadMappings());
netplay_server->SetWiimoteMapping(pmd.GetModifiedWiimoteMappings());
}
void NetPlayDialog::OnKick(wxCommandEvent&)
{
wxString selection = m_player_lbox->GetStringSelection();
unsigned long player = 0;
selection.substr(selection.rfind('[') + 1, selection.rfind(']')).ToULong(&player);
netplay_server->KickPlayer((u8)player);
m_player_lbox->SetSelection(wxNOT_FOUND);
wxCommandEvent event;
OnPlayerSelect(event);
}
void NetPlayDialog::OnPlayerSelect(wxCommandEvent&)
{
m_kick_btn->Enable(m_player_lbox->GetSelection() > 0);
}
bool NetPlayDialog::IsRecording()
{
return m_record_chkbox->GetValue();
}
void NetPlayDialog::OnCopyIP(wxCommandEvent&)
{
if (m_host_copy_btn_is_retry)
{
g_TraversalClient->ReconnectToServer();
Update();
}
else
{
if (wxTheClipboard->Open())
{
wxTheClipboard->SetData(new wxTextDataObject(m_host_label->GetLabel()));
wxTheClipboard->Close();
}
}
}
void NetPlayDialog::OnChoice(wxCommandEvent& event)
{
UpdateHostLabel();
}
void NetPlayDialog::UpdateHostLabel()
{
wxString label = _(" (internal IP)");
auto DeLabel = [=](wxString str) {
if (str == _("Localhost"))
return std::string("!local!");
return WxStrToStr(str.Left(str.Len() - label.Len()));
};
auto EnLabel = [=](std::string str) -> wxString {
if (str == "!local!")
return _("Localhost");
return StrToWxStr(str) + label;
};
int sel = m_host_type_choice->GetSelection();
if (sel == 0)
{
// the traversal ID
switch (g_TraversalClient->m_State)
{
case TraversalClient::Connecting:
m_host_label->SetForegroundColour(*wxLIGHT_GREY);
m_host_label->SetLabel("...");
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Disable();
break;
case TraversalClient::Connected:
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(
wxString(g_TraversalClient->m_HostId.data(), g_TraversalClient->m_HostId.size()));
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = false;
break;
case TraversalClient::Failure:
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(FailureReasonStringForHostLabel(g_TraversalClient->m_FailureReason));
m_host_copy_btn->SetLabel(_("Retry"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = true;
break;
}
}
else if (sel != wxNOT_FOUND) // wxNOT_FOUND shouldn't generally happen
{
m_host_label->SetForegroundColour(*wxBLACK);
m_host_label->SetLabel(
netplay_server->GetInterfaceHost(DeLabel(m_host_type_choice->GetString(sel))));
m_host_copy_btn->SetLabel(_("Copy"));
m_host_copy_btn->Enable();
m_host_copy_btn_is_retry = false;
}
auto set = netplay_server->GetInterfaceSet();
for (const std::string& iface : set)
{
wxString wxIface = EnLabel(iface);
if (m_host_type_choice->FindString(wxIface) == wxNOT_FOUND)
m_host_type_choice->Append(wxIface);
}
for (unsigned i = 1, count = m_host_type_choice->GetCount(); i != count; i++)
{
if (set.find(DeLabel(m_host_type_choice->GetString(i))) == set.end())
{
m_host_type_choice->Delete(i);
i--;
count--;
}
}
}