Browse Source

[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>
pull/2974/head v0.0.4-rc2
crueter 1 month ago
parent
commit
08f3639c80
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 2
      CMakeLists.txt
  2. 2
      cpmfile.json
  3. 4
      src/common/CMakeLists.txt
  4. 19
      src/common/fs/ryujinx_compat.cpp
  5. 11
      src/common/fs/ryujinx_compat.h
  6. 39
      src/common/fs/symlink.cpp
  7. 2
      src/common/fs/symlink.h
  8. 57
      src/core/hle/service/acc/profile_manager.cpp
  9. 2
      src/core/hle/service/acc/profile_manager.h
  10. 11
      src/qt_common/abstract/frontend.cpp
  11. 4
      src/qt_common/abstract/frontend.h
  12. 26
      src/qt_common/config/qt_config.cpp
  13. 54
      src/qt_common/config/shared_translation.h
  14. 2
      src/qt_common/config/uisettings.h
  15. 6
      src/qt_common/qt_string_lookup.h
  16. 7
      src/qt_common/util/content.h
  17. 76
      src/qt_common/util/fs.cpp
  18. 9
      src/qt_common/util/fs.h
  19. 8
      src/yuzu/CMakeLists.txt
  20. 13
      src/yuzu/applets/qt_amiibo_settings.cpp
  21. 7
      src/yuzu/applets/qt_amiibo_settings.h
  22. 10
      src/yuzu/applets/qt_controller.cpp
  23. 7
      src/yuzu/applets/qt_controller.h
  24. 13
      src/yuzu/applets/qt_error.cpp
  25. 7
      src/yuzu/applets/qt_error.h
  26. 10
      src/yuzu/applets/qt_profile_select.cpp
  27. 7
      src/yuzu/applets/qt_profile_select.h
  28. 25
      src/yuzu/applets/qt_software_keyboard.cpp
  29. 7
      src/yuzu/applets/qt_software_keyboard.h
  30. 15
      src/yuzu/applets/qt_web_browser.cpp
  31. 7
      src/yuzu/applets/qt_web_browser.h
  32. 12
      src/yuzu/bootmanager.cpp
  33. 7
      src/yuzu/bootmanager.h
  34. 60
      src/yuzu/data_dialog.cpp
  35. 25
      src/yuzu/data_dialog.ui
  36. 4
      src/yuzu/deps_dialog.cpp
  37. 10
      src/yuzu/game_list.cpp
  38. 8
      src/yuzu/game_list.h
  39. 4945
      src/yuzu/main.cpp
  40. 4899
      src/yuzu/main_window.cpp
  41. 13
      src/yuzu/main_window.h
  42. 37
      src/yuzu/migration_worker.cpp
  43. 22
      src/yuzu/migration_worker.h
  44. 2
      src/yuzu/multiplayer/direct_connect.cpp
  45. 2
      src/yuzu/multiplayer/host_room.cpp
  46. 2
      src/yuzu/multiplayer/lobby.cpp
  47. 26
      src/yuzu/ryujinx_dialog.cpp
  48. 61
      src/yuzu/user_data_migration.cpp
  49. 1
      src/yuzu/user_data_migration.h
  50. 50
      src/yuzu/util/util.cpp
  51. 16
      src/yuzu/util/util.h

2
CMakeLists.txt

@ -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)

2
cpmfile.json

@ -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",

4
src/common/CMakeLists.txt

@ -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()

19
src/common/fs/ryujinx_compat.cpp

@ -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?
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "user" / "save" / hex / "0";
// TODO: what's the difference between 0 and 1?
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)

11
src/common/fs/ryujinx_compat.h

@ -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();
fs::path GetRyuSavePath(const u64 &program_id);
std::filesystem::path GetKvdbPath();
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

39
src/common/fs/symlink.cpp

@ -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
const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string());
return system(command.c_str()) == 0;
#else
from.make_preferred();
to.make_preferred();
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
auto attributes = GetFileAttributesW(path.wstring().c_str());
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
#else
return fs::is_symlink(path);
#endif
return boost::filesystem::is_symlink(boost::filesystem::path{path});
} }
} // namespace Common::FS } // namespace Common::FS

2
src/common/fs/symlink.h

@ -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

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

@ -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
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
// some exceptions, e.g. the "system" profile
static constexpr const std::array<const char* const, 1> EXCEPTION_UUIDS
= {"00000000000000000000000000000000"};
for (const char *const uuid : EXCEPTION_UUIDS) { 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) {
std::string uuid_string = [p]() -> std::string {
auto uuid = p.user_uuid;
// "ignore" invalid uuids
if (uuid.IsInvalid()) {
return "0";
}
auto user_id = uuid.AsU128();
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}();
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
}
auto existing = FindExistingProfileStrings();
good_uuids.insert(good_uuids.end(), existing.begin(), existing.end());
return good_uuids; 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) {

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

@ -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();

11
src/qt_common/abstract/frontend.cpp

@ -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
} }

4
src/qt_common/abstract/frontend.h

@ -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

26
src/qt_common/config/qt_config.cpp

@ -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();
} }

54
src/qt_common/config/shared_translation.h

@ -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::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "None"))},
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FXAA"))},
{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"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
{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"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))},
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Area"))},
{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::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Docked"))},
{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::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Normal"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "High"))},
{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::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL"))},
{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::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLSL"))},
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SPIRV"))},
}; };
} // namespace ConfigurationShared } // namespace ConfigurationShared

2
src/qt_common/config/uisettings.h

@ -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};

6
src/qt_common/qt_string_lookup.h

@ -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

7
src/qt_common/util/content.h

@ -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>(
(int) result + (int) QtCommon::StringLookup::FwInstallSuccess));
return LOOKUP_ENUM(result, 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 QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>(
(int) result + (int) QtCommon::StringLookup::KeyInstallSuccess));
return LOOKUP_ENUM(result, KeyInstallSuccess);
} }
void InstallFirmware(const QString &location, bool recursive); void InstallFirmware(const QString &location, bool recursive);

76
src/qt_common/util/fs.cpp

@ -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 = StringLookup::Lookup(
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::KvdbNonexistent));
QString caption = LOOKUP_ENUM(res, 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;
}
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);
return fs::path{};
} }
} // namespace QtCommon::FS } // namespace QtCommon::FS

9
src/qt_common/util/fs.h

@ -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);
/// @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);
const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path &path_hint, const u64 &program_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

8
src/yuzu/CMakeLists.txt

@ -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)

13
src/yuzu/applets/qt_amiibo_settings.cpp

@ -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);
connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
&MainWindow::AmiiboSettingsRequestExit, Qt::QueuedConnection);
connect(&parent, &MainWindow::AmiiboSettingsFinished, this,
&QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection); &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
} }

7
src/yuzu/applets/qt_amiibo_settings.h

@ -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;

10
src/yuzu/applets/qt_controller.cpp

@ -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);
connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
&MainWindow::ControllerSelectorRequestExit, Qt::QueuedConnection);
connect(&parent, &MainWindow::ControllerSelectorReconfigureFinished, this,
&QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection); &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
} }

7
src/yuzu/applets/qt_controller.h

@ -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;

13
src/yuzu/applets/qt_error.cpp

@ -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);
connect(&parent, &GMainWindow::ErrorDisplayFinished, this,
&MainWindow::ErrorDisplayRequestExit, Qt::QueuedConnection);
connect(&parent, &MainWindow::ErrorDisplayFinished, this,
&QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection); &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
} }

7
src/yuzu/applets/qt_error.h

@ -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;

10
src/yuzu/applets/qt_profile_select.cpp

@ -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);
connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this,
&MainWindow::ProfileSelectorRequestExit, Qt::QueuedConnection);
connect(&parent, &MainWindow::ProfileSelectorFinishedSelection, this,
&QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection);
} }

7
src/yuzu/applets/qt_profile_select.h

@ -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;

25
src/yuzu/applets/qt_software_keyboard.cpp

@ -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);
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this,
&MainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
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);
} }

7
src/yuzu/applets/qt_software_keyboard.h

@ -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 {

15
src/yuzu/applets/qt_web_browser.cpp

@ -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);
connect(&main_window, &GMainWindow::WebBrowserExtractOfflineRomFS, this,
&MainWindow::WebBrowserRequestExit, Qt::QueuedConnection);
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);
} }

7
src/yuzu/applets/qt_web_browser.h

@ -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;

12
src/yuzu/bootmanager.cpp

@ -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::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &MainWindow::OnLoadComplete);
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &MainWindow::OnExecuteProgram,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
connect(this, &GRenderWindow::ExitSignal, parent, &MainWindow::OnExit, Qt::QueuedConnection);
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);

7
src/yuzu/bootmanager.h

@ -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;

60
src/yuzu/data_dialog.cpp

@ -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(UserNand)
WIDGET(SysNand)
WIDGET(Mods)
WIDGET(Saves)
WIDGET(tr("Shaders"), Shaders)
WIDGET(tr("UserNAND"), UserNand)
WIDGET(tr("SysNAND"), SysNand)
WIDGET(tr("Mods"), Mods)
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]);
}

25
src/yuzu/data_dialog.ui

@ -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>

4
src/yuzu/deps_dialog.cpp

@ -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);

10
src/yuzu/game_list.cpp

@ -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} {
connect(parent, &GMainWindow::UpdateThemedIcons, this,
GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} {
connect(parent, &MainWindow::UpdateThemedIcons, this,
&GameListPlaceholder::onUpdateThemedIcons); &GameListPlaceholder::onUpdateThemedIcons);
layout = new QVBoxLayout; layout = new QVBoxLayout;

8
src/yuzu/game_list.h

@ -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:

4945
src/yuzu/main.cpp
File diff suppressed because it is too large
View File

4899
src/yuzu/main_window.cpp
File diff suppressed because it is too large
View File

13
src/yuzu/main.h → src/yuzu/main_window.h

@ -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);
~GMainWindow() override;
explicit MainWindow(bool has_broken_vulkan);
~MainWindow() override;
bool DropAction(QDropEvent* event); bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event); void AcceptDropEvent(QDropEvent* event);
@ -467,11 +466,9 @@ private:
*/ */
bool question(QWidget* parent, const QString& title, const QString& text, bool question(QWidget* parent, const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons buttons =
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;

37
src/yuzu/migration_worker.cpp

@ -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_config_dir = selected_legacy_emu.get_config_dir();
const fs::path legacy_cache_dir = selected_legacy_emu.get_cache_dir();
const fs::path legacy_user_dir = selected_emu.get_user_dir();
const fs::path legacy_config_dir = selected_emu.get_config_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);
const fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
const 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 eden_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::EdenDir);
fs::path config_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
fs::path cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir);
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);
@ -55,8 +60,8 @@ void MigrationWorker::process()
std::exit(-1); std::exit(-1);
} }
// Windows doesn't need any more links, because cache and config
// are already children of the root directory
// Windows doesn't need any more links, because cache and config
// are already children of the root directory
#ifndef WIN32 #ifndef WIN32
if (fs::is_directory(legacy_config_dir)) { if (fs::is_directory(legacy_config_dir)) {
Common::FS::CreateSymlink(legacy_config_dir, config_dir); Common::FS::CreateSymlink(legacy_config_dir, config_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())));
@ -79,8 +84,8 @@ void MigrationWorker::process()
// Rename directories if deletion is requested (achieves the same result) // Rename directories if deletion is requested (achieves the same result)
fs::rename(legacy_user_dir, eden_dir); fs::rename(legacy_user_dir, eden_dir);
// Windows doesn't need any more renames, because cache and config
// are already children of the root directory
// Windows doesn't need any more renames, because cache and config
// are already children of the root directory
#ifndef WIN32 #ifndef WIN32
if (fs::is_directory(legacy_config_dir)) { if (fs::is_directory(legacy_config_dir)) {
fs::rename(legacy_config_dir, config_dir); fs::rename(legacy_config_dir, config_dir);
@ -96,8 +101,8 @@ void MigrationWorker::process()
// Default behavior: copy // Default behavior: copy
fs::copy(legacy_user_dir, eden_dir, copy_options); fs::copy(legacy_user_dir, eden_dir, copy_options);
// Windows doesn't need any more copies, because cache and config
// are already children of the root directory
// Windows doesn't need any more copies, because cache and config
// are already children of the root directory
#ifndef WIN32 #ifndef WIN32
if (fs::is_directory(legacy_config_dir)) { if (fs::is_directory(legacy_config_dir)) {
fs::copy(legacy_config_dir, config_dir, copy_options); fs::copy(legacy_config_dir, config_dir, copy_options);

22
src/yuzu/migration_worker.h

@ -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;
EmuPath e_config_dir;
EmuPath e_cache_dir;
Common::FS::EmuPath e_user_dir;
Common::FS::EmuPath e_config_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},
Emulator{QT_TR_NOOP("Sudachi"), SudachiDir, SudachiConfigDir, SudachiCacheDir},
Emulator{QT_TR_NOOP("Suyu"), SuyuDir, SuyuConfigDir, SuyuCacheDir},
Emulator{QT_TR_NOOP("Yuzu"), YuzuDir, YuzuConfigDir, YuzuCacheDir},
STRUCT_EMU(QT_TR_NOOP("Citron"), Citron),
STRUCT_EMU(QT_TR_NOOP("Sudachi"), Sudachi),
STRUCT_EMU(QT_TR_NOOP("Suyu"), Suyu),
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.");

2
src/yuzu/multiplayer/direct_connect.cpp

@ -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"

2
src/yuzu/multiplayer/host_room.cpp

@ -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"

2
src/yuzu/multiplayer/lobby.cpp

@ -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"

26
src/yuzu/ryujinx_dialog.cpp

@ -1,25 +1,25 @@
// 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)
: QDialog(parent) : QDialog(parent)
, ui(new Ui::RyujinxDialog)
, m_eden(eden_path.make_preferred())
, m_ryu(ryu_path.make_preferred())
, ui(new Ui::RyujinxDialog)
, m_eden(eden_path.make_preferred())
, m_ryu(ryu_path.make_preferred())
{ {
ui->setupUi(this); ui->setupUi(this);
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()

61
src/yuzu/user_data_migration.cpp

@ -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;
// 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
using namespace QtCommon::StringLookup;
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(clear_old, QObject::tr("Clear Old Data"), clear_old_data_tooltip, false)
RADIO(link_old, QObject::tr("Link Old Directory"), link_old_dir_tooltip, false)
RADIO(keep_old, QObject::tr("Keep Old Data"), MigrationTooltipKeepOld, true)
RADIO(clear_old, QObject::tr("Clear Old Data"), MigrationTooltipClearOld, 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;
switch (group->checkedId()) {
default:
[[fallthrough]];
case 1:
strategy = MigrationWorker::MigrationStrategy::Copy;
break;
case 2:
strategy = MigrationWorker::MigrationStrategy::Move;
break;
case 3:
strategy = MigrationWorker::MigrationStrategy::Link;
break;
}
MigrationWorker::MigrationStrategy strategy = static_cast<MigrationWorker::MigrationStrategy>(
group->checkedId());
selected_emu = button->property("emulator").value<Emulator>();
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);

1
src/yuzu/user_data_migration.h

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

50
src/yuzu/util/util.cpp

@ -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]);
}

16
src/yuzu/util/util.h

@ -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();
Loading…
Cancel
Save