summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeModules/CopyYuzuQt5Deps.cmake3
-rw-r--r--appveyor.yml4
-rw-r--r--src/core/file_sys/card_image.cpp1
-rw-r--r--src/core/file_sys/content_archive.cpp4
-rw-r--r--src/core/file_sys/content_archive.h1
-rw-r--r--src/core/file_sys/control_metadata.h7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp68
-rw-r--r--src/core/loader/deconstructed_rom_directory.h7
-rw-r--r--src/core/loader/loader.cpp2
-rw-r--r--src/core/loader/loader.h2
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/xci.cpp33
-rw-r--r--src/core/loader/xci.h5
-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.cpp63
-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.cpp78
-rw-r--r--src/yuzu/game_list_p.h38
-rw-r--r--src/yuzu/main.cpp1
-rw-r--r--src/yuzu/ui_settings.h6
25 files changed, 491 insertions, 21 deletions
diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake
index e4a9796c8..aaf80b77b 100644
--- a/CMakeModules/CopyYuzuQt5Deps.cmake
+++ b/CMakeModules/CopyYuzuQt5Deps.cmake
@@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
4 set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") 4 set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
5 set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") 5 set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
6 set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") 6 set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
7 set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
7 set(PLATFORMS ${DLL_DEST}platforms/) 8 set(PLATFORMS ${DLL_DEST}platforms/)
8 set(STYLES ${DLL_DEST}styles/) 9 set(STYLES ${DLL_DEST}styles/)
10 set(IMAGEFORMATS ${DLL_DEST}imageformats/)
9 windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} 11 windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
10 icudt*.dll 12 icudt*.dll
11 icuin*.dll 13 icuin*.dll
@@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
17 ) 19 )
18 windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) 20 windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
19 windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) 21 windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
22 windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
20endfunction(copy_yuzu_Qt5_deps) 23endfunction(copy_yuzu_Qt5_deps)
diff --git a/appveyor.yml b/appveyor.yml
index 17d1b5fee..a6f12b267 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -117,6 +117,7 @@ after_build:
117 mkdir $RELEASE_DIST 117 mkdir $RELEASE_DIST
118 mkdir $RELEASE_DIST/platforms 118 mkdir $RELEASE_DIST/platforms
119 mkdir $RELEASE_DIST/styles 119 mkdir $RELEASE_DIST/styles
120 mkdir $RELEASE_DIST/imageformats
120 121
121 # copy the compiled binaries and other release files to the release folder 122 # copy the compiled binaries and other release files to the release folder
122 Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST 123 Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
@@ -140,6 +141,9 @@ after_build:
140 # copy the qt windows vista style dll to platforms 141 # copy the qt windows vista style dll to platforms
141 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" 142 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
142 143
144 # copy the qt jpeg imageformat dll to platforms
145 Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
146
143 7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* 147 7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
144 7z a $MINGW_SEVENZIP $RELEASE_DIST 148 7z a $MINGW_SEVENZIP $RELEASE_DIST
145 } 149 }
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 395eea8ae..e897d9913 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -5,6 +5,7 @@
5#include <array> 5#include <array>
6#include <string> 6#include <string>
7#include <core/loader/loader.h> 7#include <core/loader/loader.h>
8#include "common/logging/log.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/partition_filesystem.h" 10#include "core/file_sys/partition_filesystem.h"
10#include "core/file_sys/vfs_offset.h" 11#include "core/file_sys/vfs_offset.h"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 3529166ac..d3007d981 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
170} 170}
171 171
172NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 172NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
173 if (file == nullptr) {
174 status = Loader::ResultStatus::ErrorInvalidFormat;
175 return;
176 }
173 if (sizeof(NCAHeader) != file->ReadObject(&header)) 177 if (sizeof(NCAHeader) != file->ReadObject(&header))
174 LOG_ERROR(Loader, "File reader errored out during header read."); 178 LOG_ERROR(Loader, "File reader errored out during header read.");
175 179
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index a8879d9a8..5cfd5031a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -12,6 +12,7 @@
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/swap.h" 14#include "common/swap.h"
15#include "control_metadata.h"
15#include "core/crypto/key_manager.h" 16#include "core/crypto/key_manager.h"
16#include "core/file_sys/partition_filesystem.h" 17#include "core/file_sys/partition_filesystem.h"
17#include "core/loader/loader.h" 18#include "core/loader/loader.h"
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index cc3b745f7..6582cc240 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -62,6 +62,13 @@ enum class Language : u8 {
62 Chinese = 14, 62 Chinese = 14,
63}; 63};
64 64
65static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
66 "AmericanEnglish", "BritishEnglish", "Japanese",
67 "French", "German", "LatinAmericanSpanish",
68 "Spanish", "Italian", "Dutch",
69 "CanadianFrench", "Portugese", "Russian",
70 "Korean", "Taiwanese", "Chinese"};
71
65// A class representing the format used by NX metadata files, typically named Control.nacp. 72// A class representing the format used by NX metadata files, typically named Control.nacp.
66// These store application name, dev name, title id, and other miscellaneous data. 73// These store application name, dev name, title id, and other miscellaneous data.
67class NACP { 74class NACP {
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 9a8cdd0ff..915d525b0 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,7 @@
7#include "common/file_util.h" 7#include "common/file_util.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/gdbstub/gdbstub.h" 11#include "core/gdbstub/gdbstub.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
12#include "core/hle/kernel/resource_limit.h" 13#include "core/hle/kernel/resource_limit.h"
@@ -17,8 +18,50 @@
17 18
18namespace Loader { 19namespace Loader {
19 20
20AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) 21AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
21 : AppLoader(std::move(file)) {} 22 : AppLoader(std::move(file_)) {
23 const auto dir = file->GetContainingDirectory();
24
25 // Icon
26 FileSys::VirtualFile icon_file = nullptr;
27 for (const auto& language : FileSys::LANGUAGE_NAMES) {
28 icon_file = dir->GetFile("icon_" + std::string(language) + ".dat");
29 if (icon_file != nullptr) {
30 icon_data = icon_file->ReadAllBytes();
31 break;
32 }
33 }
34
35 if (icon_data.empty()) {
36 // Any png, jpeg, or bmp file
37 const auto& files = dir->GetFiles();
38 const auto icon_iter =
39 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
40 return file->GetExtension() == "png" || file->GetExtension() == "jpg" ||
41 file->GetExtension() == "bmp" || file->GetExtension() == "jpeg";
42 });
43 if (icon_iter != files.end())
44 icon_data = (*icon_iter)->ReadAllBytes();
45 }
46
47 // Metadata
48 FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp");
49 if (nacp_file == nullptr) {
50 const auto& files = dir->GetFiles();
51 const auto nacp_iter =
52 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
53 return file->GetExtension() == "nacp";
54 });
55 if (nacp_iter != files.end())
56 nacp_file = *nacp_iter;
57 }
58
59 if (nacp_file != nullptr) {
60 FileSys::NACP nacp(nacp_file);
61 title_id = nacp.GetTitleId();
62 name = nacp.GetApplicationName();
63 }
64}
22 65
23AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( 66AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
24 FileSys::VirtualDir directory) 67 FileSys::VirtualDir directory)
@@ -105,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile
105 return ResultStatus::Success; 148 return ResultStatus::Success;
106} 149}
107 150
151ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
152 if (icon_data.empty())
153 return ResultStatus::ErrorNotUsed;
154 buffer = icon_data;
155 return ResultStatus::Success;
156}
157
158ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
159 if (name.empty())
160 return ResultStatus::ErrorNotUsed;
161 out_program_id = title_id;
162 return ResultStatus::Success;
163}
164
165ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
166 if (name.empty())
167 return ResultStatus::ErrorNotUsed;
168 title = name;
169 return ResultStatus::Success;
170}
171
108} // namespace Loader 172} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 7d5433563..b20804f75 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -39,11 +39,18 @@ public:
39 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 39 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
40 40
41 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 41 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override;
44 ResultStatus ReadTitle(std::string& title) override;
42 45
43private: 46private:
44 FileSys::ProgramMetadata metadata; 47 FileSys::ProgramMetadata metadata;
45 FileSys::VirtualFile romfs; 48 FileSys::VirtualFile romfs;
46 FileSys::VirtualDir dir; 49 FileSys::VirtualDir dir;
50
51 std::vector<u8> icon_data;
52 std::string name;
53 u64 title_id{};
47}; 54};
48 55
49} // namespace Loader 56} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 57e6c0365..0781fb8c1 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -68,7 +68,7 @@ FileType GuessFromFilename(const std::string& name) {
68 return FileType::Unknown; 68 return FileType::Unknown;
69} 69}
70 70
71const char* GetFileTypeString(FileType type) { 71std::string GetFileTypeString(FileType type) {
72 switch (type) { 72 switch (type) {
73 case FileType::ELF: 73 case FileType::ELF:
74 return "ELF"; 74 return "ELF";
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index e69ab85ef..7bd0adedb 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -61,7 +61,7 @@ FileType GuessFromFilename(const std::string& name);
61/** 61/**
62 * Convert a FileType into a string which can be displayed to the user. 62 * Convert a FileType into a string which can be displayed to the user.
63 */ 63 */
64const char* GetFileTypeString(FileType type); 64std::string GetFileTypeString(FileType type);
65 65
66/// Return type for functions in Loader namespace 66/// Return type for functions in Loader namespace
67enum class ResultStatus { 67enum class ResultStatus {
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index dbc67c0b5..46f5cd393 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -77,8 +77,8 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
77} 77}
78 78
79ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { 79ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
80 if (nca == nullptr) 80 if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
81 return ResultStatus::ErrorNotLoaded; 81 return ResultStatus::ErrorInvalidFormat;
82 out_program_id = nca->GetTitleId(); 82 out_program_id = nca->GetTitleId();
83 return ResultStatus::Success; 83 return ResultStatus::Success;
84} 84}
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 0fd2d0417..7f7d8ea0b 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,6 @@ public:
33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
34 34
35 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 35 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
36
37 ResultStatus ReadProgramId(u64& out_program_id) override; 36 ResultStatus ReadProgramId(u64& out_program_id) override;
38 37
39 ~AppLoader_NCA(); 38 ~AppLoader_NCA();
@@ -41,6 +40,7 @@ public:
41private: 40private:
42 FileSys::ProgramMetadata metadata; 41 FileSys::ProgramMetadata metadata;
43 42
43 FileSys::NCAHeader header;
44 std::unique_ptr<FileSys::NCA> nca; 44 std::unique_ptr<FileSys::NCA> nca;
45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; 45 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
46}; 46};
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index eb4dee2c2..d3fe24419 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -26,7 +26,25 @@ namespace Loader {
26AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) 26AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
28 nca_loader(std::make_unique<AppLoader_NCA>( 28 nca_loader(std::make_unique<AppLoader_NCA>(
29 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} 29 xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
30 if (xci->GetStatus() != ResultStatus::Success)
31 return;
32 const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
33 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
34 return;
35 const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
36 if (romfs == nullptr)
37 return;
38 for (const auto& language : FileSys::LANGUAGE_NAMES) {
39 icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
40 if (icon_file != nullptr)
41 break;
42 }
43 const auto nacp_raw = romfs->GetFile("control.nacp");
44 if (nacp_raw == nullptr)
45 return;
46 nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
47}
30 48
31AppLoader_XCI::~AppLoader_XCI() = default; 49AppLoader_XCI::~AppLoader_XCI() = default;
32 50
@@ -71,4 +89,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
71 return nca_loader->ReadProgramId(out_program_id); 89 return nca_loader->ReadProgramId(out_program_id);
72} 90}
73 91
92ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
93 if (icon_file == nullptr)
94 return ResultStatus::ErrorInvalidFormat;
95 buffer = icon_file->ReadAllBytes();
96 return ResultStatus::Success;
97}
98
99ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
100 if (nacp_file == nullptr)
101 return ResultStatus::ErrorInvalidFormat;
102 title = nacp_file->GetApplicationName();
103 return ResultStatus::Success;
104}
74} // namespace Loader 105} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 0dbcfbdf8..973833050 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -33,12 +33,17 @@ public:
33 33
34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
35 ResultStatus ReadProgramId(u64& out_program_id) override; 35 ResultStatus ReadProgramId(u64& out_program_id) override;
36 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
37 ResultStatus ReadTitle(std::string& title) override;
36 38
37private: 39private:
38 FileSys::ProgramMetadata metadata; 40 FileSys::ProgramMetadata metadata;
39 41
40 std::unique_ptr<FileSys::XCI> xci; 42 std::unique_ptr<FileSys::XCI> xci;
41 std::unique_ptr<AppLoader_NCA> nca_loader; 43 std::unique_ptr<AppLoader_NCA> nca_loader;
44
45 FileSys::VirtualFile icon_file;
46 std::shared_ptr<FileSys::NACP> nacp_file;
42}; 47};
43 48
44} // namespace Loader 49} // namespace Loader
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
17 configuration/configure_debug.h 17 configuration/configure_debug.h
18 configuration/configure_dialog.cpp 18 configuration/configure_dialog.cpp
19 configuration/configure_dialog.h 19 configuration/configure_dialog.h
20 configuration/configure_gamelist.cpp
21 configuration/configure_gamelist.h
20 configuration/configure_general.cpp 22 configuration/configure_general.cpp
21 configuration/configure_general.h 23 configuration/configure_general.h
22 configuration/configure_graphics.cpp 24 configuration/configure_graphics.cpp
@@ -59,6 +61,7 @@ set(UIS
59 configuration/configure.ui 61 configuration/configure.ui
60 configuration/configure_audio.ui 62 configuration/configure_audio.ui
61 configuration/configure_debug.ui 63 configuration/configure_debug.ui
64 configuration/configure_gamelist.ui
62 configuration/configure_general.ui 65 configuration/configure_general.ui
63 configuration/configure_graphics.ui 66 configuration/configure_graphics.ui
64 configuration/configure_input.ui 67 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() {
122 qt_config->beginGroup("UI"); 122 qt_config->beginGroup("UI");
123 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); 123 UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString();
124 124
125 qt_config->beginGroup("UIGameList");
126 UISettings::values.show_unknown = qt_config->value("show_unknown", true).toBool();
127 UISettings::values.icon_size = qt_config->value("icon_size", 48).toUInt();
128 UISettings::values.row_1_text_id = qt_config->value("row_1_text_id", 0).toUInt();
129 UISettings::values.row_2_text_id = qt_config->value("row_2_text_id", 3).toUInt();
130 qt_config->endGroup();
131
125 qt_config->beginGroup("UILayout"); 132 qt_config->beginGroup("UILayout");
126 UISettings::values.geometry = qt_config->value("geometry").toByteArray(); 133 UISettings::values.geometry = qt_config->value("geometry").toByteArray();
127 UISettings::values.state = qt_config->value("state").toByteArray(); 134 UISettings::values.state = qt_config->value("state").toByteArray();
@@ -234,6 +241,13 @@ void Config::SaveValues() {
234 qt_config->beginGroup("UI"); 241 qt_config->beginGroup("UI");
235 qt_config->setValue("theme", UISettings::values.theme); 242 qt_config->setValue("theme", UISettings::values.theme);
236 243
244 qt_config->beginGroup("UIGameList");
245 qt_config->setValue("show_unknown", UISettings::values.show_unknown);
246 qt_config->setValue("icon_size", UISettings::values.icon_size);
247 qt_config->setValue("row_1_text_id", UISettings::values.row_1_text_id);
248 qt_config->setValue("row_2_text_id", UISettings::values.row_2_text_id);
249 qt_config->endGroup();
250
237 qt_config->beginGroup("UILayout"); 251 qt_config->beginGroup("UILayout");
238 qt_config->setValue("geometry", UISettings::values.geometry); 252 qt_config->setValue("geometry", UISettings::values.geometry);
239 qt_config->setValue("state", UISettings::values.state); 253 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 @@
24 <string>General</string> 24 <string>General</string>
25 </attribute> 25 </attribute>
26 </widget> 26 </widget>
27 <widget class="ConfigureGameList" name="gameListTab">
28 <attribute name="title">
29 <string>Game List</string>
30 </attribute>
31 </widget>
27 <widget class="ConfigureSystem" name="systemTab"> 32 <widget class="ConfigureSystem" name="systemTab">
28 <attribute name="title"> 33 <attribute name="title">
29 <string>System</string> 34 <string>System</string>
@@ -67,6 +72,12 @@
67 <header>configuration/configure_general.h</header> 72 <header>configuration/configure_general.h</header>
68 <container>1</container> 73 <container>1</container>
69 </customwidget> 74 </customwidget>
75 <customwidget>
76 <class>ConfigureGameList</class>
77 <extends>QWidget</extends>
78 <header>configuration/configure_gamelist.h</header>
79 <container>1</container>
80 </customwidget>
70 <customwidget> 81 <customwidget>
71 <class>ConfigureSystem</class> 82 <class>ConfigureSystem</class>
72 <extends>QWidget</extends> 83 <extends>QWidget</extends>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index cc4b326ae..daa4cc0d9 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -21,6 +21,7 @@ void ConfigureDialog::setConfiguration() {}
21 21
22void ConfigureDialog::applyConfiguration() { 22void ConfigureDialog::applyConfiguration() {
23 ui->generalTab->applyConfiguration(); 23 ui->generalTab->applyConfiguration();
24 ui->gameListTab->applyConfiguration();
24 ui->systemTab->applyConfiguration(); 25 ui->systemTab->applyConfiguration();
25 ui->inputTab->applyConfiguration(); 26 ui->inputTab->applyConfiguration();
26 ui->graphicsTab->applyConfiguration(); 27 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..1ae3423cf
--- /dev/null
+++ b/src/yuzu/configuration/configure_gamelist.cpp
@@ -0,0 +1,63 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/settings.h"
7#include "ui_configure_gamelist.h"
8#include "ui_settings.h"
9#include "yuzu/configuration/configure_gamelist.h"
10
11ConfigureGameList::ConfigureGameList(QWidget* parent)
12 : QWidget(parent), ui(new Ui::ConfigureGameList) {
13 ui->setupUi(this);
14
15 static const std::vector<std::pair<u32, std::string>> default_icon_sizes{
16 std::make_pair(0, "None"), std::make_pair(32, "Small"),
17 std::make_pair(64, "Standard"), std::make_pair(128, "Large"),
18 std::make_pair(256, "Full Size"),
19 };
20
21 for (const auto& size : default_icon_sizes) {
22 ui->icon_size_combobox->addItem(QString::fromStdString(size.second + " (" +
23 std::to_string(size.first) + "x" +
24 std::to_string(size.first) + ")"),
25 size.first);
26 }
27
28 static const std::vector<std::string> row_text_names{
29 "Filename",
30 "Filetype",
31 "Title ID",
32 "Title Name",
33 };
34
35 for (size_t i = 0; i < row_text_names.size(); ++i) {
36 ui->row_1_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
37 QVariant::fromValue(i));
38 ui->row_2_text_combobox->addItem(QString::fromStdString(row_text_names[i]),
39 QVariant::fromValue(i));
40 }
41
42 this->setConfiguration();
43}
44
45ConfigureGameList::~ConfigureGameList() {}
46
47void ConfigureGameList::setConfiguration() {
48 ui->show_unknown->setChecked(UISettings::values.show_unknown);
49 ui->icon_size_combobox->setCurrentIndex(
50 ui->icon_size_combobox->findData(UISettings::values.icon_size));
51 ui->row_1_text_combobox->setCurrentIndex(
52 ui->row_1_text_combobox->findData(UISettings::values.row_1_text_id));
53 ui->row_2_text_combobox->setCurrentIndex(
54 ui->row_2_text_combobox->findData(UISettings::values.row_2_text_id));
55}
56
57void ConfigureGameList::applyConfiguration() {
58 UISettings::values.show_unknown = ui->show_unknown->isChecked();
59 UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
60 UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt();
61 UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt();
62 Settings::Apply();
63}
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 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <QWidget>
9
10namespace Ui {
11class ConfigureGameList;
12}
13
14class ConfigureGameList : public QWidget {
15 Q_OBJECT
16
17public:
18 explicit ConfigureGameList(QWidget* parent = nullptr);
19 ~ConfigureGameList();
20
21 void applyConfiguration();
22
23private:
24 void setConfiguration();
25
26private:
27 std::unique_ptr<Ui::ConfigureGameList> ui;
28};
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 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureGameList</class>
4 <widget class="QWidget" name="ConfigureGeneral">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>300</width>
10 <height>377</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QHBoxLayout" name="HorizontalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="VerticalLayout">
19 <item>
20 <widget class="QGroupBox" name="GeneralGroupBox">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QHBoxLayout" name="GeneralHorizontalLayout">
25 <item>
26 <layout class="QVBoxLayout" name="GeneralVerticalLayout">
27 <item>
28 <widget class="QCheckBox" name="show_unknown">
29 <property name="text">
30 <string>Show files with type 'Unknown'</string>
31 </property>
32 </widget>
33 </item>
34 </layout>
35 </item>
36 </layout>
37 </widget>
38 </item>
39 <item>
40 <widget class="QGroupBox" name="IconSizeGroupBox">
41 <property name="title">
42 <string>Icon Size</string>
43 </property>
44 <layout class="QHBoxLayout" name="icon_size_qhbox_layout">
45 <item>
46 <layout class="QVBoxLayout" name="icon_size_qvbox_layout">
47 <item>
48 <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
49 <item>
50 <widget class="QLabel" name="icon_size_label">
51 <property name="text">
52 <string>Icon Size:</string>
53 </property>
54 </widget>
55 </item>
56 <item>
57 <widget class="QComboBox" name="icon_size_combobox"/>
58 </item>
59 </layout>
60 </item>
61 </layout>
62 </item>
63 </layout>
64 </widget>
65 </item>
66 <item>
67 <widget class="QGroupBox" name="RowGroupBox">
68 <property name="title">
69 <string>Row Text</string>
70 </property>
71 <layout class="QHBoxLayout" name="RowHorizontalLayout">
72 <item>
73 <layout class="QVBoxLayout" name="RowVerticalLayout">
74 <item>
75 <layout class="QHBoxLayout" name="row_1_qhbox_layout">
76 <item>
77 <widget class="QLabel" name="row_1_label">
78 <property name="text">
79 <string>Row 1 Text:</string>
80 </property>
81 </widget>
82 </item>
83 <item>
84 <widget class="QComboBox" name="row_1_text_combobox"/>
85 </item>
86 </layout>
87 </item>
88 <item>
89 <layout class="QHBoxLayout" name="row_2_qhbox_layout">
90 <item>
91 <widget class="QLabel" name="row_2_label">
92 <property name="text">
93 <string>Row 2 Text:</string>
94 </property>
95 </widget>
96 </item>
97 <item>
98 <widget class="QComboBox" name="row_2_text_combobox"/>
99 </item>
100 </layout>
101 </item>
102 </layout>
103 </item>
104 </layout>
105 </widget>
106 </item>
107 <item>
108 <spacer name="verticalSpacer">
109 <property name="orientation">
110 <enum>Qt::Vertical</enum>
111 </property>
112 <property name="sizeHint" stdset="0">
113 <size>
114 <width>20</width>
115 <height>40</height>
116 </size>
117 </property>
118 </spacer>
119 </item>
120 </layout>
121 </item>
122 </layout>
123 </widget>
124 <resources/>
125 <connections/>
126</ui>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 24f38a3c7..5f47f5a2b 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -9,9 +9,12 @@
9#include <QKeyEvent> 9#include <QKeyEvent>
10#include <QMenu> 10#include <QMenu>
11#include <QThreadPool> 11#include <QThreadPool>
12#include <boost/container/flat_map.hpp>
12#include "common/common_paths.h" 13#include "common/common_paths.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/string_util.h" 15#include "common/string_util.h"
16#include "core/file_sys/content_archive.h"
17#include "core/file_sys/control_metadata.h"
15#include "core/file_sys/vfs_real.h" 18#include "core/file_sys/vfs_real.h"
16#include "core/loader/loader.h" 19#include "core/loader/loader.h"
17#include "game_list.h" 20#include "game_list.h"
@@ -398,8 +401,32 @@ void GameList::RefreshGameDirectory() {
398} 401}
399 402
400void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) { 403void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
401 const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory, 404 boost::container::flat_map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map;
402 const std::string& virtual_name) -> bool { 405
406 const auto nca_control_callback =
407 [this, &nca_control_map](u64* num_entries_out, const std::string& directory,
408 const std::string& virtual_name) -> bool {
409 std::string physical_name = directory + DIR_SEP + virtual_name;
410
411 if (stop_processing)
412 return false; // Breaks the callback loop.
413
414 bool is_dir = FileUtil::IsDirectory(physical_name);
415 QFileInfo file_info(physical_name.c_str());
416 if (!is_dir && file_info.suffix().toStdString() == "nca") {
417 auto nca = std::make_shared<FileSys::NCA>(
418 std::make_shared<FileSys::RealVfsFile>(physical_name));
419 if (nca->GetType() == FileSys::NCAContentType::Control)
420 nca_control_map.insert_or_assign(nca->GetTitleId(), nca);
421 }
422 return true;
423 };
424
425 FileUtil::ForeachDirectoryEntry(nullptr, dir_path, nca_control_callback);
426
427 const auto callback = [this, recursion,
428 &nca_control_map](u64* num_entries_out, const std::string& directory,
429 const std::string& virtual_name) -> bool {
403 std::string physical_name = directory + DIR_SEP + virtual_name; 430 std::string physical_name = directory + DIR_SEP + virtual_name;
404 431
405 if (stop_processing) 432 if (stop_processing)
@@ -410,17 +437,50 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
410 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { 437 (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
411 std::unique_ptr<Loader::AppLoader> loader = 438 std::unique_ptr<Loader::AppLoader> loader =
412 Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); 439 Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name));
413 if (!loader) 440 if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown ||
441 loader->GetFileType() == Loader::FileType::Error) &&
442 !UISettings::values.show_unknown))
414 return true; 443 return true;
415 444
416 std::vector<u8> smdh; 445 std::vector<u8> icon;
417 loader->ReadIcon(smdh); 446 const auto res1 = loader->ReadIcon(icon);
418 447
419 u64 program_id = 0; 448 u64 program_id;
420 loader->ReadProgramId(program_id); 449 const auto res2 = loader->ReadProgramId(program_id);
450
451 std::string name = " ";
452 const auto res3 = loader->ReadTitle(name);
453
454 if ((res1 == Loader::ResultStatus::ErrorNotUsed ||
455 res1 == Loader::ResultStatus::ErrorNotImplemented) &&
456 (res3 == Loader::ResultStatus::ErrorNotUsed ||
457 res3 == Loader::ResultStatus::ErrorNotImplemented) &&
458 res2 == Loader::ResultStatus::Success) {
459 // Use from metadata pool.
460 if (nca_control_map.find(program_id) != nca_control_map.end()) {
461 const auto nca = nca_control_map[program_id];
462 const auto control_dir = nca->GetSubdirectories()[0];
463
464 const auto nacp_file = control_dir->GetFile("control.nacp");
465 FileSys::NACP nacp(nacp_file);
466 name = nacp.GetApplicationName();
467
468 FileSys::VirtualFile icon_file = nullptr;
469 for (const auto& language : FileSys::LANGUAGE_NAMES) {
470 icon_file = control_dir->GetFile("icon_" + std::string(language) + ".dat");
471 if (icon_file != nullptr) {
472 icon = icon_file->ReadAllBytes();
473 break;
474 }
475 }
476 }
477 }
421 478
422 emit EntryReady({ 479 emit EntryReady({
423 new GameListItemPath(FormatGameName(physical_name), smdh, program_id), 480 new GameListItemPath(
481 FormatGameName(physical_name), icon, QString::fromStdString(name),
482 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
483 program_id),
424 new GameListItem( 484 new GameListItem(
425 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), 485 QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
426 new GameListItemSize(FileUtil::GetSize(physical_name)), 486 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 @@
11#include <QStandardItem> 11#include <QStandardItem>
12#include <QString> 12#include <QString>
13#include "common/string_util.h" 13#include "common/string_util.h"
14#include "ui_settings.h"
14#include "yuzu/util/util.h" 15#include "yuzu/util/util.h"
15 16
16/** 17/**
@@ -18,8 +19,7 @@
18 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24) 19 * @param large If true, returns large icon (48x48), otherwise returns small icon (24x24)
19 * @return QPixmap default icon 20 * @return QPixmap default icon
20 */ 21 */
21static QPixmap GetDefaultIcon(bool large) { 22static QPixmap GetDefaultIcon(u32 size) {
22 int size = large ? 48 : 24;
23 QPixmap icon(size, size); 23 QPixmap icon(size, size);
24 icon.fill(Qt::transparent); 24 icon.fill(Qt::transparent);
25 return icon; 25 return icon;
@@ -44,11 +44,25 @@ public:
44 static const int FullPathRole = Qt::UserRole + 1; 44 static const int FullPathRole = Qt::UserRole + 1;
45 static const int TitleRole = Qt::UserRole + 2; 45 static const int TitleRole = Qt::UserRole + 2;
46 static const int ProgramIdRole = Qt::UserRole + 3; 46 static const int ProgramIdRole = Qt::UserRole + 3;
47 static const int FileTypeRole = Qt::UserRole + 4;
47 48
48 GameListItemPath() = default; 49 GameListItemPath() = default;
49 GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) { 50 GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
51 const QString& game_name, const QString& game_type, u64 program_id)
52 : GameListItem() {
50 setData(game_path, FullPathRole); 53 setData(game_path, FullPathRole);
54 setData(game_name, TitleRole);
51 setData(qulonglong(program_id), ProgramIdRole); 55 setData(qulonglong(program_id), ProgramIdRole);
56 setData(game_type, FileTypeRole);
57
58 QPixmap picture;
59 u32 size = UISettings::values.icon_size;
60 if (!picture.loadFromData(picture_data.data(), picture_data.size()))
61 picture = GetDefaultIcon(size);
62
63 picture = picture.scaled(size, size);
64
65 setData(picture, Qt::DecorationRole);
52 } 66 }
53 67
54 QVariant data(int role) const override { 68 QVariant data(int role) const override {
@@ -57,7 +71,23 @@ public:
57 Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename, 71 Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
58 nullptr); 72 nullptr);
59 QString title = data(TitleRole).toString(); 73 QString title = data(TitleRole).toString();
60 return QString::fromStdString(filename) + (title.isEmpty() ? "" : "\n " + title); 74
75 std::vector<QString> row_data{
76 QString::fromStdString(filename),
77 data(FileTypeRole).toString(),
78 QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
79 data(TitleRole).toString(),
80 };
81
82 auto row1 = row_data.at(UISettings::values.row_1_text_id);
83 auto row2 = row_data.at(UISettings::values.row_2_text_id);
84
85 if (row1.isEmpty() || row1 == row2)
86 return row2;
87 if (row2.isEmpty())
88 return row1;
89
90 return row1 + "\n " + row2;
61 } else { 91 } else {
62 return GameListItem::data(role); 92 return GameListItem::data(role);
63 } 93 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 17ed62c72..a6241e63e 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -768,6 +768,7 @@ void GMainWindow::OnConfigure() {
768 configureDialog.applyConfiguration(); 768 configureDialog.applyConfiguration();
769 if (UISettings::values.theme != old_theme) 769 if (UISettings::values.theme != old_theme)
770 UpdateUITheme(); 770 UpdateUITheme();
771 game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
771 config->Save(); 772 config->Save();
772 } 773 }
773} 774}
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 {
54 54
55 // logging 55 // logging
56 bool show_console; 56 bool show_console;
57
58 // Game List
59 bool show_unknown;
60 uint32_t icon_size;
61 uint8_t row_1_text_id;
62 uint8_t row_2_text_id;
57}; 63};
58 64
59extern Values values; 65extern Values values;