diff options
| author | Sepalani <sepalani@hotmail.fr> | 2026-02-10 00:56:38 +0400 |
|---|---|---|
| committer | Jordan Woyak <jordan.woyak@gmail.com> | 2026-02-15 20:14:15 -0600 |
| commit | 2d9b3c4118d772e57c99a55153843740f13fce17 (patch) | |
| tree | 3216e8d3ecd210030d8c6177781cb20db2077edb /Source | |
| parent | f657b3252149c6e1854aa82a5378a9d3a97f3978 (diff) | |
Debugger/NetworkWidget: Add Triforce support
Diffstat (limited to 'Source')
| -rw-r--r-- | Source/Core/Common/Network.cpp | 15 | ||||
| -rw-r--r-- | Source/Core/Common/Network.h | 1 | ||||
| -rw-r--r-- | Source/Core/Core/HW/DVD/AMMediaboard.cpp | 81 | ||||
| -rw-r--r-- | Source/Core/Core/HW/DVD/AMMediaboard.h | 21 | ||||
| -rw-r--r-- | Source/Core/DolphinQt/Debugger/NetworkWidget.cpp | 127 | ||||
| -rw-r--r-- | Source/Core/DolphinQt/Debugger/NetworkWidget.h | 7 |
6 files changed, 193 insertions, 59 deletions
diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index 9c161f1b26..24289cc04d 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -55,6 +55,21 @@ bool IPv4PortRange::IsMatch(IPv4Port subject) const port_u16 >= first.GetPortValue() && port_u16 <= last.GetPortValue(); } +std::string IPv4PortRange::ToString() const +{ + const std::string ip_range = first.ip_address == last.ip_address ? + Common::IPAddressToString(first.ip_address) : + fmt::format("{}-{}", Common::IPAddressToString(first.ip_address), + Common::IPAddressToString(last.ip_address)); + + if (first.port == 0) + return ip_range; + else if (first.port == last.port) + return fmt::format("{}:{}", ip_range, first.GetPortValue()); + else + return fmt::format("{}:{}-{}", ip_range, first.GetPortValue(), last.GetPortValue()); +} + std::string IPAddressToString(IPAddress ip_address) { return fmt::format("{}", fmt::join(ip_address, ".")); diff --git a/Source/Core/Common/Network.h b/Source/Core/Common/Network.h index 1939d10b2c..5e0da6559e 100644 --- a/Source/Core/Common/Network.h +++ b/Source/Core/Common/Network.h @@ -284,6 +284,7 @@ struct IPv4PortRange IPv4Port last; bool IsMatch(IPv4Port subject) const; + std::string ToString() const; }; std::string IPAddressToString(IPAddress ip_address); diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 4a79a01252..5768203e28 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -16,7 +16,6 @@ #include "Common/FileUtil.h" #include "Common/IOFile.h" #include "Common/Logging/Log.h" -#include "Common/Network.h" #include "Common/ScopeGuard.h" #include "Core/Boot/Boot.h" @@ -216,7 +215,8 @@ static const std::unordered_map<u16, GameType> s_game_map = { // Sockets FDs are required to go from 0 to 63. // Games use the FD as indexes so we have to workaround it. -static SOCKET s_sockets[64]; + +static SOCKET s_sockets[SOCKET_FD_MAX]; // TODO: Verify this. static constexpr u32 FIRST_VALID_FD = 1; @@ -533,51 +533,48 @@ std::optional<ParsedIPOverride> ParseIPOverride(std::string_view str) }; } -struct IPAddressOverride +// Caller should check if it matches first! +Common::IPv4Port IPAddressOverride::ApplyOverride(Common::IPv4Port subject) const { - Common::IPv4PortRange original; - Common::IPv4PortRange replacement; + // This logic could probably be better. + // Ranges of different sizes will be weird in general. - // Caller should check if it matches first! - Common::IPv4Port ApplyOverride(Common::IPv4Port subject) const - { - // This logic could probably be better. - // Ranges of different sizes will be weird in general. + const auto replacement_first_ip_u32 = replacement.first.GetIPAddressValue(); + const auto ip_count = 1u + u64(replacement.last.GetIPAddressValue()) - replacement_first_ip_u32; + const auto result_ip = + u32(replacement_first_ip_u32 + + ((subject.GetIPAddressValue() - original.first.GetIPAddressValue()) % ip_count)); - const auto replacement_first_ip_u32 = replacement.first.GetIPAddressValue(); - const auto ip_count = 1u + u64(replacement.last.GetIPAddressValue()) - replacement_first_ip_u32; - const auto result_ip = - u32(replacement_first_ip_u32 + - ((subject.GetIPAddressValue() - original.first.GetIPAddressValue()) % ip_count)); + subject.ip_address = std::bit_cast<Common::IPAddress>(Common::BigEndianValue{result_ip}); - subject.ip_address = std::bit_cast<Common::IPAddress>(Common::BigEndianValue{result_ip}); + const auto replacement_first_port_u16 = replacement.first.GetPortValue(); + const auto port_count = 1u + u32(replacement.last.GetPortValue()) - replacement_first_port_u16; - const auto replacement_first_port_u16 = replacement.first.GetPortValue(); - const auto port_count = 1u + u32(replacement.last.GetPortValue()) - replacement_first_port_u16; + // If the replacement includes all ports then we don't alter the port. + // This allows "10.0.0.1:80-88=10.0.0.2" to have the expected behavior. + if (port_count != 65536u) + { + const auto result_port_u16 = + u16(replacement_first_port_u16 + + ((subject.GetPortValue() - original.first.GetPortValue()) % port_count)); + subject.port = std::bit_cast<u16>(Common::BigEndianValue{result_port_u16}); + } - // If the replacement includes all ports then we don't alter the port. - // This allows "10.0.0.1:80-88=10.0.0.2" to have the expected behavior. - if (port_count != 65536u) - { - const auto result_port_u16 = - u16(replacement_first_port_u16 + - ((subject.GetPortValue() - original.first.GetPortValue()) % port_count)); - subject.port = std::bit_cast<u16>(Common::BigEndianValue{result_port_u16}); - } + return subject; +} - return subject; - } +Common::IPv4Port IPAddressOverride::ReverseOverride(Common::IPv4Port subject) const +{ + // Low effort implementation.. + return IPAddressOverride{.original = replacement, .replacement = original}.ApplyOverride(subject); +} - Common::IPv4Port ReverseOverride(Common::IPv4Port subject) const - { - // Low effort implementation.. - return IPAddressOverride{.original = replacement, .replacement = original}.ApplyOverride( - subject); - } -}; +std::string IPAddressOverride::ToString() const +{ + return fmt::format("{}={}", original.ToString(), replacement.ToString()); +} -using IPOverrides = std::vector<IPAddressOverride>; -static IPOverrides GetIPOverrides() +IPOverrides GetIPOverrides() { IPOverrides result; @@ -2180,4 +2177,12 @@ void DoState(PointerWrap& p) } } +s32 DebuggerGetSocket(u32 triforce_fd) +{ + if (triforce_fd < std::size(s_sockets)) + return s32(s_sockets[triforce_fd]); + + WARN_LOG_FMT(AMMEDIABOARD, "GC-AM: Bad socket fd used by the debugger: {}", triforce_fd); + return -1; +} } // namespace AMMediaboard diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h index 937d2b9228..0e0a573a4d 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.h +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -10,6 +10,7 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "Common/Network.h" #include "DiscIO/Volume.h" @@ -231,6 +232,8 @@ enum SocketStatusCodes SSC_SUCCESS = 70, }; +static constexpr std::size_t SOCKET_FD_MAX = 64; + void Init(); void FirmwareMap(bool on); void InitDIMM(const DiscIO::Volume& volume); @@ -248,6 +251,24 @@ struct ParsedIPOverride std::string_view replacement; std::string_view description; }; + std::optional<ParsedIPOverride> ParseIPOverride(std::string_view str); +struct IPAddressOverride +{ + Common::IPv4PortRange original; + Common::IPv4PortRange replacement; + + // Caller should check if it matches first! + Common::IPv4Port ApplyOverride(Common::IPv4Port subject) const; + Common::IPv4Port ReverseOverride(Common::IPv4Port subject) const; + std::string ToString() const; +}; + +using IPOverrides = std::vector<IPAddressOverride>; + +IPOverrides GetIPOverrides(); + +s32 DebuggerGetSocket(u32 triforce_fd); + }; // namespace AMMediaboard diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp index d7ca4c8dc2..48b027b7b1 100644 --- a/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp @@ -21,9 +21,13 @@ #include <sys/socket.h> #endif +#include <bit> + #include "Common/FileUtil.h" +#include "Common/Network.h" #include "Core/Config/MainSettings.h" #include "Core/Core.h" +#include "Core/HW/DVD/AMMediaboard.h" #include "Core/IOS/Network/SSL.h" #include "Core/IOS/Network/Socket.h" #include "Core/System.h" @@ -139,6 +143,49 @@ QTableWidgetItem* GetSocketName(s32 host_fd) return new QTableWidgetItem(QStringLiteral("%1->%2").arg(sock_name).arg(peer_name)); } + +QTableWidgetItem* GetSocketRedirections(s32 host_fd, const AMMediaboard::IPOverrides& ip_overrides) +{ + if (host_fd < 0 || ip_overrides.empty()) + return new QTableWidgetItem(); + + sockaddr_in sock_addr; + socklen_t sock_addr_len = sizeof(sockaddr_in); + if (getsockname(host_fd, reinterpret_cast<sockaddr*>(&sock_addr), &sock_addr_len) != 0) + return new QTableWidgetItem(); + const Common::IPv4Port sock_ip_port{std::bit_cast<Common::IPAddress>(sock_addr.sin_addr), + sock_addr.sin_port}; + + sockaddr_in peer_addr; + socklen_t peer_addr_len = sizeof(sockaddr_in); + bool has_peer = + getpeername(host_fd, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len) == 0; + const Common::IPv4Port peer_ip_port = + has_peer ? Common::IPv4Port{std::bit_cast<Common::IPAddress>(peer_addr.sin_addr), + peer_addr.sin_port} : + Common::IPv4Port{}; + + QStringList sock_rules; + QStringList peer_rules; + for (const auto& rule : ip_overrides) + { + if (rule.replacement.IsMatch(sock_ip_port)) + sock_rules << QString::fromStdString(rule.ToString()); + if (!has_peer) + continue; + if (rule.replacement.IsMatch(peer_ip_port)) + peer_rules << QString::fromStdString(rule.ToString()); + } + + if (sock_rules.isEmpty() && peer_rules.isEmpty()) + return new QTableWidgetItem(); + if (peer_rules.isEmpty()) + return new QTableWidgetItem(sock_rules.join(QStringLiteral("\n"))); + return new QTableWidgetItem(QStringLiteral("Sock rules:\n%1\n" + "\nPeer rules:%2") + .arg(sock_rules.join(QStringLiteral("\n"))) + .arg(peer_rules.join(QStringLiteral("\n")))); +} } // namespace NetworkWidget::NetworkWidget(QWidget* parent) : QDockWidget(parent) @@ -254,24 +301,12 @@ void NetworkWidget::ConnectWidgets() }); } -void NetworkWidget::Update() +void NetworkWidget::UpdateWiiSocketTable(Core::System& system) { - if (!isVisible()) - return; - - auto& system = Core::System::GetInstance(); - if (Core::GetState(system) != Core::State::Paused) - { - m_socket_table->setDisabled(true); - m_ssl_table->setDisabled(true); - return; - } - - m_socket_table->setDisabled(false); - m_ssl_table->setDisabled(false); - - // needed because there's a race condition on the IOS instance otherwise - const Core::CPUThreadGuard guard(system); + // Show Wii socket blocking state + m_socket_table->showColumn(4); + // Hide Triforce IP overrides + m_socket_table->hideColumn(6); auto* ios = system.GetIOS(); if (!ios) @@ -281,7 +316,6 @@ void NetworkWidget::Update() if (!socket_manager) return; - m_socket_table->setRowCount(0); for (s32 wii_fd = 0; wii_fd < IOS::HLE::WII_SOCKET_FD_MAX; wii_fd++) { m_socket_table->insertRow(wii_fd); @@ -293,9 +327,7 @@ void NetworkWidget::Update() m_socket_table->setItem(wii_fd, 4, GetSocketBlocking(*socket_manager, wii_fd)); m_socket_table->setItem(wii_fd, 5, GetSocketName(host_fd)); } - m_socket_table->resizeColumnsToContents(); - m_ssl_table->setRowCount(0); for (s32 ssl_id = 0; ssl_id < IOS::HLE::NET_SSL_MAXINSTANCES; ssl_id++) { m_ssl_table->insertRow(ssl_id); @@ -312,6 +344,58 @@ void NetworkWidget::Update() m_ssl_table->setItem(ssl_id, 3, GetSocketState(host_fd)); m_ssl_table->setItem(ssl_id, 4, GetSocketName(host_fd)); } +} + +void NetworkWidget::UpdateTriforceSocketTable() +{ + // No easy way to get socket blocking state on Windows + m_socket_table->hideColumn(4); + // Show active IP overrides + m_socket_table->showColumn(6); + const auto ip_overrides = AMMediaboard::GetIPOverrides(); + for (s32 triforce_fd = 0; triforce_fd < AMMediaboard::SOCKET_FD_MAX; triforce_fd++) + { + m_socket_table->insertRow(triforce_fd); + const s32 host_fd = AMMediaboard::DebuggerGetSocket(triforce_fd); + m_socket_table->setItem(triforce_fd, 0, new QTableWidgetItem(QString::number(triforce_fd))); + m_socket_table->setItem(triforce_fd, 1, GetSocketDomain(host_fd)); + m_socket_table->setItem(triforce_fd, 2, GetSocketType(host_fd)); + m_socket_table->setItem(triforce_fd, 3, GetSocketState(host_fd)); + m_socket_table->setItem(triforce_fd, 4, new QTableWidgetItem(QTableWidget::tr("Unknown"))); + m_socket_table->setItem(triforce_fd, 5, GetSocketName(host_fd)); + m_socket_table->setItem(triforce_fd, 6, GetSocketRedirections(host_fd, ip_overrides)); + } +} + +void NetworkWidget::Update() +{ + if (!isVisible()) + return; + + auto& system = Core::System::GetInstance(); + if (Core::GetState(system) != Core::State::Paused) + { + m_socket_table->setDisabled(true); + m_ssl_table->setDisabled(true); + return; + } + + // needed because there's a race condition on the IOS instance otherwise + const Core::CPUThreadGuard guard(system); + m_socket_table->setDisabled(false); + m_socket_table->setRowCount(0); + m_ssl_table->setRowCount(0); + if (system.IsTriforce()) + { + UpdateTriforceSocketTable(); + } + else if (system.IsWii()) + { + m_ssl_table->setDisabled(false); + UpdateWiiSocketTable(system); + } + m_socket_table->resizeColumnsToContents(); + m_socket_table->resizeRowsToContents(); m_ssl_table->resizeColumnsToContents(); const bool is_pcap = Config::Get(Config::MAIN_NETWORK_DUMP_AS_PCAP); @@ -348,7 +432,8 @@ QGroupBox* NetworkWidget::CreateSocketTableGroup() m_socket_table = new QTableWidget(); // i18n: FD stands for file descriptor (and in this case refers to sockets, not regular files) - QStringList header{tr("FD"), tr("Domain"), tr("Type"), tr("State"), tr("Blocking"), tr("Name")}; + QStringList header{tr("FD"), tr("Domain"), tr("Type"), tr("State"), + tr("Blocking"), tr("Name"), tr("Redirections")}; m_socket_table->setColumnCount(static_cast<int>(header.size())); m_socket_table->setHorizontalHeaderLabels(header); diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.h b/Source/Core/DolphinQt/Debugger/NetworkWidget.h index cfcf50a5c7..c2bb13e1d6 100644 --- a/Source/Core/DolphinQt/Debugger/NetworkWidget.h +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.h @@ -14,6 +14,11 @@ class QShowEvent; class QTableWidget; class QTableWidgetItem; +namespace Core +{ +class System; +} + class NetworkWidget : public QDockWidget { Q_OBJECT @@ -29,6 +34,8 @@ private: void CreateWidgets(); void ConnectWidgets(); + void UpdateWiiSocketTable(Core::System& system); + void UpdateTriforceSocketTable(); void Update(); QGroupBox* CreateSocketTableGroup(); |
