From fe51843638ebc55b7dbad4707d365402e69e26e3 Mon Sep 17 00:00:00 2001 From: crueter Date: Sat, 8 Nov 2025 15:31:56 -0500 Subject: [PATCH] fix cancelling, From Eden linking also, only prompt for profiles if there's more than one valid profile. Currently only affects save data ops, that is, Ryujinx linking and open save data directory Signed-off-by: crueter --- src/core/hle/service/acc/profile_manager.cpp | 50 ++++++---- src/core/hle/service/acc/profile_manager.h | 2 + src/qt_common/util/fs.cpp | 1 - src/qt_common/util/fs.h | 1 + src/yuzu/main_window.cpp | 99 +++++++------------- src/yuzu/main_window.h | 3 +- src/yuzu/ryujinx_dialog.cpp | 26 +++-- 7 files changed, 92 insertions(+), 90 deletions(-) diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 07b14a8ab9..b610792271 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -490,6 +490,32 @@ void ProfileManager::ResetUserSaveFile() ParseUserSaveFile(); } +std::vector ProfileManager::FindExistingProfileUUIDs() +{ + std::vector uuids; + for (const ProfileInfo& p : profiles) { + auto uuid = p.user_uuid; + if (!uuid.IsInvalid()) { + uuids.emplace_back(uuid); + } + } + + return uuids; +} + +std::vector ProfileManager::FindExistingProfileStrings() +{ + std::vector uuids = FindExistingProfileUUIDs(); + std::vector uuid_strings; + + for (const UUID &uuid : uuids) { + auto user_id = uuid.AsU128(); + uuid_strings.emplace_back(fmt::format("{:016X}{:016X}", user_id[1], user_id[0])); + } + + return uuid_strings; +} + std::vector ProfileManager::FindGoodProfiles() { namespace fs = std::filesystem; @@ -499,31 +525,17 @@ std::vector ProfileManager::FindGoodProfiles() const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000"; - // some exceptions because certain games just LOVE TO CAUSE ISSUES - static constexpr const std::array EXCEPTION_UUIDS - = {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"}; + // some exceptions, e.g. the "system" profile + static constexpr const std::array EXCEPTION_UUIDS + = {"00000000000000000000000000000000"}; for (const char *const uuid : EXCEPTION_UUIDS) { if (fs::exists(path / uuid)) good_uuids.emplace_back(uuid); } - for (const ProfileInfo& p : profiles) { - std::string uuid_string = [p]() -> std::string { - auto uuid = p.user_uuid; - - // "ignore" invalid uuids - if (uuid.IsInvalid()) { - return "0"; - } - - auto user_id = uuid.AsU128(); - - return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]); - }(); - - if (uuid_string != "0") good_uuids.emplace_back(uuid_string); - } + auto existing = FindExistingProfileStrings(); + good_uuids.insert(good_uuids.end(), existing.begin(), existing.end()); return good_uuids; } diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 9c91fcde41..4948118b92 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -105,6 +105,8 @@ public: void ResetUserSaveFile(); + std::vector FindExistingProfileUUIDs(); + std::vector FindExistingProfileStrings(); std::vector FindGoodProfiles(); std::vector FindOrphanedProfiles(); diff --git a/src/qt_common/util/fs.cpp b/src/qt_common/util/fs.cpp index c1465bea88..dd105849aa 100644 --- a/src/qt_common/util/fs.cpp +++ b/src/qt_common/util/fs.cpp @@ -140,7 +140,6 @@ const fs::path GetRyujinxSavePath(const fs::path &path_hint, const u64 &program_ tr("Could not find Ryujinx save data"), StringLookup::Lookup(StringLookup::RyujinxNoSaveId).arg(program_id, 0, 16)); } else { - // TODO: make this long thing a function or something QString caption = LOOKUP_ENUM(res, KvdbNonexistent); QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption); } diff --git a/src/qt_common/util/fs.h b/src/qt_common/util/fs.h index 277eab3535..41669e8019 100644 --- a/src/qt_common/util/fs.h +++ b/src/qt_common/util/fs.h @@ -3,6 +3,7 @@ #include "common/common_types.h" #include +#include #pragma once diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 354d18a884..cee3498ea8 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "common/fs/ryujinx_compat.h" -#include "common/fs/symlink.h" #include "main_window.h" #include "network/network.h" #include "qt_common/discord/discord.h" @@ -2371,33 +2370,8 @@ void MainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, if (has_user_save) { // User save data - 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 user_id = - QtCommon::system->GetProfileManager().GetUser(static_cast(index)); - ASSERT(user_id); + const auto user_id = GetProfileID(); + assert(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( {}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, @@ -2824,8 +2798,14 @@ void MainWindow::OnGameListOpenPerGameProperties(const std::string& file) { OpenPerGameConfiguration(title_id, file); } -std::string MainWindow::GetProfileID() +const std::optional MainWindow::GetProfileID() { + // if there's only a single profile, the user probably wants to use that... right? + const auto& profiles = QtCommon::system->GetProfileManager().FindExistingProfileUUIDs(); + if (profiles.size() == 1) { + return profiles[0]; + } + const auto select_profile = [this] { const Core::Frontend::ProfileSelectParameters parameters{ .mode = Service::AM::Frontend::UiMode::UserSelector, @@ -2847,13 +2827,21 @@ std::string MainWindow::GetProfileID() const auto index = select_profile(); if (index == -1) { - return ""; + return std::nullopt; } const auto uuid = QtCommon::system->GetProfileManager().GetUser(static_cast(index)); ASSERT(uuid); - const auto user_id = uuid->AsU128(); + return uuid; +} + +std::string MainWindow::GetProfileIDString() +{ + const auto uuid = GetProfileID(); + if (!uuid) return ""; + + auto user_id = uuid->AsU128(); return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]); } @@ -2862,44 +2850,29 @@ void MainWindow::OnLinkToRyujinx(const u64& program_id) { namespace fs = std::filesystem; - const std::string user_id = GetProfileID(); - const std::string hex_program = fmt::format("{:016X}", program_id); + fs::path ryu_dir; - const fs::path eden_dir = FrontendCommon::DataManager::GetDataDir( - FrontendCommon::DataManager::DataDir::Saves, user_id) / - hex_program; + // find an existing Ryujinx linked path in config.ini; if it exists, use it as a "hint" + // If it's not defined in config.ini, use default + const fs::path existing_path = + UISettings::values.ryujinx_link_paths + .value(program_id, QDir(Common::FS::GetLegacyPath(Common::FS::RyujinxDir))) + .filesystemAbsolutePath(); - fs::path ryu_dir; + // this function also prompts the user to manually specify a portable location + ryu_dir = QtCommon::FS::GetRyujinxSavePath(existing_path, program_id); - // filesystem error: read_symlink: Function not implemented - // Theoretically, the check immediately after this should account for it; - // keyword THEORETICALLY -#ifndef __MINGW32__ - // If the Eden directory is a symlink we can just read that and use it as our Ryu dir - if (Common::FS::IsSymlink(eden_dir)) { - ryu_dir = fs::read_symlink(eden_dir); - - // Fallback: if the Eden save dir is symlinked to a nonexistent location, - // just delete and recreate it to remove the symlink. - if (!fs::exists(ryu_dir)) { - fs::remove(eden_dir); - fs::create_directories(eden_dir); - ryu_dir = fs::path{}; - } - } -#endif + if (ryu_dir.empty()) return; - // Otherwise, prompt the user - if (ryu_dir.empty()) { - const fs::path existing_path = - UISettings::values.ryujinx_link_paths - .value(program_id, QDir(Common::FS::GetLegacyPath(Common::FS::RyujinxDir))) - .filesystemAbsolutePath(); + const std::string user_id = GetProfileIDString(); + if (user_id.empty()) return; - ryu_dir = QtCommon::FS::GetRyujinxSavePath(existing_path, program_id); + const std::string hex_program = fmt::format("{:016X}", program_id); + + const fs::path eden_dir = FrontendCommon::DataManager::GetDataDir( + FrontendCommon::DataManager::DataDir::Saves, user_id) / + hex_program; - if (ryu_dir.empty()) return; - } // CheckUnlink basically just checks to see if one or both are linked, and prompts the user to // unlink if this is the case. diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index fcf3dc052e..a5e23aaa80 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -469,7 +469,8 @@ private: QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - std::string GetProfileID(); + const std::optional GetProfileID(); + std::string GetProfileIDString(); std::unique_ptr ui; diff --git a/src/yuzu/ryujinx_dialog.cpp b/src/yuzu/ryujinx_dialog.cpp index db10c06d93..940b348b68 100644 --- a/src/yuzu/ryujinx_dialog.cpp +++ b/src/yuzu/ryujinx_dialog.cpp @@ -1,25 +1,25 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "qt_common/abstract/frontend.h" #include "ryujinx_dialog.h" #include "qt_common/util/fs.h" #include "ui_ryujinx_dialog.h" #include -namespace fs = std::filesystem; - RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path, std::filesystem::path ryu_path, QWidget *parent) : QDialog(parent) - , ui(new Ui::RyujinxDialog) - , m_eden(eden_path.make_preferred()) - , m_ryu(ryu_path.make_preferred()) + , ui(new Ui::RyujinxDialog) + , m_eden(eden_path.make_preferred()) + , m_ryu(ryu_path.make_preferred()) { ui->setupUi(this); connect(ui->eden, &QPushButton::clicked, this, &RyujinxDialog::fromEden); connect(ui->ryujinx, &QPushButton::clicked, this, &RyujinxDialog::fromRyujinx); + connect(ui->cancel, &QPushButton::clicked, this, &RyujinxDialog::reject); } RyujinxDialog::~RyujinxDialog() @@ -30,7 +30,21 @@ RyujinxDialog::~RyujinxDialog() void RyujinxDialog::fromEden() { accept(); - QtCommon::FS::LinkRyujinx(m_eden, m_ryu); + + // Workaround: Ryujinx deletes and re-creates its directory structure??? + // So we just copy Eden's data to Ryujinx and then link the other way + namespace fs = std::filesystem; + try { + fs::remove_all(m_ryu); + fs::create_directories(m_ryu); + fs::copy(m_eden, m_ryu, fs::copy_options::recursive); + } catch (std::exception &e) { + QtCommon::Frontend::Critical(tr("Failed to link save data"), + tr("OS returned error: %1").arg(e.what())); + } + + // ?ploo + QtCommon::FS::LinkRyujinx(m_ryu, m_eden); } void RyujinxDialog::fromRyujinx()