Merge pull request #850 from DarkLordZach/icon-meta
Add Icons and Metadata Support
This commit is contained in:
		
						commit
						fd9da4232b
					
				| @ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|     set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin") | ||||
|     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") | ||||
|     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") | ||||
|     set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") | ||||
|     set(PLATFORMS ${DLL_DEST}platforms/) | ||||
|     set(STYLES ${DLL_DEST}styles/) | ||||
|     set(IMAGEFORMATS ${DLL_DEST}imageformats/) | ||||
|     windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST} | ||||
|         icudt*.dll | ||||
|         icuin*.dll | ||||
| @ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|     ) | ||||
|     windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*) | ||||
|     windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*) | ||||
|     windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*) | ||||
| endfunction(copy_yuzu_Qt5_deps) | ||||
|  | ||||
| @ -117,6 +117,7 @@ after_build: | ||||
|           mkdir $RELEASE_DIST | ||||
|           mkdir $RELEASE_DIST/platforms | ||||
|           mkdir $RELEASE_DIST/styles | ||||
|           mkdir $RELEASE_DIST/imageformats | ||||
| 
 | ||||
|           # copy the compiled binaries and other release files to the release folder | ||||
|           Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST | ||||
| @ -140,6 +141,9 @@ after_build: | ||||
|           # copy the qt windows vista style dll to platforms | ||||
|           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles" | ||||
| 
 | ||||
|           # copy the qt jpeg imageformat dll to platforms | ||||
|           Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats" | ||||
| 
 | ||||
|           7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\* | ||||
|           7z a $MINGW_SEVENZIP $RELEASE_DIST | ||||
|         } | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <array> | ||||
| #include <string> | ||||
| #include <core/loader/loader.h> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/file_sys/card_image.h" | ||||
| #include "core/file_sys/partition_filesystem.h" | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
|  | ||||
| @ -170,6 +170,10 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting | ||||
| } | ||||
| 
 | ||||
| NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | ||||
|     if (file == nullptr) { | ||||
|         status = Loader::ResultStatus::ErrorInvalidFormat; | ||||
|         return; | ||||
|     } | ||||
|     if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||||
|         LOG_ERROR(Loader, "File reader errored out during header read."); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/swap.h" | ||||
| #include "control_metadata.h" | ||||
| #include "core/crypto/key_manager.h" | ||||
| #include "core/file_sys/partition_filesystem.h" | ||||
| #include "core/loader/loader.h" | ||||
|  | ||||
| @ -62,6 +62,13 @@ enum class Language : u8 { | ||||
|     Chinese = 14, | ||||
| }; | ||||
| 
 | ||||
| static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | ||||
|     "AmericanEnglish", "BritishEnglish", "Japanese", | ||||
|     "French",          "German",         "LatinAmericanSpanish", | ||||
|     "Spanish",         "Italian",        "Dutch", | ||||
|     "CanadianFrench",  "Portugese",      "Russian", | ||||
|     "Korean",          "Taiwanese",      "Chinese"}; | ||||
| 
 | ||||
| // A class representing the format used by NX metadata files, typically named Control.nacp.
 | ||||
| // These store application name, dev name, title id, and other miscellaneous data.
 | ||||
| class NACP { | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/file_sys/content_archive.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| @ -17,8 +18,50 @@ | ||||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) | ||||
|     : AppLoader(std::move(file)) {} | ||||
| AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_) | ||||
|     : AppLoader(std::move(file_)) { | ||||
|     const auto dir = file->GetContainingDirectory(); | ||||
| 
 | ||||
|     // Icon
 | ||||
|     FileSys::VirtualFile icon_file = nullptr; | ||||
|     for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||||
|         icon_file = dir->GetFile("icon_" + std::string(language) + ".dat"); | ||||
|         if (icon_file != nullptr) { | ||||
|             icon_data = icon_file->ReadAllBytes(); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (icon_data.empty()) { | ||||
|         // Any png, jpeg, or bmp file
 | ||||
|         const auto& files = dir->GetFiles(); | ||||
|         const auto icon_iter = | ||||
|             std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||||
|                 return file->GetExtension() == "png" || file->GetExtension() == "jpg" || | ||||
|                        file->GetExtension() == "bmp" || file->GetExtension() == "jpeg"; | ||||
|             }); | ||||
|         if (icon_iter != files.end()) | ||||
|             icon_data = (*icon_iter)->ReadAllBytes(); | ||||
|     } | ||||
| 
 | ||||
|     // Metadata
 | ||||
|     FileSys::VirtualFile nacp_file = dir->GetFile("control.nacp"); | ||||
|     if (nacp_file == nullptr) { | ||||
|         const auto& files = dir->GetFiles(); | ||||
|         const auto nacp_iter = | ||||
|             std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||||
|                 return file->GetExtension() == "nacp"; | ||||
|             }); | ||||
|         if (nacp_iter != files.end()) | ||||
|             nacp_file = *nacp_iter; | ||||
|     } | ||||
| 
 | ||||
|     if (nacp_file != nullptr) { | ||||
|         FileSys::NACP nacp(nacp_file); | ||||
|         title_id = nacp.GetTitleId(); | ||||
|         name = nacp.GetApplicationName(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( | ||||
|     FileSys::VirtualDir directory) | ||||
| @ -105,4 +148,25 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) { | ||||
|     if (icon_data.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|     buffer = icon_data; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { | ||||
|     if (name.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|     out_program_id = title_id; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) { | ||||
|     if (name.empty()) | ||||
|         return ResultStatus::ErrorNotUsed; | ||||
|     title = name; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  | ||||
| @ -39,11 +39,18 @@ public: | ||||
|     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||||
| 
 | ||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||
|     ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||
|     ResultStatus ReadTitle(std::string& title) override; | ||||
| 
 | ||||
| private: | ||||
|     FileSys::ProgramMetadata metadata; | ||||
|     FileSys::VirtualFile romfs; | ||||
|     FileSys::VirtualDir dir; | ||||
| 
 | ||||
|     std::vector<u8> icon_data; | ||||
|     std::string name; | ||||
|     u64 title_id{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  | ||||
| @ -68,7 +68,7 @@ FileType GuessFromFilename(const std::string& name) { | ||||
|     return FileType::Unknown; | ||||
| } | ||||
| 
 | ||||
| const char* GetFileTypeString(FileType type) { | ||||
| std::string GetFileTypeString(FileType type) { | ||||
|     switch (type) { | ||||
|     case FileType::ELF: | ||||
|         return "ELF"; | ||||
|  | ||||
| @ -61,7 +61,7 @@ FileType GuessFromFilename(const std::string& name); | ||||
| /**
 | ||||
|  * Convert a FileType into a string which can be displayed to the user. | ||||
|  */ | ||||
| const char* GetFileTypeString(FileType type); | ||||
| std::string GetFileTypeString(FileType type); | ||||
| 
 | ||||
| /// Return type for functions in Loader namespace
 | ||||
| enum class ResultStatus { | ||||
|  | ||||
| @ -77,8 +77,8 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { | ||||
|     if (nca == nullptr) | ||||
|         return ResultStatus::ErrorNotLoaded; | ||||
|     if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|     out_program_id = nca->GetTitleId(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
|  | ||||
| @ -33,7 +33,6 @@ public: | ||||
|     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | ||||
| 
 | ||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||
| 
 | ||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||
| 
 | ||||
|     ~AppLoader_NCA(); | ||||
| @ -41,6 +40,7 @@ public: | ||||
| private: | ||||
|     FileSys::ProgramMetadata metadata; | ||||
| 
 | ||||
|     FileSys::NCAHeader header; | ||||
|     std::unique_ptr<FileSys::NCA> nca; | ||||
|     std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; | ||||
| }; | ||||
|  | ||||
| @ -26,7 +26,25 @@ namespace Loader { | ||||
| AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | ||||
|     : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), | ||||
|       nca_loader(std::make_unique<AppLoader_NCA>( | ||||
|           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} | ||||
|           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) { | ||||
|     if (xci->GetStatus() != ResultStatus::Success) | ||||
|         return; | ||||
|     const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); | ||||
|     if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) | ||||
|         return; | ||||
|     const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); | ||||
|     if (romfs == nullptr) | ||||
|         return; | ||||
|     for (const auto& language : FileSys::LANGUAGE_NAMES) { | ||||
|         icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); | ||||
|         if (icon_file != nullptr) | ||||
|             break; | ||||
|     } | ||||
|     const auto nacp_raw = romfs->GetFile("control.nacp"); | ||||
|     if (nacp_raw == nullptr) | ||||
|         return; | ||||
|     nacp_file = std::make_shared<FileSys::NACP>(nacp_raw); | ||||
| } | ||||
| 
 | ||||
| AppLoader_XCI::~AppLoader_XCI() = default; | ||||
| 
 | ||||
| @ -71,4 +89,17 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { | ||||
|     return nca_loader->ReadProgramId(out_program_id); | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { | ||||
|     if (icon_file == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|     buffer = icon_file->ReadAllBytes(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | ||||
|     if (nacp_file == nullptr) | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|     title = nacp_file->GetApplicationName(); | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| } // namespace Loader
 | ||||
|  | ||||
| @ -33,12 +33,17 @@ public: | ||||
| 
 | ||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||
|     ResultStatus ReadIcon(std::vector<u8>& buffer) override; | ||||
|     ResultStatus ReadTitle(std::string& title) override; | ||||
| 
 | ||||
| private: | ||||
|     FileSys::ProgramMetadata metadata; | ||||
| 
 | ||||
|     std::unique_ptr<FileSys::XCI> xci; | ||||
|     std::unique_ptr<AppLoader_NCA> nca_loader; | ||||
| 
 | ||||
|     FileSys::VirtualFile icon_file; | ||||
|     std::shared_ptr<FileSys::NACP> nacp_file; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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> | ||||
|  | ||||
| @ -21,6 +21,7 @@ void ConfigureDialog::setConfiguration() {} | ||||
| 
 | ||||
| void ConfigureDialog::applyConfiguration() { | ||||
|     ui->generalTab->applyConfiguration(); | ||||
|     ui->gameListTab->applyConfiguration(); | ||||
|     ui->systemTab->applyConfiguration(); | ||||
|     ui->inputTab->applyConfiguration(); | ||||
|     ui->graphicsTab->applyConfiguration(); | ||||
|  | ||||
							
								
								
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/yuzu/configuration/configure_gamelist.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/yuzu/configuration/configure_gamelist.h
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| }; | ||||
							
								
								
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								src/yuzu/configuration/configure_gamelist.ui
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
| @ -9,9 +9,12 @@ | ||||
| #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/vfs_real.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "game_list.h" | ||||
| @ -398,8 +401,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 +437,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); | ||||
|             std::vector<u8> icon; | ||||
|             const auto res1 = loader->ReadIcon(icon); | ||||
| 
 | ||||
|             u64 program_id = 0; | ||||
|             loader->ReadProgramId(program_id); | ||||
|             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]; | ||||
|                     const auto control_dir = nca->GetSubdirectories()[0]; | ||||
| 
 | ||||
|                     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)), | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|  | ||||
| @ -768,6 +768,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(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei