summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-06 20:24:18 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2026-02-15 20:14:15 -0600
commit7ab267269880b2007776b6b5e0090538bd577740 (patch)
treec6cd55a2292d918bbb112a0ecf4561dbf16390a6
parent3e2b7fee46a8fcf623821b47b4fc4d50edeb65a8 (diff)
DolphinQt: Add user interface for Triforce IP address overrides.
-rw-r--r--Source/Core/DolphinQt/CMakeLists.txt2
-rw-r--r--Source/Core/DolphinQt/DolphinQt.vcxproj4
-rw-r--r--Source/Core/DolphinQt/Settings/GameCubePane.cpp14
-rw-r--r--Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.cpp154
-rw-r--r--Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.h26
5 files changed, 194 insertions, 6 deletions
diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index 457ed8a157..0899ef5307 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -376,6 +376,8 @@ add_executable(dolphin-emu
Settings/OnScreenDisplayPane.h
Settings/PathPane.cpp
Settings/PathPane.h
+ Settings/TriforceBaseboardSettingsDialog.cpp
+ Settings/TriforceBaseboardSettingsDialog.h
Settings/USBDevicePicker.cpp
Settings/USBDevicePicker.h
Settings/WiiPane.cpp
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 09f9163036..33741cb09c 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -223,6 +223,7 @@
<ClCompile Include="Settings\GeneralPane.cpp" />
<ClCompile Include="Settings\InterfacePane.cpp" />
<ClCompile Include="Settings\OnScreenDisplayPane.cpp" />
+ <ClCompile Include="Settings\TriforceBaseboardSettingsDialog.cpp" />
<ClCompile Include="Settings\PathPane.cpp" />
<ClCompile Include="Settings\USBDevicePicker.cpp" />
<ClCompile Include="Settings\WiiPane.cpp" />
@@ -439,6 +440,7 @@
<QtMoc Include="Settings\GeneralPane.h" />
<QtMoc Include="Settings\InterfacePane.h" />
<QtMoc Include="Settings\PathPane.h" />
+ <QtMoc Include="Settings\TriforceBaseboardSettingsDialog.h" />
<QtMoc Include="Settings\USBDevicePicker.h" />
<QtMoc Include="Settings\WiiPane.h" />
<QtMoc Include="SkylanderPortal\SkylanderPortalWindow.h" />
@@ -523,4 +525,4 @@
<Message Text="Copy: @(BinaryFiles) -&gt; $(BinaryOutputDir)" Importance="High" />
<Copy SourceFiles="@(BinaryFiles)" DestinationFolder="$(BinaryOutputDir)" />
</Target>
-</Project> \ No newline at end of file
+</Project>
diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
index 975130531e..692258edb5 100644
--- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp
+++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
@@ -50,6 +50,7 @@
#include "DolphinQt/QtUtils/SignalBlocking.h"
#include "DolphinQt/Settings.h"
#include "DolphinQt/Settings/BroadbandAdapterSettingsDialog.h"
+#include "DolphinQt/Settings/TriforceBaseboardSettingsDialog.h"
constexpr std::initializer_list<ExpansionInterface::Slot> GUI_SLOTS = {
ExpansionInterface::Slot::A, ExpansionInterface::Slot::B, ExpansionInterface::Slot::SP1};
@@ -351,11 +352,12 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot)
break;
}
case ExpansionInterface::Slot::SP1:
- has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet ||
- device == ExpansionInterface::EXIDeviceType::EthernetXLink ||
- device == ExpansionInterface::EXIDeviceType::EthernetTapServer ||
- device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn ||
- device == ExpansionInterface::EXIDeviceType::ModemTapServer);
+ has_config = device == ExpansionInterface::EXIDeviceType::Ethernet ||
+ device == ExpansionInterface::EXIDeviceType::EthernetXLink ||
+ device == ExpansionInterface::EXIDeviceType::EthernetTapServer ||
+ device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn ||
+ device == ExpansionInterface::EXIDeviceType::ModemTapServer ||
+ device == ExpansionInterface::EXIDeviceType::Baseboard;
break;
case ExpansionInterface::Slot::SP2:
has_config = false;
@@ -421,6 +423,8 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
}
case ExpansionInterface::EXIDeviceType::Baseboard:
{
+ TriforceBaseboardSettingsDialog dialog(this);
+ dialog.exec();
return;
}
default:
diff --git a/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.cpp b/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.cpp
new file mode 100644
index 0000000000..23d300dc43
--- /dev/null
+++ b/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.cpp
@@ -0,0 +1,154 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "DolphinQt/Settings/TriforceBaseboardSettingsDialog.h"
+
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QGroupBox>
+#include <QHeaderView>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QTableWidget>
+#include <QVBoxLayout>
+
+#include <fmt/format.h>
+#include <fmt/ranges.h>
+
+#include "Common/Logging/Log.h"
+
+#include "Core/Config/MainSettings.h"
+#include "Core/HW/DVD/AMMediaboard.h"
+
+#include "DolphinQt/QtUtils/QtUtils.h"
+
+TriforceBaseboardSettingsDialog::TriforceBaseboardSettingsDialog(QWidget* parent) : QDialog{parent}
+{
+ setWindowTitle(tr("Triforce Baseboard"));
+
+ connect(this, &TriforceBaseboardSettingsDialog::accepted, this,
+ &TriforceBaseboardSettingsDialog::SaveConfig);
+
+ auto* const dialog_layout = new QVBoxLayout{this};
+
+ auto* const ip_override_group = new QGroupBox{tr("IP Address Overrides")};
+ dialog_layout->addWidget(ip_override_group);
+
+ auto* const ip_override_layout = new QFormLayout{ip_override_group};
+
+ m_bind_ip_edit = new QLineEdit;
+ ip_override_layout->addRow(tr("Bind IP: "), m_bind_ip_edit);
+
+ m_ip_overrides_table = new QTableWidget;
+ ip_override_layout->addRow(tr("IP Overrides: "), m_ip_overrides_table);
+
+ m_ip_overrides_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
+
+ m_ip_overrides_table->setColumnCount(2);
+ m_ip_overrides_table->setHorizontalHeaderLabels({tr("Match"), tr("Replacement")});
+
+ m_ip_overrides_table->setEditTriggers(QAbstractItemView::DoubleClicked |
+ QAbstractItemView::EditKeyPressed);
+
+ auto* const button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ dialog_layout->addWidget(button_box);
+
+ auto* const load_default_button = new QPushButton(tr("Default"));
+ connect(load_default_button, &QPushButton::clicked, this,
+ &TriforceBaseboardSettingsDialog::LoadDefault);
+
+ button_box->addButton(load_default_button, QDialogButtonBox::ActionRole);
+
+ connect(button_box, &QDialogButtonBox::accepted, this, &TriforceBaseboardSettingsDialog::accept);
+ connect(button_box, &QDialogButtonBox::rejected, this, &TriforceBaseboardSettingsDialog::reject);
+
+ connect(m_ip_overrides_table, &QTableWidget::itemChanged, this, [this](QTableWidgetItem* item) {
+ const int row_count = m_ip_overrides_table->rowCount();
+ const bool is_last_row = item->row() == row_count - 1;
+ if (is_last_row)
+ {
+ if (!item->text().isEmpty())
+ m_ip_overrides_table->insertRow(row_count);
+ }
+ else
+ {
+ // Just remove inner rows that have text erased.
+ // This UX could be less weird, but it's good enough for now.
+ if (item->text().isEmpty())
+ m_ip_overrides_table->removeRow(item->row());
+ }
+ });
+
+ LoadConfig();
+
+ QtUtils::AdjustSizeWithinScreen(this);
+}
+
+void TriforceBaseboardSettingsDialog::LoadConfig()
+{
+ m_bind_ip_edit->setText(QString::fromUtf8(Config::Get(Config::MAIN_TRIFORCE_BIND_IP)));
+
+ m_ip_overrides_table->setRowCount(0);
+
+ // Add the final empty row.
+ m_ip_overrides_table->insertRow(0);
+
+ int row = 0;
+
+ const auto ip_overrides_str = Config::Get(Config::MAIN_TRIFORCE_IP_OVERRIDES);
+ for (auto&& ip_pair : ip_overrides_str | std::views::split(','))
+ {
+ const auto ip_pair_str = std::string_view{ip_pair};
+ const auto parts = AMMediaboard::ParseIPOverride(ip_pair_str);
+ if (!parts.has_value())
+ {
+ ERROR_LOG_FMT(COMMON, "Bad IP pair string: {}", ip_pair_str);
+ continue;
+ }
+
+ m_ip_overrides_table->insertRow(row);
+
+ m_ip_overrides_table->setItem(row, 0, new QTableWidgetItem(QString::fromUtf8(parts->first)));
+ m_ip_overrides_table->setItem(row, 1, new QTableWidgetItem(QString::fromUtf8(parts->second)));
+
+ ++row;
+ }
+}
+
+void TriforceBaseboardSettingsDialog::SaveConfig()
+{
+ Config::SetBaseOrCurrent(Config::MAIN_TRIFORCE_BIND_IP, m_bind_ip_edit->text().toStdString());
+
+ // Ignore our empty row.
+ const int row_count = m_ip_overrides_table->rowCount() - 1;
+
+ std::vector<std::string> replacements(row_count);
+ for (int row = 0; row < row_count; ++row)
+ {
+ // Skip incomplete rows.
+ if (m_ip_overrides_table->item(row, 0) == nullptr ||
+ m_ip_overrides_table->item(row, 1) == nullptr)
+ {
+ continue;
+ }
+
+ replacements[row] =
+ std::string(StripWhitespace(m_ip_overrides_table->item(row, 0)->text().toStdString())) +
+ "=" +
+ std::string(StripWhitespace(m_ip_overrides_table->item(row, 1)->text().toStdString()));
+ }
+
+ // TODO: Ideally we'd remove duplicates based on the first IP.
+
+ Config::SetBaseOrCurrent(Config::MAIN_TRIFORCE_IP_OVERRIDES,
+ fmt::format("{}", fmt::join(replacements, ",")));
+}
+
+void TriforceBaseboardSettingsDialog::LoadDefault()
+{
+ // This alters the config before "OK" is pressed. Bad UX..
+ Config::SetBaseOrCurrent(Config::MAIN_TRIFORCE_BIND_IP, Config::DefaultState{});
+ Config::SetBaseOrCurrent(Config::MAIN_TRIFORCE_IP_OVERRIDES, Config::DefaultState{});
+
+ LoadConfig();
+}
diff --git a/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.h b/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.h
new file mode 100644
index 0000000000..e472739f71
--- /dev/null
+++ b/Source/Core/DolphinQt/Settings/TriforceBaseboardSettingsDialog.h
@@ -0,0 +1,26 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QDialog>
+
+class QLineEdit;
+class QTableWidget;
+
+class TriforceBaseboardSettingsDialog final : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit TriforceBaseboardSettingsDialog(QWidget* parent);
+
+private:
+ void LoadConfig();
+ void SaveConfig();
+
+ void LoadDefault();
+
+ QLineEdit* m_bind_ip_edit;
+ QTableWidget* m_ip_overrides_table;
+};