From 56402db7eed2e9bd67bb4d2e492b3ea338bb752a Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Sun, 22 May 2022 13:49:40 +0100 Subject: [PATCH] DEV9: Add DNS host export/import --- pcsx2-qt/CMakeLists.txt | 3 + pcsx2-qt/Settings/DEV9DnsHostDialog.cpp | 146 +++++++++++++++++++++ pcsx2-qt/Settings/DEV9DnsHostDialog.h | 51 ++++++++ pcsx2-qt/Settings/DEV9DnsHostDialog.ui | 69 ++++++++++ pcsx2-qt/Settings/DEV9SettingsWidget.cpp | 157 ++++++++++++++++++++--- pcsx2-qt/Settings/DEV9SettingsWidget.h | 5 + pcsx2-qt/Settings/DEV9SettingsWidget.ui | 14 ++ pcsx2-qt/pcsx2-qt.vcxproj | 6 + pcsx2-qt/pcsx2-qt.vcxproj.filters | 12 ++ 9 files changed, 448 insertions(+), 15 deletions(-) create mode 100644 pcsx2-qt/Settings/DEV9DnsHostDialog.cpp create mode 100644 pcsx2-qt/Settings/DEV9DnsHostDialog.h create mode 100644 pcsx2-qt/Settings/DEV9DnsHostDialog.ui diff --git a/pcsx2-qt/CMakeLists.txt b/pcsx2-qt/CMakeLists.txt index 7bf685454d..a490c3b75a 100644 --- a/pcsx2-qt/CMakeLists.txt +++ b/pcsx2-qt/CMakeLists.txt @@ -91,6 +91,9 @@ target_sources(pcsx2-qt PRIVATE Settings/MemoryCardSettingsWidget.cpp Settings/MemoryCardSettingsWidget.h Settings/MemoryCardSettingsWidget.ui + Settings/DEV9DnsHostDialog.cpp + Settings/DEV9DnsHostDialog.h + Settings/DEV9DnsHostDialog.ui Settings/DEV9SettingsWidget.cpp Settings/DEV9SettingsWidget.h Settings/DEV9SettingsWidget.ui diff --git a/pcsx2-qt/Settings/DEV9DnsHostDialog.cpp b/pcsx2-qt/Settings/DEV9DnsHostDialog.cpp new file mode 100644 index 0000000000..06417723ab --- /dev/null +++ b/pcsx2-qt/Settings/DEV9DnsHostDialog.cpp @@ -0,0 +1,146 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include +#include +#include +#include + +#include "common/StringUtil.h" +#include "DEV9DnsHostDialog.h" +#include "EmuThread.h" +#include "QtUtils.h" +#include "SettingWidgetBinder.h" +#include "SettingsDialog.h" + +//Figure out lists +//On export, we take list from settings (or are given it from the DEV9 panel) +//We display, then export + +//On import, we read file +//we display, then pass list back to main DEV9 panel + +DEV9DnsHostDialog::DEV9DnsHostDialog(std::vector hosts, QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + m_ethHost_model = new QStandardItemModel(0, 5, m_ui.hostList); + + QStringList headers; + headers.push_back(tr("Selected")); + headers.push_back(tr("Name")); + headers.push_back(tr("Url")); + headers.push_back(tr("Address")); + headers.push_back(tr("Enabled")); + m_ethHost_model->setHorizontalHeaderLabels(headers); + + m_ethHosts_proxy = new QSortFilterProxyModel(m_ui.hostList); + m_ethHosts_proxy->setSourceModel(m_ethHost_model); + + m_ui.hostList->setModel(m_ethHosts_proxy); + m_ui.hostList->setItemDelegateForColumn(3, new IPItemDelegate(m_ui.hostList)); + + for (size_t i = 0; i < hosts.size(); i++) + { + HostEntryUi entry = hosts[i]; + const int row = m_ethHost_model->rowCount(); + m_ethHost_model->insertRow(row); + + QSignalBlocker sb(m_ethHost_model); + + QStandardItem* includeItem = new QStandardItem(); + includeItem->setEditable(false); + includeItem->setCheckable(true); + includeItem->setCheckState(Qt::CheckState::Checked); + m_ethHost_model->setItem(row, 0, includeItem); + + QStandardItem* nameItem = new QStandardItem(); + nameItem->setText(QString::fromStdString(entry.Desc)); + nameItem->setEnabled(false); + m_ethHost_model->setItem(row, 1, nameItem); + + QStandardItem* urlItem = new QStandardItem(); + urlItem->setText(QString::fromStdString(entry.Url)); + urlItem->setEnabled(false); + m_ethHost_model->setItem(row, 2, urlItem); + + QStandardItem* addressItem = new QStandardItem(); + addressItem->setText(QString::fromStdString(entry.Address)); + addressItem->setEnabled(false); + m_ethHost_model->setItem(row, 3, addressItem); + + QStandardItem* enabledItem = new QStandardItem(); + enabledItem->setEditable(false); + enabledItem->setCheckable(true); + enabledItem->setCheckState(entry.Enabled ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + enabledItem->setEnabled(false); + m_ethHost_model->setItem(row, 4, enabledItem); + } + + m_ui.hostList->sortByColumn(1, Qt::AscendingOrder); + + m_ui.hostList->installEventFilter(this); + + connect(m_ui.btnOK, &QPushButton::clicked, this, &DEV9DnsHostDialog::onOK); + connect(m_ui.btnCancel, &QPushButton::clicked, this, &DEV9DnsHostDialog::onCancel); + + m_hosts = hosts; +} + +std::optional> DEV9DnsHostDialog::PromptList() +{ + int ret = exec(); + + if (ret != Accepted) + return std::nullopt; + + std::vector selectedList; + + for (int i = 0; i < m_ethHost_model->rowCount(); i++) + { + if (m_ethHost_model->item(i, 0)->checkState() == Qt::CheckState::Checked) + selectedList.push_back(m_hosts[i]); + } + + return selectedList; +} + +void DEV9DnsHostDialog::onOK() +{ + accept(); +} + +void DEV9DnsHostDialog::onCancel() +{ + reject(); +} + +bool DEV9DnsHostDialog::eventFilter(QObject* object, QEvent* event) +{ + if (object == m_ui.hostList) + { + //Check isVisible to avoind an unnessecery call to ResizeColumnsForTableView() + if (event->type() == QEvent::Resize && m_ui.hostList->isVisible()) + QtUtils::ResizeColumnsForTableView(m_ui.hostList, {80, -1, 170, 90, 80}); + else if (event->type() == QEvent::Show) + QtUtils::ResizeColumnsForTableView(m_ui.hostList, {80, -1, 170, 90, 80}); + } + return false; +} + +DEV9DnsHostDialog::~DEV9DnsHostDialog() = default; diff --git a/pcsx2-qt/Settings/DEV9DnsHostDialog.h b/pcsx2-qt/Settings/DEV9DnsHostDialog.h new file mode 100644 index 0000000000..947bd4dd39 --- /dev/null +++ b/pcsx2-qt/Settings/DEV9DnsHostDialog.h @@ -0,0 +1,51 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include +#include + +#include "ui_DEV9DnsHostDialog.h" + +#include "DEV9UiCommon.h" + +class SettingsDialog; + +class DEV9DnsHostDialog : public QDialog +{ + Q_OBJECT + +private Q_SLOTS: + void onOK(); + void onCancel(); + +public: + DEV9DnsHostDialog(std::vector hosts, QWidget* parent); + ~DEV9DnsHostDialog(); + + std::optional> PromptList(); + +protected: + bool eventFilter(QObject* object, QEvent* event); + +private: + Ui::DEV9DnsHostDialog m_ui; + + std::vector m_hosts; + + QStandardItemModel* m_ethHost_model; + QSortFilterProxyModel* m_ethHosts_proxy; +}; diff --git a/pcsx2-qt/Settings/DEV9DnsHostDialog.ui b/pcsx2-qt/Settings/DEV9DnsHostDialog.ui new file mode 100644 index 0000000000..736a036b3a --- /dev/null +++ b/pcsx2-qt/Settings/DEV9DnsHostDialog.ui @@ -0,0 +1,69 @@ + + + DEV9DnsHostDialog + + + + 0 + 0 + 600 + 300 + + + + Network DNS Hosts Inport/Export + + + + + + Select Hosts + + + + + + + false + + + false + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + OK + + + + + + + Cancel + + + + + + + + + + diff --git a/pcsx2-qt/Settings/DEV9SettingsWidget.cpp b/pcsx2-qt/Settings/DEV9SettingsWidget.cpp index 10eb0f56a3..862f2a0c3b 100644 --- a/pcsx2-qt/Settings/DEV9SettingsWidget.cpp +++ b/pcsx2-qt/Settings/DEV9SettingsWidget.cpp @@ -28,6 +28,7 @@ #include "QtUtils.h" #include "SettingWidgetBinder.h" #include "SettingsDialog.h" +#include "Frontend/INISettingsInterface.h" #include "HddCreateQt.h" @@ -245,6 +246,9 @@ DEV9SettingsWidget::DEV9SettingsWidget(SettingsDialog* dialog, QWidget* parent) connect(m_ui.ethHostAdd, &QPushButton::clicked, this, &DEV9SettingsWidget::onEthHostAdd); connect(m_ui.ethHostDel, &QPushButton::clicked, this, &DEV9SettingsWidget::onEthHostDel); + connect(m_ui.ethHostExport, &QPushButton::clicked, this, &DEV9SettingsWidget::onEthHostExport); + connect(m_ui.ethHostImport, &QPushButton::clicked, this, &DEV9SettingsWidget::onEthHostImport); + if (m_dialog->isPerGameSettings()) m_ui.ethTabWidget->setTabEnabled(1, false); @@ -487,6 +491,122 @@ void DEV9SettingsWidget::onEthHostDel() } } +void DEV9SettingsWidget::onEthHostExport() +{ + std::vector hosts = ListHostsConfig(); + + DEV9DnsHostDialog exportDialog(hosts, this); + + std::optional> selectedHosts = exportDialog.PromptList(); + + if (!selectedHosts.has_value()) + return; + + hosts = selectedHosts.value(); + + if (hosts.size() == 0) + return; + + QString path = + QDir::toNativeSeparators(QFileDialog::getSaveFileName(QtUtils::GetRootWidget(this), tr("Hosts File"), + "hosts.ini", tr("ini (*.ini)"), nullptr)); + + if (path.isEmpty()) + return; + + std::unique_ptr exportFile = std::make_unique(path.toUtf8().constData()); + + //Count is not exported + for (size_t i = 0; i < hosts.size(); i++) + { + std::string section = "Host" + std::to_string(i); + HostEntryUi entry = hosts[i]; + + // clang-format off + exportFile->SetStringValue(section.c_str(), "Url", entry.Url.c_str()); + exportFile->SetStringValue(section.c_str(), "Desc", entry.Desc.c_str()); + exportFile->SetStringValue(section.c_str(), "Address", entry.Address.c_str()); + exportFile->SetBoolValue(section.c_str(), "Enabled", entry.Enabled); + // clang-format on + } + + exportFile->Save(); + + QMessageBox::information(this, tr("DNS Hosts"), + tr("Exported Successfully"), + QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok); +} + +void DEV9SettingsWidget::onEthHostImport() +{ + std::vector hosts; + + QString path = + QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Hosts File"), + "hosts.ini", tr("ini (*.ini)"), nullptr)); + + if (path.isEmpty()) + return; + + std::unique_ptr importFile = std::make_unique(path.toUtf8().constData()); + + if (!importFile->Load()) + { + QMessageBox::warning(this, tr("DNS Hosts"), + tr("Failed to open file"), + QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok); + return; + } + + size_t count = 0; + while (true) + { + std::string section = "Host" + std::to_string(count); + HostEntryUi entry; + + entry.Url = importFile->GetStringValue(section.c_str(), "Url"); + + if (entry.Url.empty()) + break; + + // clang-format off + entry.Desc = importFile->GetStringValue(section.c_str(), "Desc"); + entry.Address = importFile->GetStringValue(section.c_str(), "Address"); + entry.Enabled = importFile->GetBoolValue (section.c_str(), "Enabled"); + // clang-format on + + hosts.push_back(entry); + count++; + } + + if (hosts.size() == 0) + { + QMessageBox::warning(this, tr("DNS Hosts"), + tr("No Hosts in file"), + QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok); + return; + } + + DEV9DnsHostDialog exportDialog(hosts, this); + + std::optional> selectedHosts = exportDialog.PromptList(); + + if (!selectedHosts.has_value()) + return; + + hosts = selectedHosts.value(); + + if (hosts.size() == 0) + return; + + for (size_t i = 0; i < hosts.size(); i++) + AddNewHostConfig(hosts[i]); + + QMessageBox::information(this, tr("DNS Hosts"), + tr("Imported Successfully"), + QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok); +} + void DEV9SettingsWidget::onEthHostEdit(QStandardItem* item) { const int row = item->row(); @@ -711,22 +831,9 @@ void DEV9SettingsWidget::RefreshHostList() m_ethHost_model->removeRow(0); //Load list - std::vector hosts; + std::vector hosts = ListHostsConfig(); - const int hostLength = CountHostsConfig(); - for (int i = 0; i < hostLength; i++) - { - std::string section = "DEV9/Eth/Hosts/Host" + std::to_string(i); - - HostEntryUi entry; - entry.Url = m_dialog->getStringValue(section.c_str(), "Url", "").value(); - entry.Desc = m_dialog->getStringValue(section.c_str(), "Desc", "").value(); - entry.Address = m_dialog->getStringValue(section.c_str(), "Address", "").value(); - entry.Enabled = m_dialog->getBoolValue(section.c_str(), "Enabled", false).value(); - hosts.push_back(entry); - } - - for (int i = 0; i < hostLength; i++) + for (size_t i = 0; i < hosts.size(); i++) { HostEntryUi entry = hosts[i]; const int row = m_ethHost_model->rowCount(); @@ -761,6 +868,26 @@ int DEV9SettingsWidget::CountHostsConfig() return m_dialog->getIntValue("DEV9/Eth/Hosts", "Count", 0).value(); } +std::vector DEV9SettingsWidget::ListHostsConfig() +{ + std::vector hosts; + + const int hostLength = CountHostsConfig(); + for (int i = 0; i < hostLength; i++) + { + std::string section = "DEV9/Eth/Hosts/Host" + std::to_string(i); + + HostEntryUi entry; + entry.Url = m_dialog->getStringValue(section.c_str(), "Url", "").value(); + entry.Desc = m_dialog->getStringValue(section.c_str(), "Desc", "").value(); + entry.Address = m_dialog->getStringValue(section.c_str(), "Address", "").value(); + entry.Enabled = m_dialog->getBoolValue(section.c_str(), "Enabled", false).value(); + hosts.push_back(entry); + } + + return hosts; +} + void DEV9SettingsWidget::AddNewHostConfig(const HostEntryUi& host) { const int hostLength = CountHostsConfig(); diff --git a/pcsx2-qt/Settings/DEV9SettingsWidget.h b/pcsx2-qt/Settings/DEV9SettingsWidget.h index 69883231b6..f4338520d7 100644 --- a/pcsx2-qt/Settings/DEV9SettingsWidget.h +++ b/pcsx2-qt/Settings/DEV9SettingsWidget.h @@ -20,6 +20,8 @@ #include "ui_DEV9SettingsWidget.h" +#include "DEV9UiCommon.h" +#include "DEV9DnsHostDialog.h" #include "DEV9/net.h" class SettingsDialog; @@ -38,6 +40,8 @@ private Q_SLOTS: void onEthDNSModeChanged(QComboBox* sender, int index, QLineEdit* input, const char* section, const char* key); void onEthHostAdd(); void onEthHostDel(); + void onEthHostExport(); + void onEthHostImport(); void onEthHostEdit(QStandardItem* item); void onHddEnabledChanged(int state); @@ -60,6 +64,7 @@ private: void AddAdapter(const AdapterEntry& adapter); void RefreshHostList(); int CountHostsConfig(); + std::vector ListHostsConfig(); void AddNewHostConfig(const HostEntryUi& host); void DeleteHostConfig(int index); diff --git a/pcsx2-qt/Settings/DEV9SettingsWidget.ui b/pcsx2-qt/Settings/DEV9SettingsWidget.ui index 5dfbaea000..bfbbd8cda5 100644 --- a/pcsx2-qt/Settings/DEV9SettingsWidget.ui +++ b/pcsx2-qt/Settings/DEV9SettingsWidget.ui @@ -192,6 +192,20 @@ + + + + Export + + + + + + + Import + + + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index 4e6ee34696..e2c09299cc 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -154,6 +154,7 @@ + @@ -190,6 +191,7 @@ + @@ -235,6 +237,7 @@ + @@ -309,6 +312,9 @@ Document + + Document + Document diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index bf637a1c21..a21ac8c3a7 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -170,6 +170,9 @@ moc + + moc + moc @@ -191,6 +194,9 @@ Settings + + Settings + Settings @@ -297,6 +303,9 @@ Settings + + Settings + Settings @@ -369,6 +378,9 @@ Settings + + Settings + Settings