From 5e33cd48da132e0b2b4e459ef83388233d4e3a2f Mon Sep 17 00:00:00 2001 From: Sepalani Date: Sun, 19 Apr 2020 23:30:50 +0400 Subject: [PATCH] Debugger: Add a Network widget Display socket table, SSL context and options --- Source/Core/Core/IOS/Network/SSL.cpp | 24 +- Source/Core/Core/IOS/Network/SSL.h | 11 +- Source/Core/Core/IOS/Network/Socket.cpp | 4 +- Source/Core/Core/IOS/Network/Socket.h | 2 + Source/Core/DolphinQt/CMakeLists.txt | 2 + .../Core/DolphinQt/Debugger/NetworkWidget.cpp | 301 ++++++++++++++++++ .../Core/DolphinQt/Debugger/NetworkWidget.h | 46 +++ Source/Core/DolphinQt/DolphinQt.vcxproj | 3 + Source/Core/DolphinQt/MainWindow.cpp | 4 + Source/Core/DolphinQt/MainWindow.h | 2 + Source/Core/DolphinQt/MenuBar.cpp | 9 + Source/Core/DolphinQt/MenuBar.h | 1 + Source/Core/DolphinQt/Settings.cpp | 14 + Source/Core/DolphinQt/Settings.h | 3 + 14 files changed, 406 insertions(+), 20 deletions(-) create mode 100644 Source/Core/DolphinQt/Debugger/NetworkWidget.cpp create mode 100644 Source/Core/DolphinQt/Debugger/NetworkWidget.h diff --git a/Source/Core/Core/IOS/Network/SSL.cpp b/Source/Core/Core/IOS/Network/SSL.cpp index 4cdc90b662..08bc4c244c 100644 --- a/Source/Core/Core/IOS/Network/SSL.cpp +++ b/Source/Core/Core/IOS/Network/SSL.cpp @@ -23,7 +23,7 @@ namespace IOS::HLE::Device { -WII_SSL NetSSL::_SSL[NET_SSL_MAXINSTANCES]; +WII_SSL NetSSL::_SSL[IOS::HLE::NET_SSL_MAXINSTANCES]; static constexpr mbedtls_x509_crt_profile mbedtls_x509_crt_profile_wii = { /* Hashes from SHA-1 and above */ @@ -247,7 +247,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_SHUTDOWN: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; @@ -292,7 +292,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3); int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; int ret = @@ -333,7 +333,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3); int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; const std::string cert_base_path = File::GetUserPath(D_SESSION_WIIROOT_IDX); @@ -380,7 +380,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3); int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; mbedtls_x509_crt_free(&ssl->clicert); @@ -399,7 +399,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_SETBUILTINROOTCA: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; const std::string cert_base_path = File::GetUserPath(D_SESSION_WIIROOT_IDX); @@ -437,7 +437,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_CONNECT: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WII_SSL* ssl = &_SSL[sslID]; mbedtls_ssl_setup(&ssl->ctx, &ssl->config); @@ -464,7 +464,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_DOHANDSHAKE: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WiiSockMan& sm = WiiSockMan::GetInstance(); sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_DOHANDSHAKE); @@ -479,7 +479,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_WRITE: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WiiSockMan& sm = WiiSockMan::GetInstance(); sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_WRITE); @@ -503,7 +503,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) { int ret = 0; int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WiiSockMan& sm = WiiSockMan::GetInstance(); sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_READ); @@ -526,7 +526,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) case IOCTLV_NET_SSL_SETROOTCADEFAULT: { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WriteReturnValue(SSL_OK, BufferIn); } @@ -554,7 +554,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request) BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3); int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IsSSLIDValid(sslID)) { WriteReturnValue(SSL_OK, BufferIn); } diff --git a/Source/Core/Core/IOS/Network/SSL.h b/Source/Core/Core/IOS/Network/SSL.h index e8cd273f81..d1a29f253e 100644 --- a/Source/Core/Core/IOS/Network/SSL.h +++ b/Source/Core/Core/IOS/Network/SSL.h @@ -24,11 +24,7 @@ namespace IOS::HLE { -#define NET_SSL_MAXINSTANCES 4 - -// TODO: remove this macro. -#define SSLID_VALID(x) \ - (x >= 0 && x < NET_SSL_MAXINSTANCES && ::IOS::HLE::Device::NetSSL::_SSL[x].active) +constexpr int NET_SSL_MAXINSTANCES = 4; enum ssl_err_t : s32 { @@ -103,5 +99,10 @@ public: private: bool m_cert_error_shown = false; }; + +constexpr bool IsSSLIDValid(int id) +{ + return (id >= 0 && id < NET_SSL_MAXINSTANCES && IOS::HLE::Device::NetSSL::_SSL[id].active); +} } // namespace Device } // namespace IOS::HLE diff --git a/Source/Core/Core/IOS/Network/Socket.cpp b/Source/Core/Core/IOS/Network/Socket.cpp index a2b081bd5d..fa39308514 100644 --- a/Source/Core/Core/IOS/Network/Socket.cpp +++ b/Source/Core/Core/IOS/Network/Socket.cpp @@ -33,8 +33,6 @@ namespace IOS::HLE { -constexpr int WII_SOCKET_FD_MAX = 24; - char* WiiSockMan::DecodeError(s32 ErrorCode) { #ifdef _WIN32 @@ -325,7 +323,7 @@ void WiiSocket::Update(bool read, bool write, bool except) if (it->is_ssl) { int sslID = Memory::Read_U32(BufferOut) - 1; - if (SSLID_VALID(sslID)) + if (IOS::HLE::Device::IsSSLIDValid(sslID)) { switch (it->ssl_type) { diff --git a/Source/Core/Core/IOS/Network/Socket.h b/Source/Core/Core/IOS/Network/Socket.h index 0109366fc5..615560f305 100644 --- a/Source/Core/Core/IOS/Network/Socket.h +++ b/Source/Core/Core/IOS/Network/Socket.h @@ -57,6 +57,8 @@ typedef struct pollfd pollfd_t; namespace IOS::HLE { +constexpr int WII_SOCKET_FD_MAX = 24; + enum { SO_MSG_OOB = 0x01, diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 6a124cd03b..4589d4881f 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -176,6 +176,8 @@ add_executable(dolphin-emu Debugger/MemoryViewWidget.h Debugger/MemoryWidget.cpp Debugger/MemoryWidget.h + Debugger/NetworkWidget.cpp + Debugger/NetworkWidget.h Debugger/NewBreakpointDialog.cpp Debugger/NewBreakpointDialog.h Debugger/PatchInstructionDialog.cpp diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp new file mode 100644 index 0000000000..8adee91ca2 --- /dev/null +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp @@ -0,0 +1,301 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt/Debugger/NetworkWidget.h" + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "Core/ConfigManager.h" +#include "Core/IOS/Network/SSL.h" +#include "Core/IOS/Network/Socket.h" +#include "DolphinQt/Host.h" +#include "DolphinQt/Settings.h" + +namespace +{ +QTableWidgetItem* GetSocketDomain(s32 host_fd) +{ + if (host_fd < 0) + return new QTableWidgetItem(); + + sockaddr sa; + socklen_t sa_len = sizeof(sa); + const int ret = getsockname(host_fd, &sa, &sa_len); + if (ret != 0) + return new QTableWidgetItem(QTableWidget::tr("Unknown")); + + switch (sa.sa_family) + { + case 2: + return new QTableWidgetItem(QLatin1Literal("AF_INET")); + case 23: + return new QTableWidgetItem(QLatin1Literal("AF_INET6")); + default: + return new QTableWidgetItem(QString::number(sa.sa_family)); + } +} + +QTableWidgetItem* GetSocketType(s32 host_fd) +{ + if (host_fd < 0) + return new QTableWidgetItem(); + + int so_type; + socklen_t opt_len = sizeof(so_type); + const int ret = + getsockopt(host_fd, SOL_SOCKET, SO_TYPE, reinterpret_cast(&so_type), &opt_len); + if (ret != 0) + return new QTableWidgetItem(QTableWidget::tr("Unknown")); + + switch (so_type) + { + case 1: + return new QTableWidgetItem(QLatin1Literal("SOCK_STREAM")); + case 2: + return new QTableWidgetItem(QLatin1Literal("SOCK_DGRAM")); + default: + return new QTableWidgetItem(QString::number(so_type)); + } +} + +QTableWidgetItem* GetSocketState(s32 host_fd) +{ + if (host_fd < 0) + return new QTableWidgetItem(); + + sockaddr_in peer_addr; + socklen_t peer_addr_len = sizeof(sockaddr_in); + if (getpeername(host_fd, reinterpret_cast(&peer_addr), &peer_addr_len) == 0) + return new QTableWidgetItem(QTableWidget::tr("Connected")); + + int so_accept = 0; + socklen_t opt_len = sizeof(so_accept); + const int ret = + getsockopt(host_fd, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast(&so_accept), &opt_len); + if (ret == 0 && so_accept > 0) + return new QTableWidgetItem(QTableWidget::tr("Listening")); + return new QTableWidgetItem(QTableWidget::tr("Unbound")); +} + +QTableWidgetItem* GetSocketName(s32 host_fd) +{ + if (host_fd < 0) + return new QTableWidgetItem(); + + sockaddr_in sock_addr; + socklen_t sock_addr_len = sizeof(sockaddr_in); + if (getsockname(host_fd, reinterpret_cast(&sock_addr), &sock_addr_len) != 0) + return new QTableWidgetItem(QTableWidget::tr("Unknown")); + + const QString sock_name = QStringLiteral("%1:%2") + .arg(QString::fromLatin1(inet_ntoa(sock_addr.sin_addr))) + .arg(ntohs(sock_addr.sin_port)); + + sockaddr_in peer_addr; + socklen_t peer_addr_len = sizeof(sockaddr_in); + if (getpeername(host_fd, reinterpret_cast(&peer_addr), &peer_addr_len) != 0) + return new QTableWidgetItem(sock_name); + + const QString peer_name = QStringLiteral("%1:%2") + .arg(QString::fromLatin1(inet_ntoa(peer_addr.sin_addr))) + .arg(ntohs(peer_addr.sin_port)); + return new QTableWidgetItem(QStringLiteral("%1->%2").arg(sock_name).arg(peer_name)); +} +} // namespace + +NetworkWidget::NetworkWidget(QWidget* parent) : QDockWidget(parent) +{ + setWindowTitle(tr("Network")); + setObjectName(QStringLiteral("network")); + + setHidden(!Settings::Instance().IsNetworkVisible() || !Settings::Instance().IsDebugModeEnabled()); + + setAllowedAreas(Qt::AllDockWidgetAreas); + + CreateWidgets(); + + auto& settings = Settings::GetQSettings(); + + restoreGeometry(settings.value(QStringLiteral("networkwidget/geometry")).toByteArray()); + // macOS: setHidden() needs to be evaluated before setFloating() for proper window presentation + // according to Settings + setFloating(settings.value(QStringLiteral("networkwidget/floating")).toBool()); + + ConnectWidgets(); + + connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &NetworkWidget::Update); + + connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged, + [this](bool visible) { setHidden(!visible); }); + + connect(&Settings::Instance(), &Settings::DebugModeToggled, [this](bool enabled) { + setHidden(!enabled || !Settings::Instance().IsNetworkVisible()); + }); +} + +NetworkWidget::~NetworkWidget() +{ + auto& settings = Settings::GetQSettings(); + + settings.setValue(QStringLiteral("networkwidget/geometry"), saveGeometry()); + settings.setValue(QStringLiteral("networkwidget/floating"), isFloating()); +} + +void NetworkWidget::closeEvent(QCloseEvent*) +{ + Settings::Instance().SetNetworkVisible(false); +} + +void NetworkWidget::showEvent(QShowEvent* event) +{ + Update(); +} + +void NetworkWidget::CreateWidgets() +{ + auto* widget = new QWidget; + auto* layout = new QVBoxLayout; + widget->setLayout(layout); + layout->addWidget(CreateSocketTableGroup()); + layout->addWidget(CreateSSLContextGroup()); + layout->addWidget(CreateSSLOptionsGroup()); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding)); + setWidget(widget); + + Update(); +} + +void NetworkWidget::ConnectWidgets() +{ + connect(m_dump_ssl_read_checkbox, &QCheckBox::stateChanged, + [](int state) { SConfig::GetInstance().m_SSLDumpRead = state == Qt::Checked; }); + connect(m_dump_ssl_write_checkbox, &QCheckBox::stateChanged, + [](int state) { SConfig::GetInstance().m_SSLDumpWrite = state == Qt::Checked; }); + connect(m_dump_root_ca_checkbox, &QCheckBox::stateChanged, + [](int state) { SConfig::GetInstance().m_SSLDumpRootCA = state == Qt::Checked; }); + connect(m_dump_peer_cert_checkbox, &QCheckBox::stateChanged, + [](int state) { SConfig::GetInstance().m_SSLDumpPeerCert = state == Qt::Checked; }); + connect(m_verify_certificates_checkbox, &QCheckBox::stateChanged, + [](int state) { SConfig::GetInstance().m_SSLVerifyCert = state == Qt::Checked; }); +} + +void NetworkWidget::Update() +{ + m_socket_table->setRowCount(0); + for (u32 wii_fd = 0; wii_fd < IOS::HLE::WII_SOCKET_FD_MAX; wii_fd++) + { + m_socket_table->insertRow(wii_fd); + const s32 host_fd = IOS::HLE::WiiSockMan::GetInstance().GetHostSocket(wii_fd); + m_socket_table->setItem(wii_fd, 0, new QTableWidgetItem(QString::number(wii_fd))); + m_socket_table->setItem(wii_fd, 1, GetSocketDomain(host_fd)); + m_socket_table->setItem(wii_fd, 2, GetSocketType(host_fd)); + m_socket_table->setItem(wii_fd, 3, GetSocketState(host_fd)); + m_socket_table->setItem(wii_fd, 4, GetSocketName(host_fd)); + } + m_socket_table->resizeColumnsToContents(); + + m_ssl_table->setRowCount(0); + for (u32 ssl_id = 0; ssl_id < IOS::HLE::NET_SSL_MAXINSTANCES; ssl_id++) + { + m_ssl_table->insertRow(ssl_id); + s32 host_fd = -1; + if (IOS::HLE::Device::IsSSLIDValid(ssl_id) && + IOS::HLE::Device::NetSSL::_SSL[ssl_id].ctx.p_bio != nullptr) + { + host_fd = + static_cast(IOS::HLE::Device::NetSSL::_SSL[ssl_id].ctx.p_bio)->fd; + } + m_ssl_table->setItem(ssl_id, 0, new QTableWidgetItem(QString::number(ssl_id))); + m_ssl_table->setItem(ssl_id, 1, GetSocketDomain(host_fd)); + m_ssl_table->setItem(ssl_id, 2, GetSocketType(host_fd)); + m_ssl_table->setItem(ssl_id, 3, GetSocketState(host_fd)); + m_ssl_table->setItem(ssl_id, 4, GetSocketName(host_fd)); + } + m_ssl_table->resizeColumnsToContents(); + + const auto& config = SConfig::GetInstance(); + m_dump_ssl_read_checkbox->setChecked(config.m_SSLDumpRead); + m_dump_ssl_write_checkbox->setChecked(config.m_SSLDumpWrite); + m_dump_root_ca_checkbox->setChecked(config.m_SSLDumpRootCA); + m_dump_peer_cert_checkbox->setChecked(config.m_SSLDumpPeerCert); + m_verify_certificates_checkbox->setChecked(config.m_SSLVerifyCert); +} + +QGroupBox* NetworkWidget::CreateSocketTableGroup() +{ + QGroupBox* socket_table_group = new QGroupBox(tr("Socket table")); + QGridLayout* socket_table_layout = new QGridLayout; + socket_table_group->setLayout(socket_table_layout); + + m_socket_table = new QTableWidget(); + QStringList header{tr("FD"), tr("Domain"), tr("Type"), tr("State"), tr("Name")}; + m_socket_table->setColumnCount(header.size()); + + m_socket_table->setHorizontalHeaderLabels(header); + m_socket_table->setTabKeyNavigation(false); + m_socket_table->verticalHeader()->setVisible(false); + m_socket_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_socket_table->setSelectionMode(QAbstractItemView::NoSelection); + m_socket_table->setWordWrap(false); + + socket_table_layout->addWidget(m_socket_table, 0, 0); + socket_table_layout->setSpacing(1); + return socket_table_group; +} + +QGroupBox* NetworkWidget::CreateSSLContextGroup() +{ + QGroupBox* ssl_context_group = new QGroupBox(tr("SSL context")); + QGridLayout* ssl_context_layout = new QGridLayout; + ssl_context_group->setLayout(ssl_context_layout); + + m_ssl_table = new QTableWidget(); + QStringList header{tr("ID"), tr("Domain"), tr("Type"), tr("State"), tr("Name")}; + m_ssl_table->setColumnCount(header.size()); + + m_ssl_table->setHorizontalHeaderLabels(header); + m_ssl_table->setTabKeyNavigation(false); + m_ssl_table->verticalHeader()->setVisible(false); + m_ssl_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_ssl_table->setSelectionMode(QAbstractItemView::NoSelection); + m_ssl_table->setWordWrap(false); + + ssl_context_layout->addWidget(m_ssl_table, 0, 0); + ssl_context_layout->setSpacing(1); + return ssl_context_group; +} + +QGroupBox* NetworkWidget::CreateSSLOptionsGroup() +{ + QGroupBox* ssl_options_group = new QGroupBox(tr("SSL options")); + QGridLayout* ssl_options_layout = new QGridLayout; + ssl_options_group->setLayout(ssl_options_layout); + + m_dump_ssl_read_checkbox = new QCheckBox(tr("Dump SSL read")); + m_dump_ssl_write_checkbox = new QCheckBox(tr("Dump SSL write")); + m_dump_root_ca_checkbox = new QCheckBox(tr("Dump root CA")); + m_dump_peer_cert_checkbox = new QCheckBox(tr("Dump peer certificates")); + m_verify_certificates_checkbox = new QCheckBox(tr("Verify certificates")); + + ssl_options_layout->addWidget(m_dump_ssl_read_checkbox, 0, 0); + ssl_options_layout->addWidget(m_dump_ssl_write_checkbox, 1, 0); + ssl_options_layout->addWidget(m_verify_certificates_checkbox, 2, 0); + ssl_options_layout->addWidget(m_dump_root_ca_checkbox, 0, 1); + ssl_options_layout->addWidget(m_dump_peer_cert_checkbox, 1, 1); + + ssl_options_layout->setSpacing(1); + return ssl_options_group; +} diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.h b/Source/Core/DolphinQt/Debugger/NetworkWidget.h new file mode 100644 index 0000000000..735c398dc9 --- /dev/null +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.h @@ -0,0 +1,46 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/CommonTypes.h" + +class QCheckBox; +class QCloseEvent; +class QGroupBox; +class QShowEvent; +class QTableWidget; +class QTableWidgetItem; + +class NetworkWidget : public QDockWidget +{ + Q_OBJECT +public: + explicit NetworkWidget(QWidget* parent = nullptr); + ~NetworkWidget(); + +protected: + void closeEvent(QCloseEvent*) override; + void showEvent(QShowEvent* event) override; + +private: + void CreateWidgets(); + void ConnectWidgets(); + + void Update(); + + QGroupBox* CreateSocketTableGroup(); + QGroupBox* CreateSSLContextGroup(); + QGroupBox* CreateSSLOptionsGroup(); + + QTableWidget* m_socket_table; + QTableWidget* m_ssl_table; + QCheckBox* m_dump_ssl_read_checkbox; + QCheckBox* m_dump_ssl_write_checkbox; + QCheckBox* m_dump_root_ca_checkbox; + QCheckBox* m_dump_peer_cert_checkbox; + QCheckBox* m_verify_certificates_checkbox; +}; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 1763c06300..c6dccf12c1 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -147,6 +147,7 @@ + @@ -275,6 +276,7 @@ + @@ -377,6 +379,7 @@ + diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 89a19b42ba..270f4394c6 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -71,6 +71,7 @@ #include "DolphinQt/Debugger/CodeWidget.h" #include "DolphinQt/Debugger/JITWidget.h" #include "DolphinQt/Debugger/MemoryWidget.h" +#include "DolphinQt/Debugger/NetworkWidget.h" #include "DolphinQt/Debugger/RegisterWidget.h" #include "DolphinQt/Debugger/WatchWidget.h" #include "DolphinQt/DiscordHandler.h" @@ -389,6 +390,7 @@ void MainWindow::CreateComponents() m_log_widget = new LogWidget(this); m_log_config_widget = new LogConfigWidget(this); m_memory_widget = new MemoryWidget(this); + m_network_widget = new NetworkWidget(this); m_register_widget = new RegisterWidget(this); m_watch_widget = new WatchWidget(this); m_breakpoint_widget = new BreakpointWidget(this); @@ -643,6 +645,7 @@ void MainWindow::ConnectStack() addDockWidget(Qt::LeftDockWidgetArea, m_watch_widget); addDockWidget(Qt::LeftDockWidgetArea, m_breakpoint_widget); addDockWidget(Qt::LeftDockWidgetArea, m_memory_widget); + addDockWidget(Qt::LeftDockWidgetArea, m_network_widget); addDockWidget(Qt::LeftDockWidgetArea, m_jit_widget); tabifyDockWidget(m_log_widget, m_log_config_widget); @@ -651,6 +654,7 @@ void MainWindow::ConnectStack() tabifyDockWidget(m_log_widget, m_watch_widget); tabifyDockWidget(m_log_widget, m_breakpoint_widget); tabifyDockWidget(m_log_widget, m_memory_widget); + tabifyDockWidget(m_log_widget, m_network_widget); tabifyDockWidget(m_log_widget, m_jit_widget); } diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h index 50fea52a24..d84edb5af7 100644 --- a/Source/Core/DolphinQt/MainWindow.h +++ b/Source/Core/DolphinQt/MainWindow.h @@ -34,6 +34,7 @@ class MemoryWidget; class MenuBar; class NetPlayDialog; class NetPlaySetupDialog; +class NetworkWidget; class RegisterWidget; class RenderWidget; class SearchBar; @@ -225,6 +226,7 @@ private: LogWidget* m_log_widget; LogConfigWidget* m_log_config_widget; MemoryWidget* m_memory_widget; + NetworkWidget* m_network_widget; RegisterWidget* m_register_widget; WatchWidget* m_watch_widget; CheatsManager* m_cheats_manager; diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 14b433b77b..75b01caba0 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -169,6 +169,7 @@ void MenuBar::OnDebugModeToggled(bool enabled) m_show_watch->setVisible(enabled); m_show_breakpoints->setVisible(enabled); m_show_memory->setVisible(enabled); + m_show_network->setVisible(enabled); m_show_jit->setVisible(enabled); if (enabled) @@ -469,6 +470,14 @@ void MenuBar::AddViewMenu() connect(&Settings::Instance(), &Settings::MemoryVisibilityChanged, m_show_memory, &QAction::setChecked); + m_show_network = view_menu->addAction(tr("&Network")); + m_show_network->setCheckable(true); + m_show_network->setChecked(Settings::Instance().IsNetworkVisible()); + + connect(m_show_network, &QAction::toggled, &Settings::Instance(), &Settings::SetNetworkVisible); + connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged, m_show_network, + &QAction::setChecked); + m_show_jit = view_menu->addAction(tr("&JIT")); m_show_jit->setCheckable(true); m_show_jit->setChecked(Settings::Instance().IsJITVisible()); diff --git a/Source/Core/DolphinQt/MenuBar.h b/Source/Core/DolphinQt/MenuBar.h index 9875888cbc..3bdf6debcb 100644 --- a/Source/Core/DolphinQt/MenuBar.h +++ b/Source/Core/DolphinQt/MenuBar.h @@ -235,6 +235,7 @@ private: QAction* m_show_watch; QAction* m_show_breakpoints; QAction* m_show_memory; + QAction* m_show_network; QAction* m_show_jit; QMenu* m_cols_menu; diff --git a/Source/Core/DolphinQt/Settings.cpp b/Source/Core/DolphinQt/Settings.cpp index ea7f014e94..c1a93939d9 100644 --- a/Source/Core/DolphinQt/Settings.cpp +++ b/Source/Core/DolphinQt/Settings.cpp @@ -422,6 +422,20 @@ bool Settings::IsMemoryVisible() const return QSettings().value(QStringLiteral("debugger/showmemory")).toBool(); } +void Settings::SetNetworkVisible(bool enabled) +{ + if (IsNetworkVisible() == enabled) + return; + + GetQSettings().setValue(QStringLiteral("debugger/shownetwork"), enabled); + emit NetworkVisibilityChanged(enabled); +} + +bool Settings::IsNetworkVisible() const +{ + return GetQSettings().value(QStringLiteral("debugger/shownetwork")).toBool(); +} + void Settings::SetJITVisible(bool enabled) { if (IsJITVisible() == enabled) diff --git a/Source/Core/DolphinQt/Settings.h b/Source/Core/DolphinQt/Settings.h index 01712f5858..7aa169dee0 100644 --- a/Source/Core/DolphinQt/Settings.h +++ b/Source/Core/DolphinQt/Settings.h @@ -126,6 +126,8 @@ public: bool IsCodeVisible() const; void SetMemoryVisible(bool enabled); bool IsMemoryVisible() const; + void SetNetworkVisible(bool enabled); + bool IsNetworkVisible() const; void SetJITVisible(bool enabled); bool IsJITVisible() const; QFont GetDebugFont() const; @@ -168,6 +170,7 @@ signals: void BreakpointsVisibilityChanged(bool visible); void CodeVisibilityChanged(bool visible); void MemoryVisibilityChanged(bool visible); + void NetworkVisibilityChanged(bool visible); void JITVisibilityChanged(bool visible); void DebugModeToggled(bool enabled); void DebugFontChanged(QFont font);