Files
KytyPS5/src/launcher/src/configurationEditDialog.cpp
T
nmzik 26781e6fea fix(windows): add experimental guest red-zone protection.
Port the static instruction patcher from shadPS4.
2026-08-07 05:19:14 +02:00

380 lines
12 KiB
C++

#include "configurationEditDialog.h"
#include "configuration.h"
#include "mandatoryLineEdit.h"
#include <QAbstractItemView>
#include <QCheckBox>
#include <QComboBox>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLayout>
#include <QListView>
#include <QListWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QSettings>
#include <QSpinBox>
#include <QStyle>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>
#include <QtAlgorithms>
#include "ui_configuration_edit_dialog.h"
constexpr char SETTINGS_CFG_DIALOG[] = "ConfigurationEditDialog";
constexpr char SETTINGS_CFG_LAST_GEOMETRY[] = "geometry";
constexpr int GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH = 560;
const QStringList CONSOLE_LANGUAGE_NAMES = {
"Japanese",
"English (United States)",
"French (France)",
"Spanish (Spain)",
"German",
"Italian",
"Dutch",
"Portuguese (Portugal)",
"Russian",
"Korean",
"Chinese (Traditional)",
"Chinese (Simplified)",
"Finnish",
"Swedish",
"Danish",
"Norwegian",
"Polish",
"Portuguese (Brazil)",
"English (United Kingdom)",
"Turkish",
"Spanish (Latin America)",
"Arabic",
"French (Canada)",
"Czech",
"Hungarian",
"Greek",
"Romanian",
"Thai",
"Vietnamese",
"Indonesian",
};
static QString NormalizeGameDirectory(const QString& dir) {
const auto trimmed = dir.trimmed();
if (trimmed.isEmpty()) {
return {};
}
return QDir::cleanPath(QDir(trimmed).absolutePath());
}
static QString GameDirectoryKey(const QString& dir) {
const auto normalized = NormalizeGameDirectory(dir);
if (normalized.isEmpty()) {
return {};
}
auto canonical = QFileInfo(normalized).canonicalFilePath();
if (canonical.isEmpty()) {
canonical = normalized;
}
canonical = QDir::cleanPath(canonical);
#ifdef __linux__
return canonical;
#else
return canonical.toCaseFolded();
#endif
}
ConfigurationEditDialog::ConfigurationEditDialog(Configuration& info, QWidget* parent)
: QDialog(parent, Qt::WindowCloseButtonHint), m_ui(new Ui::ConfigurationEditDialog),
m_info(info) {
m_ui->setupUi(this);
InitGameDirectories();
connect(m_ui->ok_button, &QPushButton::clicked, this, &ConfigurationEditDialog::save);
connect(m_ui->clear_button, &QPushButton::clicked, this, &ConfigurationEditDialog::clear);
connect(m_ui->comboBox_shader_log_direction, &QComboBox::currentTextChanged, this,
[this](const QString& text) {
auto log = TextToEnum<Configuration::ShaderLogDirection>(text);
m_ui->lineEdit_shader_log_folder->setEnabled(
log == Configuration::ShaderLogDirection::File);
});
connect(m_ui->checkBox_cmd_dump, &QCheckBox::toggled, this,
[this](bool flag) { m_ui->lineEdit_cmd_dump_folder->setEnabled(flag); });
connect(m_ui->comboBox_printf_direction, &QComboBox::currentTextChanged, this,
[this](const QString& text) {
auto log = TextToEnum<Configuration::LogDirection>(text);
m_ui->lineEdit_printf_file->setEnabled(log == Configuration::LogDirection::File);
});
layout()->setSizeConstraint(QLayout::SetFixedSize);
restoreGeometry(g_last_geometry);
Init(info);
}
QByteArray ConfigurationEditDialog::g_last_geometry;
ConfigurationEditDialog::~ConfigurationEditDialog() {
delete m_ui;
}
void ConfigurationEditDialog::WriteSettings(QSettings& s) {
s.beginGroup(SETTINGS_CFG_DIALOG);
if (!g_last_geometry.isEmpty()) {
s.setValue(SETTINGS_CFG_LAST_GEOMETRY, g_last_geometry);
}
s.endGroup();
}
void ConfigurationEditDialog::ReadSettings(QSettings& s) {
s.beginGroup(SETTINGS_CFG_DIALOG);
g_last_geometry = s.value(SETTINGS_CFG_LAST_GEOMETRY, g_last_geometry).toByteArray();
s.endGroup();
}
template <class T>
static void ListInit(QComboBox* combo, T value) {
combo->clear();
combo->addItems(EnumToList<T>());
combo->setCurrentText(EnumToText(value));
}
void ConfigurationEditDialog::Init(const Configuration& info) {
ListInit(m_ui->comboBox_screen_resolution, info.screen_resolution);
m_ui->checkBox_fullscreen->setChecked(info.fullscreen_enabled);
m_ui->spinBox_vblank_frequency->setValue(info.vblank_frequency);
m_ui->comboBox_console_language->clear();
m_ui->comboBox_console_language->addItems(CONSOLE_LANGUAGE_NAMES);
m_ui->comboBox_console_language->setCurrentIndex(
info.console_language >= 0 && info.console_language < CONSOLE_LANGUAGE_NAMES.size()
? info.console_language
: Configuration::DEFAULT_CONSOLE_LANGUAGE);
m_ui->checkBox_shader_validation->setChecked(info.shader_validation_enabled);
m_ui->checkBox_vulkan_validation->setChecked(info.vulkan_validation_enabled);
m_ui->checkBox_renderdoc_capture->setChecked(info.renderdoc_enabled);
#if defined(_WIN32)
m_ui->checkBox_red_zone_protection->setChecked(info.red_zone_protection_enabled);
#else
m_ui->checkBox_red_zone_protection->setVisible(false);
#endif
ListInit(m_ui->comboBox_shader_optimization_type, info.shader_optimization_type);
ListInit(m_ui->comboBox_shader_log_direction, info.shader_log_direction);
m_ui->lineEdit_shader_log_folder->setText(info.shader_log_folder);
m_ui->lineEdit_shader_log_folder->setEnabled(info.shader_log_direction ==
Configuration::ShaderLogDirection::File);
m_ui->checkBox_cmd_dump->setChecked(info.command_buffer_dump_enabled);
m_ui->lineEdit_cmd_dump_folder->setText(info.command_buffer_dump_folder);
m_ui->lineEdit_cmd_dump_folder->setEnabled(info.command_buffer_dump_enabled);
ListInit(m_ui->comboBox_printf_direction, info.printf_direction);
m_ui->lineEdit_printf_file->setText(info.printf_output_file);
m_ui->lineEdit_printf_file->setEnabled(info.printf_direction ==
Configuration::LogDirection::File);
ListInit(m_ui->comboBox_profiler_direction, info.profiler_direction);
}
void ConfigurationEditDialog::InitGameDirectories() {
m_game_dirs_group = new QGroupBox(tr("Game folders"), this);
auto* group_layout = new QVBoxLayout(m_game_dirs_group);
group_layout->setContentsMargins(8, 8, 8, 8);
group_layout->setSpacing(6);
m_game_dirs_list = new QListWidget(m_game_dirs_group);
m_game_dirs_list->setMinimumHeight(120);
m_game_dirs_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
group_layout->addWidget(m_game_dirs_list);
auto* button_layout = new QHBoxLayout;
button_layout->setContentsMargins(0, 0, 0, 0);
button_layout->setSpacing(4);
auto* add_button = new QToolButton(m_game_dirs_group);
add_button->setIcon(style()->standardIcon(QStyle::SP_DirOpenIcon));
add_button->setToolTip(tr("Add game folder"));
add_button->setAutoRaise(true);
button_layout->addWidget(add_button);
m_remove_game_dir_button = new QToolButton(m_game_dirs_group);
m_remove_game_dir_button->setIcon(style()->standardIcon(QStyle::SP_DialogDiscardButton));
m_remove_game_dir_button->setToolTip(tr("Remove selected game folders"));
m_remove_game_dir_button->setAutoRaise(true);
button_layout->addWidget(m_remove_game_dir_button);
button_layout->addStretch(1);
group_layout->addLayout(button_layout);
m_ui->gridLayout->addWidget(m_game_dirs_group, 1, 0, 1, 1);
m_game_dirs_group->setVisible(false);
connect(add_button, &QToolButton::clicked, this, &ConfigurationEditDialog::add_game_directory);
connect(m_remove_game_dir_button, &QToolButton::clicked, this,
&ConfigurationEditDialog::remove_selected_game_directories);
connect(m_game_dirs_list, &QListWidget::itemSelectionChanged, this,
&ConfigurationEditDialog::update_game_directory_buttons);
update_game_directory_buttons();
}
void ConfigurationEditDialog::SetTitle(const QString& str) {
setWindowTitle(str);
}
void ConfigurationEditDialog::SetGameDirectories(const QStringList& dirs) {
m_show_game_dirs = true;
m_game_dirs_list->clear();
m_game_dirs_group->setMinimumWidth(GLOBAL_SETTINGS_GAME_DIRS_MIN_WIDTH);
for (const auto& dir: dirs) {
AddGameDirectoryItem(dir);
}
m_game_dirs_group->setVisible(true);
update_game_directory_buttons();
adjustSize();
}
QStringList ConfigurationEditDialog::GetGameDirectories() const {
QStringList dirs;
if (!m_show_game_dirs) {
return dirs;
}
for (int index = 0; index < m_game_dirs_list->count(); index++) {
auto* item = m_game_dirs_list->item(index);
dirs.append(item->text());
}
return dirs;
}
void ConfigurationEditDialog::AddGameDirectoryItem(const QString& dir) {
const auto normalized = NormalizeGameDirectory(dir);
const auto key = GameDirectoryKey(normalized);
if (normalized.isEmpty() || key.isEmpty()) {
return;
}
for (int index = 0; index < m_game_dirs_list->count(); index++) {
auto* item = m_game_dirs_list->item(index);
if (GameDirectoryKey(item->text()) == key) {
return;
}
}
auto* item = new QListWidgetItem(normalized, m_game_dirs_list);
item->setToolTip(normalized);
}
void ConfigurationEditDialog::moveEvent(QMoveEvent* event) {
QDialog::moveEvent(event);
g_last_geometry = saveGeometry();
}
static void UpdateInfo(Configuration& info, Ui::ConfigurationEditDialog& ui) {
info.screen_resolution =
TextToEnum<Configuration::Resolution>(ui.comboBox_screen_resolution->currentText());
info.fullscreen_enabled = ui.checkBox_fullscreen->isChecked();
info.vblank_frequency = ui.spinBox_vblank_frequency->value();
info.console_language = ui.comboBox_console_language->currentIndex();
info.vulkan_validation_enabled = ui.checkBox_vulkan_validation->isChecked();
info.shader_validation_enabled = ui.checkBox_shader_validation->isChecked();
info.renderdoc_enabled = ui.checkBox_renderdoc_capture->isChecked();
#if defined(_WIN32)
info.red_zone_protection_enabled = ui.checkBox_red_zone_protection->isChecked();
#endif
info.shader_optimization_type = TextToEnum<Configuration::ShaderOptimizationType>(
ui.comboBox_shader_optimization_type->currentText());
info.shader_log_direction = TextToEnum<Configuration::ShaderLogDirection>(
ui.comboBox_shader_log_direction->currentText());
info.shader_log_folder = ui.lineEdit_shader_log_folder->text();
info.command_buffer_dump_enabled = ui.checkBox_cmd_dump->isChecked();
info.command_buffer_dump_folder = ui.lineEdit_cmd_dump_folder->text();
info.printf_direction =
TextToEnum<Configuration::LogDirection>(ui.comboBox_printf_direction->currentText());
info.printf_output_file = ui.lineEdit_printf_file->text();
info.profiler_direction =
TextToEnum<Configuration::ProfilerDirection>(ui.comboBox_profiler_direction->currentText());
}
void ConfigurationEditDialog::update_info() {
UpdateInfo(m_info, *m_ui);
}
void ConfigurationEditDialog::adjust_size() {
this->adjustSize();
}
void ConfigurationEditDialog::save() {
if (MandatoryLineEdit::FindEmpty(this)) {
QMessageBox::critical(this, tr("Save failed"), tr("Please fill all mandatory fields"));
return;
}
update_info();
emit accept();
}
void ConfigurationEditDialog::clear() {
Configuration default_info;
Init(default_info);
if (m_show_game_dirs) {
m_game_dirs_list->clear();
update_game_directory_buttons();
}
}
void ConfigurationEditDialog::add_game_directory() {
QString start_dir = QDir::homePath();
if (m_game_dirs_list->count() > 0) {
start_dir = m_game_dirs_list->item(m_game_dirs_list->count() - 1)->text();
}
QFileDialog dialog(this, tr("Select game folders"), start_dir);
dialog.setFileMode(QFileDialog::Directory);
dialog.setOption(QFileDialog::ShowDirsOnly, true);
dialog.setOption(QFileDialog::DontUseNativeDialog, true);
dialog.setLabelText(QFileDialog::Accept, tr("Add"));
auto* list_view = dialog.findChild<QListView*>(QStringLiteral("listView"));
if (list_view != nullptr) {
list_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
}
auto* tree_view = dialog.findChild<QTreeView*>(QStringLiteral("treeView"));
if (tree_view != nullptr) {
tree_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
}
if (dialog.exec() != QDialog::Accepted) {
return;
}
for (const auto& dir: dialog.selectedFiles()) {
AddGameDirectoryItem(dir);
}
update_game_directory_buttons();
}
void ConfigurationEditDialog::remove_selected_game_directories() {
qDeleteAll(m_game_dirs_list->selectedItems());
update_game_directory_buttons();
}
void ConfigurationEditDialog::update_game_directory_buttons() {
m_remove_game_dir_button->setEnabled(!m_game_dirs_list->selectedItems().isEmpty());
}