DolphinQt: Add user interface for Triforce IP address overrides.

This commit is contained in:
Jordan Woyak
2026-02-15 20:14:15 -06:00
parent 3e2b7fee46
commit 7ab2672698
5 changed files with 194 additions and 6 deletions
+2
View File
@@ -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
+3 -1
View File
@@ -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>
</Project>
@@ -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:
@@ -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();
}
@@ -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;
};