From eed68a221e70f2694f1241db49ebea5d5f585cb5 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 27 Oct 2025 01:41:20 -0400 Subject: [PATCH] add unlink check Signed-off-by: crueter --- src/common/fs/symlink.cpp | 2 +- src/qt_common/util/fs.cpp | 82 ++++++++++++++++++++++++++++++++++--- src/qt_common/util/fs.h | 10 ++++- src/yuzu/main.cpp | 21 +++++----- src/yuzu/ryujinx_dialog.cpp | 10 +---- src/yuzu/ryujinx_dialog.h | 5 --- 6 files changed, 100 insertions(+), 30 deletions(-) diff --git a/src/common/fs/symlink.cpp b/src/common/fs/symlink.cpp index 0c1f26534e..d488fd4771 100644 --- a/src/common/fs/symlink.cpp +++ b/src/common/fs/symlink.cpp @@ -36,7 +36,7 @@ bool IsSymlink(const fs::path &path) #ifdef _WIN32 return fs::status(path).type() == fs::file_type::junction; #else - return fs::status(path).type() == fs::file_type::symlink; + return fs::is_symlink(path); #endif } diff --git a/src/qt_common/util/fs.cpp b/src/qt_common/util/fs.cpp index c2c0dbfcd0..a966f7ea90 100644 --- a/src/qt_common/util/fs.cpp +++ b/src/qt_common/util/fs.cpp @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include #include "fs.h" #include "common/fs/ryujinx_compat.h" #include "common/fs/symlink.h" +#include "frontend_common/data_manager.h" #include "qt_common/abstract/frontend.h" #include "qt_common/qt_string_lookup.h" @@ -11,7 +13,8 @@ namespace fs = std::filesystem; namespace QtCommon::FS { -void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to) { +void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to) +{ std::error_code ec; // "ignore" errors--if the dir fails to be deleted, error handling later will handle it @@ -20,11 +23,63 @@ void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to) { if (Common::FS::CreateSymlink(from, to)) { QtCommon::Frontend::Information(tr("Linked Save Data"), tr("Save data has been linked.")); } else { - QtCommon::Frontend::Critical(tr("Failed to link save data"), - tr("Could not link directory:\n\t%1\nTo:\n\t%2") - .arg(QString::fromStdString(from.string()), - QString::fromStdString(to.string()))); + QtCommon::Frontend::Critical( + tr("Failed to link save data"), + tr("Could not link directory:\n\t%1\nTo:\n\t%2").arg(QString::fromStdString(from.string()), QString::fromStdString(to.string()))); + } +} + +bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir) +{ + bool eden_link = Common::FS::IsSymlink(eden_dir); + bool ryu_link = Common::FS::IsSymlink(ryu_dir); + + if (!(eden_link || ryu_link)) + return false; + + auto result = QtCommon::Frontend::Warning( + tr("Already Linked"), + tr("This title is already linked to Ryujinx. Would you like to unlink it?"), + QtCommon::Frontend::StandardButton::Yes | QtCommon::Frontend::StandardButton::No); + + if (result != QtCommon::Frontend::StandardButton::Yes) + return true; + + fs::path linked; + fs::path orig; + + if (eden_link) { + linked = eden_dir; + orig = ryu_dir; + } else { + linked = ryu_dir; + orig = eden_dir; + } + + // first cleanup the symlink/junction, + try { + fs::remove_all(linked); + } catch (std::exception &e) { + QtCommon::Frontend::Critical( + tr("Failed to unlink old directory"), + tr("OS returned error: %1").arg(QString::fromStdString(e.what()))); + return true; } + + // then COPY the other dir + try { + fs::copy(orig, linked, fs::copy_options::recursive); + } catch (std::exception &e) { + QtCommon::Frontend::Critical( + tr("Failed to copy save data"), + tr("OS returned error: %1").arg(QString::fromStdString(e.what()))); + } + + QtCommon::Frontend::Information( + tr("Unlink Successful"), + tr("Successfully unlinked Ryujinx save data. Save data has been kept intact.")); + + return true; } u64 GetRyujinxSaveID(const u64 &program_id) @@ -53,4 +108,21 @@ u64 GetRyujinxSaveID(const u64 &program_id) return -1; } +std::optional > GetEmuPaths( + const u64 program_id, const u64 save_id, const std::string &user_id) +{ + fs::path ryu_dir = Common::FS::GetRyuSavePath(save_id); + + if (user_id.empty()) + return std::nullopt; + + std::string hex_program = fmt::format("{:016X}", program_id); + fs::path eden_dir + = FrontendCommon::DataManager::GetDataDir(FrontendCommon::DataManager::DataDir::Saves, + user_id) + / hex_program; + + return std::make_pair(eden_dir, ryu_dir); +} + } // namespace QtCommon::FS diff --git a/src/qt_common/util/fs.h b/src/qt_common/util/fs.h index a8835db825..ee8859a2e5 100644 --- a/src/qt_common/util/fs.h +++ b/src/qt_common/util/fs.h @@ -3,12 +3,20 @@ #include "common/common_types.h" #include +#include #pragma once namespace QtCommon::FS { void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to); -u64 GetRyujinxSaveID(const u64& program_id); +u64 GetRyujinxSaveID(const u64 &program_id); + +/// @brief {eden, ryu} +std::optional> GetEmuPaths( + const u64 program_id, const u64 save_id, const std::string &user_id); + +/// returns FALSE if the dirs are NOT linked +bool CheckUnlink(const std::filesystem::path &eden_dir, const std::filesystem::path &ryu_dir); } // namespace QtCommon::FS diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a0243cffdf..849ffa84de 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2917,22 +2917,23 @@ std::string GMainWindow::GetProfileID() void GMainWindow::OnLinkToRyujinx(const u64& program_id) { - namespace fs = std::filesystem; - u64 save_id = QtCommon::FS::GetRyujinxSaveID(program_id); if (save_id == (u64) -1) return; - fs::path ryu_dir = Common::FS::GetRyuSavePath(save_id); - std::string user_id = GetProfileID(); - if (user_id.empty()) + const std::string user_id = GetProfileID(); + + auto paths = QtCommon::FS::GetEmuPaths(program_id, save_id, user_id); + if (!paths) return; - std::string hex_program = fmt::format("{:016X}", program_id); - fs::path eden_dir = FrontendCommon::DataManager::GetDataDir(FrontendCommon::DataManager::DataDir::Saves, user_id) - / hex_program; - RyujinxDialog dialog(eden_dir, ryu_dir, this); - dialog.exec(); + auto eden_dir = paths.value().first; + auto ryu_dir = paths.value().second; + + if (!QtCommon::FS::CheckUnlink(eden_dir, ryu_dir)) { + RyujinxDialog dialog(eden_dir, ryu_dir, this); + dialog.exec(); + } } void GMainWindow::OnMenuLoadFile() { diff --git a/src/yuzu/ryujinx_dialog.cpp b/src/yuzu/ryujinx_dialog.cpp index 6833c75f33..db10c06d93 100644 --- a/src/yuzu/ryujinx_dialog.cpp +++ b/src/yuzu/ryujinx_dialog.cpp @@ -5,7 +5,6 @@ #include "qt_common/util/fs.h" #include "ui_ryujinx_dialog.h" #include -#include namespace fs = std::filesystem; @@ -31,16 +30,11 @@ RyujinxDialog::~RyujinxDialog() void RyujinxDialog::fromEden() { accept(); - link(m_eden, m_ryu); + QtCommon::FS::LinkRyujinx(m_eden, m_ryu); } void RyujinxDialog::fromRyujinx() { accept(); - link(m_ryu, m_eden); -} - -void RyujinxDialog::link(std::filesystem::path &from, std::filesystem::path &to) -{ - QtCommon::FS::LinkRyujinx(from, to); + QtCommon::FS::LinkRyujinx(m_ryu, m_eden); } diff --git a/src/yuzu/ryujinx_dialog.h b/src/yuzu/ryujinx_dialog.h index 55dfb6e1da..63cebe483c 100644 --- a/src/yuzu/ryujinx_dialog.h +++ b/src/yuzu/ryujinx_dialog.h @@ -27,11 +27,6 @@ private: Ui::RyujinxDialog *ui; std::filesystem::path m_eden; std::filesystem::path m_ryu; - - /// @brief Link two directories - /// @param from The symlink target - /// @param to The symlink name (will be deleted) - void link(std::filesystem::path &from, std::filesystem::path &to); }; #endif // RYUJINX_DIALOG_H