From f9f11057eaa2ea14423796aa20f87add9103ee93 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 27 Oct 2025 01:06:26 -0400 Subject: [PATCH] QtCommon::FS, symlink abstractor Signed-off-by: crueter --- src/common/CMakeLists.txt | 3 +- src/common/{ => fs}/ryujinx_compat.cpp | 0 src/common/{ => fs}/ryujinx_compat.h | 0 src/common/fs/symlink.cpp | 43 ++++++++++++++ src/common/fs/symlink.h | 12 ++++ src/qt_common/CMakeLists.txt | 3 +- .../{qt_frontend_util.cpp => frontend.cpp} | 2 +- .../{qt_frontend_util.h => frontend.h} | 6 +- src/qt_common/qt_common.cpp | 30 +--------- src/qt_common/qt_common.h | 2 - src/qt_common/util/content.cpp | 2 +- src/qt_common/util/fs.cpp | 56 +++++++++++++++++++ src/qt_common/util/fs.h | 14 +++++ src/qt_common/util/game.cpp | 2 +- src/qt_common/util/path.cpp | 2 +- src/yuzu/main.cpp | 7 ++- src/yuzu/migration_worker.cpp | 9 +-- src/yuzu/ryujinx_dialog.cpp | 26 +-------- 18 files changed, 149 insertions(+), 70 deletions(-) rename src/common/{ => fs}/ryujinx_compat.cpp (100%) rename src/common/{ => fs}/ryujinx_compat.h (100%) create mode 100644 src/common/fs/symlink.cpp create mode 100644 src/common/fs/symlink.h rename src/qt_common/abstract/{qt_frontend_util.cpp => frontend.cpp} (98%) rename src/qt_common/abstract/{qt_frontend_util.h => frontend.h} (98%) create mode 100644 src/qt_common/util/fs.cpp create mode 100644 src/qt_common/util/fs.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 9ad58af1c5..3afd68a19d 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -155,7 +155,8 @@ add_library( wall_clock.h zstd_compression.cpp zstd_compression.h - ryujinx_compat.h ryujinx_compat.cpp + fs/ryujinx_compat.h fs/ryujinx_compat.cpp + fs/symlink.h fs/symlink.cpp ) if(WIN32) diff --git a/src/common/ryujinx_compat.cpp b/src/common/fs/ryujinx_compat.cpp similarity index 100% rename from src/common/ryujinx_compat.cpp rename to src/common/fs/ryujinx_compat.cpp diff --git a/src/common/ryujinx_compat.h b/src/common/fs/ryujinx_compat.h similarity index 100% rename from src/common/ryujinx_compat.h rename to src/common/fs/ryujinx_compat.h diff --git a/src/common/fs/symlink.cpp b/src/common/fs/symlink.cpp new file mode 100644 index 0000000000..0c1f26534e --- /dev/null +++ b/src/common/fs/symlink.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "symlink.h" + +#ifdef _WIN32 +#include +#endif + +namespace fs = std::filesystem; + +// The sole purpose of this file is to treat symlinks like symlinks on POSIX, +// or treat them as directory junctions on Windows. +// This is because, for some inexplicable reason, Microsoft has locked symbolic +// links behind a "security policy", whereas directory junctions--functionally identical +// for directories, by the way--are not. Why? I don't know. + +namespace Common::FS { + +bool CreateSymlink(const fs::path &from, const fs::path &to) +{ + // TODO: test this, + does it need symlink perms? +#ifdef _WIN32 + return CreateSymbolicLinkW(to.wstring().c_str(), + from.wstring().c_str(), + SYMBOLIC_LINK_FLAG_DIRECTORY); +#else + std::error_code ec; + fs::create_directory_symlink(from, to, ec); + return !ec; +#endif +} + +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; +#endif +} + +} // namespace Common::FS diff --git a/src/common/fs/symlink.h b/src/common/fs/symlink.h new file mode 100644 index 0000000000..3bfff95ebf --- /dev/null +++ b/src/common/fs/symlink.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +namespace Common::FS { + +bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to); +bool IsSymlink(const std::filesystem::path &path); + +} // namespace Common::FS diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt index 39dd1a7460..75ef717583 100644 --- a/src/qt_common/CMakeLists.txt +++ b/src/qt_common/CMakeLists.txt @@ -20,13 +20,14 @@ add_library(qt_common STATIC util/applet.h util/applet.cpp util/compress.h util/compress.cpp - abstract/qt_frontend_util.h abstract/qt_frontend_util.cpp + abstract/frontend.h abstract/frontend.cpp abstract/qt_progress_dialog.h abstract/qt_progress_dialog.cpp qt_string_lookup.h qt_compat.h discord/discord.h + util/fs.h util/fs.cpp ) create_target_directory_groups(qt_common) diff --git a/src/qt_common/abstract/qt_frontend_util.cpp b/src/qt_common/abstract/frontend.cpp similarity index 98% rename from src/qt_common/abstract/qt_frontend_util.cpp rename to src/qt_common/abstract/frontend.cpp index 3fe0ba0a80..2ed76f8ea9 100644 --- a/src/qt_common/abstract/qt_frontend_util.cpp +++ b/src/qt_common/abstract/frontend.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include "qt_frontend_util.h" +#include "frontend.h" #include "qt_common/qt_common.h" #ifdef YUZU_QT_WIDGETS diff --git a/src/qt_common/abstract/qt_frontend_util.h b/src/qt_common/abstract/frontend.h similarity index 98% rename from src/qt_common/abstract/qt_frontend_util.h rename to src/qt_common/abstract/frontend.h index 59e8f15a4e..b496b37cbe 100644 --- a/src/qt_common/abstract/qt_frontend_util.h +++ b/src/qt_common/abstract/frontend.h @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#ifndef QT_FRONTEND_UTIL_H -#define QT_FRONTEND_UTIL_H +#ifndef FRONTEND_H +#define FRONTEND_H #include #include "qt_common/qt_common.h" @@ -136,4 +136,4 @@ const QString GetSaveFileName(const QString &title, Options options = Options()); } // namespace QtCommon::Frontend -#endif // QT_FRONTEND_UTIL_H +#endif // FRONTEND_H diff --git a/src/qt_common/qt_common.cpp b/src/qt_common/qt_common.cpp index a056cfe9ed..f2091df866 100644 --- a/src/qt_common/qt_common.cpp +++ b/src/qt_common/qt_common.cpp @@ -3,13 +3,13 @@ #include "qt_common.h" #include "common/fs/fs.h" -#include "common/ryujinx_compat.h" +#include "common/fs/ryujinx_compat.h" #include #include #include "common/logging/log.h" #include "core/frontend/emu_window.h" -#include "qt_common/abstract/qt_frontend_util.h" +#include "qt_common/abstract/frontend.h" #include "qt_common/qt_string_lookup.h" #include @@ -126,30 +126,4 @@ std::filesystem::path GetEdenCommand() return command; } -u64 GetRyujinxSaveID(const u64& program_id) -{ - auto path = Common::FS::GetKvdbPath(); - std::vector imens; - Common::FS::IMENReadResult res = Common::FS::ReadKvdb(path, imens); - - if (res == Common::FS::IMENReadResult::Success) { - // TODO: this can probably be done with std::find_if but I'm lazy - for (const Common::FS::IMEN& imen : imens) { - if (imen.title_id == program_id) - return imen.save_id; - } - - QtCommon::Frontend::Critical( - 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 = StringLookup::Lookup( - static_cast((int) res + (int) StringLookup::KvdbNonexistent)); - QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption); - } - - return -1; -} - } // namespace QtCommon diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h index 5a936873e1..a2700427ab 100644 --- a/src/qt_common/qt_common.h +++ b/src/qt_common/qt_common.h @@ -24,8 +24,6 @@ extern std::unique_ptr system; extern std::shared_ptr vfs; extern std::unique_ptr provider; -u64 GetRyujinxSaveID(const u64 &program_id); - typedef std::function QtProgressCallback; Core::Frontend::WindowSystemType GetWindowSystemType(); diff --git a/src/qt_common/util/content.cpp b/src/qt_common/util/content.cpp index a1bd9b941e..4abe1114ef 100644 --- a/src/qt_common/util/content.cpp +++ b/src/qt_common/util/content.cpp @@ -11,7 +11,7 @@ #include "frontend_common/firmware_manager.h" #include "compress.h" -#include "qt_common/abstract/qt_frontend_util.h" +#include "qt_common/abstract/frontend.h" #include "qt_common/abstract/qt_progress_dialog.h" #include "qt_common/qt_common.h" diff --git a/src/qt_common/util/fs.cpp b/src/qt_common/util/fs.cpp new file mode 100644 index 0000000000..c2c0dbfcd0 --- /dev/null +++ b/src/qt_common/util/fs.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "fs.h" +#include "common/fs/ryujinx_compat.h" +#include "common/fs/symlink.h" +#include "qt_common/abstract/frontend.h" +#include "qt_common/qt_string_lookup.h" + +namespace fs = std::filesystem; + +namespace QtCommon::FS { + +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 + fs::remove_all(to, ec); + + 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()))); + } +} + +u64 GetRyujinxSaveID(const u64 &program_id) +{ + auto path = Common::FS::GetKvdbPath(); + std::vector imens; + Common::FS::IMENReadResult res = Common::FS::ReadKvdb(path, imens); + + if (res == Common::FS::IMENReadResult::Success) { + // TODO: this can probably be done with std::find_if but I'm lazy + for (const Common::FS::IMEN &imen : imens) { + if (imen.title_id == program_id) + return imen.save_id; + } + + QtCommon::Frontend::Critical( + 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 = StringLookup::Lookup( + static_cast((int) res + (int) StringLookup::KvdbNonexistent)); + QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption); + } + + return -1; +} + +} // namespace QtCommon::FS diff --git a/src/qt_common/util/fs.h b/src/qt_common/util/fs.h new file mode 100644 index 0000000000..a8835db825 --- /dev/null +++ b/src/qt_common/util/fs.h @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/common_types.h" +#include + +#pragma once + +namespace QtCommon::FS { + +void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to); +u64 GetRyujinxSaveID(const u64& program_id); + +} // namespace QtCommon::FS diff --git a/src/qt_common/util/game.cpp b/src/qt_common/util/game.cpp index e5018d24cb..e34a388993 100644 --- a/src/qt_common/util/game.cpp +++ b/src/qt_common/util/game.cpp @@ -8,7 +8,7 @@ #include "core/file_sys/savedata_factory.h" #include "core/hle/service/am/am_types.h" #include "frontend_common/content_manager.h" -#include "qt_common/abstract/qt_frontend_util.h" +#include "qt_common/abstract/frontend.h" #include "qt_common/config/uisettings.h" #include "qt_common/qt_common.h" #include "yuzu/util/util.h" diff --git a/src/qt_common/util/path.cpp b/src/qt_common/util/path.cpp index 93e8007eb9..73689058c6 100644 --- a/src/qt_common/util/path.cpp +++ b/src/qt_common/util/path.cpp @@ -7,7 +7,7 @@ #include #include "common/fs/fs.h" #include "common/fs/path_util.h" -#include "qt_common/abstract/qt_frontend_util.h" +#include "qt_common/abstract/frontend.h" #include namespace QtCommon::Path { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1d98515512..a0243cffdf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -6,11 +6,12 @@ #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" #include "qt_common/qt_common.h" -#include "qt_common/abstract/qt_frontend_util.h" +#include "qt_common/abstract/frontend.h" #include "qt_common/util/content.h" #include "qt_common/util/game.h" #include "qt_common/util/meta.h" #include "qt_common/util/path.h" +#include "qt_common/util/fs.h" #include #include #include @@ -109,7 +110,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/detached_tasks.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" -#include "common/ryujinx_compat.h" +#include "common/fs/ryujinx_compat.h" #include "common/literals.h" #include "common/logging/backend.h" #include "common/logging/log.h" @@ -2918,7 +2919,7 @@ void GMainWindow::OnLinkToRyujinx(const u64& program_id) { namespace fs = std::filesystem; - u64 save_id = QtCommon::GetRyujinxSaveID(program_id); + u64 save_id = QtCommon::FS::GetRyujinxSaveID(program_id); if (save_id == (u64) -1) return; fs::path ryu_dir = Common::FS::GetRyuSavePath(save_id); diff --git a/src/yuzu/migration_worker.cpp b/src/yuzu/migration_worker.cpp index b39bd22584..50b6d1ebd4 100644 --- a/src/yuzu/migration_worker.cpp +++ b/src/yuzu/migration_worker.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "migration_worker.h" +#include "common/fs/symlink.h" #include #include @@ -37,7 +38,7 @@ void MigrationWorker::process() try { fs::remove_all(eden_dir); } catch (fs::filesystem_error &_) { - // ignore because linux does stupid crap sometimes. + // ignore because linux does stupid crap sometimes } switch (strategy) { @@ -46,7 +47,7 @@ void MigrationWorker::process() // Windows 11 has random permission nonsense to deal with. try { - fs::create_directory_symlink(legacy_user_dir, eden_dir); + Common::FS::CreateSymlink(legacy_user_dir, eden_dir); } catch (const fs::filesystem_error &e) { emit error(tr("Linking the old directory failed. You may need to re-run with " "administrative privileges on Windows.\nOS gave error: %1") @@ -58,11 +59,11 @@ void MigrationWorker::process() // are already children of the root directory #ifndef WIN32 if (fs::is_directory(legacy_config_dir)) { - fs::create_directory_symlink(legacy_config_dir, config_dir); + Common::FS::CreateSymlink(legacy_config_dir, config_dir); } if (fs::is_directory(legacy_cache_dir)) { - fs::create_directory_symlink(legacy_cache_dir, cache_dir); + Common::FS::CreateSymlink(legacy_cache_dir, cache_dir); } #endif diff --git a/src/yuzu/ryujinx_dialog.cpp b/src/yuzu/ryujinx_dialog.cpp index 42c84f2df1..6833c75f33 100644 --- a/src/yuzu/ryujinx_dialog.cpp +++ b/src/yuzu/ryujinx_dialog.cpp @@ -1,11 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include "qt_common/abstract/qt_frontend_util.h" #include "ryujinx_dialog.h" +#include "qt_common/util/fs.h" #include "ui_ryujinx_dialog.h" #include -#include #include namespace fs = std::filesystem; @@ -43,26 +42,5 @@ void RyujinxDialog::fromRyujinx() void RyujinxDialog::link(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 - fs::remove_all(to, ec); - -#ifdef _WIN32 - const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string()); - system(command.c_str()); -#else - try { - fs::create_directory_symlink(from, to); - } catch (std::exception &e) { - QtCommon::Frontend::Critical(tr("Failed to link save data"), - tr("Could not link directory:\n\t%1\nTo:\n\t%2\n\nError: %3") - .arg(QString::fromStdString(from.string()), - QString::fromStdString(to.string()), - QString::fromStdString(e.what()))); - return; - } -#endif - - QtCommon::Frontend::Information(tr("Linked Save Data"), tr("Save data has been linked.")); + QtCommon::FS::LinkRyujinx(from, to); }