Browse Source

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 <crueter@eden-emu.dev>
pull/2929/head
crueter 3 months ago
parent
commit
fe51843638
  1. 50
      src/core/hle/service/acc/profile_manager.cpp
  2. 2
      src/core/hle/service/acc/profile_manager.h
  3. 1
      src/qt_common/util/fs.cpp
  4. 1
      src/qt_common/util/fs.h
  5. 99
      src/yuzu/main_window.cpp
  6. 3
      src/yuzu/main_window.h
  7. 26
      src/yuzu/ryujinx_dialog.cpp

50
src/core/hle/service/acc/profile_manager.cpp

@ -490,6 +490,32 @@ void ProfileManager::ResetUserSaveFile()
ParseUserSaveFile();
}
std::vector<UUID> ProfileManager::FindExistingProfileUUIDs()
{
std::vector<UUID> uuids;
for (const ProfileInfo& p : profiles) {
auto uuid = p.user_uuid;
if (!uuid.IsInvalid()) {
uuids.emplace_back(uuid);
}
}
return uuids;
}
std::vector<std::string> ProfileManager::FindExistingProfileStrings()
{
std::vector<UUID> uuids = FindExistingProfileUUIDs();
std::vector<std::string> 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<std::string> ProfileManager::FindGoodProfiles()
{
namespace fs = std::filesystem;
@ -499,31 +525,17 @@ std::vector<std::string> 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<const char* const, 2> EXCEPTION_UUIDS
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
// some exceptions, e.g. the "system" profile
static constexpr const std::array<const char* const, 1> 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;
}

2
src/core/hle/service/acc/profile_manager.h

@ -105,6 +105,8 @@ public:
void ResetUserSaveFile();
std::vector<Common::UUID> FindExistingProfileUUIDs();
std::vector<std::string> FindExistingProfileStrings();
std::vector<std::string> FindGoodProfiles();
std::vector<std::string> FindOrphanedProfiles();

1
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);
}

1
src/qt_common/util/fs.h

@ -3,6 +3,7 @@
#include "common/common_types.h"
#include <filesystem>
#include <optional>
#pragma once

99
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<std::size_t>(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<Common::UUID> 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<std::size_t>(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.

3
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<Common::UUID> GetProfileID();
std::string GetProfileIDString();
std::unique_ptr<Ui::MainWindow> ui;

26
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 <filesystem>
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()

Loading…
Cancel
Save