mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 05:10:03 +00:00 
			
		
		
		
	Merge pull request #4235 from FearlessTobi/port-1259
Port various game_list changes from yuzu
This commit is contained in:
		
						commit
						6742472133
					
				
					 10 changed files with 265 additions and 198 deletions
				
			
		|  | @ -14,6 +14,8 @@ add_executable(citra-qt | ||||||
|     applets/swkbd.h |     applets/swkbd.h | ||||||
|     bootmanager.cpp |     bootmanager.cpp | ||||||
|     bootmanager.h |     bootmanager.h | ||||||
|  |     compatibility_list.cpp | ||||||
|  |     compatibility_list.h | ||||||
|     camera/camera_util.cpp |     camera/camera_util.cpp | ||||||
|     camera/camera_util.h |     camera/camera_util.h | ||||||
|     camera/still_image_camera.cpp |     camera/still_image_camera.cpp | ||||||
|  | @ -76,6 +78,8 @@ add_executable(citra-qt | ||||||
|     game_list.cpp |     game_list.cpp | ||||||
|     game_list.h |     game_list.h | ||||||
|     game_list_p.h |     game_list_p.h | ||||||
|  |     game_list_worker.cpp | ||||||
|  |     game_list_worker.h | ||||||
|     hotkeys.cpp |     hotkeys.cpp | ||||||
|     hotkeys.h |     hotkeys.h | ||||||
|     main.cpp |     main.cpp | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								src/citra_qt/compatibility_list.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/citra_qt/compatibility_list.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | // Copyright 2018 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <fmt/format.h> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
|  | 
 | ||||||
|  | CompatibilityList::const_iterator FindMatchingCompatibilityEntry( | ||||||
|  |     const CompatibilityList& compatibility_list, u64 program_id) { | ||||||
|  |     return std::find_if(compatibility_list.begin(), compatibility_list.end(), | ||||||
|  |                         [program_id](const auto& element) { | ||||||
|  |                             std::string pid = fmt::format("{:016X}", program_id); | ||||||
|  |                             return element.first == pid; | ||||||
|  |                         }); | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/citra_qt/compatibility_list.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/citra_qt/compatibility_list.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | // Copyright 2018 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <QString> | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | using CompatibilityList = std::unordered_map<std::string, std::pair<QString, QString>>; | ||||||
|  | 
 | ||||||
|  | CompatibilityList::const_iterator FindMatchingCompatibilityEntry( | ||||||
|  |     const CompatibilityList& compatibility_list, u64 program_id); | ||||||
|  | @ -21,8 +21,10 @@ | ||||||
| #include <QToolButton> | #include <QToolButton> | ||||||
| #include <QTreeView> | #include <QTreeView> | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
| #include "citra_qt/game_list.h" | #include "citra_qt/game_list.h" | ||||||
| #include "citra_qt/game_list_p.h" | #include "citra_qt/game_list_p.h" | ||||||
|  | #include "citra_qt/game_list_worker.h" | ||||||
| #include "citra_qt/main.h" | #include "citra_qt/main.h" | ||||||
| #include "citra_qt/ui_settings.h" | #include "citra_qt/ui_settings.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
|  | @ -30,7 +32,6 @@ | ||||||
| #include "core/file_sys/archive_extsavedata.h" | #include "core/file_sys/archive_extsavedata.h" | ||||||
| #include "core/file_sys/archive_source_sd_savedata.h" | #include "core/file_sys/archive_source_sd_savedata.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
| #include "core/loader/loader.h" |  | ||||||
| 
 | 
 | ||||||
| GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} | GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} | ||||||
| 
 | 
 | ||||||
|  | @ -648,11 +649,6 @@ void GameList::LoadInterfaceLayout() { | ||||||
| const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf", | const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf", | ||||||
|                                                          "cci", "cxi",  "app"}; |                                                          "cci", "cxi",  "app"}; | ||||||
| 
 | 
 | ||||||
| static bool HasSupportedFileExtension(const std::string& file_name) { |  | ||||||
|     QFileInfo file = QFileInfo(QString::fromStdString(file_name)); |  | ||||||
|     return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void GameList::RefreshGameDirectory() { | void GameList::RefreshGameDirectory() { | ||||||
|     if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { |     if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { | ||||||
|         LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); |         LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | ||||||
|  | @ -678,123 +674,6 @@ QString GameList::FindGameByProgramID(QStandardItem* current_item, u64 program_i | ||||||
|     return ""; |     return ""; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, |  | ||||||
|                                              GameListDir* parent_dir) { |  | ||||||
|     const auto callback = [this, recursion, parent_dir](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); |  | ||||||
|         if (!is_dir && HasSupportedFileExtension(physical_name)) { |  | ||||||
|             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); |  | ||||||
|             if (!loader) |  | ||||||
|                 return true; |  | ||||||
| 
 |  | ||||||
|             u64 program_id = 0; |  | ||||||
|             loader->ReadProgramId(program_id); |  | ||||||
| 
 |  | ||||||
|             u64 extdata_id = 0; |  | ||||||
|             loader->ReadExtdataId(extdata_id); |  | ||||||
| 
 |  | ||||||
|             std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> { |  | ||||||
|                 std::vector<u8> original_smdh; |  | ||||||
|                 loader->ReadIcon(original_smdh); |  | ||||||
| 
 |  | ||||||
|                 if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF) |  | ||||||
|                     return original_smdh; |  | ||||||
| 
 |  | ||||||
|                 std::string update_path = Service::AM::GetTitleContentPath( |  | ||||||
|                     Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000); |  | ||||||
| 
 |  | ||||||
|                 if (!FileUtil::Exists(update_path)) |  | ||||||
|                     return original_smdh; |  | ||||||
| 
 |  | ||||||
|                 std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path); |  | ||||||
| 
 |  | ||||||
|                 if (!update_loader) |  | ||||||
|                     return original_smdh; |  | ||||||
| 
 |  | ||||||
|                 std::vector<u8> update_smdh; |  | ||||||
|                 update_loader->ReadIcon(update_smdh); |  | ||||||
|                 return update_smdh; |  | ||||||
|             }(); |  | ||||||
| 
 |  | ||||||
|             if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) { |  | ||||||
|                 // Skip this invalid entry
 |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |  | ||||||
| 
 |  | ||||||
|             // The game list uses this as compatibility number for untested games
 |  | ||||||
|             QString compatibility("99"); |  | ||||||
|             if (it != compatibility_list.end()) |  | ||||||
|                 compatibility = it->second.first; |  | ||||||
| 
 |  | ||||||
|             emit EntryReady( |  | ||||||
|                 { |  | ||||||
|                     new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id, |  | ||||||
|                                          extdata_id), |  | ||||||
|                     new GameListItemCompat(compatibility), |  | ||||||
|                     new GameListItemRegion(smdh), |  | ||||||
|                     new GameListItem( |  | ||||||
|                         QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), |  | ||||||
|                     new GameListItemSize(FileUtil::GetSize(physical_name)), |  | ||||||
|                 }, |  | ||||||
|                 parent_dir); |  | ||||||
| 
 |  | ||||||
|         } else if (is_dir && recursion > 0) { |  | ||||||
|             watch_list.append(QString::fromStdString(physical_name)); |  | ||||||
|             AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return true; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void GameListWorker::run() { |  | ||||||
|     stop_processing = false; |  | ||||||
|     for (UISettings::GameDir& game_dir : game_dirs) { |  | ||||||
|         if (game_dir.path == "INSTALLED") { |  | ||||||
|             QString path = |  | ||||||
|                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) + |  | ||||||
|                 "Nintendo " |  | ||||||
|                 "3DS/00000000000000000000000000000000/" |  | ||||||
|                 "00000000000000000000000000000000/title/00040000"; |  | ||||||
|             watch_list.append(path); |  | ||||||
|             GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); |  | ||||||
|             emit DirEntryReady({game_list_dir}); |  | ||||||
|             AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); |  | ||||||
|         } else if (game_dir.path == "SYSTEM") { |  | ||||||
|             QString path = |  | ||||||
|                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) + |  | ||||||
|                 "00000000000000000000000000000000/title/00040010"; |  | ||||||
|             watch_list.append(path); |  | ||||||
|             GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); |  | ||||||
|             emit DirEntryReady({game_list_dir}); |  | ||||||
|             AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); |  | ||||||
|         } else { |  | ||||||
|             watch_list.append(game_dir.path); |  | ||||||
|             GameListDir* game_list_dir = new GameListDir(game_dir); |  | ||||||
|             emit DirEntryReady({game_list_dir}); |  | ||||||
|             AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0, |  | ||||||
|                                     game_list_dir); |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|     emit Finished(watch_list); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void GameListWorker::Cancel() { |  | ||||||
|     this->disconnect(); |  | ||||||
|     stop_processing = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { | GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { | ||||||
|     this->main_window = parent; |     this->main_window = parent; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,9 +4,10 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <unordered_map> | #include <QMenu> | ||||||
| #include <QString> | #include <QString> | ||||||
| #include <QWidget> | #include <QWidget> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "ui_settings.h" | #include "ui_settings.h" | ||||||
| 
 | 
 | ||||||
|  | @ -70,9 +71,8 @@ signals: | ||||||
|     void GameChosen(QString game_path); |     void GameChosen(QString game_path); | ||||||
|     void ShouldCancelWorker(); |     void ShouldCancelWorker(); | ||||||
|     void OpenFolderRequested(u64 program_id, GameListOpenTarget target); |     void OpenFolderRequested(u64 program_id, GameListOpenTarget target); | ||||||
|     void NavigateToGamedbEntryRequested( |     void NavigateToGamedbEntryRequested(u64 program_id, | ||||||
|         u64 program_id, |                                         const CompatibilityList& compatibility_list); | ||||||
|         std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); |  | ||||||
|     void OpenDirectory(QString directory); |     void OpenDirectory(QString directory); | ||||||
|     void AddDirectory(); |     void AddDirectory(); | ||||||
|     void ShowList(bool show); |     void ShowList(bool show); | ||||||
|  | @ -103,7 +103,7 @@ private: | ||||||
|     QStandardItemModel* item_model = nullptr; |     QStandardItemModel* item_model = nullptr; | ||||||
|     GameListWorker* current_worker = nullptr; |     GameListWorker* current_worker = nullptr; | ||||||
|     QFileSystemWatcher* watcher = nullptr; |     QFileSystemWatcher* watcher = nullptr; | ||||||
|     std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list; |     CompatibilityList compatibility_list; | ||||||
| 
 | 
 | ||||||
|     friend class GameListSearchField; |     friend class GameListSearchField; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <atomic> |  | ||||||
| #include <map> | #include <map> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <utility> | #include <utility> | ||||||
|  | @ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) { | ||||||
|     return icon; |     return icon; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static auto FindMatchingCompatibilityEntry( |  | ||||||
|     const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list, |  | ||||||
|     u64 program_id) { |  | ||||||
|     return std::find_if( |  | ||||||
|         compatibility_list.begin(), compatibility_list.end(), |  | ||||||
|         [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) { |  | ||||||
|             std::string pid = fmt::format("{:016X}", program_id); |  | ||||||
|             return element.first == pid; |  | ||||||
|         }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Gets the short game title from SMDH data. |  * Gets the short game title from SMDH data. | ||||||
|  * @param smdh SMDH data |  * @param smdh SMDH data | ||||||
|  | @ -216,7 +204,7 @@ class GameListItemCompat : public GameListItem { | ||||||
| public: | public: | ||||||
|     static const int CompatNumberRole = SortRole; |     static const int CompatNumberRole = SortRole; | ||||||
|     GameListItemCompat() = default; |     GameListItemCompat() = default; | ||||||
|     explicit GameListItemCompat(const QString& compatiblity) { |     explicit GameListItemCompat(const QString& compatibility) { | ||||||
|         setData(type(), TypeRole); |         setData(type(), TypeRole); | ||||||
| 
 | 
 | ||||||
|         struct CompatStatus { |         struct CompatStatus { | ||||||
|  | @ -235,13 +223,13 @@ public: | ||||||
|         {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; |         {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; | ||||||
|         // clang-format on
 |         // clang-format on
 | ||||||
| 
 | 
 | ||||||
|         auto iterator = status_data.find(compatiblity); |         auto iterator = status_data.find(compatibility); | ||||||
|         if (iterator == status_data.end()) { |         if (iterator == status_data.end()) { | ||||||
|             LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); |             LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString()); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         CompatStatus status = iterator->second; |         const CompatStatus& status = iterator->second; | ||||||
|         setData(compatiblity, CompatNumberRole); |         setData(compatibility, CompatNumberRole); | ||||||
|         setText(QObject::tr(status.text)); |         setText(QObject::tr(status.text)); | ||||||
|         setToolTip(QObject::tr(status.tooltip)); |         setToolTip(QObject::tr(status.tooltip)); | ||||||
|         setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); |         setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); | ||||||
|  | @ -373,51 +361,6 @@ public: | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Asynchronous worker object for populating the game list. |  | ||||||
|  * Communicates with other threads through Qt's signal/slot system. |  | ||||||
|  */ |  | ||||||
| class GameListWorker : public QObject, public QRunnable { |  | ||||||
|     Q_OBJECT |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     explicit GameListWorker( |  | ||||||
|         QList<UISettings::GameDir>& game_dirs, |  | ||||||
|         const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) |  | ||||||
|         : game_dirs(game_dirs), compatibility_list(compatibility_list) {} |  | ||||||
| 
 |  | ||||||
| public slots: |  | ||||||
|     /// Starts the processing of directory tree information.
 |  | ||||||
|     void run() override; |  | ||||||
|     /// Tells the worker that it should no longer continue processing. Thread-safe.
 |  | ||||||
|     void Cancel(); |  | ||||||
| 
 |  | ||||||
| signals: |  | ||||||
|     /**
 |  | ||||||
|      * The `EntryReady` signal is emitted once an entry has been prepared and is ready |  | ||||||
|      * to be added to the game list. |  | ||||||
|      * @param entry_items a list with `QStandardItem`s that make up the columns of the new |  | ||||||
|      * entry. |  | ||||||
|      */ |  | ||||||
|     void DirEntryReady(GameListDir* entry_items); |  | ||||||
|     void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * After the worker has traversed the game directory looking for entries, this signal is |  | ||||||
|      * emitted with a list of folders that should be watched for changes as well. |  | ||||||
|      */ |  | ||||||
|     void Finished(QStringList watch_list); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     QStringList watch_list; |  | ||||||
|     const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; |  | ||||||
|     QList<UISettings::GameDir>& game_dirs; |  | ||||||
|     std::atomic_bool stop_processing; |  | ||||||
| 
 |  | ||||||
|     void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, |  | ||||||
|                                  GameListDir* parent_dir); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class GameList; | class GameList; | ||||||
| class QHBoxLayout; | class QHBoxLayout; | ||||||
| class QTreeView; | class QTreeView; | ||||||
|  |  | ||||||
							
								
								
									
										150
									
								
								src/citra_qt/game_list_worker.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/citra_qt/game_list_worker.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,150 @@ | ||||||
|  | // Copyright 2018 yuzu emulator team
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  | #include <QDir> | ||||||
|  | #include <QFileInfo> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
|  | #include "citra_qt/game_list.h" | ||||||
|  | #include "citra_qt/game_list_p.h" | ||||||
|  | #include "citra_qt/game_list_worker.h" | ||||||
|  | #include "citra_qt/ui_settings.h" | ||||||
|  | #include "common/common_paths.h" | ||||||
|  | #include "common/file_util.h" | ||||||
|  | #include "core/hle/service/am/am.h" | ||||||
|  | #include "core/hle/service/fs/archive.h" | ||||||
|  | #include "core/loader/loader.h" | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | bool HasSupportedFileExtension(const std::string& file_name) { | ||||||
|  |     const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); | ||||||
|  |     return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive); | ||||||
|  | } | ||||||
|  | } // Anonymous namespace
 | ||||||
|  | 
 | ||||||
|  | GameListWorker::GameListWorker(QList<UISettings::GameDir>& game_dirs, | ||||||
|  |                                const CompatibilityList& compatibility_list) | ||||||
|  |     : game_dirs(game_dirs), compatibility_list(compatibility_list) {} | ||||||
|  | 
 | ||||||
|  | GameListWorker::~GameListWorker() = default; | ||||||
|  | 
 | ||||||
|  | void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, | ||||||
|  |                                              GameListDir* parent_dir) { | ||||||
|  |     const auto callback = [this, recursion, parent_dir](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); | ||||||
|  |         if (!is_dir && HasSupportedFileExtension(physical_name)) { | ||||||
|  |             std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); | ||||||
|  |             if (!loader) | ||||||
|  |                 return true; | ||||||
|  | 
 | ||||||
|  |             u64 program_id = 0; | ||||||
|  |             loader->ReadProgramId(program_id); | ||||||
|  | 
 | ||||||
|  |             u64 extdata_id = 0; | ||||||
|  |             loader->ReadExtdataId(extdata_id); | ||||||
|  | 
 | ||||||
|  |             std::vector<u8> smdh = [program_id, &loader]() -> std::vector<u8> { | ||||||
|  |                 std::vector<u8> original_smdh; | ||||||
|  |                 loader->ReadIcon(original_smdh); | ||||||
|  | 
 | ||||||
|  |                 if (program_id < 0x0004000000000000 || program_id > 0x00040000FFFFFFFF) | ||||||
|  |                     return original_smdh; | ||||||
|  | 
 | ||||||
|  |                 std::string update_path = Service::AM::GetTitleContentPath( | ||||||
|  |                     Service::FS::MediaType::SDMC, program_id + 0x0000000E00000000); | ||||||
|  | 
 | ||||||
|  |                 if (!FileUtil::Exists(update_path)) | ||||||
|  |                     return original_smdh; | ||||||
|  | 
 | ||||||
|  |                 std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path); | ||||||
|  | 
 | ||||||
|  |                 if (!update_loader) | ||||||
|  |                     return original_smdh; | ||||||
|  | 
 | ||||||
|  |                 std::vector<u8> update_smdh; | ||||||
|  |                 update_loader->ReadIcon(update_smdh); | ||||||
|  |                 return update_smdh; | ||||||
|  |             }(); | ||||||
|  | 
 | ||||||
|  |             if (!Loader::IsValidSMDH(smdh) && UISettings::values.game_list_hide_no_icon) { | ||||||
|  |                 // Skip this invalid entry
 | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||||||
|  | 
 | ||||||
|  |             // The game list uses this as compatibility number for untested games
 | ||||||
|  |             QString compatibility("99"); | ||||||
|  |             if (it != compatibility_list.end()) | ||||||
|  |                 compatibility = it->second.first; | ||||||
|  | 
 | ||||||
|  |             emit EntryReady( | ||||||
|  |                 { | ||||||
|  |                     new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id, | ||||||
|  |                                          extdata_id), | ||||||
|  |                     new GameListItemCompat(compatibility), | ||||||
|  |                     new GameListItemRegion(smdh), | ||||||
|  |                     new GameListItem( | ||||||
|  |                         QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | ||||||
|  |                     new GameListItemSize(FileUtil::GetSize(physical_name)), | ||||||
|  |                 }, | ||||||
|  |                 parent_dir); | ||||||
|  | 
 | ||||||
|  |         } else if (is_dir && recursion > 0) { | ||||||
|  |             watch_list.append(QString::fromStdString(physical_name)); | ||||||
|  |             AddFstEntriesToGameList(physical_name, recursion - 1, parent_dir); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return true; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GameListWorker::run() { | ||||||
|  |     stop_processing = false; | ||||||
|  |     for (UISettings::GameDir& game_dir : game_dirs) { | ||||||
|  |         if (game_dir.path == "INSTALLED") { | ||||||
|  |             QString path = | ||||||
|  |                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) + | ||||||
|  |                 "Nintendo " | ||||||
|  |                 "3DS/00000000000000000000000000000000/" | ||||||
|  |                 "00000000000000000000000000000000/title/00040000"; | ||||||
|  |             watch_list.append(path); | ||||||
|  |             GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::InstalledDir); | ||||||
|  |             emit DirEntryReady({game_list_dir}); | ||||||
|  |             AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); | ||||||
|  |         } else if (game_dir.path == "SYSTEM") { | ||||||
|  |             QString path = | ||||||
|  |                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) + | ||||||
|  |                 "00000000000000000000000000000000/title/00040010"; | ||||||
|  |             watch_list.append(path); | ||||||
|  |             GameListDir* game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir); | ||||||
|  |             emit DirEntryReady({game_list_dir}); | ||||||
|  |             AddFstEntriesToGameList(path.toStdString(), 2, game_list_dir); | ||||||
|  |         } else { | ||||||
|  |             watch_list.append(game_dir.path); | ||||||
|  |             GameListDir* game_list_dir = new GameListDir(game_dir); | ||||||
|  |             emit DirEntryReady({game_list_dir}); | ||||||
|  |             AddFstEntriesToGameList(game_dir.path.toStdString(), game_dir.deep_scan ? 256 : 0, | ||||||
|  |                                     game_list_dir); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     emit Finished(watch_list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GameListWorker::Cancel() { | ||||||
|  |     this->disconnect(); | ||||||
|  |     stop_processing = true; | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/citra_qt/game_list_worker.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/citra_qt/game_list_worker.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | ||||||
|  | // Copyright 2018 yuzu emulator team
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <atomic> | ||||||
|  | #include <map> | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <unordered_map> | ||||||
|  | #include <QList> | ||||||
|  | #include <QObject> | ||||||
|  | #include <QRunnable> | ||||||
|  | #include <QString> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
|  | #include "common/common_types.h" | ||||||
|  | 
 | ||||||
|  | class QStandardItem; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Asynchronous worker object for populating the game list. | ||||||
|  |  * Communicates with other threads through Qt's signal/slot system. | ||||||
|  |  */ | ||||||
|  | class GameListWorker : public QObject, public QRunnable { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     GameListWorker(QList<UISettings::GameDir>& game_dirs, | ||||||
|  |                    const CompatibilityList& compatibility_list); | ||||||
|  |     ~GameListWorker() override; | ||||||
|  | 
 | ||||||
|  |     /// Starts the processing of directory tree information.
 | ||||||
|  |     void run() override; | ||||||
|  | 
 | ||||||
|  |     /// Tells the worker that it should no longer continue processing. Thread-safe.
 | ||||||
|  |     void Cancel(); | ||||||
|  | 
 | ||||||
|  | signals: | ||||||
|  |     /**
 | ||||||
|  |      * The `EntryReady` signal is emitted once an entry has been prepared and is ready | ||||||
|  |      * to be added to the game list. | ||||||
|  |      * @param entry_items a list with `QStandardItem`s that make up the columns of the new entry. | ||||||
|  |      */ | ||||||
|  |     void DirEntryReady(GameListDir* entry_items); | ||||||
|  |     void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir); | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * After the worker has traversed the game directory looking for entries, this signal is emitted | ||||||
|  |      * with a list of folders that should be watched for changes as well. | ||||||
|  |      */ | ||||||
|  |     void Finished(QStringList watch_list); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion, | ||||||
|  |                                  GameListDir* parent_dir); | ||||||
|  | 
 | ||||||
|  |     QStringList watch_list; | ||||||
|  |     const CompatibilityList& compatibility_list; | ||||||
|  |     QList<UISettings::GameDir>& game_dirs; | ||||||
|  |     std::atomic_bool stop_processing; | ||||||
|  | }; | ||||||
|  | @ -21,6 +21,7 @@ | ||||||
| #include "citra_qt/camera/qt_multimedia_camera.h" | #include "citra_qt/camera/qt_multimedia_camera.h" | ||||||
| #include "citra_qt/camera/still_image_camera.h" | #include "citra_qt/camera/still_image_camera.h" | ||||||
| #include "citra_qt/compatdb.h" | #include "citra_qt/compatdb.h" | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
| #include "citra_qt/configuration/config.h" | #include "citra_qt/configuration/config.h" | ||||||
| #include "citra_qt/configuration/configure_dialog.h" | #include "citra_qt/configuration/configure_dialog.h" | ||||||
| #include "citra_qt/debugger/console.h" | #include "citra_qt/debugger/console.h" | ||||||
|  | @ -960,14 +961,11 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) { | ||||||
|     QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |     QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GMainWindow::OnGameListNavigateToGamedbEntry( | void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | ||||||
|     u64 program_id, |                                                   const CompatibilityList& compatibility_list) { | ||||||
|     std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) { |  | ||||||
| 
 |  | ||||||
|     auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |     auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | ||||||
| 
 | 
 | ||||||
|     QString directory; |     QString directory; | ||||||
| 
 |  | ||||||
|     if (it != compatibility_list.end()) |     if (it != compatibility_list.end()) | ||||||
|         directory = it->second.second; |         directory = it->second.second; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <QMainWindow> | #include <QMainWindow> | ||||||
| #include <QTimer> | #include <QTimer> | ||||||
| #include <QTranslator> | #include <QTranslator> | ||||||
|  | #include "citra_qt/compatibility_list.h" | ||||||
| #include "citra_qt/hotkeys.h" | #include "citra_qt/hotkeys.h" | ||||||
| #include "common/announce_multiplayer_room.h" | #include "common/announce_multiplayer_room.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | @ -153,9 +154,8 @@ private slots: | ||||||
|     /// Called whenever a user selects a game in the game list widget.
 |     /// Called whenever a user selects a game in the game list widget.
 | ||||||
|     void OnGameListLoadFile(QString game_path); |     void OnGameListLoadFile(QString game_path); | ||||||
|     void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); |     void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); | ||||||
|     void OnGameListNavigateToGamedbEntry( |     void OnGameListNavigateToGamedbEntry(u64 program_id, | ||||||
|         u64 program_id, |                                          const CompatibilityList& compatibility_list); | ||||||
|         std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list); |  | ||||||
|     void OnGameListOpenDirectory(QString path); |     void OnGameListOpenDirectory(QString path); | ||||||
|     void OnGameListAddDirectory(); |     void OnGameListAddDirectory(); | ||||||
|     void OnGameListShowList(bool show); |     void OnGameListShowList(bool show); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue