summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-06 20:23:40 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2026-02-15 20:14:15 -0600
commit3e2b7fee46a8fcf623821b47b4fc4d50edeb65a8 (patch)
treefc1ceeca4efc12f7f6d98eedc7d6458aabd64392
parent5545798e00497850531525875bcbb19b6ed8920d (diff)
AMMediaboard: Allow IP address overrides to be configured.
-rw-r--r--Source/Core/Core/Config/MainSettings.cpp27
-rw-r--r--Source/Core/Core/Config/MainSettings.h3
-rw-r--r--Source/Core/Core/HW/DVD/AMMediaboard.cpp152
-rw-r--r--Source/Core/Core/HW/DVD/AMMediaboard.h5
4 files changed, 162 insertions, 25 deletions
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp
index 3d370c8b1e..6edcccf186 100644
--- a/Source/Core/Core/Config/MainSettings.cpp
+++ b/Source/Core/Core/Config/MainSettings.cpp
@@ -6,6 +6,7 @@
#include <sstream>
#include <fmt/format.h>
+#include <fmt/ranges.h>
#include "AudioCommon/AudioCommon.h"
#include "Common/Assert.h"
@@ -27,6 +28,7 @@
#include "Core/HW/HSP/HSP_Device.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/SI/SI_Device.h"
+#include "Core/IOS/Network/Socket.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
#include "Core/USBUtils.h"
@@ -641,6 +643,31 @@ const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUM
Info<s16>{{System::Main, "EmulatedUSBDevices", "LogitechMic3VolumeModifier"}, 0},
Info<s16>{{System::Main, "EmulatedUSBDevices", "LogitechMic4VolumeModifier"}, 0}};
+static std::string GetDefaultTriforceIPOverrides()
+{
+ constexpr std::string_view entries[] = {
+ // CyCraft Connect IP
+ "192.168.11.111=127.0.0.1",
+
+ // Key of Avalon
+ // Note: The server and client can't run on the same system,
+ // but it's replaced with 127.0.0.1 here just for reference.
+ "192.168.13.1=127.0.0.1",
+
+ // NAMCO Camera
+ "192.168.29.104-108=127.0.0.1",
+
+ // All.Net Connect IP
+ "192.168.150.16=127.0.0.1",
+ };
+
+ return fmt::format("{}", fmt::join(entries, ","));
+}
+
+const Info<std::string> MAIN_TRIFORCE_BIND_IP{{System::Main, "Core", "TriforceBindIP"}, "0.0.0.0"};
+const Info<std::string> MAIN_TRIFORCE_IP_OVERRIDES{{System::Main, "Core", "TriforceIPOverrides"},
+ GetDefaultTriforceIPOverrides()};
+
// The reason we need this function is because some memory card code
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
DiscIO::Region ToGameCubeRegion(DiscIO::Region region)
diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h
index 00c11ea6dd..ad28ca9119 100644
--- a/Source/Core/Core/Config/MainSettings.h
+++ b/Source/Core/Core/Config/MainSettings.h
@@ -386,6 +386,9 @@ extern const std::array<Info<std::string>, EMULATED_LOGITECH_MIC_COUNT>
extern const std::array<Info<bool>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_MUTED;
extern const std::array<Info<s16>, EMULATED_LOGITECH_MIC_COUNT> MAIN_LOGITECH_MIC_VOLUME_MODIFIER;
+extern const Info<std::string> MAIN_TRIFORCE_BIND_IP;
+extern const Info<std::string> MAIN_TRIFORCE_IP_OVERRIDES;
+
// GameCube path utility functions
// Replaces NTSC-K with some other region, and doesn't replace non-NTSC-K regions
diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp
index 4225e3a608..0dd60755b7 100644
--- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp
+++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp
@@ -4,6 +4,8 @@
#include "Core/HW/DVD/AMMediaboard.h"
#include <algorithm>
+#include <bit>
+#include <ranges>
#include <string>
#include <unordered_map>
@@ -18,6 +20,7 @@
#include "Core/Boot/Boot.h"
#include "Core/BootManager.h"
+#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HLE/HLE.h"
@@ -521,38 +524,118 @@ static GuestSocket NetDIMMAccept(GuestSocket guest_socket, sockaddr* addr, sockl
return INVALID_GUEST_SOCKET;
}
-static s32 NetDIMMConnect(GuestSocket guest_socket, sockaddr_in* addr, int len)
+std::optional<std::pair<std::string_view, std::string_view>> ParseIPOverride(std::string_view str)
+{
+ // Ignore everything after a space. Future proofing to allow for a comment/description string.
+ const auto ip_pair_str = std::string_view{str.begin(), std::ranges::find(str, ' ')};
+
+ const auto parts = SplitStringIntoArray<2>(ip_pair_str, '=');
+ if (parts.has_value())
+ return std::make_pair((*parts)[0], (*parts)[1]);
+
+ return std::nullopt;
+}
+
+struct IPAddressOverride
{
- // All.Net Connect IP
- if (addr->sin_addr.s_addr == inet_addr("192.168.150.16"))
+ Common::IPv4PortRange match;
+ Common::IPv4PortRange replacement;
+
+ // Caller should check if it matches first!
+ Common::IPv4Port ApplyOverride(Common::IPv4Port subject) const
{
- addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+ 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() - match.first.GetIPAddressValue()) % ip_count));
+
+ Common::IPv4Port result{
+ .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;
+
+ if (port_count == 65536)
+ {
+ // If the replacement includes all ports then don't alter the port.
+ // This allows "1.1.1.1:80=2.2.2.2" to do the obvious thing.
+ // This logic could probably be better.
+ // Ranges of different sizes will be weird in general.
+
+ result.port = subject.port;
+ }
+ else
+ {
+ const auto result_port =
+ u16(replacement_first_port_u16 +
+ ((subject.GetPortValue() - match.first.GetPortValue()) % port_count));
+ result.port = std::bit_cast<u16>(Common::BigEndianValue(result_port));
+ }
+
+ return result;
}
+};
- // CyCraft Connect IP
- if (addr->sin_addr.s_addr == inet_addr("192.168.11.111"))
+using IPOverrides = std::vector<IPAddressOverride>;
+static IPOverrides GetIPOverrides()
+{
+ IPOverrides result;
+
+ const auto ip_overrides_str = Config::Get(Config::MAIN_TRIFORCE_IP_OVERRIDES);
+ for (auto&& ip_pair : ip_overrides_str | std::views::split(','))
{
- addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+ const auto ip_pair_str = std::string_view{ip_pair};
+ const auto parts = ParseIPOverride(ip_pair_str);
+ if (parts.has_value())
+ {
+ const auto match = Common::StringToIPv4PortRange(parts->first);
+ const auto replacement = Common::StringToIPv4PortRange(parts->second);
+
+ if (match.has_value() && replacement.has_value())
+ {
+ result.emplace_back(*match, *replacement);
+ continue;
+ }
+
+ ERROR_LOG_FMT(AMMEDIABOARD, "Bad IP pair string: {}", ip_pair_str);
+ }
}
- // NAMCO Camera ( IPs are: 192.168.29.104-108 )
- if ((addr->sin_addr.s_addr & 0xFFFFFF00) == 0xC0A81D00)
- {
- addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+ return result;
+}
+
+static std::optional<Common::IPv4Port> GetAdjustedIPv4PortFromConfig(in_addr addr, u16 port)
+{
+ Common::IPv4Port subject{
+ .ip_address = std::bit_cast<Common::IPAddress>(addr),
+ .port = port,
+ };
- // BUG: An invalid family value is being used
- addr->sin_family = htons(AF_INET);
+ // TODO: We should parse this elsewhere to avoid repeated string manipulations.
+ for (auto&& override : GetIPOverrides())
+ {
+ if (override.match.IsMatch(subject))
+ return override.ApplyOverride(subject);
}
- // Key of Avalon Client
- if (addr->sin_addr.s_addr == inet_addr("192.168.13.1"))
+ return std::nullopt;
+}
+
+static s32 NetDIMMConnect(GuestSocket guest_socket, sockaddr_in* addr, int len)
+{
+ INFO_LOG_FMT(AMMEDIABOARD, "NetDIMMConnect: {}:{}", inet_ntoa(addr->sin_addr),
+ ntohs(addr->sin_port));
+
+ const auto adjusted_ipv4port = GetAdjustedIPv4PortFromConfig(addr->sin_addr, addr->sin_port);
+ if (adjusted_ipv4port.has_value())
{
- // Unlike the other addresses, this one isn't converted to loopback
- // because the server and client can't run on the same system.
- //
- // NOTE: Due to lack of touch-screen support, it's not in a playable state.
- // TODO: Make the IP configurable.
- addr->sin_addr.s_addr = inet_addr("10.0.0.45");
+ addr->sin_addr = std::bit_cast<in_addr>(adjusted_ipv4port->ip_address);
+ addr->sin_port = adjusted_ipv4port->port;
+ INFO_LOG_FMT(AMMEDIABOARD, "NetDIMMConnect: Overriding to: {}:{}",
+ Common::IPAddressToString(adjusted_ipv4port->ip_address),
+ ntohs(adjusted_ipv4port->port));
}
addr->sin_family = Common::swap16(addr->sin_family);
@@ -1219,11 +1302,30 @@ u32 ExecuteCommand(std::array<u32, 3>& dicmd_buf, u32* diimm_buf, u32 address, u
addr.sin_family = Common::swap16(addr.sin_family);
// Triforce titles typically rely on hardcoded IP addresses.
- // This behavior has been modified to bind to the wildcard address instead.
- //
- // addr.sin_addr.s_addr = htonl(addr.sin_addr.s_addr);
+ // Our config allows this to be overriden.
+
+ INFO_LOG_FMT(AMMEDIABOARD, "AMMBCommand::Bind: {}:{}", inet_ntoa(addr.sin_addr),
+ ntohs(addr.sin_port));
- addr.sin_addr.s_addr = INADDR_ANY;
+ // Apply "BindIP" if it's valid.
+ const auto override_bind_ip = inet_addr(Config::Get(Config::MAIN_TRIFORCE_BIND_IP).c_str());
+ if (override_bind_ip != INADDR_NONE)
+ {
+ addr.sin_addr.s_addr = override_bind_ip;
+ INFO_LOG_FMT(AMMEDIABOARD, "AMMBCommand::Bind: Overriding IP to: {}",
+ Common::IPAddressToString(std::bit_cast<Common::IPAddress>(addr.sin_addr)));
+ }
+
+ // Apply "IPOverrides" in case config wants ports adjusted.
+ const auto adjusted_ipv4port = GetAdjustedIPv4PortFromConfig(addr.sin_addr, addr.sin_port);
+ if (adjusted_ipv4port.has_value())
+ {
+ addr.sin_addr = std::bit_cast<in_addr>(adjusted_ipv4port->ip_address);
+ addr.sin_port = adjusted_ipv4port->port;
+ INFO_LOG_FMT(AMMEDIABOARD, "AMMBCommand::Bind: Overriding to: {}:{}",
+ Common::IPAddressToString(std::bit_cast<Common::IPAddress>(addr.sin_addr)),
+ ntohs(addr.sin_port));
+ }
const int ret = bind(fd, reinterpret_cast<const sockaddr*>(&addr), len);
const int err = WSAGetLastError();
diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h
index d57a800107..cc0bcf9b89 100644
--- a/Source/Core/Core/HW/DVD/AMMediaboard.h
+++ b/Source/Core/Core/HW/DVD/AMMediaboard.h
@@ -4,6 +4,9 @@
#pragma once
#include <array>
+#include <optional>
+#include <string_view>
+#include <utility>
#include "Common/CommonTypes.h"
@@ -234,4 +237,6 @@ u32 GetMediaType();
bool GetTestMenu();
void Shutdown();
+std::optional<std::pair<std::string_view, std::string_view>> ParseIPOverride(std::string_view str);
+
}; // namespace AMMediaboard