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/configuration/config.cpp14
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp61
-rw-r--r--src/yuzu/configuration/configure_gamelist.h28
-rw-r--r--src/yuzu/configuration/configure_gamelist.ui126
-rw-r--r--src/yuzu/game_list.cpp77
-rw-r--r--src/yuzu/game_list_p.h38
-rw-r--r--src/yuzu/main.cpp1
-rw-r--r--src/yuzu/ui_settings.h6
11 files changed, 353 insertions, 13 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/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_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 1ca7e876c..d04aa4e31 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -18,6 +18,7 @@ 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_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp
new file mode 100644
index 000000000..072b3f96f
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -0,0 +1,61 @@
+// 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 std::vector<std::pair<u32, std::string>> default_icon_sizes{
+ std::make_pair(0, "None"), std::make_pair(24, "Small"),
+ std::make_pair(48, "Standard"), std::make_pair(96, "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 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]), i);
+ ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]), 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/game_list.cpp b/src/yuzu/game_list.cpp
index 24f38a3c7..893c5f693 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -12,6 +12,8 @@
#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/vfs_real.h"
#include "core/loader/loader.h"
#include "game_list.h"
@@ -398,8 +400,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>(
+ std::make_shared<FileSys::RealVfsFile>(physical_name));
+ 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)
@@ -410,17 +436,50 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
std::unique_ptr<Loader::AppLoader> loader =
Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
- if (!loader)
+ 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::ErrorNotUsed ||
+ res1 == Loader::ResultStatus::ErrorNotImplemented) &&
+ (res3 == Loader::ResultStatus::ErrorNotUsed ||
+ res3 == Loader::ResultStatus::ErrorNotImplemented) &&
+ 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];
+ auto control_dir = nca->GetSection(0);
+
+ 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_" + 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_p.h b/src/yuzu/game_list_p.h
index aa69a098f..a22025e67 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -11,6 +11,7 @@
#include <QStandardItem>
#include <QString>
#include "common/string_util.h"
+#include "ui_settings.h"
#include "yuzu/util/util.h"
/**
@@ -18,8 +19,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;
@@ -44,11 +44,25 @@ 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() = default;
- GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) {
+ GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
+ const QString& game_name, const QString& game_type, u64 program_id)
+ : GameListItem() {
setData(game_path, FullPathRole);
+ setData(game_name, TitleRole);
setData(qulonglong(program_id), ProgramIdRole);
+ setData(game_type, FileTypeRole);
+
+ QPixmap picture;
+ u32 size = UISettings::values.icon_size;
+ if (!picture.loadFromData(picture_data.data(), picture_data.size()))
+ picture = GetDefaultIcon(size);
+
+ picture = picture.scaled(size, size);
+
+ setData(picture, Qt::DecorationRole);
}
QVariant data(int role) const override {
@@ -57,7 +71,23 @@ public:
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
nullptr);
QString title = data(TitleRole).toString();
- return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title);
+
+ std::vector<QString> row_data{
+ QString::fromStdString(filename),
+ data(FileTypeRole).toString(),
+ QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
+ data(TitleRole).toString(),
+ };
+
+ auto row1 = row_data.at(UISettings::values.row_1_text_id);
+ 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;
} else {
return GameListItem::data(role);
}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index dd71bd763..3cba6f403 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -764,6 +764,7 @@ void GMainWindow::OnConfigure() {
configureDialog.applyConfiguration();
if (UISettings::values.theme != old_theme)
UpdateUITheme();
+ game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
config->Save();
}
}
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;