mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	yuzu: Move GameListWorker to its own source files
This has gotten sufficiently large enough to warrant moving it to its own source files. Especially given it dumps the file_sys headers around code that doesn't use it for the most part. This'll also make it easier to introduce a type alias for the compatibility list, so a large unordered_map type declaration doesn't need to be specified all the time (we don't want to propagate the game_list_p.h include via the main game_list.h header).
This commit is contained in:
		
							parent
							
								
									eadd8b91b2
								
							
						
					
					
						commit
						9238015dd4
					
				
					 6 changed files with 230 additions and 179 deletions
				
			
		|  | @ -76,6 +76,8 @@ add_executable(citra-qt | |||
|     game_list.cpp | ||||
|     game_list.h | ||||
|     game_list_p.h | ||||
|     game_list_worker.cpp | ||||
|     game_list_worker.h | ||||
|     hotkeys.cpp | ||||
|     hotkeys.h | ||||
|     main.cpp | ||||
|  |  | |||
|  | @ -23,6 +23,7 @@ | |||
| #include <fmt/format.h> | ||||
| #include "citra_qt/game_list.h" | ||||
| #include "citra_qt/game_list_p.h" | ||||
| #include "citra_qt/game_list_worker.h" | ||||
| #include "citra_qt/main.h" | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "common/common_paths.h" | ||||
|  | @ -30,7 +31,6 @@ | |||
| #include "core/file_sys/archive_extsavedata.h" | ||||
| #include "core/file_sys/archive_source_sd_savedata.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} | ||||
| 
 | ||||
|  | @ -648,11 +648,6 @@ void GameList::LoadInterfaceLayout() { | |||
| const QStringList GameList::supported_file_extensions = {"3ds", "3dsx", "elf", "axf", | ||||
|                                                          "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() { | ||||
|     if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { | ||||
|         LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); | ||||
|  | @ -678,123 +673,6 @@ QString GameList::FindGameByProgramID(QStandardItem* current_item, u64 program_i | |||
|     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} { | ||||
|     this->main_window = parent; | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <unordered_map> | ||||
| #include <QMenu> | ||||
| #include <QString> | ||||
| #include <QWidget> | ||||
| #include "common/common_types.h" | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <map> | ||||
| #include <unordered_map> | ||||
| #include <utility> | ||||
|  | @ -59,17 +58,6 @@ static QPixmap GetDefaultIcon(bool large) { | |||
|     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. | ||||
|  * @param smdh SMDH data | ||||
|  | @ -373,50 +361,16 @@ 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); | ||||
| }; | ||||
| inline 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; | ||||
|         }); | ||||
| } | ||||
| 
 | ||||
| class GameList; | ||||
| class QHBoxLayout; | ||||
|  |  | |||
							
								
								
									
										152
									
								
								src/citra_qt/game_list_worker.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/citra_qt/game_list_worker.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,152 @@ | |||
| // 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/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 std::unordered_map<std::string, std::pair<QString, QString>>& 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; | ||||
| } | ||||
							
								
								
									
										64
									
								
								src/citra_qt/game_list_worker.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/citra_qt/game_list_worker.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| // 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 "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 std::unordered_map<std::string, std::pair<QString, QString>>& 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 std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list; | ||||
|     QList<UISettings::GameDir>& game_dirs; | ||||
|     std::atomic_bool stop_processing; | ||||
| }; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue