[desktop, fs] main_window separation; fix Ryujinx save data link issues (#2929)

Some genius decided to put the entire MainWindow class into main.h and
main.cpp, which is not only horrific practice but also completely
destroys clangd beyond repair. Please, just don't do this.

(this will probably merge conflict to hell and back)

Also, fixes a bunch of issues with Ryujinx save data link:
- Paths with spaces would cause mklink to fail
- Add support for portable directories
- Symlink detection was incorrect sometimes(????)
- Some other stuff I'm forgetting

Furthermore, when selecting "From Eden" and attempting to save in Ryujinx, Ryujinx would destroy the link for... some reason? So to get around this we just copy the Eden data to Ryujinx then treat it like a "From Ryujinx" op

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2929
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
This commit is contained in:
crueter 2025-11-09 18:07:38 +01:00
parent e13c7ef3f8
commit 08f3639c80
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
51 changed files with 5386 additions and 5283 deletions

View file

@ -586,7 +586,7 @@ else()
find_package(zstd 1.5 REQUIRED MODULE) find_package(zstd 1.5 REQUIRED MODULE)
# wow # wow
find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber) find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber filesystem)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID) if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID)
find_package(gamemode 1.7 MODULE) find_package(gamemode 1.7 MODULE)

View file

@ -20,7 +20,7 @@
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9", "hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9",
"git_version": "1.89.0", "git_version": "1.89.0",
"version": "1.57", "version": "1.57",
"find_args": "CONFIG", "find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"patches": [ "patches": [
"0001-clang-cl.patch", "0001-clang-cl.patch",
"0002-use-marmasm.patch", "0002-use-marmasm.patch",

View file

@ -252,11 +252,13 @@ if(CXX_CLANG)
endif() endif()
if (BOOST_NO_HEADERS) if (BOOST_NO_HEADERS)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool) target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool Boost::filesystem)
else() else()
target_link_libraries(common PUBLIC Boost::headers) target_link_libraries(common PUBLIC Boost::headers)
endif() endif()
target_link_libraries(common PUBLIC Boost::filesystem)
if (lz4_ADDED) if (lz4_ADDED)
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib) target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
endif() endif()

View file

@ -14,16 +14,29 @@ namespace fs = std::filesystem;
fs::path GetKvdbPath() fs::path GetKvdbPath()
{ {
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "system" / "save" / "8000000000000000" / "0" return GetKvdbPath(GetLegacyPath(EmuPath::RyujinxDir));
}
fs::path GetKvdbPath(const fs::path& path) {
return path / "bis" / "system" / "save" / "8000000000000000" / "0"
/ "imkvdb.arc"; / "imkvdb.arc";
} }
fs::path GetRyuPathFromSavePath(const fs::path& path) {
// This is a horrible hack, but I cba to find something better
return path.parent_path().parent_path().parent_path().parent_path().parent_path();
}
fs::path GetRyuSavePath(const u64 &save_id) fs::path GetRyuSavePath(const u64 &save_id)
{ {
return GetRyuSavePath(GetLegacyPath(EmuPath::RyujinxDir), save_id);
}
std::filesystem::path GetRyuSavePath(const std::filesystem::path& path, const u64& save_id) {
std::string hex = fmt::format("{:016x}", save_id); std::string hex = fmt::format("{:016x}", save_id);
// TODO: what's the difference between 0 and 1? // TODO: what's the difference between 0 and 1?
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "user" / "save" / hex / "0"; return path / "bis" / "user" / "save" / hex / "0";
} }
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens) IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens)

View file

@ -7,16 +7,17 @@
#include <filesystem> #include <filesystem>
#include <vector> #include <vector>
namespace fs = std::filesystem;
namespace Common::FS { namespace Common::FS {
constexpr const char IMEN_MAGIC[4] = {0x49, 0x4d, 0x45, 0x4e}; constexpr const char IMEN_MAGIC[4] = {0x49, 0x4d, 0x45, 0x4e};
constexpr const char IMKV_MAGIC[4] = {0x49, 0x4d, 0x4b, 0x56}; constexpr const char IMKV_MAGIC[4] = {0x49, 0x4d, 0x4b, 0x56};
constexpr const u8 IMEN_SIZE = 0x8c; constexpr const u8 IMEN_SIZE = 0x8c;
fs::path GetKvdbPath(); std::filesystem::path GetKvdbPath();
fs::path GetRyuSavePath(const u64 &program_id); std::filesystem::path GetKvdbPath(const std::filesystem::path &path);
std::filesystem::path GetRyuPathFromSavePath(const std::filesystem::path &path);
std::filesystem::path GetRyuSavePath(const u64 &save_id);
std::filesystem::path GetRyuSavePath(const std::filesystem::path &path, const u64 &save_id);
enum class IMENReadResult { enum class IMENReadResult {
Nonexistent, // ryujinx not found Nonexistent, // ryujinx not found
@ -35,6 +36,6 @@ struct IMEN
static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size."); static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size.");
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens); IMENReadResult ReadKvdb(const std::filesystem::path &path, std::vector<IMEN> &imens);
} // namespace Common::FS } // namespace Common::FS

View file

@ -4,10 +4,12 @@
#include "symlink.h" #include "symlink.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <windows.h>
#endif #endif
#include <boost/filesystem.hpp>
namespace fs = std::filesystem; namespace fs = std::filesystem;
// The sole purpose of this file is to treat symlinks like symlinks on POSIX, // The sole purpose of this file is to treat symlinks like symlinks on POSIX,
@ -15,29 +17,40 @@ namespace fs = std::filesystem;
// This is because, for some inexplicable reason, Microsoft has locked symbolic // This is because, for some inexplicable reason, Microsoft has locked symbolic
// links behind a "security policy", whereas directory junctions--functionally identical // links behind a "security policy", whereas directory junctions--functionally identical
// for directories, by the way--are not. Why? I don't know. // for directories, by the way--are not. Why? I don't know.
// And no, they do NOT provide a standard API for this (at least to my knowledge).
// CreateSymbolicLink, even when EXPLICITLY TOLD to create a junction, still fails
// because of their security policy.
// I don't know what kind of drugs the Windows developers have been on since NT started.
// Microsoft still has not implemented any of this in their std::filesystem implemenation,
// which ALSO means that it DOES NOT FOLLOW ANY DIRECTORY JUNCTIONS... AT ALL.
// Nor does any of their command line utilities or APIs. So you're quite literally
// on your own.
namespace Common::FS { namespace Common::FS {
bool CreateSymlink(const fs::path &from, const fs::path &to) bool CreateSymlink(fs::path from, fs::path to)
{ {
#ifdef _WIN32 from.make_preferred();
const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string()); to.make_preferred();
return system(command.c_str()) == 0;
#else
std::error_code ec; std::error_code ec;
fs::create_directory_symlink(from, to, ec); fs::create_directory_symlink(from, to, ec);
return !ec; #ifdef _WIN32
if (ec) {
const std::string command = fmt::format("mklink /J \"{}\" \"{}\"",
to.string(),
from.string());
return system(command.c_str()) == 0;
}
#endif #endif
return !ec;
} }
bool IsSymlink(const fs::path &path) bool IsSymlink(const fs::path &path)
{ {
#ifdef _WIN32 return boost::filesystem::is_symlink(boost::filesystem::path{path});
auto attributes = GetFileAttributesW(path.wstring().c_str());
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
#else
return fs::is_symlink(path);
#endif
} }
} // namespace Common::FS } // namespace Common::FS

View file

@ -6,7 +6,7 @@
#include <filesystem> #include <filesystem>
namespace Common::FS { namespace Common::FS {
bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to); bool CreateSymlink(std::filesystem::path from, std::filesystem::path to);
bool IsSymlink(const std::filesystem::path &path); bool IsSymlink(const std::filesystem::path &path);
} // namespace Common::FS } // namespace Common::FS

View file

@ -7,8 +7,6 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <filesystem> #include <filesystem>
#include <iostream>
#include <random>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/find.hpp> #include <boost/algorithm/string/find.hpp>
@ -18,11 +16,11 @@
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/fs_types.h" #include "common/fs/fs_types.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/fs/symlink.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/acc/profile_manager.h"
#include <ranges>
namespace Service::Account { namespace Service::Account {
@ -492,6 +490,32 @@ void ProfileManager::ResetUserSaveFile()
ParseUserSaveFile(); 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() std::vector<std::string> ProfileManager::FindGoodProfiles()
{ {
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -501,31 +525,17 @@ std::vector<std::string> ProfileManager::FindGoodProfiles()
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000"; / "user/save/0000000000000000";
// some exceptions because certain games just LOVE TO CAUSE ISSUES // some exceptions, e.g. the "system" profile
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS static constexpr const std::array<const char* const, 1> EXCEPTION_UUIDS
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"}; = {"00000000000000000000000000000000"};
for (const char *const uuid : EXCEPTION_UUIDS) { for (const char *const uuid : EXCEPTION_UUIDS) {
if (fs::exists(path / uuid)) if (fs::exists(path / uuid))
good_uuids.emplace_back(uuid); good_uuids.emplace_back(uuid);
} }
for (const ProfileInfo& p : profiles) { auto existing = FindExistingProfileStrings();
std::string uuid_string = [p]() -> std::string { good_uuids.insert(good_uuids.end(), existing.begin(), existing.end());
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);
}
return good_uuids; return good_uuids;
} }
@ -562,7 +572,8 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
override = true; override = true;
// if there are any regular files (NOT directories) there, do NOT delete it :p // if there are any regular files (NOT directories) there, do NOT delete it :p
if (file.is_regular_file()) // Also: check for symlinks
if (file.is_regular_file() || Common::FS::IsSymlink(file.path()))
return false; return false;
} }
} catch (const fs::filesystem_error& e) { } catch (const fs::filesystem_error& e) {

View file

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

View file

@ -28,7 +28,7 @@ const QString GetOpenFileName(const QString &title,
Options options) Options options)
{ {
#ifdef YUZU_QT_WIDGETS #ifdef YUZU_QT_WIDGETS
return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options); return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif #endif
} }
@ -39,7 +39,14 @@ const QString GetSaveFileName(const QString &title,
Options options) Options options)
{ {
#ifdef YUZU_QT_WIDGETS #ifdef YUZU_QT_WIDGETS
return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options); return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif
}
const QString GetExistingDirectory(const QString& caption, const QString& dir,
Options options) {
#ifdef YUZU_QT_WIDGETS
return QFileDialog::getExistingDirectory(rootObject, caption, dir, options);
#endif #endif
} }

View file

@ -135,5 +135,9 @@ const QString GetSaveFileName(const QString &title,
QString *selectedFilter = nullptr, QString *selectedFilter = nullptr,
Options options = Options()); Options options = Options());
const QString GetExistingDirectory(const QString &caption = QString(),
const QString &dir = QString(),
Options options = Option::ShowDirsOnly);
} // namespace QtCommon::Frontend } // namespace QtCommon::Frontend
#endif // FRONTEND_H #endif // FRONTEND_H

View file

@ -294,6 +294,17 @@ void QtConfig::ReadUIGamelistValues() {
} }
EndArray(); EndArray();
const int linked_size = BeginArray("ryujinx_linked");
for (int i = 0; i < linked_size; ++i) {
SetArrayIndex(i);
QDir ryu_dir = QString::fromStdString(ReadStringSetting("ryujinx_path"));
u64 program_id = ReadUnsignedIntegerSetting("program_id");
UISettings::values.ryujinx_link_paths.insert(program_id, ryu_dir);
}
EndArray();
EndGroup(); EndGroup();
} }
@ -499,6 +510,21 @@ void QtConfig::SaveUIGamelistValues() {
} }
EndArray(); // favorites EndArray(); // favorites
BeginArray(std::string("ryujinx_linked"));
int i = 0;
QMapIterator iter(UISettings::values.ryujinx_link_paths);
while (iter.hasNext()) {
iter.next();
SetArrayIndex(i);
WriteIntegerSetting("program_id", iter.key());
WriteStringSetting("ryujinx_path", iter.value().absolutePath().toStdString());
++i;
}
EndArray(); // ryujinx
EndGroup(); EndGroup();
} }

View file

@ -28,54 +28,54 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent); std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent);
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = { static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "None"))},
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FXAA"))},
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SMAA"))},
}; };
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = { static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
{Settings::ScalingFilter::NearestNeighbor, {Settings::ScalingFilter::NearestNeighbor,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Nearest"))},
{Settings::ScalingFilter::Bilinear, {Settings::ScalingFilter::Bilinear,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))}, {Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))}, {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))}, {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
{Settings::ScalingFilter::Spline1, {Settings::ScalingFilter::Spline1,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
{Settings::ScalingFilter::Gaussian, {Settings::ScalingFilter::Gaussian,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Gaussian"))},
{Settings::ScalingFilter::Lanczos, {Settings::ScalingFilter::Lanczos,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Lanczos"))},
{Settings::ScalingFilter::ScaleForce, {Settings::ScalingFilter::ScaleForce,
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))}, {Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Area"))},
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))}, {Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "MMPX"))},
}; };
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = { static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Docked"))},
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Handheld"))},
}; };
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = { static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Normal"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "High"))},
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Extreme"))},
}; };
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = { static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Null"))},
}; };
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = { static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLSL"))},
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SPIRV"))},
}; };
} // namespace ConfigurationShared } // namespace ConfigurationShared

View file

@ -14,6 +14,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QVector> #include <QVector>
#include <qdir.h>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
@ -201,6 +202,7 @@ struct Values {
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList}; Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList}; Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
QVector<u64> favorited_ids; QVector<u64> favorited_ids;
QMap<u64, QDir> ryujinx_link_paths;
// Compatibility List // Compatibility List
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList}; Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};

View file

@ -8,6 +8,12 @@
#include "frozen/map.h" #include "frozen/map.h"
#include "frozen/string.h" #include "frozen/string.h"
/// Small helper to look up enums.
/// res = the result code
/// base = the base matching value in the StringKey table
#define LOOKUP_ENUM(res, base) StringLookup::Lookup( \
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::base))
namespace QtCommon::StringLookup { namespace QtCommon::StringLookup {
Q_NAMESPACE Q_NAMESPACE

View file

@ -25,8 +25,7 @@ enum class FirmwareInstallResult {
inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result) inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result)
{ {
return QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>( return LOOKUP_ENUM(result, FwInstallSuccess);
(int) result + (int) QtCommon::StringLookup::FwInstallSuccess));
} }
/** /**
@ -36,9 +35,7 @@ inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result
*/ */
inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult result) inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult result)
{ {
// this can probably be made into a common function of sorts return LOOKUP_ENUM(result, KeyInstallSuccess);
return QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>(
(int) result + (int) QtCommon::StringLookup::KeyInstallSuccess));
} }
void InstallFirmware(const QString &location, bool recursive); void InstallFirmware(const QString &location, bool recursive);

View file

@ -1,11 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <filesystem> #include <filesystem>
#include "fs.h"
#include "common/fs/ryujinx_compat.h" #include "common/fs/ryujinx_compat.h"
#include "common/fs/symlink.h" #include "common/fs/symlink.h"
#include "frontend_common/data_manager.h" #include "fs.h"
#include "qt_common/abstract/frontend.h" #include "qt_common/abstract/frontend.h"
#include "qt_common/qt_string_lookup.h" #include "qt_common/qt_string_lookup.h"
@ -56,6 +56,9 @@ bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir)
orig = eden_dir; orig = eden_dir;
} }
linked.make_preferred();
orig.make_preferred();
// first cleanup the symlink/junction, // first cleanup the symlink/junction,
try { try {
// NB: do NOT use remove_all, as Windows treats this as a remove_all to the target, // NB: do NOT use remove_all, as Windows treats this as a remove_all to the target,
@ -84,47 +87,64 @@ bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir)
return true; return true;
} }
u64 GetRyujinxSaveID(const u64 &program_id) const fs::path GetRyujinxSavePath(const fs::path &path_hint, const u64 &program_id)
{ {
auto path = Common::FS::GetKvdbPath(); auto ryu_path = path_hint;
auto kvdb_path = Common::FS::GetKvdbPath(ryu_path);
if (!fs::exists(kvdb_path)) {
using namespace QtCommon::Frontend;
auto res = Warning(
tr("Could not find Ryujinx installation"),
tr("Could not find a valid Ryujinx installation. This may typically occur if you are "
"using Ryujinx in portable mode.\n\nWould you like to manually select a portable "
"folder to use?"), StandardButton::Yes | StandardButton::No);
if (res == StandardButton::Yes) {
auto selected_path = GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath()).toStdString();
if (selected_path.empty())
return fs::path{};
ryu_path = selected_path;
// In case the user selects the actual ryujinx installation dir INSTEAD OF
// the portable dir
if (fs::exists(ryu_path / "portable")) {
ryu_path = ryu_path / "portable";
}
kvdb_path = Common::FS::GetKvdbPath(ryu_path);
if (!fs::exists(kvdb_path)) {
QtCommon::Frontend::Critical(
tr("Not a valid Ryujinx directory"),
tr("The specified directory does not contain valid Ryujinx data."));
return fs::path{};
}
} else {
return fs::path{};
}
}
std::vector<Common::FS::IMEN> imens; std::vector<Common::FS::IMEN> imens;
Common::FS::IMENReadResult res = Common::FS::ReadKvdb(path, imens); Common::FS::IMENReadResult res = Common::FS::ReadKvdb(kvdb_path, imens);
if (res == Common::FS::IMENReadResult::Success) { 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) { for (const Common::FS::IMEN &imen : imens) {
if (imen.title_id == program_id) if (imen.title_id == program_id)
return imen.save_id; return Common::FS::GetRyuSavePath(ryu_path, imen.save_id);
} }
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Could not find Ryujinx save data"), tr("Could not find Ryujinx save data"),
StringLookup::Lookup(StringLookup::RyujinxNoSaveId).arg(program_id, 0, 16)); StringLookup::Lookup(StringLookup::RyujinxNoSaveId).arg(program_id, 0, 16));
} else { } else {
// TODO: make this long thing a function or something QString caption = LOOKUP_ENUM(res, KvdbNonexistent);
QString caption = StringLookup::Lookup(
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::KvdbNonexistent));
QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption); QtCommon::Frontend::Critical(tr("Could not find Ryujinx save data"), caption);
} }
return -1; return fs::path{};
}
std::optional<std::pair<fs::path, fs::path> > 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 } // namespace QtCommon::FS

View file

@ -10,13 +10,10 @@
namespace QtCommon::FS { namespace QtCommon::FS {
void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to); void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to);
u64 GetRyujinxSaveID(const u64 &program_id); const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path &path_hint, const u64 &program_id);
/// @brief {eden, ryu}
std::optional<std::pair<std::filesystem::path, std::filesystem::path>> GetEmuPaths(
const u64 program_id, const u64 save_id, const std::string &user_id);
/// returns FALSE if the dirs are NOT linked /// returns FALSE if the dirs are NOT linked
bool CheckUnlink(const std::filesystem::path &eden_dir, const std::filesystem::path &ryu_dir); bool CheckUnlink(const std::filesystem::path& eden_dir,
const std::filesystem::path& ryu_dir);
} // namespace QtCommon::FS } // namespace QtCommon::FS

View file

@ -12,6 +12,8 @@ if (YUZU_USE_BUNDLED_QT AND PLATFORM_LINUX)
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/") set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
endif() endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(yuzu add_executable(yuzu
Info.plist Info.plist
about_dialog.cpp about_dialog.cpp
@ -169,9 +171,9 @@ add_executable(yuzu
loading_screen.cpp loading_screen.cpp
loading_screen.h loading_screen.h
loading_screen.ui loading_screen.ui
main.cpp main.cpp
main.h
main.ui
multiplayer/chat_room.cpp multiplayer/chat_room.cpp
multiplayer/chat_room.h multiplayer/chat_room.h
multiplayer/chat_room.ui multiplayer/chat_room.ui
@ -235,6 +237,7 @@ add_executable(yuzu
data_dialog.h data_dialog.cpp data_dialog.ui data_dialog.h data_dialog.cpp data_dialog.ui
data_widget.ui data_widget.ui
ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui
main_window.h main_window.cpp
) )
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden") set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
@ -441,6 +444,7 @@ endif()
if (YUZU_ROOM) if (YUZU_ROOM)
target_link_libraries(yuzu PRIVATE yuzu-room) target_link_libraries(yuzu PRIVATE yuzu-room)
target_link_libraries(yuzu PRIVATE Qt6::Widgets)
endif() endif()
create_target_directory_groups(yuzu) create_target_directory_groups(yuzu)

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -18,7 +21,7 @@
#include "web_service/web_backend.h" #include "web_service/web_backend.h"
#endif #endif
#include "yuzu/applets/qt_amiibo_settings.h" #include "yuzu/applets/qt_amiibo_settings.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
Core::Frontend::CabinetParameters parameters_, Core::Frontend::CabinetParameters parameters_,
@ -244,12 +247,12 @@ void QtAmiiboSettingsDialog::SetSettingsDescription() {
} }
} }
QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) { QtAmiiboSettings::QtAmiiboSettings(MainWindow& parent) {
connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent, connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
&GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection); &MainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
connect(this, &QtAmiiboSettings::MainWindowRequestExit, &parent, connect(this, &QtAmiiboSettings::MainWindowRequestExit, &parent,
&GMainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection); &MainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection);
connect(&parent, &GMainWindow::AmiiboSettingsFinished, this, connect(&parent, &MainWindow::AmiiboSettingsFinished, this,
&QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection); &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include <QDialog> #include <QDialog>
#include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/cabinet.h"
class GMainWindow; class MainWindow;
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
class QDialogButtonBox; class QDialogButtonBox;
@ -65,7 +68,7 @@ class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApp
Q_OBJECT Q_OBJECT
public: public:
explicit QtAmiiboSettings(GMainWindow& parent); explicit QtAmiiboSettings(MainWindow& parent);
~QtAmiiboSettings() override; ~QtAmiiboSettings() override;
void Close() const override; void Close() const override;

View file

@ -25,7 +25,7 @@
#include "yuzu/configuration/configure_motion_touch.h" #include "yuzu/configuration/configure_motion_touch.h"
#include "yuzu/configuration/configure_vibration.h" #include "yuzu/configuration/configure_vibration.h"
#include "yuzu/configuration/input_profiles.h" #include "yuzu/configuration/input_profiles.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h" #include "yuzu/util/controller_navigation.h"
namespace { namespace {
@ -753,12 +753,12 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
} }
} }
QtControllerSelector::QtControllerSelector(GMainWindow& parent) { QtControllerSelector::QtControllerSelector(MainWindow& parent) {
connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent, connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
&GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection); &MainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
connect(this, &QtControllerSelector::MainWindowRequestExit, &parent, connect(this, &QtControllerSelector::MainWindowRequestExit, &parent,
&GMainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection); &MainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection);
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this, connect(&parent, &MainWindow::ControllerSelectorReconfigureFinished, this,
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection); &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include <QDialog> #include <QDialog>
#include "core/frontend/applets/controller.h" #include "core/frontend/applets/controller.h"
class GMainWindow; class MainWindow;
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
class QDialogButtonBox; class QDialogButtonBox;
@ -163,7 +166,7 @@ class QtControllerSelector final : public QObject, public Core::Frontend::Contro
Q_OBJECT Q_OBJECT
public: public:
explicit QtControllerSelector(GMainWindow& parent); explicit QtControllerSelector(MainWindow& parent);
~QtControllerSelector() override; ~QtControllerSelector() override;
void Close() const override; void Close() const override;

View file

@ -1,16 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <QDateTime> #include <QDateTime>
#include "yuzu/applets/qt_error.h" #include "yuzu/applets/qt_error.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { QtErrorDisplay::QtErrorDisplay(MainWindow& parent) {
connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent, connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent,
&GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection); &MainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
connect(this, &QtErrorDisplay::MainWindowRequestExit, &parent, connect(this, &QtErrorDisplay::MainWindowRequestExit, &parent,
&GMainWindow::ErrorDisplayRequestExit, Qt::QueuedConnection); &MainWindow::ErrorDisplayRequestExit, Qt::QueuedConnection);
connect(&parent, &GMainWindow::ErrorDisplayFinished, this, connect(&parent, &MainWindow::ErrorDisplayFinished, this,
&QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection); &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -7,13 +10,13 @@
#include "core/frontend/applets/error.h" #include "core/frontend/applets/error.h"
class GMainWindow; class MainWindow;
class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet { class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet {
Q_OBJECT Q_OBJECT
public: public:
explicit QtErrorDisplay(GMainWindow& parent); explicit QtErrorDisplay(MainWindow& parent);
~QtErrorDisplay() override; ~QtErrorDisplay() override;
void Close() const override; void Close() const override;

View file

@ -20,7 +20,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/acc/profile_manager.h"
#include "yuzu/applets/qt_profile_select.h" #include "yuzu/applets/qt_profile_select.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h" #include "yuzu/util/controller_navigation.h"
namespace { namespace {
@ -230,12 +230,12 @@ void QtProfileSelectionDialog::SetDialogPurpose(
} }
} }
QtProfileSelector::QtProfileSelector(GMainWindow& parent) { QtProfileSelector::QtProfileSelector(MainWindow& parent) {
connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
&GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); &MainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
connect(this, &QtProfileSelector::MainWindowRequestExit, &parent, connect(this, &QtProfileSelector::MainWindowRequestExit, &parent,
&GMainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection); &MainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection);
connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this, connect(&parent, &MainWindow::ProfileSelectorFinishedSelection, this,
&QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +12,7 @@
#include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/profile_select.h"
class ControllerNavigation; class ControllerNavigation;
class GMainWindow; class MainWindow;
class QDialogButtonBox; class QDialogButtonBox;
class QGraphicsScene; class QGraphicsScene;
class QLabel; class QLabel;
@ -69,7 +72,7 @@ class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSe
Q_OBJECT Q_OBJECT
public: public:
explicit QtProfileSelector(GMainWindow& parent); explicit QtProfileSelector(MainWindow& parent);
~QtProfileSelector() override; ~QtProfileSelector() override;
void Close() const override; void Close() const override;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -15,7 +18,7 @@
#include "hid_core/hid_types.h" #include "hid_core/hid_types.h"
#include "ui_qt_software_keyboard.h" #include "ui_qt_software_keyboard.h"
#include "yuzu/applets/qt_software_keyboard.h" #include "yuzu/applets/qt_software_keyboard.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/util/overlay_dialog.h" #include "yuzu/util/overlay_dialog.h"
namespace { namespace {
@ -1541,24 +1544,24 @@ void QtSoftwareKeyboardDialog::InputThread() {
} }
} }
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) { QtSoftwareKeyboard::QtSoftwareKeyboard(MainWindow& main_window) {
connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window,
&GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window,
&GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window,
&GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window,
&GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window,
&GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window,
&GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection);
connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window, connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window,
&GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection); &MainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this, connect(&main_window, &MainWindow::SoftwareKeyboardSubmitNormalText, this,
&QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection); &QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection);
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this, connect(&main_window, &MainWindow::SoftwareKeyboardSubmitInlineText, this,
&QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection); &QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -27,7 +30,7 @@ namespace Ui {
class QtSoftwareKeyboardDialog; class QtSoftwareKeyboardDialog;
} }
class GMainWindow; class MainWindow;
class QtSoftwareKeyboardDialog final : public QDialog { class QtSoftwareKeyboardDialog final : public QDialog {
Q_OBJECT Q_OBJECT
@ -230,7 +233,7 @@ class QtSoftwareKeyboard final : public QObject, public Core::Frontend::Software
Q_OBJECT Q_OBJECT
public: public:
explicit QtSoftwareKeyboard(GMainWindow& parent); explicit QtSoftwareKeyboard(MainWindow& parent);
~QtSoftwareKeyboard() override; ~QtSoftwareKeyboard() override;
void Close() const override { void Close() const override {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -18,7 +21,7 @@
#endif #endif
#include "yuzu/applets/qt_web_browser.h" #include "yuzu/applets/qt_web_browser.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#ifdef YUZU_USE_QT_WEB_ENGINE #ifdef YUZU_USE_QT_WEB_ENGINE
@ -391,14 +394,14 @@ void QtNXWebEngineView::FocusFirstLinkElement() {
#endif #endif
QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { QtWebBrowser::QtWebBrowser(MainWindow& main_window) {
connect(this, &QtWebBrowser::MainWindowOpenWebPage, &main_window, connect(this, &QtWebBrowser::MainWindowOpenWebPage, &main_window,
&GMainWindow::WebBrowserOpenWebPage, Qt::QueuedConnection); &MainWindow::WebBrowserOpenWebPage, Qt::QueuedConnection);
connect(this, &QtWebBrowser::MainWindowRequestExit, &main_window, connect(this, &QtWebBrowser::MainWindowRequestExit, &main_window,
&GMainWindow::WebBrowserRequestExit, Qt::QueuedConnection); &MainWindow::WebBrowserRequestExit, Qt::QueuedConnection);
connect(&main_window, &GMainWindow::WebBrowserExtractOfflineRomFS, this, connect(&main_window, &MainWindow::WebBrowserExtractOfflineRomFS, this,
&QtWebBrowser::MainWindowExtractOfflineRomFS, Qt::QueuedConnection); &QtWebBrowser::MainWindowExtractOfflineRomFS, Qt::QueuedConnection);
connect(&main_window, &GMainWindow::WebBrowserClosed, this, connect(&main_window, &MainWindow::WebBrowserClosed, this,
&QtWebBrowser::MainWindowWebBrowserClosed, Qt::QueuedConnection); &QtWebBrowser::MainWindowWebBrowserClosed, Qt::QueuedConnection);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -14,7 +17,7 @@
#include "core/frontend/applets/web_browser.h" #include "core/frontend/applets/web_browser.h"
class GMainWindow; class MainWindow;
class InputInterpreter; class InputInterpreter;
class UrlRequestInterceptor; class UrlRequestInterceptor;
@ -193,7 +196,7 @@ class QtWebBrowser final : public QObject, public Core::Frontend::WebBrowserAppl
Q_OBJECT Q_OBJECT
public: public:
explicit QtWebBrowser(GMainWindow& parent); explicit QtWebBrowser(MainWindow& parent);
~QtWebBrowser() override; ~QtWebBrowser() override;
void Close() const override; void Close() const override;

View file

@ -57,7 +57,7 @@
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "yuzu/bootmanager.h" #include "yuzu/bootmanager.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
class QObject; class QObject;
@ -272,7 +272,7 @@ struct NullRenderWidget : public RenderWidget {
explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {} explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
}; };
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, GRenderWindow::GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_, std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
Core::System& system_) Core::System& system_)
: QWidget(parent), : QWidget(parent),
@ -290,11 +290,11 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
QGuiApplication::platformName() == QStringLiteral("wayland-egl"); QGuiApplication::platformName() == QStringLiteral("wayland-egl");
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &MainWindow::OnLoadComplete);
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &MainWindow::OnExecuteProgram,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection); connect(this, &GRenderWindow::ExitSignal, parent, &MainWindow::OnExit, Qt::QueuedConnection);
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged); connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &MainWindow::OnTasStateChanged);
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout); mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse); connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -29,7 +32,7 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
class GMainWindow; class MainWindow;
class QCamera; class QCamera;
class QCameraImageCapture; class QCameraImageCapture;
class QCloseEvent; class QCloseEvent;
@ -146,7 +149,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT Q_OBJECT
public: public:
explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, explicit GRenderWindow(MainWindow* parent, EmuThread* emu_thread_,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_, std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
Core::System& system_); Core::System& system_);
~GRenderWindow() override; ~GRenderWindow() override;

View file

@ -2,12 +2,11 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "data_dialog.h" #include "data_dialog.h"
#include "core/hle/service/acc/profile_manager.h"
#include "frontend_common/data_manager.h" #include "frontend_common/data_manager.h"
#include "qt_common/qt_common.h"
#include "qt_common/util/content.h" #include "qt_common/util/content.h"
#include "qt_common/qt_string_lookup.h" #include "qt_common/qt_string_lookup.h"
#include "ui_data_dialog.h" #include "ui_data_dialog.h"
#include "util/util.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QFileDialog> #include <QFileDialog>
@ -26,17 +25,18 @@ DataDialog::DataDialog(QWidget *parent)
ui->setupUi(this); ui->setupUi(this);
// TODO: Should we make this a single widget that pulls data from a model? // TODO: Should we make this a single widget that pulls data from a model?
#define WIDGET(name) \ #define WIDGET(label, name) \
ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \ ui->page->addWidget(new DataWidget(FrontendCommon::DataManager::DataDir::name, \
QtCommon::StringLookup::name##Tooltip, \ QtCommon::StringLookup::name##Tooltip, \
QStringLiteral(#name), \ QStringLiteral(#name), \
this)); this)); \
ui->labels->addItem(label);
WIDGET(Shaders) WIDGET(tr("Shaders"), Shaders)
WIDGET(UserNand) WIDGET(tr("UserNAND"), UserNand)
WIDGET(SysNand) WIDGET(tr("SysNAND"), SysNand)
WIDGET(Mods) WIDGET(tr("Mods"), Mods)
WIDGET(Saves) WIDGET(tr("Saves"), Saves)
#undef WIDGET #undef WIDGET
@ -82,7 +82,7 @@ void DataWidget::clear()
{ {
std::string user_id{}; std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = selectProfile(); user_id = GetProfileIDString();
} }
QtCommon::Content::ClearDataDir(m_dir, user_id); QtCommon::Content::ClearDataDir(m_dir, user_id);
scan(); scan();
@ -92,7 +92,7 @@ void DataWidget::open()
{ {
std::string user_id{}; std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = selectProfile(); user_id = GetProfileIDString();
} }
QDesktopServices::openUrl(QUrl::fromLocalFile( QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id)))); QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id))));
@ -102,7 +102,7 @@ void DataWidget::upload()
{ {
std::string user_id{}; std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = selectProfile(); user_id = GetProfileIDString();
} }
QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName); QtCommon::Content::ExportDataDir(m_dir, user_id, m_exportName);
} }
@ -111,7 +111,7 @@ void DataWidget::download()
{ {
std::string user_id{}; std::string user_id{};
if (m_dir == FrontendCommon::DataManager::DataDir::Saves) { if (m_dir == FrontendCommon::DataManager::DataDir::Saves) {
user_id = selectProfile(); user_id = GetProfileIDString();
} }
QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this)); QtCommon::Content::ImportDataDir(m_dir, user_id, std::bind(&DataWidget::scan, this));
} }
@ -131,37 +131,3 @@ void DataWidget::scan() {
watcher->setFuture( watcher->setFuture(
QtConcurrent::run([this]() { return FrontendCommon::DataManager::DataDirSize(m_dir); })); 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<std::size_t>(index));
ASSERT(uuid);
const auto user_id = uuid->AsU128();
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}

View file

@ -36,31 +36,6 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<item>
<property name="text">
<string>Shaders</string>
</property>
</item>
<item>
<property name="text">
<string>UserNAND</string>
</property>
</item>
<item>
<property name="text">
<string>SysNAND</string>
</property>
</item>
<item>
<property name="text">
<string>Mods</string>
</property>
</item>
<item>
<property name="text">
<string>Saves</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>

View file

@ -18,7 +18,7 @@ DepsDialog::DepsDialog(QWidget* parent)
{ {
ui->setupUi(this); ui->setupUi(this);
constexpr size_t rows = Common::dep_hashes.size(); constexpr int rows = (int) Common::dep_hashes.size();
ui->tableDeps->setRowCount(rows); ui->tableDeps->setRowCount(rows);
QStringList labels; QStringList labels;
@ -29,7 +29,7 @@ DepsDialog::DepsDialog(QWidget* parent)
ui->tableDeps->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed); ui->tableDeps->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeMode::Fixed);
ui->tableDeps->horizontalHeader()->setMinimumSectionSize(200); ui->tableDeps->horizontalHeader()->setMinimumSectionSize(200);
for (size_t i = 0; i < rows; ++i) { for (int i = 0; i < rows; ++i) {
const std::string name = Common::dep_names.at(i); const std::string name = Common::dep_names.at(i);
const std::string sha = Common::dep_hashes.at(i); const std::string sha = Common::dep_hashes.at(i);
const std::string url = Common::dep_urls.at(i); const std::string url = Common::dep_urls.at(i);

View file

@ -23,7 +23,7 @@
#include "yuzu/compatibility_list.h" #include "yuzu/compatibility_list.h"
#include "yuzu/game_list_p.h" #include "yuzu/game_list_p.h"
#include "yuzu/game_list_worker.h" #include "yuzu/game_list_worker.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h" #include "yuzu/util/controller_navigation.h"
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include <regex> #include <regex>
@ -314,7 +314,7 @@ void GameList::OnFilterCloseClicked() {
GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_, GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_,
PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_,
GMainWindow* parent) MainWindow* parent)
: QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_},
play_time_manager{play_time_manager_}, system{system_} { play_time_manager{play_time_manager_}, system{system_} {
watcher = new QFileSystemWatcher(this); watcher = new QFileSystemWatcher(this);
@ -347,7 +347,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time); tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
item_model->setSortRole(GameListItemPath::SortRole); item_model->setSortRole(GameListItemPath::SortRole);
connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons); connect(main_window, &MainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded); connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
@ -943,8 +943,8 @@ void GameList::RemoveFavorite(u64 program_id) {
} }
} }
GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent} { GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} {
connect(parent, &GMainWindow::UpdateThemedIcons, this, connect(parent, &MainWindow::UpdateThemedIcons, this,
&GameListPlaceholder::onUpdateThemedIcons); &GameListPlaceholder::onUpdateThemedIcons);
layout = new QVBoxLayout; layout = new QVBoxLayout;

View file

@ -33,7 +33,7 @@ class ControllerNavigation;
class GameListWorker; class GameListWorker;
class GameListSearchField; class GameListSearchField;
class GameListDir; class GameListDir;
class GMainWindow; class MainWindow;
enum class AmLaunchType; enum class AmLaunchType;
enum class StartGameType; enum class StartGameType;
@ -69,7 +69,7 @@ public:
explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_, explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
FileSys::ManualContentProvider* provider_, FileSys::ManualContentProvider* provider_,
PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_, PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_,
GMainWindow* parent = nullptr); MainWindow* parent = nullptr);
~GameList() override; ~GameList() override;
QString GetLastFilterResultItem() const; QString GetLastFilterResultItem() const;
@ -153,7 +153,7 @@ private:
std::shared_ptr<FileSys::VfsFilesystem> vfs; std::shared_ptr<FileSys::VfsFilesystem> vfs;
FileSys::ManualContentProvider* provider; FileSys::ManualContentProvider* provider;
GameListSearchField* search_field; GameListSearchField* search_field;
GMainWindow* main_window = nullptr; MainWindow* main_window = nullptr;
QVBoxLayout* layout = nullptr; QVBoxLayout* layout = nullptr;
QTreeView* tree_view = nullptr; QTreeView* tree_view = nullptr;
QStandardItemModel* item_model = nullptr; QStandardItemModel* item_model = nullptr;
@ -171,7 +171,7 @@ private:
class GameListPlaceholder : public QWidget { class GameListPlaceholder : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit GameListPlaceholder(GMainWindow* parent = nullptr); explicit GameListPlaceholder(MainWindow* parent = nullptr);
~GameListPlaceholder(); ~GameListPlaceholder();
signals: signals:

File diff suppressed because it is too large Load diff

4899
src/yuzu/main_window.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -29,7 +29,7 @@
#include <QDBusObjectPath> #include <QDBusObjectPath>
#include <QVariant> #include <QVariant>
#include <QtDBus/QDBusInterface> #include <QtDBus/QDBusInterface>
#include <QtDBus/QtDBus> #include <QSocketNotifier>
#endif #endif
#ifdef ENABLE_UPDATE_CHECKER #ifdef ENABLE_UPDATE_CHECKER
@ -38,7 +38,6 @@
#endif #endif
class QtConfig; class QtConfig;
class ClickableLabel;
class EmuThread; class EmuThread;
class GameList; class GameList;
class GImageInfo; class GImageInfo;
@ -154,7 +153,7 @@ private:
constexpr static int MaxMultiplier = 8; constexpr static int MaxMultiplier = 8;
}; };
class GMainWindow : public QMainWindow { class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
/// Max number of recently loaded items to keep track of /// Max number of recently loaded items to keep track of
@ -163,8 +162,8 @@ class GMainWindow : public QMainWindow {
public: public:
void filterBarSetChecked(bool state); void filterBarSetChecked(bool state);
void UpdateUITheme(); void UpdateUITheme();
explicit GMainWindow(bool has_broken_vulkan); explicit MainWindow(bool has_broken_vulkan);
~GMainWindow() override; ~MainWindow() override;
bool DropAction(QDropEvent* event); bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event); void AcceptDropEvent(QDropEvent* event);
@ -470,8 +469,6 @@ private:
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
std::string GetProfileID();
std::unique_ptr<Ui::MainWindow> ui; std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc; std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;

View file

@ -11,11 +11,11 @@
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
MigrationWorker::MigrationWorker(const Emulator selected_legacy_emu_, MigrationWorker::MigrationWorker(const Emulator selected_emu_,
const bool clear_shader_cache_, const bool clear_shader_cache_,
const MigrationStrategy strategy_) const MigrationStrategy strategy_)
: QObject() : QObject()
, selected_legacy_emu(selected_legacy_emu_) , selected_emu(selected_emu_)
, clear_shader_cache(clear_shader_cache_) , clear_shader_cache(clear_shader_cache_)
, strategy(strategy_) , strategy(strategy_)
{} {}
@ -25,15 +25,20 @@ void MigrationWorker::process()
namespace fs = std::filesystem; namespace fs = std::filesystem;
constexpr auto copy_options = fs::copy_options::update_existing | fs::copy_options::recursive; constexpr auto copy_options = fs::copy_options::update_existing | fs::copy_options::recursive;
const fs::path legacy_user_dir = selected_legacy_emu.get_user_dir(); const fs::path legacy_user_dir = selected_emu.get_user_dir();
const fs::path legacy_config_dir = selected_legacy_emu.get_config_dir(); const fs::path legacy_config_dir = selected_emu.get_config_dir();
const fs::path legacy_cache_dir = selected_legacy_emu.get_cache_dir(); const fs::path legacy_cache_dir = selected_emu.get_cache_dir();
// TODO(crueter): Make these constexpr since they're defaulted // TODO(crueter): Make these constexpr since they're defaulted
const fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir); fs::path eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir);
const fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir); fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
const fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir); fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
const fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); fs::path shader_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
eden_dir.make_preferred();
config_dir.make_preferred();
cache_dir.make_preferred();
shader_dir.make_preferred();
try { try {
fs::remove_all(eden_dir); fs::remove_all(eden_dir);
@ -69,7 +74,7 @@ void MigrationWorker::process()
success_text.append(tr("\n\nNote that your configuration and data will be shared with %1.\n" success_text.append(tr("\n\nNote that your configuration and data will be shared with %1.\n"
"If this is not desirable, delete the following files:\n%2\n%3\n%4") "If this is not desirable, delete the following files:\n%2\n%3\n%4")
.arg(selected_legacy_emu.name(), .arg(selected_emu.name(),
QString::fromStdString(eden_dir.string()), QString::fromStdString(eden_dir.string()),
QString::fromStdString(config_dir.string()), QString::fromStdString(config_dir.string()),
QString::fromStdString(cache_dir.string()))); QString::fromStdString(cache_dir.string())));

View file

@ -7,14 +7,12 @@
#include <QObject> #include <QObject>
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
using namespace Common::FS;
typedef struct Emulator { typedef struct Emulator {
const char *m_name; const char *m_name;
EmuPath e_user_dir; Common::FS::EmuPath e_user_dir;
EmuPath e_config_dir; Common::FS::EmuPath e_config_dir;
EmuPath e_cache_dir; Common::FS::EmuPath e_cache_dir;
const std::string get_user_dir() const { const std::string get_user_dir() const {
return Common::FS::GetLegacyPath(e_user_dir).string(); return Common::FS::GetLegacyPath(e_user_dir).string();
@ -35,11 +33,13 @@ typedef struct Emulator {
} }
} Emulator; } Emulator;
#define STRUCT_EMU(name, enumName) Emulator{name, Common::FS::enumName##Dir, Common::FS::enumName##ConfigDir, Common::FS::enumName##CacheDir}
static constexpr std::array<Emulator, 4> legacy_emus = { static constexpr std::array<Emulator, 4> legacy_emus = {
Emulator{QT_TR_NOOP("Citron"), CitronDir, CitronConfigDir, CitronCacheDir}, STRUCT_EMU(QT_TR_NOOP("Citron"), Citron),
Emulator{QT_TR_NOOP("Sudachi"), SudachiDir, SudachiConfigDir, SudachiCacheDir}, STRUCT_EMU(QT_TR_NOOP("Sudachi"), Sudachi),
Emulator{QT_TR_NOOP("Suyu"), SuyuDir, SuyuConfigDir, SuyuCacheDir}, STRUCT_EMU(QT_TR_NOOP("Suyu"), Suyu),
Emulator{QT_TR_NOOP("Yuzu"), YuzuDir, YuzuConfigDir, YuzuCacheDir}, STRUCT_EMU(QT_TR_NOOP("Yuzu"), Yuzu),
}; };
class MigrationWorker : public QObject class MigrationWorker : public QObject
@ -52,7 +52,7 @@ public:
Link, Link,
}; };
MigrationWorker(const Emulator selected_legacy_emu, MigrationWorker(const Emulator selected_emu,
const bool clear_shader_cache, const bool clear_shader_cache,
const MigrationStrategy strategy); const MigrationStrategy strategy);
@ -64,7 +64,7 @@ signals:
void error(const QString &error_message); void error(const QString &error_message);
private: private:
Emulator selected_legacy_emu; Emulator selected_emu;
bool clear_shader_cache; bool clear_shader_cache;
MigrationStrategy strategy; MigrationStrategy strategy;
QString success_text = tr("Data was migrated successfully."); QString success_text = tr("Data was migrated successfully.");

View file

@ -15,7 +15,7 @@
#include "core/internal_network/network_interface.h" #include "core/internal_network/network_interface.h"
#include "network/network.h" #include "network/network.h"
#include "ui_direct_connect.h" #include "ui_direct_connect.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/multiplayer/client_room.h" #include "yuzu/multiplayer/client_room.h"
#include "yuzu/multiplayer/direct_connect.h" #include "yuzu/multiplayer/direct_connect.h"
#include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/message.h"

View file

@ -20,7 +20,7 @@
#include "network/announce_multiplayer_session.h" #include "network/announce_multiplayer_session.h"
#include "ui_host_room.h" #include "ui_host_room.h"
#include "yuzu/game_list_p.h" #include "yuzu/game_list_p.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/multiplayer/host_room.h" #include "yuzu/multiplayer/host_room.h"
#include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/message.h"
#include "yuzu/multiplayer/state.h" #include "yuzu/multiplayer/state.h"

View file

@ -15,7 +15,7 @@
#include "network/network.h" #include "network/network.h"
#include "ui_lobby.h" #include "ui_lobby.h"
#include "yuzu/game_list_p.h" #include "yuzu/game_list_p.h"
#include "yuzu/main.h" #include "yuzu/main_window.h"
#include "yuzu/multiplayer/client_room.h" #include "yuzu/multiplayer/client_room.h"
#include "yuzu/multiplayer/lobby.h" #include "yuzu/multiplayer/lobby.h"
#include "yuzu/multiplayer/lobby_p.h" #include "yuzu/multiplayer/lobby_p.h"

View file

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common/abstract/frontend.h"
#include "ryujinx_dialog.h" #include "ryujinx_dialog.h"
#include "qt_common/util/fs.h" #include "qt_common/util/fs.h"
#include "ui_ryujinx_dialog.h" #include "ui_ryujinx_dialog.h"
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem;
RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path, RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path,
std::filesystem::path ryu_path, std::filesystem::path ryu_path,
QWidget *parent) QWidget *parent)
@ -20,6 +19,7 @@ RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path,
connect(ui->eden, &QPushButton::clicked, this, &RyujinxDialog::fromEden); connect(ui->eden, &QPushButton::clicked, this, &RyujinxDialog::fromEden);
connect(ui->ryujinx, &QPushButton::clicked, this, &RyujinxDialog::fromRyujinx); connect(ui->ryujinx, &QPushButton::clicked, this, &RyujinxDialog::fromRyujinx);
connect(ui->cancel, &QPushButton::clicked, this, &RyujinxDialog::reject);
} }
RyujinxDialog::~RyujinxDialog() RyujinxDialog::~RyujinxDialog()
@ -30,7 +30,21 @@ RyujinxDialog::~RyujinxDialog()
void RyujinxDialog::fromEden() void RyujinxDialog::fromEden()
{ {
accept(); 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(QString::fromStdString(e.what())));
}
// ?ploo
QtCommon::FS::LinkRyujinx(m_ryu, m_eden);
} }
void RyujinxDialog::fromRyujinx() void RyujinxDialog::fromRyujinx()

View file

@ -23,8 +23,6 @@
#include <QThread> #include <QThread>
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem;
UserDataMigrator::UserDataMigrator(QMainWindow *main_window) UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
{ {
// NOTE: Logging is not initialized yet, do not produce logs here. // NOTE: Logging is not initialized yet, do not produce logs here.
@ -32,7 +30,7 @@ UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
// Check migration if config directory does not exist // Check migration if config directory does not exist
// TODO: ProfileManager messes with us a bit here, and force-creates the /nand/system/save/8000000000000010/su/avators/profiles.dat // TODO: ProfileManager messes with us a bit here, and force-creates the /nand/system/save/8000000000000010/su/avators/profiles.dat
// file. Find a way to reorder operations and have it create after this guy runs. // file. Find a way to reorder operations and have it create after this guy runs.
if (!fs::is_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir))) { if (!std::filesystem::is_directory(Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir))) {
ShowMigrationPrompt(main_window); ShowMigrationPrompt(main_window);
} }
} }
@ -40,23 +38,7 @@ UserDataMigrator::UserDataMigrator(QMainWindow *main_window)
void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window) void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
{ {
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace QtCommon::StringLookup;
// define strings here for easy access
QString prompt_prefix_text = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationPromptPrefix);
QString migration_prompt_message = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationPrompt);
QString clear_shader_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipClearShader);
QString keep_old_data_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipKeepOld);
QString clear_old_data_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipClearOld);
QString link_old_dir_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipLinkOld);
// actual migration code
MigrationDialog migration_prompt; MigrationDialog migration_prompt;
migration_prompt.setWindowTitle(QObject::tr("Migration")); migration_prompt.setWindowTitle(QObject::tr("Migration"));
@ -69,11 +51,11 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
#define BUTTON(clazz, name, text, tooltip, checkState) \ #define BUTTON(clazz, name, text, tooltip, checkState) \
clazz *name = new clazz(&migration_prompt); \ clazz *name = new clazz(&migration_prompt); \
name->setText(text); \ name->setText(text); \
name->setToolTip(tooltip); \ name->setToolTip(Lookup(tooltip)); \
name->setChecked(checkState); \ name->setChecked(checkState); \
migration_prompt.addBox(name); migration_prompt.addBox(name);
BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), clear_shader_tooltip, true) BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), MigrationTooltipClearShader, true)
u32 id = 0; u32 id = 0;
@ -81,9 +63,9 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
BUTTON(QRadioButton, name, text, tooltip, checkState) \ BUTTON(QRadioButton, name, text, tooltip, checkState) \
group->addButton(name, ++id); group->addButton(name, ++id);
RADIO(keep_old, QObject::tr("Keep Old Data"), keep_old_data_tooltip, true) RADIO(keep_old, QObject::tr("Keep Old Data"), MigrationTooltipKeepOld, true)
RADIO(clear_old, QObject::tr("Clear Old Data"), clear_old_data_tooltip, false) RADIO(clear_old, QObject::tr("Clear Old Data"), MigrationTooltipClearOld, false)
RADIO(link_old, QObject::tr("Link Old Directory"), link_old_dir_tooltip, false) RADIO(link_old, QObject::tr("Link Old Directory"), MigrationTooltipLinkOld, false)
#undef RADIO #undef RADIO
#undef BUTTON #undef BUTTON
@ -101,7 +83,7 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
// makes my life easier // makes my life easier
qRegisterMetaType<Emulator>(); qRegisterMetaType<Emulator>();
QString prompt_text = prompt_prefix_text; QString prompt_text = Lookup(MigrationPromptPrefix);
// natural language processing is a nightmare // natural language processing is a nightmare
for (const Emulator &emu : found) { for (const Emulator &emu : found) {
@ -114,7 +96,7 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
} }
prompt_text.append(QObject::tr("\n\n")); prompt_text.append(QObject::tr("\n\n"));
prompt_text = prompt_text % QStringLiteral("\n\n") % migration_prompt_message; prompt_text = prompt_text % QStringLiteral("\n\n") % Lookup(MigrationPrompt);
migration_prompt.setText(prompt_text); migration_prompt.setText(prompt_text);
migration_prompt.addButton(QObject::tr("No"), true); migration_prompt.addButton(QObject::tr("No"), true);
@ -127,24 +109,12 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
return ShowMigrationCancelledMessage(main_window); return ShowMigrationCancelledMessage(main_window);
} }
MigrationWorker::MigrationStrategy strategy; MigrationWorker::MigrationStrategy strategy = static_cast<MigrationWorker::MigrationStrategy>(
group->checkedId());
switch (group->checkedId()) { selected_emu = button->property("emulator").value<Emulator>();
default:
[[fallthrough]];
case 1:
strategy = MigrationWorker::MigrationStrategy::Copy;
break;
case 2:
strategy = MigrationWorker::MigrationStrategy::Move;
break;
case 3:
strategy = MigrationWorker::MigrationStrategy::Link;
break;
}
MigrateUserData(main_window, MigrateUserData(main_window,
button->property("emulator").value<Emulator>(),
clear_shaders->isChecked(), clear_shaders->isChecked(),
strategy); strategy);
} }
@ -161,12 +131,9 @@ void UserDataMigrator::ShowMigrationCancelledMessage(QMainWindow *main_window)
} }
void UserDataMigrator::MigrateUserData(QMainWindow *main_window, void UserDataMigrator::MigrateUserData(QMainWindow *main_window,
const Emulator selected_legacy_emu,
const bool clear_shader_cache, const bool clear_shader_cache,
const MigrationWorker::MigrationStrategy strategy) const MigrationWorker::MigrationStrategy strategy)
{ {
selected_emu = selected_legacy_emu;
// Create a dialog to let the user know it's migrating // Create a dialog to let the user know it's migrating
QProgressDialog *progress = new QProgressDialog(main_window); QProgressDialog *progress = new QProgressDialog(main_window);
progress->setWindowTitle(QObject::tr("Migrating")); progress->setWindowTitle(QObject::tr("Migrating"));
@ -176,7 +143,7 @@ void UserDataMigrator::MigrateUserData(QMainWindow *main_window,
progress->setWindowModality(Qt::WindowModality::ApplicationModal); progress->setWindowModality(Qt::WindowModality::ApplicationModal);
QThread *thread = new QThread(main_window); QThread *thread = new QThread(main_window);
MigrationWorker *worker = new MigrationWorker(selected_legacy_emu, clear_shader_cache, strategy); MigrationWorker *worker = new MigrationWorker(selected_emu, clear_shader_cache, strategy);
worker->moveToThread(thread); worker->moveToThread(thread);
thread->connect(thread, &QThread::started, worker, &MigrationWorker::process); thread->connect(thread, &QThread::started, worker, &MigrationWorker::process);

View file

@ -22,7 +22,6 @@ private:
void ShowMigrationPrompt(QMainWindow* main_window); void ShowMigrationPrompt(QMainWindow* main_window);
void ShowMigrationCancelledMessage(QMainWindow* main_window); void ShowMigrationCancelledMessage(QMainWindow* main_window);
void MigrateUserData(QMainWindow* main_window, void MigrateUserData(QMainWindow* main_window,
const Emulator selected_legacy_emu,
const bool clear_shader_cache, const bool clear_shader_cache,
const MigrationWorker::MigrationStrategy strategy); const MigrationWorker::MigrationStrategy strategy);
}; };

View file

@ -8,7 +8,11 @@
#include <cmath> #include <cmath>
#include <QPainter> #include <QPainter>
#include "applets/qt_profile_select.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
#include "qt_common/qt_common.h"
#include "yuzu/util/util.h" #include "yuzu/util/util.h"
#ifdef _WIN32 #ifdef _WIN32
@ -153,3 +157,49 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
return false; return false;
#endif #endif
} }
const std::optional<Common::UUID> 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 = [] {
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, QtCommon::rootObject, 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 std::nullopt;
}
const auto uuid =
QtCommon::system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
ASSERT(uuid);
return uuid;
}
std::string GetProfileIDString() {
const auto uuid = GetProfileID();
if (!uuid)
return "";
auto user_id = uuid->AsU128();
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -6,6 +9,7 @@
#include <filesystem> #include <filesystem>
#include <QFont> #include <QFont>
#include <QString> #include <QString>
#include "common/uuid.h"
/// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc.
[[nodiscard]] QFont GetMonospaceFont(); [[nodiscard]] QFont GetMonospaceFont();
@ -27,3 +31,15 @@
* @return bool If the operation succeeded * @return bool If the operation succeeded
*/ */
[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); [[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
/**
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
* @return The selected profile, or an std::nullopt if none were selected
*/
const std::optional<Common::UUID> GetProfileID();
/**
* Prompt the user for a profile ID. If there is only one valid profile, returns that profile.
* @return A string representation of the selected profile, or an empty string if none were seleeced
*/
std::string GetProfileIDString();