summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/configuration/config.cpp123
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure.ui52
-rw-r--r--src/yuzu/configuration/configure_audio.cpp7
-rw-r--r--src/yuzu/configuration/configure_input.cpp60
-rw-r--r--src/yuzu/configuration/configure_input.h8
-rw-r--r--src/yuzu/configuration/configure_input.ui48
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp142
-rw-r--r--src/yuzu/configuration/configure_input_simple.h40
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_per_general.cpp170
-rw-r--r--src/yuzu/configuration/configure_per_general.h50
-rw-r--r--src/yuzu/configuration/configure_per_general.ui276
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp1
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.h2
-rw-r--r--src/yuzu/game_list.cpp3
-rw-r--r--src/yuzu/game_list.h1
-rw-r--r--src/yuzu/game_list_worker.cpp123
-rw-r--r--src/yuzu/main.cpp29
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/ui_settings.h3
24 files changed, 1067 insertions, 181 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 61562b9b2..17ecaafde 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -33,10 +33,14 @@ add_executable(yuzu
configuration/configure_input.h
configuration/configure_input_player.cpp
configuration/configure_input_player.h
+ configuration/configure_input_simple.cpp
+ configuration/configure_input_simple.h
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_system.cpp
configuration/configure_system.h
+ configuration/configure_per_general.cpp
+ configuration/configure_per_general.h
configuration/configure_touchscreen_advanced.cpp
configuration/configure_touchscreen_advanced.h
configuration/configure_web.cpp
@@ -87,7 +91,9 @@ set(UIS
configuration/configure_graphics.ui
configuration/configure_input.ui
configuration/configure_input_player.ui
+ configuration/configure_input_simple.ui
configuration/configure_mouse_advanced.ui
+ configuration/configure_per_general.ui
configuration/configure_system.ui
configuration/configure_touchscreen_advanced.ui
configuration/configure_web.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 83ebbd1fe..759362ce3 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -4,6 +4,7 @@
#include <QSettings>
#include "common/file_util.h"
+#include "configure_input_simple.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
@@ -206,60 +207,57 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
void Config::ReadPlayerValues() {
for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
- Settings::values.players[p].connected =
- qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
+ auto& player = Settings::values.players[p];
- Settings::values.players[p].type = static_cast<Settings::ControllerType>(
+ player.connected = qt_config->value(QString("player_%1_connected").arg(p), false).toBool();
+
+ player.type = static_cast<Settings::ControllerType>(
qt_config
->value(QString("player_%1_type").arg(p),
static_cast<u8>(Settings::ControllerType::DualJoycon))
.toUInt());
- Settings::values.players[p].body_color_left =
- qt_config
- ->value(QString("player_%1_body_color_left").arg(p),
- Settings::JOYCON_BODY_NEON_BLUE)
- .toUInt();
- Settings::values.players[p].body_color_right =
- qt_config
- ->value(QString("player_%1_body_color_right").arg(p),
- Settings::JOYCON_BODY_NEON_RED)
- .toUInt();
- Settings::values.players[p].button_color_left =
- qt_config
- ->value(QString("player_%1_button_color_left").arg(p),
- Settings::JOYCON_BUTTONS_NEON_BLUE)
- .toUInt();
- Settings::values.players[p].button_color_right =
- qt_config
- ->value(QString("player_%1_button_color_right").arg(p),
- Settings::JOYCON_BUTTONS_NEON_RED)
- .toUInt();
+ player.body_color_left = qt_config
+ ->value(QString("player_%1_body_color_left").arg(p),
+ Settings::JOYCON_BODY_NEON_BLUE)
+ .toUInt();
+ player.body_color_right = qt_config
+ ->value(QString("player_%1_body_color_right").arg(p),
+ Settings::JOYCON_BODY_NEON_RED)
+ .toUInt();
+ player.button_color_left = qt_config
+ ->value(QString("player_%1_button_color_left").arg(p),
+ Settings::JOYCON_BUTTONS_NEON_BLUE)
+ .toUInt();
+ player.button_color_right = qt_config
+ ->value(QString("player_%1_button_color_right").arg(p),
+ Settings::JOYCON_BUTTONS_NEON_RED)
+ .toUInt();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- Settings::values.players[p].buttons[i] =
+ player.buttons[i] =
qt_config
->value(QString("player_%1_").arg(p) + Settings::NativeButton::mapping[i],
QString::fromStdString(default_param))
.toString()
.toStdString();
- if (Settings::values.players[p].buttons[i].empty())
- Settings::values.players[p].buttons[i] = default_param;
+ if (player.buttons[i].empty())
+ player.buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
- Settings::values.players[p].analogs[i] =
+ player.analogs[i] =
qt_config
->value(QString("player_%1_").arg(p) + Settings::NativeAnalog::mapping[i],
QString::fromStdString(default_param))
.toString()
.toStdString();
- if (Settings::values.players[p].analogs[i].empty())
- Settings::values.players[p].analogs[i] = default_param;
+ if (player.analogs[i].empty())
+ player.analogs[i] = default_param;
}
}
@@ -342,6 +340,13 @@ void Config::ReadTouchscreenValues() {
qt_config->endGroup();
}
+void Config::ApplyDefaultProfileIfInputInvalid() {
+ if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
+ [](const Settings::PlayerInput& in) { return in.connected; })) {
+ ApplyInputProfileConfiguration(UISettings::values.profile_index);
+ }
+}
+
void Config::ReadValues() {
qt_config->beginGroup("Controls");
@@ -444,6 +449,21 @@ void Config::ReadValues() {
Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString();
qt_config->endGroup();
+ const auto size = qt_config->beginReadArray("DisabledAddOns");
+ for (int i = 0; i < size; ++i) {
+ qt_config->setArrayIndex(i);
+ const auto title_id = qt_config->value("title_id", 0).toULongLong();
+ std::vector<std::string> out;
+ const auto d_size = qt_config->beginReadArray("disabled");
+ for (int j = 0; j < d_size; ++j) {
+ qt_config->setArrayIndex(j);
+ out.push_back(qt_config->value("d", "").toString().toStdString());
+ }
+ qt_config->endArray();
+ Settings::values.disabled_addons.insert_or_assign(title_id, out);
+ }
+ qt_config->endArray();
+
qt_config->beginGroup("UI");
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
UISettings::values.enable_discord_presence =
@@ -506,35 +526,36 @@ void Config::ReadValues() {
UISettings::values.first_start = qt_config->value("firstStart", true).toBool();
UISettings::values.callout_flags = qt_config->value("calloutFlags", 0).toUInt();
UISettings::values.show_console = qt_config->value("showConsole", false).toBool();
+ UISettings::values.profile_index = qt_config->value("profileIndex", 0).toUInt();
+
+ ApplyDefaultProfileIfInputInvalid();
qt_config->endGroup();
}
void Config::SavePlayerValues() {
- for (int p = 0; p < Settings::values.players.size(); ++p) {
- qt_config->setValue(QString("player_%1_connected").arg(p),
- Settings::values.players[p].connected);
- qt_config->setValue(QString("player_%1_type").arg(p),
- static_cast<u8>(Settings::values.players[p].type));
-
- qt_config->setValue(QString("player_%1_body_color_left").arg(p),
- Settings::values.players[p].body_color_left);
- qt_config->setValue(QString("player_%1_body_color_right").arg(p),
- Settings::values.players[p].body_color_right);
+ for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
+ const auto& player = Settings::values.players[p];
+
+ qt_config->setValue(QString("player_%1_connected").arg(p), player.connected);
+ qt_config->setValue(QString("player_%1_type").arg(p), static_cast<u8>(player.type));
+
+ qt_config->setValue(QString("player_%1_body_color_left").arg(p), player.body_color_left);
+ qt_config->setValue(QString("player_%1_body_color_right").arg(p), player.body_color_right);
qt_config->setValue(QString("player_%1_button_color_left").arg(p),
- Settings::values.players[p].button_color_left);
+ player.button_color_left);
qt_config->setValue(QString("player_%1_button_color_right").arg(p),
- Settings::values.players[p].button_color_right);
+ player.button_color_right);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
qt_config->setValue(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeButton::mapping[i]),
- QString::fromStdString(Settings::values.players[p].buttons[i]));
+ QString::fromStdString(player.buttons[i]));
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
qt_config->setValue(QString("player_%1_").arg(p) +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(Settings::values.players[p].analogs[i]));
+ QString::fromStdString(player.analogs[i]));
}
}
}
@@ -650,6 +671,21 @@ void Config::SaveValues() {
qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token));
qt_config->endGroup();
+ qt_config->beginWriteArray("DisabledAddOns");
+ int i = 0;
+ for (const auto& elem : Settings::values.disabled_addons) {
+ qt_config->setArrayIndex(i);
+ qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first));
+ qt_config->beginWriteArray("disabled");
+ for (std::size_t j = 0; j < elem.second.size(); ++j) {
+ qt_config->setArrayIndex(j);
+ qt_config->setValue("d", QString::fromStdString(elem.second[j]));
+ }
+ qt_config->endArray();
+ ++i;
+ }
+ qt_config->endArray();
+
qt_config->beginGroup("UI");
qt_config->setValue("theme", UISettings::values.theme);
qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence);
@@ -695,6 +731,7 @@ void Config::SaveValues() {
qt_config->setValue("firstStart", UISettings::values.first_start);
qt_config->setValue("calloutFlags", UISettings::values.callout_flags);
qt_config->setValue("showConsole", UISettings::values.show_console);
+ qt_config->setValue("profileIndex", UISettings::values.profile_index);
qt_config->endGroup();
}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index a1c27bbf9..e73ad19bb 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -34,6 +34,7 @@ private:
void ReadKeyboardValues();
void ReadMouseValues();
void ReadTouchscreenValues();
+ void ApplyDefaultProfileIfInputInvalid();
void SaveValues();
void SavePlayerValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 9b297df28..8706b80d2 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>461</width>
- <height>500</height>
+ <height>659</height>
</rect>
</property>
<property name="windowTitle">
@@ -24,17 +24,17 @@
<string>General</string>
</attribute>
</widget>
- <widget class="ConfigureGameList" name="gameListTab">
- <attribute name="title">
- <string>Game List</string>
- </attribute>
- </widget>
+ <widget class="ConfigureGameList" name="gameListTab">
+ <attribute name="title">
+ <string>Game List</string>
+ </attribute>
+ </widget>
<widget class="ConfigureSystem" name="systemTab">
<attribute name="title">
<string>System</string>
</attribute>
</widget>
- <widget class="ConfigureInput" name="inputTab">
+ <widget class="ConfigureInputSimple" name="inputTab">
<attribute name="title">
<string>Input</string>
</attribute>
@@ -54,11 +54,11 @@
<string>Debug</string>
</attribute>
</widget>
- <widget class="ConfigureWeb" name="webTab">
- <attribute name="title">
- <string>Web</string>
- </attribute>
- </widget>
+ <widget class="ConfigureWeb" name="webTab">
+ <attribute name="title">
+ <string>Web</string>
+ </attribute>
+ </widget>
</widget>
</item>
<item>
@@ -77,12 +77,12 @@
<header>configuration/configure_general.h</header>
<container>1</container>
</customwidget>
- <customwidget>
- <class>ConfigureGameList</class>
- <extends>QWidget</extends>
- <header>configuration/configure_gamelist.h</header>
- <container>1</container>
- </customwidget>
+ <customwidget>
+ <class>ConfigureGameList</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_gamelist.h</header>
+ <container>1</container>
+ </customwidget>
<customwidget>
<class>ConfigureSystem</class>
<extends>QWidget</extends>
@@ -102,9 +102,9 @@
<container>1</container>
</customwidget>
<customwidget>
- <class>ConfigureInput</class>
+ <class>ConfigureInputSimple</class>
<extends>QWidget</extends>
- <header>configuration/configure_input.h</header>
+ <header>configuration/configure_input_simple.h</header>
<container>1</container>
</customwidget>
<customwidget>
@@ -113,12 +113,12 @@
<header>configuration/configure_graphics.h</header>
<container>1</container>
</customwidget>
- <customwidget>
- <class>ConfigureWeb</class>
- <extends>QWidget</extends>
- <header>configuration/configure_web.h</header>
- <container>1</container>
- </customwidget>
+ <customwidget>
+ <class>ConfigureWeb</class>
+ <extends>QWidget</extends>
+ <header>configuration/configure_web.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources/>
<connections>
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index eb1da0f9e..5d9ccc6e8 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -17,8 +17,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
ui->output_sink_combo_box->clear();
ui->output_sink_combo_box->addItem("auto");
- for (const auto& sink_detail : AudioCore::g_sink_details) {
- ui->output_sink_combo_box->addItem(sink_detail.id);
+ for (const char* id : AudioCore::GetSinkIDs()) {
+ ui->output_sink_combo_box->addItem(id);
}
connect(ui->volume_slider, &QSlider::valueChanged, this,
@@ -97,8 +97,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
ui->audio_device_combo_box->addItem(AudioCore::auto_device_name);
const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
- const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices();
- for (const auto& device : device_list) {
+ for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) {
ui->audio_device_combo_box->addItem(QString::fromStdString(device));
}
}
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 830d26115..f39d57998 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -20,6 +20,33 @@
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
+void OnDockedModeChanged(bool last_state, bool new_state) {
+ if (last_state == new_state) {
+ return;
+ }
+
+ Core::System& system{Core::System::GetInstance()};
+ if (!system.IsPoweredOn()) {
+ return;
+ }
+ Service::SM::ServiceManager& sm = system.ServiceManager();
+
+ // Message queue is shared between these services, we just need to signal an operation
+ // change to one and it will handle both automatically
+ auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
+ auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
+ bool has_signalled = false;
+
+ if (applet_oe != nullptr) {
+ applet_oe->GetMessageQueue()->OperationModeChanged();
+ has_signalled = true;
+ }
+
+ if (applet_ae != nullptr && !has_signalled) {
+ applet_ae->GetMessageQueue()->OperationModeChanged();
+ }
+}
+
namespace {
template <typename Dialog, typename... Args>
void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
@@ -34,7 +61,7 @@ void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
} // Anonymous namespace
ConfigureInput::ConfigureInput(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
ui->setupUi(this);
players_controller = {
@@ -90,37 +117,6 @@ ConfigureInput::ConfigureInput(QWidget* parent)
ConfigureInput::~ConfigureInput() = default;
-void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
- if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) {
- ui->handheld_connected->setChecked(false);
- }
-
- if (last_state == new_state) {
- return;
- }
-
- Core::System& system{Core::System::GetInstance()};
- if (!system.IsPoweredOn()) {
- return;
- }
- Service::SM::ServiceManager& sm = system.ServiceManager();
-
- // Message queue is shared between these services, we just need to signal an operation
- // change to one and it will handle both automatically
- auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
- auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
- bool has_signalled = false;
-
- if (applet_oe != nullptr) {
- applet_oe->GetMessageQueue()->OperationModeChanged();
- has_signalled = true;
- }
-
- if (applet_ae != nullptr && !has_signalled) {
- applet_ae->GetMessageQueue()->OperationModeChanged();
- }
-}
-
void ConfigureInput::applyConfiguration() {
for (std::size_t i = 0; i < players_controller.size(); ++i) {
const auto controller_type_index = players_controller[i]->currentIndex();
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 1649e4c0b..b8e62cc2b 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,8 +7,8 @@
#include <array>
#include <memory>
+#include <QDialog>
#include <QKeyEvent>
-#include <QWidget>
#include "ui_configure_input.h"
@@ -20,7 +20,9 @@ namespace Ui {
class ConfigureInput;
}
-class ConfigureInput : public QWidget {
+void OnDockedModeChanged(bool last_state, bool new_state);
+
+class ConfigureInput : public QDialog {
Q_OBJECT
public:
@@ -33,8 +35,6 @@ public:
private:
void updateUIEnabled();
- void OnDockedModeChanged(bool last_state, bool new_state);
-
/// Load configuration settings.
void loadConfiguration();
/// Restore all buttons to their default values.
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index dae8277bc..0a2d9f024 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInput</class>
- <widget class="QWidget" name="ConfigureInput">
+ <widget class="QDialog" name="ConfigureInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>473</width>
- <height>685</height>
+ <width>384</width>
+ <height>576</height>
</rect>
</property>
<property name="windowTitle">
@@ -478,6 +478,13 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
@@ -485,5 +492,38 @@
</layout>
</widget>
<resources/>
- <connections/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureInput</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>294</x>
+ <y>553</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>191</x>
+ <y>287</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureInput</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>294</x>
+ <y>553</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>191</x>
+ <y>287</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 7dadd83c1..ba2b32c4f 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -6,6 +6,7 @@
#include <memory>
#include <utility>
#include <QColorDialog>
+#include <QGridLayout>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
new file mode 100644
index 000000000..b4f3724bd
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.cpp
@@ -0,0 +1,142 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstring>
+#include <functional>
+#include <tuple>
+
+#include <QDialog>
+
+#include "ui_configure_input_simple.h"
+#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_input_simple.h"
+#include "yuzu/ui_settings.h"
+
+namespace {
+
+template <typename Dialog, typename... Args>
+void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
+ caller->applyConfiguration();
+ Dialog dialog(caller, std::forward<Args>(args)...);
+
+ const auto res = dialog.exec();
+ if (res == QDialog::Accepted) {
+ dialog.applyConfiguration();
+ }
+}
+
+// OnProfileSelect functions should (when applicable):
+// - Set controller types
+// - Set controller enabled
+// - Set docked mode
+// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
+//
+// OnProfileSelect function should NOT however:
+// - Reset any button mappings
+// - Open any dialogs
+// - Block in any way
+
+constexpr std::size_t HANDHELD_INDEX = 8;
+
+void HandheldOnProfileSelect() {
+ Settings::values.players[HANDHELD_INDEX].connected = true;
+ Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
+
+ for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
+ Settings::values.players[player].connected = false;
+ }
+
+ Settings::values.use_docked_mode = false;
+ Settings::values.keyboard_enabled = false;
+ Settings::values.mouse_enabled = false;
+ Settings::values.debug_pad_enabled = false;
+ Settings::values.touchscreen.enabled = true;
+}
+
+void DualJoyconsDockedOnProfileSelect() {
+ Settings::values.players[0].connected = true;
+ Settings::values.players[0].type = Settings::ControllerType::DualJoycon;
+
+ for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
+ Settings::values.players[player].connected = false;
+ }
+
+ Settings::values.use_docked_mode = true;
+ Settings::values.keyboard_enabled = false;
+ Settings::values.mouse_enabled = false;
+ Settings::values.debug_pad_enabled = false;
+ Settings::values.touchscreen.enabled = false;
+}
+
+// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
+// is clicked)
+using InputProfile =
+ std::tuple<QString, std::function<void()>, std::function<void(ConfigureInputSimple*)>>;
+
+const std::array<InputProfile, 3> INPUT_PROFILES{{
+ {ConfigureInputSimple::tr("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
+ [](ConfigureInputSimple* caller) {
+ CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
+ }},
+ {ConfigureInputSimple::tr("Single Player - Dual Joycons - Docked"),
+ DualJoyconsDockedOnProfileSelect,
+ [](ConfigureInputSimple* caller) {
+ CallConfigureDialog<ConfigureInputPlayer>(caller, 1, false);
+ }},
+ {ConfigureInputSimple::tr("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
+}};
+
+} // namespace
+
+void ApplyInputProfileConfiguration(int profile_index) {
+ std::get<1>(
+ INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
+}
+
+ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
+ ui->setupUi(this);
+
+ for (const auto& profile : INPUT_PROFILES) {
+ ui->profile_combobox->addItem(std::get<0>(profile), std::get<0>(profile));
+ }
+
+ connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureInputSimple::OnSelectProfile);
+ connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure);
+
+ this->loadConfiguration();
+}
+
+ConfigureInputSimple::~ConfigureInputSimple() = default;
+
+void ConfigureInputSimple::applyConfiguration() {
+ auto index = ui->profile_combobox->currentIndex();
+ // Make the stored index for "Custom" very large so that if new profiles are added it
+ // doesn't change.
+ if (index >= static_cast<int>(INPUT_PROFILES.size() - 1))
+ index = std::numeric_limits<int>::max();
+
+ UISettings::values.profile_index = index;
+}
+
+void ConfigureInputSimple::loadConfiguration() {
+ const auto index = UISettings::values.profile_index;
+ if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0)
+ ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
+ else
+ ui->profile_combobox->setCurrentIndex(index);
+}
+
+void ConfigureInputSimple::OnSelectProfile(int index) {
+ const auto old_docked = Settings::values.use_docked_mode;
+ ApplyInputProfileConfiguration(index);
+ OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
+}
+
+void ConfigureInputSimple::OnConfigure() {
+ std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
+}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
new file mode 100644
index 000000000..5b6b69994
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.h
@@ -0,0 +1,40 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include <QWidget>
+
+class QPushButton;
+class QString;
+class QTimer;
+
+namespace Ui {
+class ConfigureInputSimple;
+}
+
+// Used by configuration loader to apply a profile if the input is invalid.
+void ApplyInputProfileConfiguration(int profile_index);
+
+class ConfigureInputSimple : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputSimple(QWidget* parent = nullptr);
+ ~ConfigureInputSimple() override;
+
+ /// Save all button configurations to settings file
+ void applyConfiguration();
+
+private:
+ /// Load configuration settings.
+ void loadConfiguration();
+
+ void OnSelectProfile(int index);
+ void OnConfigure();
+
+ std::unique_ptr<Ui::ConfigureInputSimple> ui;
+};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
new file mode 100644
index 000000000..c4889caa9
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_simple.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureInputSimple</class>
+ <widget class="QWidget" name="ConfigureInputSimple">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>473</width>
+ <height>685</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>ConfigureInputSimple</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox">
+ <property name="title">
+ <string>Profile</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="2">
+ <widget class="QPushButton" name="profile_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="3">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="profile_combobox">
+ <property name="minimumSize">
+ <size>
+ <width>250</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Choose a controller configuration:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp
new file mode 100644
index 000000000..80109b434
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.cpp
@@ -0,0 +1,170 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <QHeaderView>
+#include <QMenu>
+#include <QMessageBox>
+#include <QStandardItemModel>
+#include <QString>
+#include <QTimer>
+#include <QTreeView>
+
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/xts_archive.h"
+#include "core/loader/loader.h"
+#include "ui_configure_per_general.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_per_general.h"
+#include "yuzu/ui_settings.h"
+#include "yuzu/util/util.h"
+
+ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) {
+
+ ui->setupUi(this);
+ setFocusPolicy(Qt::ClickFocus);
+ setWindowTitle(tr("Properties"));
+
+ layout = new QVBoxLayout;
+ tree_view = new QTreeView;
+ item_model = new QStandardItemModel(tree_view);
+ tree_view->setModel(item_model);
+ tree_view->setAlternatingRowColors(true);
+ tree_view->setSelectionMode(QHeaderView::SingleSelection);
+ tree_view->setSelectionBehavior(QHeaderView::SelectRows);
+ tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel);
+ tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel);
+ tree_view->setSortingEnabled(true);
+ tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
+ tree_view->setUniformRowHeights(true);
+ tree_view->setContextMenuPolicy(Qt::NoContextMenu);
+
+ item_model->insertColumns(0, 2);
+ item_model->setHeaderData(0, Qt::Horizontal, "Patch Name");
+ item_model->setHeaderData(1, Qt::Horizontal, "Version");
+
+ // We must register all custom types with the Qt Automoc system so that we are able to use it
+ // with signals/slots. In this case, QList falls under the umbrells of custom types.
+ qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");
+
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);
+ layout->addWidget(tree_view);
+
+ ui->scrollArea->setLayout(layout);
+
+ scene = new QGraphicsScene;
+ ui->icon_view->setScene(scene);
+
+ connect(item_model, &QStandardItemModel::itemChanged,
+ [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
+
+ this->loadConfiguration();
+}
+
+ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default;
+
+void ConfigurePerGameGeneral::applyConfiguration() {
+ std::vector<std::string> disabled_addons;
+
+ for (const auto& item : list_items) {
+ const auto disabled = item.front()->checkState() == Qt::Unchecked;
+ if (disabled)
+ disabled_addons.push_back(item.front()->text().toStdString());
+ }
+
+ Settings::values.disabled_addons[title_id] = disabled_addons;
+}
+
+void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) {
+ this->file = std::move(file);
+ this->loadConfiguration();
+}
+
+void ConfigurePerGameGeneral::loadConfiguration() {
+ if (file == nullptr)
+ return;
+
+ const auto loader = Loader::GetLoader(file);
+
+ ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str());
+
+ FileSys::PatchManager pm{title_id};
+ const auto control = pm.GetControlMetadata();
+
+ if (control.first != nullptr) {
+ ui->display_version->setText(QString::fromStdString(control.first->GetVersionString()));
+ ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName()));
+ ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName()));
+ } else {
+ std::string title;
+ if (loader->ReadTitle(title) == Loader::ResultStatus::Success)
+ ui->display_name->setText(QString::fromStdString(title));
+
+ std::string developer;
+ if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success)
+ ui->display_developer->setText(QString::fromStdString(developer));
+
+ ui->display_version->setText(QStringLiteral("1.0.0"));
+ }
+
+ if (control.second != nullptr) {
+ scene->clear();
+
+ QPixmap map;
+ const auto bytes = control.second->ReadAllBytes();
+ map.loadFromData(bytes.data(), bytes.size());
+
+ scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ } else {
+ std::vector<u8> bytes;
+ if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) {
+ scene->clear();
+
+ QPixmap map;
+ map.loadFromData(bytes.data(), bytes.size());
+
+ scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(),
+ Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
+ }
+ }
+
+ FileSys::VirtualFile update_raw;
+ loader->ReadUpdateRaw(update_raw);
+
+ const auto& disabled = Settings::values.disabled_addons[title_id];
+
+ for (const auto& patch : pm.GetPatchVersionNames(update_raw)) {
+ QStandardItem* first_item = new QStandardItem;
+ const auto name = QString::fromStdString(patch.first).replace("[D] ", "");
+ first_item->setText(name);
+ first_item->setCheckable(true);
+
+ const auto patch_disabled =
+ std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end();
+
+ first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked);
+
+ list_items.push_back(QList<QStandardItem*>{
+ first_item, new QStandardItem{QString::fromStdString(patch.second)}});
+ item_model->appendRow(list_items.back());
+ }
+
+ tree_view->setColumnWidth(0, 5 * tree_view->width() / 16);
+
+ ui->display_filename->setText(QString::fromStdString(file->GetName()));
+
+ ui->display_format->setText(
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())));
+
+ const auto valueText = ReadableByteSize(file->GetSize());
+ ui->display_size->setText(valueText);
+}
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h
new file mode 100644
index 000000000..a4494446c
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.h
@@ -0,0 +1,50 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <QKeyEvent>
+#include <QList>
+#include <QWidget>
+
+#include "core/file_sys/vfs_types.h"
+
+class QTreeView;
+class QGraphicsScene;
+class QStandardItem;
+class QStandardItemModel;
+
+namespace Ui {
+class ConfigurePerGameGeneral;
+}
+
+class ConfigurePerGameGeneral : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id);
+ ~ConfigurePerGameGeneral() override;
+
+ /// Save all button configurations to settings file
+ void applyConfiguration();
+
+ void loadFromFile(FileSys::VirtualFile file);
+
+private:
+ std::unique_ptr<Ui::ConfigurePerGameGeneral> ui;
+ FileSys::VirtualFile file;
+ u64 title_id;
+
+ QVBoxLayout* layout;
+ QTreeView* tree_view;
+ QStandardItemModel* item_model;
+ QGraphicsScene* scene;
+
+ std::vector<QList<QStandardItem*>> list_items;
+
+ void loadConfiguration();
+};
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui
new file mode 100644
index 000000000..8fdd96fa4
--- /dev/null
+++ b/src/yuzu/configuration/configure_per_general.ui
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigurePerGameGeneral</class>
+ <widget class="QDialog" name="ConfigurePerGameGeneral">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>520</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>ConfigurePerGameGeneral</string>
+ </property>
+ <layout class="QHBoxLayout" name="HorizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="VerticalLayout">
+ <item>
+ <widget class="QGroupBox" name="GeneralGroupBox">
+ <property name="title">
+ <string>Info</string>
+ </property>
+ <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="6" column="1" colspan="2">
+ <widget class="QLineEdit" name="display_filename">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="display_name">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Developer</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2">
+ <widget class="QLineEdit" name="display_size">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Filename</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Version</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Format</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="display_version">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="display_format">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="display_developer">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Title ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="display_title_id">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" rowspan="5">
+ <widget class="QGraphicsView" name="icon_view">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>128</width>
+ <height>128</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>128</width>
+ <height>128</height>
+ </size>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QAbstractScrollArea::AdjustToContents</enum>
+ </property>
+ <property name="interactive">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="PerformanceGroupBox">
+ <property name="title">
+ <string>Add-Ons</string>
+ </property>
+ <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
+ <item>
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>350</width>
+ <height>169</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigurePerGameGeneral</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>269</x>
+ <y>567</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>269</x>
+ <y>294</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigurePerGameGeneral</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>269</x>
+ <y>567</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>269</x>
+ <y>294</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 707747422..209798521 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -30,6 +30,7 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
return Tegra::Texture::TextureFormat::A2B10G10R10;
default:
UNIMPLEMENTED_MSG("Unimplemented RT format");
+ return Tegra::Texture::TextureFormat::A8R8G8B8;
}
}
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 96c57fe97..6b3a757e0 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -75,7 +75,7 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
return item_list;
}
-WaitTreeText::WaitTreeText(const QString& t) : text(t) {}
+WaitTreeText::WaitTreeText(QString t) : text(std::move(t)) {}
WaitTreeText::~WaitTreeText() = default;
QString WaitTreeText::GetText() const {
@@ -153,7 +153,7 @@ QString WaitTreeWaitObject::GetText() const {
std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) {
switch (object.GetHandleType()) {
- case Kernel::HandleType::Event:
+ case Kernel::HandleType::ReadableEvent:
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object));
case Kernel::HandleType::Timer:
return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 492fb6ac9..e639ef412 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -52,7 +52,7 @@ private:
class WaitTreeText : public WaitTreeItem {
Q_OBJECT
public:
- explicit WaitTreeText(const QString& text);
+ explicit WaitTreeText(QString text);
~WaitTreeText() override;
QString GetText() const override;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index b52a50915..8e9524fd6 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -333,6 +333,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
+ context_menu.addSeparator();
+ QAction* properties = context_menu.addAction(tr("Properties"));
open_save_location->setEnabled(program_id != 0);
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -346,6 +348,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
connect(navigate_to_gamedb_entry, &QAction::triggered,
[&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
+ connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); });
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 05e115e19..b317eb2fc 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -70,6 +70,7 @@ signals:
void CopyTIDRequested(u64 program_id);
void NavigateToGamedbEntryRequested(u64 program_id,
const CompatibilityList& compatibility_list);
+ void OpenPerGameGeneralRequested(const std::string& file);
private slots:
void onTextChanged(const QString& newText);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 1edc60df7..b37710f59 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -62,7 +62,7 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
FileSys::VirtualFile update_raw;
loader.ReadUpdateRaw(update_raw);
for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
- const bool is_update = kv.first == "Update";
+ const bool is_update = kv.first == "Update" || kv.first == "[D] Update";
if (!updatable && is_update) {
continue;
}
@@ -86,6 +86,37 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
out.chop(1);
return out;
}
+
+QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
+ const std::vector<u8>& icon, Loader::AppLoader& loader,
+ u64 program_id, const CompatibilityList& compatibility_list,
+ const FileSys::PatchManager& patch) {
+ const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
+
+ // The game list uses this as compatibility number for untested games
+ QString compatibility{"99"};
+ if (it != compatibility_list.end()) {
+ compatibility = it->second.first;
+ }
+
+ const auto file_type = loader.GetFileType();
+ const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type));
+
+ QList<QStandardItem*> list{
+ new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name),
+ file_type_string, program_id),
+ new GameListItemCompat(compatibility),
+ new GameListItem(file_type_string),
+ new GameListItemSize(FileUtil::GetSize(path)),
+ };
+
+ if (UISettings::values.show_add_ons) {
+ list.insert(
+ 2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable())));
+ }
+
+ return list;
+}
} // Anonymous namespace
GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
@@ -116,29 +147,8 @@ void GameListWorker::AddInstalledTitlesToGameList() {
if (control != nullptr)
GetMetadataFromControlNCA(patch, *control, icon, name);
- auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
-
- // The game list uses this as compatibility number for untested games
- QString compatibility("99");
- if (it != compatibility_list.end())
- compatibility = it->second.first;
-
- QList<QStandardItem*> list{
- new GameListItemPath(
- FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
- program_id),
- new GameListItemCompat(compatibility),
- new GameListItem(
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
- new GameListItemSize(file->GetSize()),
- };
-
- if (UISettings::values.show_add_ons) {
- list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
- }
-
- emit EntryReady(list);
+ emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
+ compatibility_list, patch));
}
const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application,
@@ -155,14 +165,14 @@ void GameListWorker::AddInstalledTitlesToGameList() {
void GameListWorker::FillControlMap(const std::string& dir_path) {
const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
- std::string physical_name = directory + DIR_SEP + virtual_name;
-
- if (stop_processing)
- return false; // Breaks the callback loop.
+ if (stop_processing) {
+ // Breaks the callback loop
+ return false;
+ }
- bool is_dir = FileUtil::IsDirectory(physical_name);
- QFileInfo file_info(physical_name.c_str());
- if (!is_dir && file_info.suffix().toStdString() == "nca") {
+ const std::string physical_name = directory + DIR_SEP + virtual_name;
+ const QFileInfo file_info(QString::fromStdString(physical_name));
+ if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) {
auto nca =
std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
if (nca->GetType() == FileSys::NCAContentType::Control) {
@@ -179,20 +189,25 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
const std::string& virtual_name) -> bool {
- std::string physical_name = directory + DIR_SEP + virtual_name;
-
- if (stop_processing)
- return false; // Breaks the callback loop.
+ if (stop_processing) {
+ // Breaks the callback loop.
+ return false;
+ }
- bool is_dir = FileUtil::IsDirectory(physical_name);
+ const std::string physical_name = directory + DIR_SEP + virtual_name;
+ const bool is_dir = FileUtil::IsDirectory(physical_name);
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
- std::unique_ptr<Loader::AppLoader> loader =
- Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
- if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
- loader->GetFileType() == Loader::FileType::Error) &&
- !UISettings::values.show_unknown))
+ auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+ if (!loader) {
return true;
+ }
+
+ const auto file_type = loader->GetFileType();
+ if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) &&
+ !UISettings::values.show_unknown) {
+ return true;
+ }
std::vector<u8> icon;
const auto res1 = loader->ReadIcon(icon);
@@ -214,30 +229,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
}
}
- auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
-
- // The game list uses this as compatibility number for untested games
- QString compatibility("99");
- if (it != compatibility_list.end())
- compatibility = it->second.first;
-
- QList<QStandardItem*> list{
- new GameListItemPath(
- FormatGameName(physical_name), icon, QString::fromStdString(name),
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
- program_id),
- new GameListItemCompat(compatibility),
- new GameListItem(
- QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
- new GameListItemSize(FileUtil::GetSize(physical_name)),
- };
-
- if (UISettings::values.show_add_ons) {
- list.insert(2, new GameListItem(FormatPatchNameVersions(
- patch, *loader, loader->IsRomFSUpdatable())));
- }
-
- emit EntryReady(std::move(list));
+ emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
+ compatibility_list, patch));
} else if (is_dir && recursion > 0) {
watch_list.append(QString::fromStdString(physical_name));
AddFstEntriesToGameList(physical_name, recursion - 1);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 085cab74b..feb5572c6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
#include "applets/profile_select.h"
#include "applets/software_keyboard.h"
+#include "configuration/configure_per_general.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/service/acc/profile_manager.h"
@@ -464,6 +465,8 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
&GMainWindow::OnGameListNavigateToGamedbEntry);
+ connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
+ &GMainWindow::OnGameListOpenPerGameProperties);
connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting);
@@ -1012,6 +1015,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory));
}
+void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
+ u64 title_id{};
+ const auto v_file = Core::GetGameFileFromPath(vfs, file);
+ const auto loader = Loader::GetLoader(v_file);
+ if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
+ QMessageBox::information(this, tr("Properties"),
+ tr("The game properties could not be loaded."));
+ return;
+ }
+
+ ConfigurePerGameGeneral dialog(this, title_id);
+ dialog.loadFromFile(v_file);
+ auto result = dialog.exec();
+ if (result == QDialog::Accepted) {
+ dialog.applyConfiguration();
+
+ const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
+ if (reload) {
+ game_list->PopulateAsync(UISettings::values.gamedir,
+ UISettings::values.gamedir_deepscan);
+ }
+
+ config->Save();
+ }
+}
+
void GMainWindow::OnMenuLoadFile() {
const QString extensions =
QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7c5eaf16b..4a982bb02 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -170,6 +170,7 @@ private slots:
void OnGameListCopyTID(u64 program_id);
void OnGameListNavigateToGamedbEntry(u64 program_id,
const CompatibilityList& compatibility_list);
+ void OnGameListOpenPerGameProperties(const std::string& file);
void OnMenuLoadFile();
void OnMenuLoadFolder();
void OnMenuInstallToNAND();
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index e80aebc0a..2f434eab0 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -58,6 +58,9 @@ struct Values {
// logging
bool show_console;
+ // Controllers
+ int profile_index;
+
// Game List
bool show_unknown;
bool show_add_ons;