diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index dfdca83d6d..034d3a78f9 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -73,7 +73,7 @@ ResultVal SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr } std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, - u128 user_id, u64 save_id) const { + u128 user_id, u64 save_id) { // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should // be interpreted as the title id of the current process. if (type == SaveDataType::SaveData && title_id == 0) diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index f3cf50d5ac..368b360170 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -49,11 +49,11 @@ public: ResultVal Open(SaveDataSpaceId space, SaveDataDescriptor meta); + static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, + u128 user_id, u64 save_id); + private: VirtualDir dir; - - std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, - u64 save_id) const; }; } // namespace FileSys diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index a974fb9337..d5726b8b3c 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -327,7 +327,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); open_save_location->setEnabled(program_id != 0); connect(open_save_location, &QAction::triggered, - [&]() { emit OpenSaveFolderRequested(program_id); }); + [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); } diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index afe624b328..20252e778d 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -21,6 +21,8 @@ class GameListWorker; +enum class GameListOpenTarget { SaveData }; + class GameList : public QWidget { Q_OBJECT @@ -76,7 +78,7 @@ public: signals: void GameChosen(QString game_path); void ShouldCancelWorker(); - void OpenSaveFolderRequested(u64 program_id); + void OpenFolderRequested(u64 program_id, GameListOpenTarget target); private slots: void onTextChanged(const QString& newText); @@ -99,3 +101,5 @@ private: GameListWorker* current_worker = nullptr; QFileSystemWatcher* watcher = nullptr; }; + +Q_DECLARE_METATYPE(GameListOpenTarget); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3db3f9d988..e4cac59845 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -30,6 +30,7 @@ #include "core/file_sys/bis_factory.h" #include "core/file_sys/card_image.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/savedata_factory.h" #include "core/file_sys/vfs_real.h" #include "core/gdbstub/gdbstub.h" #include "core/loader/loader.h" @@ -303,8 +304,7 @@ void GMainWindow::RestoreUIState() { void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); - connect(game_list, &GameList::OpenSaveFolderRequested, this, - &GMainWindow::OnGameListOpenSaveFolder); + connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); @@ -584,8 +584,37 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { BootGame(game_path); } -void GMainWindow::OnGameListOpenSaveFolder(u64 program_id) { - UNIMPLEMENTED(); +void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) { + std::string path; + std::string open_target; + switch (target) { + case GameListOpenTarget::SaveData: { + open_target = "Save Data"; + const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); + ASSERT(program_id != 0); + // TODO(tech4me): Update this to work with arbitrary user profile + // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor + constexpr u128 user_id = {1, 0}; + path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, + FileSys::SaveDataType::SaveData, + program_id, user_id, 0); + break; + } + default: + UNIMPLEMENTED(); + } + + const QString qpath = QString::fromStdString(path); + + const QDir dir(qpath); + if (!dir.exists()) { + QMessageBox::warning(this, + tr("Error Opening %1 Folder").arg(QString::fromStdString(open_target)), + tr("Folder does not exist!")); + return; + } + LOG_INFO(Frontend, "Opening {} path for program_id={:016x}", open_target, program_id); + QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } void GMainWindow::OnMenuLoadFile() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 5f4d2ab9a9..02df308785 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -21,6 +21,7 @@ class GRenderWindow; class MicroProfileDialog; class ProfilerWidget; class WaitTreeWidget; +enum class GameListOpenTarget; namespace Tegra { class DebugContext; @@ -122,7 +123,7 @@ private slots: void OnStopGame(); /// Called whenever a user selects a game in the game list widget. void OnGameListLoadFile(QString game_path); - void OnGameListOpenSaveFolder(u64 program_id); + void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); void OnMenuLoadFile(); void OnMenuLoadFolder(); void OnMenuInstallToNAND();