diff options
| author | 2016-12-16 15:08:04 +0000 | |
|---|---|---|
| committer | 2016-12-16 15:08:04 +0000 | |
| commit | acc83a1c32ef43c6ff84926ff21dc5ef872decc0 (patch) | |
| tree | 95c20b3525c9e3a92444d45d3b5e071cea4e7dd7 /src | |
| parent | Merge pull request #2338 from wwylele/fix-dead (diff) | |
| parent | main: Open folder when open save folder location context menu is clicked (diff) | |
| download | yuzu-acc83a1c32ef43c6ff84926ff21dc5ef872decc0.tar.gz yuzu-acc83a1c32ef43c6ff84926ff21dc5ef872decc0.tar.xz yuzu-acc83a1c32ef43c6ff84926ff21dc5ef872decc0.zip | |
Merge pull request #2322 from MerryMage/ctx-mnu
game_list: Add a context menu with "Open Save Location" option
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/game_list.cpp | 28 | ||||
| -rw-r--r-- | src/citra_qt/game_list.h | 3 | ||||
| -rw-r--r-- | src/citra_qt/game_list_p.h | 5 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 19 | ||||
| -rw-r--r-- | src/citra_qt/main.h | 1 | ||||
| -rw-r--r-- | src/core/file_sys/archive_source_sd_savedata.cpp | 5 | ||||
| -rw-r--r-- | src/core/file_sys/archive_source_sd_savedata.h | 2 | ||||
| -rw-r--r-- | src/core/loader/loader.h | 9 | ||||
| -rw-r--r-- | src/core/loader/ncch.cpp | 12 | ||||
| -rw-r--r-- | src/core/loader/ncch.h | 7 |
10 files changed, 87 insertions, 4 deletions
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index e536628dd..09469f3c5 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QHeaderView> | 5 | #include <QHeaderView> |
| 6 | #include <QMenu> | ||
| 6 | #include <QThreadPool> | 7 | #include <QThreadPool> |
| 7 | #include <QVBoxLayout> | 8 | #include <QVBoxLayout> |
| 8 | #include "common/common_paths.h" | 9 | #include "common/common_paths.h" |
| @@ -28,6 +29,7 @@ GameList::GameList(QWidget* parent) : QWidget{parent} { | |||
| 28 | tree_view->setSortingEnabled(true); | 29 | tree_view->setSortingEnabled(true); |
| 29 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | 30 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); |
| 30 | tree_view->setUniformRowHeights(true); | 31 | tree_view->setUniformRowHeights(true); |
| 32 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | ||
| 31 | 33 | ||
| 32 | item_model->insertColumns(0, COLUMN_COUNT); | 34 | item_model->insertColumns(0, COLUMN_COUNT); |
| 33 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 35 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); |
| @@ -35,10 +37,10 @@ GameList::GameList(QWidget* parent) : QWidget{parent} { | |||
| 35 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 37 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); |
| 36 | 38 | ||
| 37 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 39 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| 40 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | ||
| 38 | 41 | ||
| 39 | // We must register all custom types with the Qt Automoc system so that we are able to use it | 42 | // We must register all custom types with the Qt Automoc system so that we are able to use it |
| 40 | // with | 43 | // with signals/slots. In this case, QList falls under the umbrells of custom types. |
| 41 | // signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 42 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | 44 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); |
| 43 | 45 | ||
| 44 | layout->addWidget(tree_view); | 46 | layout->addWidget(tree_view); |
| @@ -71,6 +73,23 @@ void GameList::DonePopulating() { | |||
| 71 | tree_view->setEnabled(true); | 73 | tree_view->setEnabled(true); |
| 72 | } | 74 | } |
| 73 | 75 | ||
| 76 | void GameList::PopupContextMenu(const QPoint& menu_location) { | ||
| 77 | QModelIndex item = tree_view->indexAt(menu_location); | ||
| 78 | if (!item.isValid()) | ||
| 79 | return; | ||
| 80 | |||
| 81 | int row = item_model->itemFromIndex(item)->row(); | ||
| 82 | QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); | ||
| 83 | u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); | ||
| 84 | |||
| 85 | QMenu context_menu; | ||
| 86 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | ||
| 87 | open_save_location->setEnabled(program_id != 0); | ||
| 88 | connect(open_save_location, &QAction::triggered, | ||
| 89 | [&]() { emit OpenSaveFolderRequested(program_id); }); | ||
| 90 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | ||
| 91 | } | ||
| 92 | |||
| 74 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | 93 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { |
| 75 | if (!FileUtil::Exists(dir_path.toStdString()) || | 94 | if (!FileUtil::Exists(dir_path.toStdString()) || |
| 76 | !FileUtil::IsDirectory(dir_path.toStdString())) { | 95 | !FileUtil::IsDirectory(dir_path.toStdString())) { |
| @@ -128,8 +147,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 128 | std::vector<u8> smdh; | 147 | std::vector<u8> smdh; |
| 129 | loader->ReadIcon(smdh); | 148 | loader->ReadIcon(smdh); |
| 130 | 149 | ||
| 150 | u64 program_id = 0; | ||
| 151 | loader->ReadProgramId(program_id); | ||
| 152 | |||
| 131 | emit EntryReady({ | 153 | emit EntryReady({ |
| 132 | new GameListItemPath(QString::fromStdString(physical_name), smdh), | 154 | new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id), |
| 133 | new GameListItem( | 155 | new GameListItem( |
| 134 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), | 156 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), |
| 135 | new GameListItemSize(FileUtil::GetSize(physical_name)), | 157 | new GameListItemSize(FileUtil::GetSize(physical_name)), |
diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index 30b2c79a8..1abf10051 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h | |||
| @@ -36,12 +36,15 @@ public: | |||
| 36 | signals: | 36 | signals: |
| 37 | void GameChosen(QString game_path); | 37 | void GameChosen(QString game_path); |
| 38 | void ShouldCancelWorker(); | 38 | void ShouldCancelWorker(); |
| 39 | void OpenSaveFolderRequested(u64 program_id); | ||
| 39 | 40 | ||
| 40 | private: | 41 | private: |
| 41 | void AddEntry(const QList<QStandardItem*>& entry_items); | 42 | void AddEntry(const QList<QStandardItem*>& entry_items); |
| 42 | void ValidateEntry(const QModelIndex& item); | 43 | void ValidateEntry(const QModelIndex& item); |
| 43 | void DonePopulating(); | 44 | void DonePopulating(); |
| 44 | 45 | ||
| 46 | void PopupContextMenu(const QPoint& menu_location); | ||
| 47 | |||
| 45 | QTreeView* tree_view = nullptr; | 48 | QTreeView* tree_view = nullptr; |
| 46 | QStandardItemModel* item_model = nullptr; | 49 | QStandardItemModel* item_model = nullptr; |
| 47 | GameListWorker* current_worker = nullptr; | 50 | GameListWorker* current_worker = nullptr; |
diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 5ca3fe991..a15f06c5f 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h | |||
| @@ -71,10 +71,13 @@ class GameListItemPath : public GameListItem { | |||
| 71 | public: | 71 | public: |
| 72 | static const int FullPathRole = Qt::UserRole + 1; | 72 | static const int FullPathRole = Qt::UserRole + 1; |
| 73 | static const int TitleRole = Qt::UserRole + 2; | 73 | static const int TitleRole = Qt::UserRole + 2; |
| 74 | static const int ProgramIdRole = Qt::UserRole + 3; | ||
| 74 | 75 | ||
| 75 | GameListItemPath() : GameListItem() {} | 76 | GameListItemPath() : GameListItem() {} |
| 76 | GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data) : GameListItem() { | 77 | GameListItemPath(const QString& game_path, const std::vector<u8>& smdh_data, u64 program_id) |
| 78 | : GameListItem() { | ||
| 77 | setData(game_path, FullPathRole); | 79 | setData(game_path, FullPathRole); |
| 80 | setData(qulonglong(program_id), ProgramIdRole); | ||
| 78 | 81 | ||
| 79 | if (!Loader::IsValidSMDH(smdh_data)) { | 82 | if (!Loader::IsValidSMDH(smdh_data)) { |
| 80 | // SMDH is not valid, set a default icon | 83 | // SMDH is not valid, set a default icon |
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index a3887f9ab..ad6221739 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cinttypes> | ||
| 5 | #include <clocale> | 6 | #include <clocale> |
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <thread> | 8 | #include <thread> |
| @@ -41,6 +42,7 @@ | |||
| 41 | #include "common/string_util.h" | 42 | #include "common/string_util.h" |
| 42 | #include "core/arm/disassembler/load_symbol_map.h" | 43 | #include "core/arm/disassembler/load_symbol_map.h" |
| 43 | #include "core/core.h" | 44 | #include "core/core.h" |
| 45 | #include "core/file_sys/archive_source_sd_savedata.h" | ||
| 44 | #include "core/gdbstub/gdbstub.h" | 46 | #include "core/gdbstub/gdbstub.h" |
| 45 | #include "core/loader/loader.h" | 47 | #include "core/loader/loader.h" |
| 46 | #include "core/settings.h" | 48 | #include "core/settings.h" |
| @@ -171,6 +173,8 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||
| 171 | // Setup connections | 173 | // Setup connections |
| 172 | connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), | 174 | connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)), |
| 173 | Qt::DirectConnection); | 175 | Qt::DirectConnection); |
| 176 | connect(game_list, SIGNAL(OpenSaveFolderRequested(u64)), this, | ||
| 177 | SLOT(OnGameListOpenSaveFolder(u64)), Qt::DirectConnection); | ||
| 174 | connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); | 178 | connect(ui.action_Configure, SIGNAL(triggered()), this, SLOT(OnConfigure())); |
| 175 | connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()), | 179 | connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()), |
| 176 | Qt::DirectConnection); | 180 | Qt::DirectConnection); |
| @@ -460,6 +464,21 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { | |||
| 460 | BootGame(game_path.toStdString()); | 464 | BootGame(game_path.toStdString()); |
| 461 | } | 465 | } |
| 462 | 466 | ||
| 467 | void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) { | ||
| 468 | std::string sdmc_dir = FileUtil::GetUserPath(D_SDMC_IDX); | ||
| 469 | std::string path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, program_id); | ||
| 470 | QString qpath = QString::fromStdString(path); | ||
| 471 | |||
| 472 | QDir dir(qpath); | ||
| 473 | if (!dir.exists()) { | ||
| 474 | QMessageBox::critical(this, tr("Error Opening Save Folder"), tr("Folder does not exist!")); | ||
| 475 | return; | ||
| 476 | } | ||
| 477 | |||
| 478 | LOG_INFO(Frontend, "Opening save data path for program_id=%" PRIu64, program_id); | ||
| 479 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | ||
| 480 | } | ||
| 481 | |||
| 463 | void GMainWindow::OnMenuLoadFile() { | 482 | void GMainWindow::OnMenuLoadFile() { |
| 464 | QString filename = | 483 | QString filename = |
| 465 | QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, | 484 | QFileDialog::getOpenFileName(this, tr("Load File"), UISettings::values.roms_path, |
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index f87178227..035b68a35 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h | |||
| @@ -105,6 +105,7 @@ private slots: | |||
| 105 | void OnStopGame(); | 105 | void OnStopGame(); |
| 106 | /// Called whenever a user selects a game in the game list widget. | 106 | /// Called whenever a user selects a game in the game list widget. |
| 107 | void OnGameListLoadFile(QString game_path); | 107 | void OnGameListLoadFile(QString game_path); |
| 108 | void OnGameListOpenSaveFolder(u64 program_id); | ||
| 108 | void OnMenuLoadFile(); | 109 | void OnMenuLoadFile(); |
| 109 | void OnMenuLoadSymbolMap(); | 110 | void OnMenuLoadSymbolMap(); |
| 110 | /// Called whenever a user selects the "File->Select Game List Root" menu item | 111 | /// Called whenever a user selects the "File->Select Game List Root" menu item |
diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp index 2d8a950a3..287322d3e 100644 --- a/src/core/file_sys/archive_source_sd_savedata.cpp +++ b/src/core/file_sys/archive_source_sd_savedata.cpp | |||
| @@ -90,4 +90,9 @@ ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program | |||
| 90 | return MakeResult<ArchiveFormatInfo>(info); | 90 | return MakeResult<ArchiveFormatInfo>(info); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point, | ||
| 94 | u64 program_id) { | ||
| 95 | return GetSaveDataPath(GetSaveDataContainerPath(mount_point), program_id); | ||
| 96 | } | ||
| 97 | |||
| 93 | } // namespace FileSys | 98 | } // namespace FileSys |
diff --git a/src/core/file_sys/archive_source_sd_savedata.h b/src/core/file_sys/archive_source_sd_savedata.h index b33126c31..b5fe43cc1 100644 --- a/src/core/file_sys/archive_source_sd_savedata.h +++ b/src/core/file_sys/archive_source_sd_savedata.h | |||
| @@ -23,6 +23,8 @@ public: | |||
| 23 | ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info); | 23 | ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info); |
| 24 | ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const; | 24 | ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const; |
| 25 | 25 | ||
| 26 | static std::string GetSaveDataPathFor(const std::string& mount_point, u64 program_id); | ||
| 27 | |||
| 26 | private: | 28 | private: |
| 27 | std::string mount_point; | 29 | std::string mount_point; |
| 28 | }; | 30 | }; |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 5e3d46638..a6c2a745f 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -144,6 +144,15 @@ public: | |||
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | /** | 146 | /** |
| 147 | * Get the program id of the application | ||
| 148 | * @param out_program_id Reference to store program id into | ||
| 149 | * @return ResultStatus result of function | ||
| 150 | */ | ||
| 151 | virtual ResultStatus ReadProgramId(u64& out_program_id) { | ||
| 152 | return ResultStatus::ErrorNotImplemented; | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 147 | * Get the RomFS of the application | 156 | * Get the RomFS of the application |
| 148 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer | 157 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer |
| 149 | * @param romfs_file The file containing the RomFS | 158 | * @param romfs_file The file containing the RomFS |
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index d4be61e0e..6f2164428 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp | |||
| @@ -344,6 +344,18 @@ ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { | |||
| 344 | return LoadSectionExeFS("logo", buffer); | 344 | return LoadSectionExeFS("logo", buffer); |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { | ||
| 348 | if (!file.IsOpen()) | ||
| 349 | return ResultStatus::Error; | ||
| 350 | |||
| 351 | ResultStatus result = LoadExeFS(); | ||
| 352 | if (result != ResultStatus::Success) | ||
| 353 | return result; | ||
| 354 | |||
| 355 | out_program_id = ncch_header.program_id; | ||
| 356 | return ResultStatus::Success; | ||
| 357 | } | ||
| 358 | |||
| 347 | ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | 359 | ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, |
| 348 | u64& size) { | 360 | u64& size) { |
| 349 | if (!file.IsOpen()) | 361 | if (!file.IsOpen()) |
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index bcf3ae6e3..6c93d46d8 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h | |||
| @@ -220,6 +220,13 @@ public: | |||
| 220 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; | 220 | ResultStatus ReadLogo(std::vector<u8>& buffer) override; |
| 221 | 221 | ||
| 222 | /** | 222 | /** |
| 223 | * Get the program id of the application | ||
| 224 | * @param out_program_id Reference to store program id into | ||
| 225 | * @return ResultStatus result of function | ||
| 226 | */ | ||
| 227 | ResultStatus ReadProgramId(u64& out_program_id) override; | ||
| 228 | |||
| 229 | /** | ||
| 223 | * Get the RomFS of the application | 230 | * Get the RomFS of the application |
| 224 | * @param romfs_file Reference to buffer to store data | 231 | * @param romfs_file Reference to buffer to store data |
| 225 | * @param offset Offset in the file to the RomFS | 232 | * @param offset Offset in the file to the RomFS |