diff --git a/src/frontend_common/data_manager.cpp b/src/frontend_common/data_manager.cpp index e83e3ada40..83eccbde17 100644 --- a/src/frontend_common/data_manager.cpp +++ b/src/frontend_common/data_manager.cpp @@ -11,13 +11,13 @@ namespace FrontendCommon::DataManager { namespace fs = std::filesystem; -const std::string GetDataDir(DataDir dir) +const std::string GetDataDir(DataDir dir, const std::string &user_id) { const fs::path nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir); switch (dir) { case DataDir::Saves: - return (nand_dir / "user" / "save" / "0000000000000000").string(); + return (nand_dir / "user" / "save" / "0000000000000000" / user_id).string(); case DataDir::UserNand: return (nand_dir / "user" / "Contents" / "registered").string(); case DataDir::SysNand: @@ -35,9 +35,9 @@ const std::string GetDataDir(DataDir dir) return ""; } -u64 ClearDir(DataDir dir) +u64 ClearDir(DataDir dir, const std::string &user_id) { - fs::path data_dir = GetDataDir(dir); + fs::path data_dir = GetDataDir(dir, user_id); u64 result = fs::remove_all(data_dir); // mkpath at the end just so it actually exists diff --git a/src/frontend_common/data_manager.h b/src/frontend_common/data_manager.h index f6ae836c14..d67bd20467 100644 --- a/src/frontend_common/data_manager.h +++ b/src/frontend_common/data_manager.h @@ -11,9 +11,9 @@ namespace FrontendCommon::DataManager { enum class DataDir { Saves, UserNand, SysNand, Mods, Shaders }; -const std::string GetDataDir(DataDir dir); +const std::string GetDataDir(DataDir dir, const std::string &user_id = ""); -u64 ClearDir(DataDir dir); +u64 ClearDir(DataDir dir, const std::string &user_id = ""); const std::string ReadableBytesSize(u64 size); diff --git a/src/qt_common/qt_compress.cpp b/src/qt_common/qt_compress.cpp index d1660b0ba8..1fc5c4bb2c 100644 --- a/src/qt_common/qt_compress.cpp +++ b/src/qt_common/qt_compress.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #include "qt_compress.h" #include "quazipfileinfo.h" diff --git a/src/qt_common/qt_compress.h b/src/qt_common/qt_compress.h index cc91e73fab..003a87ac87 100644 --- a/src/qt_common/qt_compress.h +++ b/src/qt_common/qt_compress.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp index 9072d455a7..fd70e0d416 100644 --- a/src/qt_common/qt_content_util.cpp +++ b/src/qt_common/qt_content_util.cpp @@ -358,7 +358,7 @@ void FixProfiles() QtCommon::Game::OpenSaveFolder(); } -void ClearDataDir(FrontendCommon::DataManager::DataDir dir) +void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id) { auto result = QtCommon::Frontend::Warning(tr("Really clear data?"), tr("Important data may be lost!"), @@ -379,15 +379,17 @@ void ClearDataDir(FrontendCommon::DataManager::DataDir dir) QtCommon::Frontend::QtProgressDialog dialog(tr("Clearing..."), QString(), 0, 0); dialog.show(); - FrontendCommon::DataManager::ClearDir(dir); + FrontendCommon::DataManager::ClearDir(dir, user_id); dialog.close(); } -void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function callback) +void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, + const std::string& user_id, + std::function callback) { using namespace QtCommon::Frontend; - const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir); + const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id); const QString zip_dump_location = GetSaveFileName(tr("Select Export Location"), QStringLiteral("export.zip"), @@ -447,9 +449,11 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function< watcher->setFuture(future); } -void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function callback) +void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, + const std::string& user_id, + std::function callback) { - const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir); + const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir, user_id); using namespace QtCommon::Frontend; @@ -484,7 +488,7 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function< // to prevent GUI mangling we have to run this in a thread as well QFuture delete_future = QtConcurrent::run([=]() { - FrontendCommon::DataManager::ClearDir(data_dir); + FrontendCommon::DataManager::ClearDir(data_dir, user_id); return !progress->wasCanceled(); }); diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h index fb6d1b85fa..aef7d8b41e 100644 --- a/src/qt_common/qt_content_util.h +++ b/src/qt_common/qt_content_util.h @@ -47,9 +47,9 @@ void InstallKeys(); void VerifyGameContents(const std::string &game_path); void VerifyInstalledContents(); -void ClearDataDir(FrontendCommon::DataManager::DataDir dir); -void ExportDataDir(FrontendCommon::DataManager::DataDir dir, std::function callback = {}); -void ImportDataDir(FrontendCommon::DataManager::DataDir dir, std::function callback = {}); +void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = ""); +void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function callback = {}); +void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function callback = {}); // Profiles // void FixProfiles(); diff --git a/src/yuzu/data_dialog.cpp b/src/yuzu/data_dialog.cpp index a097a6f893..38161be827 100644 --- a/src/yuzu/data_dialog.cpp +++ b/src/yuzu/data_dialog.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "data_dialog.h" +#include "core/hle/service/acc/profile_manager.h" #include "frontend_common/data_manager.h" #include "qt_common/qt_content_util.h" #include "qt_common/qt_frontend_util.h" @@ -15,6 +16,10 @@ #include #include +#include + +#include + DataDialog::DataDialog(QWidget *parent) : QDialog(parent) , ui(std::make_unique()) @@ -73,24 +78,40 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir, void DataWidget::clear() { - QtCommon::Content::ClearDataDir(m_dir); + std::string user_id{}; + if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { + user_id = selectProfile(); + } + QtCommon::Content::ClearDataDir(m_dir, user_id); scan(); } void DataWidget::open() { + std::string user_id{}; + if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { + user_id = selectProfile(); + } QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir)))); + QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir, user_id)))); } void DataWidget::upload() { - QtCommon::Content::ExportDataDir(m_dir); + std::string user_id{}; + if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { + user_id = selectProfile(); + } + QtCommon::Content::ExportDataDir(m_dir, user_id); } void DataWidget::download() { - QtCommon::Content::ImportDataDir(m_dir, std::bind(&DataWidget::scan, this)); + std::string user_id{}; + if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { + user_id = selectProfile(); + } + QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this)); } void DataWidget::scan() { @@ -108,3 +129,37 @@ void DataWidget::scan() { watcher->setFuture( QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); })); } + +std::string DataWidget::selectProfile() +{ + const auto select_profile = [this] { + const Core::Frontend::ProfileSelectParameters parameters{ + .mode = Service::AM::Frontend::UiMode::UserSelector, + .invalid_uid_list = {}, + .display_options = {}, + .purpose = Service::AM::Frontend::UserSelectionPurpose::General, + }; + QtProfileSelectionDialog dialog(*QtCommon::system, this, parameters); + dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint + | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); + dialog.setWindowModality(Qt::WindowModal); + + if (dialog.exec() == QDialog::Rejected) { + return -1; + } + + return dialog.GetIndex(); + }; + + const auto index = select_profile(); + if (index == -1) { + return ""; + } + + const auto uuid = QtCommon::system->GetProfileManager().GetUser(static_cast(index)); + ASSERT(uuid); + + const auto user_id = uuid->AsU128(); + + return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]); +} diff --git a/src/yuzu/data_dialog.h b/src/yuzu/data_dialog.h index 8a64659965..e8ae5c425e 100644 --- a/src/yuzu/data_dialog.h +++ b/src/yuzu/data_dialog.h @@ -45,6 +45,8 @@ public slots: private: std::unique_ptr ui; FrontendCommon::DataManager::DataDir m_dir; + + std::string selectProfile(); }; #endif // DATA_DIALOG_H diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index da12b8a5f4..77bfe5db9f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2408,7 +2408,6 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?"); - // TODO(alekpop): It returns the wrong user switch (target) { case GameListOpenTarget::SaveData: { open_target = tr("Save Data"); @@ -2794,6 +2793,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { + // TODO(crueter): QtCommon std::filesystem::path fs_path; if (directory == QStringLiteral("SDMC")) { fs_path =