aboutsummaryrefslogtreecommitdiff
path: root/src/yuzu
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu')
-rw-r--r--src/yuzu/CMakeLists.txt1
-rw-r--r--src/yuzu/data_dialog.cpp2
-rw-r--r--src/yuzu/game_list.cpp5
-rw-r--r--src/yuzu/game_list.h1
-rw-r--r--src/yuzu/main.cpp61
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/migration_worker.cpp9
-rw-r--r--src/yuzu/migration_worker.h6
-rw-r--r--src/yuzu/ryujinx_dialog.cpp40
-rw-r--r--src/yuzu/ryujinx_dialog.h32
-rw-r--r--src/yuzu/ryujinx_dialog.ui81
11 files changed, 233 insertions, 8 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4b1e87b6ed..d6082dd00b 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -236,6 +236,7 @@ add_executable(yuzu
data_dialog.h data_dialog.cpp data_dialog.ui
data_widget.ui
+ ryujinx_dialog.h ryujinx_dialog.cpp ryujinx_dialog.ui
)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
diff --git a/src/yuzu/data_dialog.cpp b/src/yuzu/data_dialog.cpp
index dc3e06f89f..008e810eae 100644
--- a/src/yuzu/data_dialog.cpp
+++ b/src/yuzu/data_dialog.cpp
@@ -95,7 +95,7 @@ void DataWidget::open()
user_id = selectProfile();
}
QDesktopServices::openUrl(QUrl::fromLocalFile(
- QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir, user_id))));
+ QString::fromStdString(FrontendCommon::DataManager::GetDataDirString(m_dir, user_id))));
}
void DataWidget::upload()
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 875e794181..ae8e4688b4 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -542,6 +542,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
}
void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
+ // TODO(crueter): Refactor this and make it less bad
QAction* favorite = context_menu.addAction(tr("Favorite"));
context_menu.addSeparator();
QAction* start_game = context_menu.addAction(tr("Start Game"));
@@ -581,6 +582,7 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
#endif
context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Configure Game"));
+ QAction* ryujinx = context_menu.addAction(tr("Link to Ryujinx"));
favorite->setVisible(program_id != 0);
favorite->setCheckable(true);
@@ -662,6 +664,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
#endif
connect(properties, &QAction::triggered,
[this, path]() { emit OpenPerGameGeneralRequested(path); });
+
+ connect(ryujinx, &QAction::triggered, [this, program_id]() { emit LinkToRyujinxRequested(program_id);
+ });
};
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index b4cdf61f0d..0b3ac7fcde 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -113,6 +113,7 @@ signals:
void NavigateToGamedbEntryRequested(u64 program_id,
const CompatibilityList& compatibility_list);
void OpenPerGameGeneralRequested(const std::string& file);
+ void LinkToRyujinxRequested(const u64 &program_id);
void OpenDirectory(const QString& directory);
void AddDirectory();
void ShowList(bool show);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 8fb5575b41..849ffa84de 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -6,10 +6,12 @@
#include "core/tools/renderdoc.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
+#include "qt_common/abstract/frontend.h"
#include "qt_common/util/content.h"
#include "qt_common/util/game.h"
#include "qt_common/util/meta.h"
#include "qt_common/util/path.h"
+#include "qt_common/util/fs.h"
#include <clocale>
#include <cmath>
#include <memory>
@@ -108,6 +110,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "common/detached_tasks.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
+#include "common/fs/ryujinx_compat.h"
#include "common/literals.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
@@ -160,6 +163,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/data_dialog.h"
#include "yuzu/deps_dialog.h"
+#include "yuzu/ryujinx_dialog.h"
#include "qt_common/discord/discord.h"
#include "yuzu/game_list.h"
#include "yuzu/game_list_p.h"
@@ -1597,6 +1601,8 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties);
+ connect(game_list, &GameList::LinkToRyujinxRequested, this,
+ &GMainWindow::OnLinkToRyujinx);
connect(this, &GMainWindow::UpdateInstallProgress, this,
&GMainWindow::IncrementInstallProgress);
@@ -2875,6 +2881,61 @@ void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
OpenPerGameConfiguration(title_id, file);
}
+std::string GMainWindow::GetProfileID()
+{
+ 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]);
+}
+
+void GMainWindow::OnLinkToRyujinx(const u64& program_id)
+{
+ u64 save_id = QtCommon::FS::GetRyujinxSaveID(program_id);
+ if (save_id == (u64) -1)
+ return;
+
+ const std::string user_id = GetProfileID();
+
+ auto paths = QtCommon::FS::GetEmuPaths(program_id, save_id, user_id);
+ if (!paths)
+ return;
+
+ auto eden_dir = paths.value().first;
+ auto ryu_dir = paths.value().second;
+
+ if (!QtCommon::FS::CheckUnlink(eden_dir, ryu_dir)) {
+ RyujinxDialog dialog(eden_dir, ryu_dir, this);
+ dialog.exec();
+ }
+}
+
void GMainWindow::OnMenuLoadFile() {
if (is_load_file_select_active) {
return;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index ce04d6d152..ae5d74215a 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -358,6 +358,7 @@ private slots:
void OnGameListAddDirectory();
void OnGameListShowList(bool show);
void OnGameListOpenPerGameProperties(const std::string& file);
+ void OnLinkToRyujinx(const u64& program_id);
void OnMenuLoadFile();
void OnMenuLoadFolder();
void IncrementInstallProgress();
@@ -470,6 +471,8 @@ private:
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
+ std::string GetProfileID();
+
std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
diff --git a/src/yuzu/migration_worker.cpp b/src/yuzu/migration_worker.cpp
index b39bd22584..50b6d1ebd4 100644
--- a/src/yuzu/migration_worker.cpp
+++ b/src/yuzu/migration_worker.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "migration_worker.h"
+#include "common/fs/symlink.h"
#include <QMap>
#include <boost/algorithm/string/predicate.hpp>
@@ -37,7 +38,7 @@ void MigrationWorker::process()
try {
fs::remove_all(eden_dir);
} catch (fs::filesystem_error &_) {
- // ignore because linux does stupid crap sometimes.
+ // ignore because linux does stupid crap sometimes
}
switch (strategy) {
@@ -46,7 +47,7 @@ void MigrationWorker::process()
// Windows 11 has random permission nonsense to deal with.
try {
- fs::create_directory_symlink(legacy_user_dir, eden_dir);
+ Common::FS::CreateSymlink(legacy_user_dir, eden_dir);
} catch (const fs::filesystem_error &e) {
emit error(tr("Linking the old directory failed. You may need to re-run with "
"administrative privileges on Windows.\nOS gave error: %1")
@@ -58,11 +59,11 @@ void MigrationWorker::process()
// are already children of the root directory
#ifndef WIN32
if (fs::is_directory(legacy_config_dir)) {
- fs::create_directory_symlink(legacy_config_dir, config_dir);
+ Common::FS::CreateSymlink(legacy_config_dir, config_dir);
}
if (fs::is_directory(legacy_cache_dir)) {
- fs::create_directory_symlink(legacy_cache_dir, cache_dir);
+ Common::FS::CreateSymlink(legacy_cache_dir, cache_dir);
}
#endif
diff --git a/src/yuzu/migration_worker.h b/src/yuzu/migration_worker.h
index 5f9e400db0..2ff6e8c481 100644
--- a/src/yuzu/migration_worker.h
+++ b/src/yuzu/migration_worker.h
@@ -12,9 +12,9 @@ using namespace Common::FS;
typedef struct Emulator {
const char *m_name;
- LegacyPath e_user_dir;
- LegacyPath e_config_dir;
- LegacyPath e_cache_dir;
+ EmuPath e_user_dir;
+ EmuPath e_config_dir;
+ EmuPath e_cache_dir;
const std::string get_user_dir() const {
return Common::FS::GetLegacyPath(e_user_dir).string();
diff --git a/src/yuzu/ryujinx_dialog.cpp b/src/yuzu/ryujinx_dialog.cpp
new file mode 100644
index 0000000000..db10c06d93
--- /dev/null
+++ b/src/yuzu/ryujinx_dialog.cpp
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "ryujinx_dialog.h"
+#include "qt_common/util/fs.h"
+#include "ui_ryujinx_dialog.h"
+#include <filesystem>
+
+namespace fs = std::filesystem;
+
+RyujinxDialog::RyujinxDialog(std::filesystem::path eden_path,
+ std::filesystem::path ryu_path,
+ QWidget *parent)
+ : QDialog(parent)
+ , ui(new Ui::RyujinxDialog)
+ , m_eden(eden_path.make_preferred())
+ , m_ryu(ryu_path.make_preferred())
+{
+ ui->setupUi(this);
+
+ connect(ui->eden, &QPushButton::clicked, this, &RyujinxDialog::fromEden);
+ connect(ui->ryujinx, &QPushButton::clicked, this, &RyujinxDialog::fromRyujinx);
+}
+
+RyujinxDialog::~RyujinxDialog()
+{
+ delete ui;
+}
+
+void RyujinxDialog::fromEden()
+{
+ accept();
+ QtCommon::FS::LinkRyujinx(m_eden, m_ryu);
+}
+
+void RyujinxDialog::fromRyujinx()
+{
+ accept();
+ QtCommon::FS::LinkRyujinx(m_ryu, m_eden);
+}
diff --git a/src/yuzu/ryujinx_dialog.h b/src/yuzu/ryujinx_dialog.h
new file mode 100644
index 0000000000..63cebe483c
--- /dev/null
+++ b/src/yuzu/ryujinx_dialog.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef RYUJINX_DIALOG_H
+#define RYUJINX_DIALOG_H
+
+#include <QDialog>
+#include <filesystem>
+
+namespace Ui {
+class RyujinxDialog;
+}
+
+class RyujinxDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RyujinxDialog(std::filesystem::path eden_path, std::filesystem::path ryu_path, QWidget *parent = nullptr);
+ ~RyujinxDialog();
+
+private slots:
+ void fromEden();
+ void fromRyujinx();
+
+private:
+ Ui::RyujinxDialog *ui;
+ std::filesystem::path m_eden;
+ std::filesystem::path m_ryu;
+};
+
+#endif // RYUJINX_DIALOG_H
diff --git a/src/yuzu/ryujinx_dialog.ui b/src/yuzu/ryujinx_dialog.ui
new file mode 100644
index 0000000000..c4fb5ae51f
--- /dev/null
+++ b/src/yuzu/ryujinx_dialog.ui
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RyujinxDialog</class>
+ <widget class="QDialog" name="RyujinxDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>404</width>
+ <height>170</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Ryujinx Link</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Linking save data to Ryujinx lets both Ryujinx and Eden reference the same save files for your games.
+
+By selecting &quot;From Eden&quot;, previous save data stored in Ryujinx will be deleted, and vice versa for &quot;From Ryujinx&quot;.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="eden">
+ <property name="text">
+ <string>From Eden</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="ryujinx">
+ <property name="text">
+ <string>From Ryujinx</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancel</sender>
+ <signal>clicked()</signal>
+ <receiver>RyujinxDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>331</x>
+ <y>147</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>201</x>
+ <y>84</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>