summaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/about_dialog.cpp2
-rw-r--r--src/yuzu/about_dialog.h2
-rw-r--r--src/yuzu/bootmanager.h4
-rw-r--r--src/yuzu/configuration/config.cpp14
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp8
-rw-r--r--src/yuzu/configuration/configure_dialog.h4
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp63
-rw-r--r--src/yuzu/configuration/configure_gamelist.h28
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui126
-rw-r--r--src/yuzu/configuration/configure_general.cpp6
-rw-r--r--src/yuzu/configuration/configure_general.h4
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp9
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.h6
-rw-r--r--src/yuzu/debugger/wait_tree.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.h4
-rw-r--r--src/yuzu/game_list.cpp127
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/game_list_p.h63
-rw-r--r--src/yuzu/hotkeys.cpp67
-rw-r--r--src/yuzu/hotkeys.h107
-rw-r--r--src/yuzu/main.cpp211
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/ui_settings.h6
29 files changed, 627 insertions, 276 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 475556806..46ed232d8 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -17,6 +17,8 @@ add_executable(yuzu
configuration/configure_debug.h
configuration/configure_dialog.cpp
configuration/configure_dialog.h
+ configuration/configure_gamelist.cpp
+ configuration/configure_gamelist.h
configuration/configure_general.cpp
configuration/configure_general.h
configuration/configure_graphics.cpp
@@ -59,6 +61,7 @@ set(UIS
configuration/configure.ui
configuration/configure_audio.ui
configuration/configure_debug.ui
+ configuration/configure_gamelist.ui
configuration/configure_general.ui
configuration/configure_graphics.ui
configuration/configure_input.ui
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index 39ed3bccf..a81ad2888 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -15,4 +15,4 @@ AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDia
Common::g_scm_desc, QString(Common::g_build_date).left(10)));
}
-AboutDialog::~AboutDialog() {}
+AboutDialog::~AboutDialog() = default;
diff --git a/src/yuzu/about_dialog.h b/src/yuzu/about_dialog.h
index 2eb6e28f5..18e8c11a7 100644
--- a/src/yuzu/about_dialog.h
+++ b/src/yuzu/about_dialog.h
@@ -16,7 +16,7 @@ class AboutDialog : public QDialog {
public:
explicit AboutDialog(QWidget* parent);
- ~AboutDialog();
+ ~AboutDialog() override;
private:
std::unique_ptr<Ui::AboutDialog> ui;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 130bc613b..f133bfadf 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -101,12 +101,12 @@ signals:
void ErrorThrown(Core::System::ResultStatus, std::string);
};
-class GRenderWindow : public QWidget, public EmuWindow {
+class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
- ~GRenderWindow();
+ ~GRenderWindow() override;
// EmuWindow implementation
void SwapBuffers() override;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bf469ee73..0bd46dbac 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -122,6 +122,13 @@ void Config::ReadValues() {
qt_config->beginGroup("UI");
UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
+ qt_config->beginGroup("UIGameList");
+ UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
+ UISettings::values.icon_size = qt_config->value("icon_size", 48).toUInt();
+ UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 0).toUInt();
+ UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 3).toUInt();
+ qt_config->endGroup();
+
qt_config->beginGroup("UILayout");
UISettings::values.geometry = qt_config->value("geometry").toByteArray();
UISettings::values.state = qt_config->value("state").toByteArray();
@@ -234,6 +241,13 @@ void Config::SaveValues() {
qt_config->beginGroup("UI");
qt_config->setValue("theme", UISettings::values.theme);
+ qt_config->beginGroup("UIGameList");
+ qt_config->setValue("show_unknown", UISettings::values.show_unknown);
+ qt_config->setValue("icon_size", UISettings::values.icon_size);
+ qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
+ qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
+ qt_config->endGroup();
+
qt_config->beginGroup("UILayout");
qt_config->setValue("geometry", UISettings::values.geometry);
qt_config->setValue("state", UISettings::values.state);
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index c8e0b88af..20f120134 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -24,6 +24,11 @@
<string>General</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>
@@ -67,6 +72,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>ConfigureSystem</class>
<extends>QWidget</extends>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 7fd07539a..45d84f19a 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,7 +24,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
});
}
-ConfigureDebug::~ConfigureDebug() {}
+ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::setConfiguration() {
ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 118e91cf1..5ae7276bd 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -23,13 +23,6 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="QLabel" name="label_1">
- <property name="text">
- <string>The GDB Stub only works correctly when the CPU JIT is off.</string>
- </property>
- </widget>
- </item>
- <item>
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QCheckBox" name="toggle_gdbstub">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index f66abf870..daa4cc0d9 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -6,18 +6,22 @@
#include "ui_configure.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_dialog.h"
+#include "yuzu/hotkeys.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ConfigureDialog) {
+ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry)
+ : QDialog(parent), ui(new Ui::ConfigureDialog) {
ui->setupUi(this);
+ ui->generalTab->PopulateHotkeyList(registry);
this->setConfiguration();
}
-ConfigureDialog::~ConfigureDialog() {}
+ConfigureDialog::~ConfigureDialog() = default;
void ConfigureDialog::setConfiguration() {}
void ConfigureDialog::applyConfiguration() {
ui->generalTab->applyConfiguration();
+ ui->gameListTab->applyConfiguration();
ui->systemTab->applyConfiguration();
ui->inputTab->applyConfiguration();
ui->graphicsTab->applyConfiguration();
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 21fa1f501..bbbdacc29 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -7,6 +7,8 @@
#include <memory>
#include <QDialog>
+class HotkeyRegistry;
+
namespace Ui {
class ConfigureDialog;
}
@@ -15,7 +17,7 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent);
+ explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
~ConfigureDialog();
void applyConfiguration();
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
new file mode 100644
index 000000000..1ae3423cf
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -0,0 +1,63 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_gamelist.h"
+#include "ui_settings.h"
+#include "yuzu/configuration/configure_gamelist.h"
+
+ConfigureGameList::ConfigureGameList(QWidget* parent)
+ : QWidget(parent), ui(new Ui::ConfigureGameList) {
+ ui->setupUi(this);
+
+ static const std::vector<std::pair<u32, std::string>> default_icon_sizes{
+ std::make_pair(0, "None"), std::make_pair(32, "Small"),
+ std::make_pair(64, "Standard"), std::make_pair(128, "Large"),
+ std::make_pair(256, "Full Size"),
+ };
+
+ for (const auto& size : default_icon_sizes) {
+ ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" +
+ std::to_string(size.first) + "x" +
+ std::to_string(size.first) + ")"),
+ size.first);
+ }
+
+ static const std::vector<std::string> row_text_names{
+ "Filename",
+ "Filetype",
+ "Title ID",
+ "Title Name",
+ };
+
+ for (size_t i = 0; i < row_text_names.size(); ++i) {
+ ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
+ QVariant::fromValue(i));
+ ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
+ QVariant::fromValue(i));
+ }
+
+ this->setConfiguration();
+}
+
+ConfigureGameList::~ConfigureGameList() {}
+
+void ConfigureGameList::setConfiguration() {
+ ui->show_unknown->setChecked(UISettings::values.show_unknown);
+ ui->icon_size_combobox->setCurrentIndex(
+ ui->icon_size_combobox->findData(UISettings::values.icon_size));
+ ui->row_1_text_combobox->setCurrentIndex(
+ ui->row_1_text_combobox->findData(UISettings::values.row_1_text_id));
+ ui->row_2_text_combobox->setCurrentIndex(
+ ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id));
+}
+
+void ConfigureGameList::applyConfiguration() {
+ UISettings::values.show_unknown = ui->show_unknown->isChecked();
+ UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
+ UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
+ UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
+ Settings::Apply();
+}
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_gamelist.h
new file mode 100644
index 000000000..94fba6373
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.h
@@ -0,0 +1,28 @@
+// 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>
+
+namespace Ui {
+class ConfigureGameList;
+}
+
+class ConfigureGameList : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureGameList(QWidget* parent = nullptr);
+ ~ConfigureGameList();
+
+ void applyConfiguration();
+
+private:
+ void setConfiguration();
+
+private:
+ std::unique_ptr<Ui::ConfigureGameList> ui;
+};
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_gamelist.ui
new file mode 100644
index 000000000..7471fdb60
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.ui
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureGameList</class>
+ <widget class="QWidget" name="ConfigureGeneral">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>300</width>
+ <height>377</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="HorizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="VerticalLayout">
+ <item>
+ <widget class="QGroupBox" name="GeneralGroupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="GeneralVerticalLayout">
+ <item>
+ <widget class="QCheckBox" name="show_unknown">
+ <property name="text">
+ <string>Show files with type 'Unknown'</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="IconSizeGroupBox">
+ <property name="title">
+ <string>Icon Size</string>
+ </property>
+ <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
+ <item>
+ <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
+ <item>
+ <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
+ <item>
+ <widget class="QLabel" name="icon_size_label">
+ <property name="text">
+ <string>Icon Size:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="icon_size_combobox"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="RowGroupBox">
+ <property name="title">
+ <string>Row Text</string>
+ </property>
+ <layout class="QHBoxLayout" name="RowHorizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="RowVerticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="row_1_qhbox_layout">
+ <item>
+ <widget class="QLabel" name="row_1_label">
+ <property name="text">
+ <string>Row 1 Text:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="row_1_text_combobox"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="row_2_qhbox_layout">
+ <item>
+ <widget class="QLabel" name="row_2_label">
+ <property name="text">
+ <string>Row 2 Text:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="row_2_text_combobox"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </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>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index cb7d3f8bf..d8caee1e8 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -24,7 +24,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
ui->use_docked_mode->setEnabled(!Core::System::GetInstance().IsPoweredOn());
}
-ConfigureGeneral::~ConfigureGeneral() {}
+ConfigureGeneral::~ConfigureGeneral() = default;
void ConfigureGeneral::setConfiguration() {
ui->toggle_deepscan->setChecked(UISettings::values.gamedir_deepscan);
@@ -35,6 +35,10 @@ void ConfigureGeneral::setConfiguration() {
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
}
+void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
+ ui->widget->Populate(registry);
+}
+
void ConfigureGeneral::applyConfiguration() {
UISettings::values.gamedir_deepscan = ui->toggle_deepscan->isChecked();
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h
index 447552d8c..4770034cc 100644
--- a/src/yuzu/configuration/configure_general.h
+++ b/src/yuzu/configuration/configure_general.h
@@ -7,6 +7,8 @@
#include <memory>
#include <QWidget>
+class HotkeyRegistry;
+
namespace Ui {
class ConfigureGeneral;
}
@@ -18,11 +20,11 @@ public:
explicit ConfigureGeneral(QWidget* parent = nullptr);
~ConfigureGeneral();
+ void PopulateHotkeyList(const HotkeyRegistry& registry);
void applyConfiguration();
private:
void setConfiguration();
-private:
std::unique_ptr<Ui::ConfigureGeneral> ui;
};
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 3379b7963..4afe0f81b 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -14,7 +14,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
this->setConfiguration();
}
-ConfigureGraphics::~ConfigureGraphics() {}
+ConfigureGraphics::~ConfigureGraphics() = default;
enum class Resolution : int {
Auto,
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9be2c939c..e9ed9c38f 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -35,7 +35,7 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
this->setConfiguration();
}
-ConfigureSystem::~ConfigureSystem() {}
+ConfigureSystem::~ConfigureSystem() = default;
void ConfigureSystem::setConfiguration() {
enabled = !Core::System::GetInstance().IsPoweredOn();
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index ff3efcdaa..e037223c2 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -34,7 +34,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat(
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
: QLabel(parent), surface_widget(surface_widget_) {}
-SurfacePicture::~SurfacePicture() {}
+
+SurfacePicture::~SurfacePicture() = default;
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
// Only do something while the left mouse button is held down
@@ -382,8 +383,10 @@ void GraphicsSurfaceWidget::OnUpdate() {
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address);
- auto unswizzled_data =
- Tegra::Texture::UnswizzleTexture(*address, surface_format, surface_width, surface_height);
+ // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
+ // Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
+ auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
+ *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height);
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
surface_width, surface_height);
diff --git a/src/yuzu/debugger/graphics/graphics_surface.h b/src/yuzu/debugger/graphics/graphics_surface.h
index 58f9db465..323e39d94 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.h
+++ b/src/yuzu/debugger/graphics/graphics_surface.h
@@ -22,11 +22,11 @@ class SurfacePicture : public QLabel {
public:
explicit SurfacePicture(QWidget* parent = nullptr,
GraphicsSurfaceWidget* surface_widget = nullptr);
- ~SurfacePicture();
+ ~SurfacePicture() override;
protected slots:
- virtual void mouseMoveEvent(QMouseEvent* event);
- virtual void mousePressEvent(QMouseEvent* event);
+ void mouseMoveEvent(QMouseEvent* event) override;
+ void mousePressEvent(QMouseEvent* event) override;
private:
GraphicsSurfaceWidget* surface_widget;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index f5a5697a0..d0926d723 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -14,7 +14,7 @@
#include "core/hle/kernel/timer.h"
#include "core/hle/kernel/wait_object.h"
-WaitTreeItem::~WaitTreeItem() {}
+WaitTreeItem::~WaitTreeItem() = default;
QColor WaitTreeItem::GetColor() const {
return QColor(Qt::GlobalColor::black);
@@ -316,7 +316,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
list.push_back(std::make_unique<WaitTreeText>(
tr("reset type = %1")
- .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).reset_type))));
+ .arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
return list;
}
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 6cbce6856..513b3c45d 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -25,11 +25,13 @@ class WaitTreeThread;
class WaitTreeItem : public QObject {
Q_OBJECT
public:
+ ~WaitTreeItem() override;
+
virtual bool IsExpandable() const;
virtual std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const;
virtual QString GetText() const = 0;
virtual QColor GetColor() const;
- virtual ~WaitTreeItem();
+
void Expand();
WaitTreeItem* Parent() const;
const std::vector<std::unique_ptr<WaitTreeItem>>& Children() const;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 71e24a9e2..85cb12594 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -9,9 +9,13 @@
#include <QKeyEvent>
#include <QMenu>
#include <QThreadPool>
+#include <boost/container/flat_map.hpp>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/loader/loader.h"
#include "game_list.h"
@@ -162,15 +166,15 @@ void GameList::onTextChanged(const QString& newText) {
}
search_field->setFilterResult(rowCount, rowCount);
} else {
- QStandardItem* child_file;
- QString file_path, file_name, file_title, file_programmid;
int result_count = 0;
for (int i = 0; i < rowCount; ++i) {
- child_file = item_model->item(i, 0);
- file_path = child_file->data(GameListItemPath::FullPathRole).toString().toLower();
- file_name = file_path.mid(file_path.lastIndexOf("/") + 1);
- file_title = child_file->data(GameListItemPath::TitleRole).toString().toLower();
- file_programmid =
+ const QStandardItem* child_file = item_model->item(i, 0);
+ const QString file_path =
+ child_file->data(GameListItemPath::FullPathRole).toString().toLower();
+ QString file_name = file_path.mid(file_path.lastIndexOf('/') + 1);
+ const QString file_title =
+ child_file->data(GameListItemPath::TitleRole).toString().toLower();
+ const QString file_programmid =
child_file->data(GameListItemPath::ProgramIdRole).toString().toLower();
// Only items which filename in combination with its title contains all words
@@ -194,7 +198,8 @@ void GameList::onFilterCloseClicked() {
main_window->filterBarSetChecked(false);
}
-GameList::GameList(GMainWindow* parent) : QWidget{parent} {
+GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
+ : QWidget{parent}, vfs(std::move(vfs)) {
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
@@ -258,18 +263,20 @@ void GameList::AddEntry(const QList<QStandardItem*>& entry_items) {
void GameList::ValidateEntry(const QModelIndex& item) {
// We don't care about the individual QStandardItem that was selected, but its row.
- int row = item_model->itemFromIndex(item)->row();
- QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
- QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
+ const int row = item_model->itemFromIndex(item)->row();
+ const QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME);
+ const QString file_path = child_file->data(GameListItemPath::FullPathRole).toString();
if (file_path.isEmpty())
return;
- std::string std_file_path(file_path.toStdString());
- if (!FileUtil::Exists(std_file_path))
+
+ if (!QFileInfo::exists(file_path))
return;
- if (FileUtil::IsDirectory(std_file_path)) {
- QDir dir(std_file_path.c_str());
- QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
+
+ const QFileInfo file_info{file_path};
+ if (file_info.isDir()) {
+ const QDir dir{file_path};
+ const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
}
@@ -336,7 +343,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
emit ShouldCancelWorker();
- GameListWorker* worker = new GameListWorker(dir_path, deep_scan);
+ GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
@@ -368,21 +375,23 @@ void GameList::LoadInterfaceLayout() {
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
static bool HasSupportedFileExtension(const std::string& file_name) {
- QFileInfo file = QFileInfo(file_name.c_str());
+ const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
static bool IsExtractedNCAMain(const std::string& file_name) {
- return QFileInfo(file_name.c_str()).fileName() == "main";
+ return QFileInfo(QString::fromStdString(file_name)).fileName() == "main";
}
static QString FormatGameName(const std::string& physical_name) {
- QFileInfo file_info(physical_name.c_str());
+ const QString physical_name_as_qstring = QString::fromStdString(physical_name);
+ const QFileInfo file_info(physical_name_as_qstring);
+
if (IsExtractedNCAMain(physical_name)) {
return file_info.dir().path();
- } else {
- return QString::fromStdString(physical_name);
}
+
+ return physical_name_as_qstring;
}
void GameList::RefreshGameDirectory() {
@@ -394,8 +403,32 @@ void GameList::RefreshGameDirectory() {
}
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 {
+ boost::container::flat_map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
+
+ const auto nca_control_callback =
+ [this, &nca_control_map](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.
+
+ bool is_dir = FileUtil::IsDirectory(physical_name);
+ QFileInfo file_info(physical_name.c_str());
+ if (!is_dir && file_info.suffix().toStdString() == "nca") {
+ auto nca =
+ std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
+ if (nca->GetType() == FileSys::NCAContentType::Control)
+ nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
+ }
+ return true;
+ };
+
+ FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
+
+ const auto callback = [this, recursion,
+ &nca_control_map](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)
@@ -405,18 +438,48 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
std::unique_ptr<Loader::AppLoader> loader =
- Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
- if (!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))
return true;
- std::vector<u8> smdh;
- loader->ReadIcon(smdh);
-
- u64 program_id = 0;
- loader->ReadProgramId(program_id);
+ std::vector<u8> icon;
+ const auto res1 = loader->ReadIcon(icon);
+
+ u64 program_id;
+ const auto res2 = loader->ReadProgramId(program_id);
+
+ std::string name = " ";
+ const auto res3 = loader->ReadTitle(name);
+
+ if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
+ res2 == Loader::ResultStatus::Success) {
+ // Use from metadata pool.
+ if (nca_control_map.find(program_id) != nca_control_map.end()) {
+ const auto nca = nca_control_map[program_id];
+ const auto control_dir = FileSys::ExtractRomFS(nca->GetRomFS());
+
+ const auto nacp_file = control_dir->GetFile("control.nacp");
+ FileSys::NACP nacp(nacp_file);
+ name = nacp.GetApplicationName();
+
+ FileSys::VirtualFile icon_file = nullptr;
+ for (const auto& language : FileSys::LANGUAGE_NAMES) {
+ icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat");
+ if (icon_file != nullptr) {
+ icon = icon_file->ReadAllBytes();
+ break;
+ }
+ }
+ }
+ }
emit EntryReady({
- new GameListItemPath(FormatGameName(physical_name), smdh, program_id),
+ new GameListItemPath(
+ FormatGameName(physical_name), icon, QString::fromStdString(name),
+ QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
+ program_id),
new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)),
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 3bc14f07f..afe624b32 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -59,7 +59,7 @@ public:
QToolButton* button_filter_close = nullptr;
};
- explicit GameList(GMainWindow* parent = nullptr);
+ explicit GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent = nullptr);
~GameList() override;
void clearFilter();
@@ -90,6 +90,7 @@ private:
void PopupContextMenu(const QPoint& menu_location);
void RefreshGameDirectory();
+ FileSys::VirtualFilesystem vfs;
SearchField* search_field;
GMainWindow* main_window = nullptr;
QVBoxLayout* layout = nullptr;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index a758b77aa..8fe5e8b80 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -4,12 +4,15 @@
#pragma once
+#include <array>
#include <atomic>
+#include <utility>
#include <QImage>
#include <QRunnable>
#include <QStandardItem>
#include <QString>
#include "common/string_util.h"
+#include "ui_settings.h"
#include "yuzu/util/util.h"
/**
@@ -17,8 +20,7 @@
* @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
* @return QPixmap default icon
*/
-static QPixmap GetDefaultIcon(bool large) {
- int size = large ? 48 : 24;
+static QPixmap GetDefaultIcon(u32 size) {
QPixmap icon(size, size);
icon.fill(Qt::transparent);
return icon;
@@ -27,9 +29,8 @@ static QPixmap GetDefaultIcon(bool large) {
class GameListItem : public QStandardItem {
public:
- GameListItem() : QStandardItem() {}
- GameListItem(const QString& string) : QStandardItem(string) {}
- virtual ~GameListItem() override {}
+ GameListItem() = default;
+ explicit GameListItem(const QString& string) : QStandardItem(string) {}
};
/**
@@ -39,17 +40,29 @@ public:
* If this class receives valid SMDH data, it will also display game icons and titles.
*/
class GameListItemPath : public GameListItem {
-
public:
static const int FullPathRole = Qt::UserRole + 1;
static const int TitleRole = Qt::UserRole + 2;
static const int ProgramIdRole = Qt::UserRole + 3;
+ static const int FileTypeRole = Qt::UserRole + 4;
- GameListItemPath() : GameListItem() {}
- GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id)
- : GameListItem() {
+ GameListItemPath() = default;
+ GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
+ const QString& game_name, const QString& game_type, u64 program_id) {
setData(game_path, FullPathRole);
+ setData(game_name, TitleRole);
setData(qulonglong(program_id), ProgramIdRole);
+ setData(game_type, FileTypeRole);
+
+ const u32 size = UISettings::values.icon_size;
+
+ QPixmap picture;
+ if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
+ picture = GetDefaultIcon(size);
+ }
+ picture = picture.scaled(size, size);
+
+ setData(picture, Qt::DecorationRole);
}
QVariant data(int role) const override {
@@ -57,11 +70,26 @@ public:
std::string filename;
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
nullptr);
- QString title = data(TitleRole).toString();
- return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
- } else {
- return GameListItem::data(role);
+
+ const std::array<QString, 4> row_data{{
+ QString::fromStdString(filename),
+ data(FileTypeRole).toString(),
+ QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
+ data(TitleRole).toString(),
+ }};
+
+ const auto& row1 = row_data.at(UISettings::values.row_1_text_id);
+ const auto& row2 = row_data.at(UISettings::values.row_2_text_id);
+
+ if (row1.isEmpty() || row1 == row2)
+ return row2;
+ if (row2.isEmpty())
+ return row1;
+
+ return row1 + "\n " + row2;
}
+
+ return GameListItem::data(role);
}
};
@@ -75,8 +103,8 @@ class GameListItemSize : public GameListItem {
public:
static const int SizeRole = Qt::UserRole + 1;
- GameListItemSize() : GameListItem() {}
- GameListItemSize(const qulonglong size_bytes) : GameListItem() {
+ GameListItemSize() = default;
+ explicit GameListItemSize(const qulonglong size_bytes) {
setData(size_bytes, SizeRole);
}
@@ -110,8 +138,8 @@ class GameListWorker : public QObject, public QRunnable {
Q_OBJECT
public:
- GameListWorker(QString dir_path, bool deep_scan)
- : QObject(), QRunnable(), dir_path(dir_path), deep_scan(deep_scan) {}
+ GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
+ : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
public slots:
/// Starts the processing of directory tree information.
@@ -134,6 +162,7 @@ signals:
void Finished(QStringList watch_list);
private:
+ FileSys::VirtualFilesystem vfs;
QStringList watch_list;
QString dir_path;
bool deep_scan;
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 61acb38ee..dce399774 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -10,58 +10,53 @@
#include "yuzu/hotkeys.h"
#include "yuzu/ui_settings.h"
-struct Hotkey {
- Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {}
+HotkeyRegistry::HotkeyRegistry() = default;
+HotkeyRegistry::~HotkeyRegistry() = default;
- QKeySequence keyseq;
- QShortcut* shortcut;
- Qt::ShortcutContext context;
-};
-
-typedef std::map<QString, Hotkey> HotkeyMap;
-typedef std::map<QString, HotkeyMap> HotkeyGroupMap;
-
-HotkeyGroupMap hotkey_groups;
-
-void SaveHotkeys() {
- UISettings::values.shortcuts.clear();
- for (auto group : hotkey_groups) {
- for (auto hotkey : group.second) {
- UISettings::values.shortcuts.emplace_back(
- UISettings::Shortcut(group.first + "/" + hotkey.first,
- UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
- hotkey.second.context)));
- }
- }
-}
-
-void LoadHotkeys() {
+void HotkeyRegistry::LoadHotkeys() {
// Make sure NOT to use a reference here because it would become invalid once we call
// beginGroup()
for (auto shortcut : UISettings::values.shortcuts) {
- QStringList cat = shortcut.first.split("/");
+ const QStringList cat = shortcut.first.split('/');
Q_ASSERT(cat.size() >= 2);
// RegisterHotkey assigns default keybindings, so use old values as default parameters
Hotkey& hk = hotkey_groups[cat[0]][cat[1]];
if (!shortcut.second.first.isEmpty()) {
hk.keyseq = QKeySequence::fromString(shortcut.second.first);
- hk.context = (Qt::ShortcutContext)shortcut.second.second;
+ hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second);
}
if (hk.shortcut)
hk.shortcut->setKey(hk.keyseq);
}
}
-void RegisterHotkey(const QString& group, const QString& action, const QKeySequence& default_keyseq,
- Qt::ShortcutContext default_context) {
- if (hotkey_groups[group].find(action) == hotkey_groups[group].end()) {
- hotkey_groups[group][action].keyseq = default_keyseq;
- hotkey_groups[group][action].context = default_context;
+void HotkeyRegistry::SaveHotkeys() {
+ UISettings::values.shortcuts.clear();
+ for (const auto& group : hotkey_groups) {
+ for (const auto& hotkey : group.second) {
+ UISettings::values.shortcuts.emplace_back(
+ UISettings::Shortcut(group.first + '/' + hotkey.first,
+ UISettings::ContextualShortcut(hotkey.second.keyseq.toString(),
+ hotkey.second.context)));
+ }
}
}
-QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget) {
+void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action,
+ const QKeySequence& default_keyseq,
+ Qt::ShortcutContext default_context) {
+ auto& hotkey_group = hotkey_groups[group];
+ if (hotkey_group.find(action) != hotkey_group.end()) {
+ return;
+ }
+
+ auto& hotkey_action = hotkey_groups[group][action];
+ hotkey_action.keyseq = default_keyseq;
+ hotkey_action.context = default_context;
+}
+
+QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) {
Hotkey& hk = hotkey_groups[group][action];
if (!hk.shortcut)
@@ -72,10 +67,12 @@ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widge
GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) {
ui.setupUi(this);
+}
- for (auto group : hotkey_groups) {
+void GHotkeysDialog::Populate(const HotkeyRegistry& registry) {
+ for (const auto& group : registry.hotkey_groups) {
QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first));
- for (auto hotkey : group.second) {
+ for (const auto& hotkey : group.second) {
QStringList columns;
columns << hotkey.first << hotkey.second.keyseq.toString();
QTreeWidgetItem* item = new QTreeWidgetItem(columns);
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index a4ccc193b..f38e6c002 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -4,6 +4,7 @@
#pragma once
+#include <map>
#include "ui_hotkeys.h"
class QDialog;
@@ -11,47 +12,69 @@ class QKeySequence;
class QSettings;
class QShortcut;
-/**
- * Register a hotkey.
- *
- * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
- * @param action Name of the action (e.g. "Start Emulation", "Load Image")
- * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the settings
- * file before
- * @param default_context Default context to assign if the hotkey wasn't present in the settings
- * file before
- * @warning Both the group and action strings will be displayed in the hotkey settings dialog
- */
-void RegisterHotkey(const QString& group, const QString& action,
- const QKeySequence& default_keyseq = QKeySequence(),
- Qt::ShortcutContext default_context = Qt::WindowShortcut);
-
-/**
- * Returns a QShortcut object whose activated() signal can be connected to other QObjects' slots.
- *
- * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
- * @param action Name of the action (e.g. "Start Emulation", "Load Image").
- * @param widget Parent widget of the returned QShortcut.
- * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
- * will be the same. Thus, you shouldn't rely on the caller really being the QShortcut's parent.
- */
-QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
-
-/**
- * Saves all registered hotkeys to the settings file.
- *
- * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
- * settings group will be created to store the key sequence and the hotkey context.
- */
-void SaveHotkeys();
-
-/**
- * Loads hotkeys from the settings file.
- *
- * @note Yet unregistered hotkeys which are present in the settings will automatically be
- * registered.
- */
-void LoadHotkeys();
+class HotkeyRegistry final {
+public:
+ friend class GHotkeysDialog;
+
+ explicit HotkeyRegistry();
+ ~HotkeyRegistry();
+
+ /**
+ * Loads hotkeys from the settings file.
+ *
+ * @note Yet unregistered hotkeys which are present in the settings will automatically be
+ * registered.
+ */
+ void LoadHotkeys();
+
+ /**
+ * Saves all registered hotkeys to the settings file.
+ *
+ * @note Each hotkey group will be stored a settings group; For each hotkey inside that group, a
+ * settings group will be created to store the key sequence and the hotkey context.
+ */
+ void SaveHotkeys();
+
+ /**
+ * Returns a QShortcut object whose activated() signal can be connected to other QObjects'
+ * slots.
+ *
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger").
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image").
+ * @param widget Parent widget of the returned QShortcut.
+ * @warning If multiple QWidgets' call this function for the same action, the returned QShortcut
+ * will be the same. Thus, you shouldn't rely on the caller really being the
+ * QShortcut's parent.
+ */
+ QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget);
+
+ /**
+ * Register a hotkey.
+ *
+ * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger")
+ * @param action Name of the action (e.g. "Start Emulation", "Load Image")
+ * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the
+ * settings file before
+ * @param default_context Default context to assign if the hotkey wasn't present in the settings
+ * file before
+ * @warning Both the group and action strings will be displayed in the hotkey settings dialog
+ */
+ void RegisterHotkey(const QString& group, const QString& action,
+ const QKeySequence& default_keyseq = {},
+ Qt::ShortcutContext default_context = Qt::WindowShortcut);
+
+private:
+ struct Hotkey {
+ QKeySequence keyseq;
+ QShortcut* shortcut = nullptr;
+ Qt::ShortcutContext context = Qt::WindowShortcut;
+ };
+
+ using HotkeyMap = std::map<QString, Hotkey>;
+ using HotkeyGroupMap = std::map<QString, HotkeyMap>;
+
+ HotkeyGroupMap hotkey_groups;
+};
class GHotkeysDialog : public QWidget {
Q_OBJECT
@@ -59,6 +82,8 @@ class GHotkeysDialog : public QWidget {
public:
explicit GHotkeysDialog(QWidget* parent = nullptr);
+ void Populate(const HotkeyRegistry& registry);
+
private:
Ui::hotkeys ui;
};
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e28679cd1..94fb8ae6a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -24,6 +24,7 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
+#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "core/settings.h"
@@ -81,7 +82,11 @@ static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
void GMainWindow::ShowCallouts() {}
-GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) {
+const int GMainWindow::max_recent_files_item;
+
+GMainWindow::GMainWindow()
+ : config(new Config()), emu_thread(nullptr),
+ vfs(std::make_shared<FileSys::RealVfsFilesystem>()) {
debug_context = Tegra::DebugContext::Construct();
@@ -130,7 +135,7 @@ void GMainWindow::InitializeWidgets() {
render_window = new GRenderWindow(this, emu_thread.get());
render_window->hide();
- game_list = new GameList(this);
+ game_list = new GameList(vfs, this);
ui.horizontalLayout->addWidget(game_list);
// Create status bar
@@ -206,43 +211,46 @@ void GMainWindow::InitializeRecentFileMenuActions() {
}
void GMainWindow::InitializeHotkeys() {
- RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
- RegisterHotkey("Main Window", "Start Emulation");
- RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
- RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
- RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
- Qt::ApplicationShortcut);
- RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
- Qt::ApplicationShortcut);
- LoadHotkeys();
-
- connect(GetHotkey("Main Window", "Load File", this), &QShortcut::activated, this,
- &GMainWindow::OnMenuLoadFile);
- connect(GetHotkey("Main Window", "Start Emulation", this), &QShortcut::activated, this,
- &GMainWindow::OnStartGame);
- connect(GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, this, [&] {
- if (emulation_running) {
- if (emu_thread->IsRunning()) {
- OnPauseGame();
- } else {
- OnStartGame();
- }
- }
- });
- connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activated,
- ui.action_Fullscreen, &QAction::trigger);
- connect(GetHotkey("Main Window", "Fullscreen", render_window), &QShortcut::activatedAmbiguously,
- ui.action_Fullscreen, &QAction::trigger);
- connect(GetHotkey("Main Window", "Exit Fullscreen", this), &QShortcut::activated, this, [&] {
- if (emulation_running) {
- ui.action_Fullscreen->setChecked(false);
- ToggleFullscreen();
- }
- });
- connect(GetHotkey("Main Window", "Toggle Speed Limit", this), &QShortcut::activated, this, [&] {
- Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
- UpdateStatusBar();
- });
+ hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
+ hotkey_registry.RegisterHotkey("Main Window", "Start Emulation");
+ hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4));
+ hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen);
+ hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape),
+ Qt::ApplicationShortcut);
+ hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"),
+ Qt::ApplicationShortcut);
+ hotkey_registry.LoadHotkeys();
+
+ connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
+ this, &GMainWindow::OnMenuLoadFile);
+ connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this),
+ &QShortcut::activated, this, &GMainWindow::OnStartGame);
+ connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated,
+ this, [&] {
+ if (emulation_running) {
+ if (emu_thread->IsRunning()) {
+ OnPauseGame();
+ } else {
+ OnStartGame();
+ }
+ }
+ });
+ connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
+ &QShortcut::activated, ui.action_Fullscreen, &QAction::trigger);
+ connect(hotkey_registry.GetHotkey("Main Window", "Fullscreen", render_window),
+ &QShortcut::activatedAmbiguously, ui.action_Fullscreen, &QAction::trigger);
+ connect(hotkey_registry.GetHotkey("Main Window", "Exit Fullscreen", this),
+ &QShortcut::activated, this, [&] {
+ if (emulation_running) {
+ ui.action_Fullscreen->setChecked(false);
+ ToggleFullscreen();
+ }
+ });
+ connect(hotkey_registry.GetHotkey("Main Window", "Toggle Speed Limit", this),
+ &QShortcut::activated, this, [&] {
+ Settings::values.toggle_framelimit = !Settings::values.toggle_framelimit;
+ UpdateStatusBar();
+ });
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -321,7 +329,8 @@ void GMainWindow::ConnectMenuEvents() {
connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible);
// Fullscreen
- ui.action_Fullscreen->setShortcut(GetHotkey("Main Window", "Fullscreen", this)->key());
+ ui.action_Fullscreen->setShortcut(
+ hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
// Help
@@ -400,6 +409,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
}
Core::System& system{Core::System::GetInstance()};
+ system.SetFilesystem(vfs);
system.SetGPUDebugContext(debug_context);
@@ -414,67 +424,11 @@ bool GMainWindow::LoadROM(const QString& filename) {
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("The ROM format is not supported."));
break;
- case Core::System::ResultStatus::ErrorUnsupportedArch:
- LOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString());
- QMessageBox::critical(this, tr("Error while loading ROM!"),
- tr("The ROM uses currently unusable 32-bit architecture"));
- break;
case Core::System::ResultStatus::ErrorSystemMode:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
QMessageBox::critical(this, tr("Error while loading ROM!"),
tr("Could not determine the system mode."));
break;
-
- case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: {
- const auto reg_found = Core::Crypto::KeyManager::KeyFileExists(false);
- const auto title_found = Core::Crypto::KeyManager::KeyFileExists(true);
-
- std::string file_text;
-
- if (!reg_found && !title_found) {
- file_text = "A proper key file (prod.keys, dev.keys, or title.keys) could not be "
- "found. You will need to dump your keys from your switch to continue.";
- } else if (reg_found && title_found) {
- file_text =
- "Both key files were found in your config directory, but the correct key could"
- "not be found. You may be missing a titlekey or general key, depending on "
- "the game.";
- } else if (reg_found) {
- file_text =
- "The regular keys file (prod.keys/dev.keys) was found in your config, but the "
- "titlekeys file (title.keys) was not. You are either missing the correct "
- "titlekey or missing a general key required to decrypt the game.";
- } else {
- file_text = "The title keys file (title.keys) was found in your config, but "
- "the regular keys file (prod.keys/dev.keys) was not. Unfortunately, "
- "having the titlekey is not enough, you need additional general keys "
- "to properly decrypt the game. You should double-check to make sure "
- "your keys are correct.";
- }
-
- QMessageBox::critical(
- this, tr("Error while loading ROM!"),
- tr(("The game you are trying to load is encrypted and the required keys to load "
- "the game could not be found in your configuration. " +
- file_text + " Please refer to the yuzu wiki for help.")
- .c_str()));
- break;
- }
- case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: {
- QMessageBox::critical(
- this, tr("Error while loading ROM!"),
- tr("There was a general error while decrypting the game. This means that the keys "
- "necessary were found, but were either incorrect, the game itself was not a "
- "valid game or the game uses an unhandled cryptographic scheme. Please double "
- "check that you have the correct "
- "keys."));
- break;
- }
- case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
- QMessageBox::critical(this, tr("Error while loading ROM!"),
- tr("The ROM format is not supported."));
- break;
-
case Core::System::ResultStatus::ErrorVideoCore:
QMessageBox::critical(
this, tr("An error occurred initializing the video core."),
@@ -489,9 +443,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
break;
default:
- QMessageBox::critical(
- this, tr("Error while loading ROM!"),
- tr("An unknown error occurred. Please see the log for more details."));
+ if (static_cast<u32>(result) >
+ static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
+ LOG_CRITICAL(Frontend, "Failed to load ROM!");
+ const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
+ const u16 error_id = static_cast<u16>(result) - loader_id;
+ QMessageBox::critical(
+ this, tr("Error while loading ROM!"),
+ QString::fromStdString(fmt::format(
+ "While attempting to load the ROM requested, an error occured. Please "
+ "refer to the yuzu wiki for more information or the yuzu discord for "
+ "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
+ loader_id, error_id, Loader::GetMessageForResultStatus(error_id))));
+ } else {
+ QMessageBox::critical(
+ this, tr("Error while loading ROM!"),
+ tr("An unknown error occurred. Please see the log for more details."));
+ }
break;
}
return false;
@@ -579,11 +547,11 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
}
void GMainWindow::UpdateRecentFiles() {
- unsigned int num_recent_files =
- std::min(UISettings::values.recent_files.size(), static_cast<int>(max_recent_files_item));
+ const int num_recent_files =
+ std::min(UISettings::values.recent_files.size(), max_recent_files_item);
- for (unsigned int i = 0; i < num_recent_files; i++) {
- QString text = QString("&%1. %2").arg(i + 1).arg(
+ for (int i = 0; i < num_recent_files; i++) {
+ const QString text = QString("&%1. %2").arg(i + 1).arg(
QFileInfo(UISettings::values.recent_files[i]).fileName());
actions_recent_files[i]->setText(text);
actions_recent_files[i]->setData(UISettings::values.recent_files[i]);
@@ -595,12 +563,8 @@ void GMainWindow::UpdateRecentFiles() {
actions_recent_files[j]->setVisible(false);
}
- // Grey out the recent files menu if the list is empty
- if (num_recent_files == 0) {
- ui.menu_recent_files->setEnabled(false);
- } else {
- ui.menu_recent_files->setEnabled(true);
- }
+ // Enable the recent files menu if the list isn't empty
+ ui.menu_recent_files->setEnabled(num_recent_files != 0);
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
@@ -631,9 +595,15 @@ void GMainWindow::OnMenuLoadFile() {
}
void GMainWindow::OnMenuLoadFolder() {
- QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
+ const QString dir_path =
+ QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
+
+ if (dir_path.isNull()) {
+ return;
+ }
- QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
+ const QDir dir{dir_path};
+ const QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
BootGame(dir.path() + DIR_SEP + matching_main[0]);
} else {
@@ -654,9 +624,8 @@ void GMainWindow::OnMenuRecentFile() {
QAction* action = qobject_cast<QAction*>(sender());
assert(action);
- QString filename = action->data().toString();
- QFileInfo file_info(filename);
- if (file_info.exists()) {
+ const QString filename = action->data().toString();
+ if (QFileInfo::exists(filename)) {
BootGame(filename);
} else {
// Display an error message and remove the file from the list.
@@ -754,13 +723,14 @@ void GMainWindow::ToggleWindowMode() {
}
void GMainWindow::OnConfigure() {
- ConfigureDialog configureDialog(this);
+ ConfigureDialog configureDialog(this, hotkey_registry);
auto old_theme = UISettings::values.theme;
auto result = configureDialog.exec();
if (result == QDialog::Accepted) {
configureDialog.applyConfiguration();
if (UISettings::values.theme != old_theme)
UpdateUITheme();
+ game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
config->Save();
}
}
@@ -893,7 +863,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
UISettings::values.first_start = false;
game_list->SaveInterfaceLayout();
- SaveHotkeys();
+ hotkey_registry.SaveHotkeys();
// Shutdown session if the emu thread is active...
if (emu_thread != nullptr)
@@ -947,15 +917,14 @@ void GMainWindow::UpdateUITheme() {
QStringList theme_paths(default_theme_paths);
if (UISettings::values.theme != UISettings::themes[0].second &&
!UISettings::values.theme.isEmpty()) {
- QString theme_uri(":" + UISettings::values.theme + "/style.qss");
+ const QString theme_uri(":" + UISettings::values.theme + "/style.qss");
QFile f(theme_uri);
- if (!f.exists()) {
- LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
- } else {
- f.open(QFile::ReadOnly | QFile::Text);
+ if (f.open(QFile::ReadOnly | QFile::Text)) {
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
GMainWindow::setStyleSheet(ts.readAll());
+ } else {
+ LOG_ERROR(Frontend, "Unable to set style, stylesheet file not found");
}
theme_paths.append(QStringList{":/icons/default", ":/icons/" + UISettings::values.theme});
QIcon::setThemeName(":/icons/" + UISettings::values.theme);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 074bba3f9..74487c58c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -9,6 +9,7 @@
#include <QTimer>
#include "core/core.h"
#include "ui_main.h"
+#include "yuzu/hotkeys.h"
class Config;
class EmuThread;
@@ -43,7 +44,7 @@ public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
GMainWindow();
- ~GMainWindow();
+ ~GMainWindow() override;
signals:
@@ -160,6 +161,9 @@ private:
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
+ // FS
+ FileSys::VirtualFilesystem vfs;
+
// Debugger panes
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;
@@ -172,6 +176,8 @@ private:
// stores default icon theme search paths for the platform
QStringList default_theme_paths;
+ HotkeyRegistry hotkey_registry;
+
protected:
void dropEvent(QDropEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h
index 2286c2559..051494bc5 100644
--- a/src/yuzu/ui_settings.h
+++ b/src/yuzu/ui_settings.h
@@ -54,6 +54,12 @@ struct Values {
// logging
bool show_console;
+
+ // Game List
+ bool show_unknown;
+ uint32_t icon_size;
+ uint8_t row_1_text_id;
+ uint8_t row_2_text_id;
};
extern Values values;