diff --git a/dist/qt_themes/colorful/icons/256x256/plus_folder.png b/dist/qt_themes/colorful/icons/256x256/plus_folder.png
index 31471e3a2a..92539549ee 100644
Binary files a/dist/qt_themes/colorful/icons/256x256/plus_folder.png and b/dist/qt_themes/colorful/icons/256x256/plus_folder.png differ
diff --git a/dist/qt_themes/colorful/icons/48x48/download.png b/dist/qt_themes/colorful/icons/48x48/download.png
new file mode 100644
index 0000000000..baceb7ae99
Binary files /dev/null and b/dist/qt_themes/colorful/icons/48x48/download.png differ
diff --git a/dist/qt_themes/colorful/icons/48x48/trash.png b/dist/qt_themes/colorful/icons/48x48/trash.png
deleted file mode 100644
index e60dce2e32..0000000000
Binary files a/dist/qt_themes/colorful/icons/48x48/trash.png and /dev/null differ
diff --git a/dist/qt_themes/colorful/icons/48x48/upload.png b/dist/qt_themes/colorful/icons/48x48/upload.png
new file mode 100644
index 0000000000..053b8c3fea
Binary files /dev/null and b/dist/qt_themes/colorful/icons/48x48/upload.png differ
diff --git a/dist/qt_themes/colorful/icons/48x48/user-trash.png b/dist/qt_themes/colorful/icons/48x48/user-trash.png
new file mode 100644
index 0000000000..19ce265069
Binary files /dev/null and b/dist/qt_themes/colorful/icons/48x48/user-trash.png differ
diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc
index fc85922d6b..f64d405707 100644
--- a/dist/qt_themes/colorful/style.qrc
+++ b/dist/qt_themes/colorful/style.qrc
@@ -18,7 +18,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
icons/48x48/bad_folder.png
icons/48x48/chip.png
icons/48x48/folder.png
- icons/48x48/trash.png
+ icons/48x48/user-trash.png
+ icons/48x48/download.png
+ icons/48x48/upload.png
icons/48x48/list-add.png
icons/48x48/no_avatar.png
icons/48x48/sd_card.png
diff --git a/dist/qt_themes/colorful_midnight_blue/style.qrc b/dist/qt_themes/colorful_midnight_blue/style.qrc
index 2aee8ea1e1..18f8a6b823 100644
--- a/dist/qt_themes/colorful_midnight_blue/style.qrc
+++ b/dist/qt_themes/colorful_midnight_blue/style.qrc
@@ -11,7 +11,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
../colorful/icons/48x48/bad_folder.png
../colorful/icons/48x48/chip.png
../colorful/icons/48x48/folder.png
- ../colorful/icons/48x48/trash.png
+ ../colorful/icons/48x48/user-trash.png
+ ../colorful/icons/48x48/download.png
+ ../colorful/icons/48x48/upload.png
../colorful/icons/48x48/list-add.png
../colorful/icons/48x48/sd_card.png
../colorful/icons/256x256/plus_folder.png
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index 45a91ef2dc..e59f0ade9c 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -14,7 +14,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
icons/48x48/bad_folder.png
icons/48x48/chip.png
icons/48x48/folder.png
- icons/48x48/trash.png
+ icons/48x48/user-trash.png
+ icons/48x48/download.png
+ icons/48x48/upload.png
icons/48x48/list-add.png
icons/48x48/sd_card.png
icons/48x48/star.png
diff --git a/dist/qt_themes/default/icons/256x256/eden.png b/dist/qt_themes/default/icons/256x256/eden.png
index c0d7d8e37c..dee7b91dc7 100644
Binary files a/dist/qt_themes/default/icons/256x256/eden.png and b/dist/qt_themes/default/icons/256x256/eden.png differ
diff --git a/dist/qt_themes/default/icons/48x48/download.png b/dist/qt_themes/default/icons/48x48/download.png
new file mode 100644
index 0000000000..c0e3213655
Binary files /dev/null and b/dist/qt_themes/default/icons/48x48/download.png differ
diff --git a/dist/qt_themes/default/icons/48x48/trash.png b/dist/qt_themes/default/icons/48x48/trash.png
deleted file mode 100644
index 547b821de9..0000000000
Binary files a/dist/qt_themes/default/icons/48x48/trash.png and /dev/null differ
diff --git a/dist/qt_themes/default/icons/48x48/upload.png b/dist/qt_themes/default/icons/48x48/upload.png
new file mode 100644
index 0000000000..46120cbdaf
Binary files /dev/null and b/dist/qt_themes/default/icons/48x48/upload.png differ
diff --git a/dist/qt_themes/default/icons/48x48/user-trash.png b/dist/qt_themes/default/icons/48x48/user-trash.png
new file mode 100644
index 0000000000..ef360a4775
Binary files /dev/null and b/dist/qt_themes/default/icons/48x48/user-trash.png differ
diff --git a/dist/qt_themes/default_dark/style.qrc b/dist/qt_themes/default_dark/style.qrc
index ac6c9fe4e9..b76f9a0bda 100644
--- a/dist/qt_themes/default_dark/style.qrc
+++ b/dist/qt_themes/default_dark/style.qrc
@@ -13,7 +13,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
../colorful/icons/48x48/bad_folder.png
../colorful/icons/48x48/chip.png
../colorful/icons/48x48/folder.png
- ../colorful/icons/48x48/trash.png
+ ../colorful/icons/48x48/user-trash.png
+ ../colorful/icons/48x48/download.png
+ ../colorful/icons/48x48/upload.png
../qdarkstyle/icons/48x48/no_avatar.png
../colorful/icons/48x48/list-add.png
../colorful/icons/48x48/sd_card.png
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/download.png b/dist/qt_themes/qdarkstyle/icons/48x48/download.png
new file mode 100644
index 0000000000..d4d26fe2ea
Binary files /dev/null and b/dist/qt_themes/qdarkstyle/icons/48x48/download.png differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/trash.png b/dist/qt_themes/qdarkstyle/icons/48x48/trash.png
deleted file mode 100644
index e59fe85c03..0000000000
Binary files a/dist/qt_themes/qdarkstyle/icons/48x48/trash.png and /dev/null differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/upload.png b/dist/qt_themes/qdarkstyle/icons/48x48/upload.png
new file mode 100644
index 0000000000..ce255fb467
Binary files /dev/null and b/dist/qt_themes/qdarkstyle/icons/48x48/upload.png differ
diff --git a/dist/qt_themes/qdarkstyle/icons/48x48/user-trash.png b/dist/qt_themes/qdarkstyle/icons/48x48/user-trash.png
new file mode 100644
index 0000000000..e273528101
Binary files /dev/null and b/dist/qt_themes/qdarkstyle/icons/48x48/user-trash.png differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index 2b5ca31e72..3902996058 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -9,7 +9,9 @@
icons/48x48/bad_folder.png
icons/48x48/chip.png
icons/48x48/folder.png
- icons/48x48/trash.png
+ icons/48x48/user-trash.png
+ icons/48x48/download.png
+ icons/48x48/upload.png
icons/48x48/no_avatar.png
icons/48x48/list-add.png
icons/48x48/sd_card.png
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index dc3d7fecbd..75212008a3 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -6,6 +6,9 @@
../qdarkstyle/icons/48x48/bad_folder.png
../qdarkstyle/icons/48x48/chip.png
../qdarkstyle/icons/48x48/folder.png
+ ../qdarkstyle/icons/48x48/user-trash.png
+ ../qdarkstyle/icons/48x48/download.png
+ ../qdarkstyle/icons/48x48/upload.png
../qdarkstyle/icons/48x48/no_avatar.png
../qdarkstyle/icons/48x48/list-add.png
../qdarkstyle/icons/48x48/sd_card.png
diff --git a/src/android/app/src/main/legacy/drawable/ic_icon_bg.png b/src/android/app/src/main/legacy/drawable/ic_icon_bg.png
index 3327014f8f..f3d4c55dea 100644
Binary files a/src/android/app/src/main/legacy/drawable/ic_icon_bg.png and b/src/android/app/src/main/legacy/drawable/ic_icon_bg.png differ
diff --git a/src/android/app/src/main/legacy/drawable/ic_icon_bg_orig.png b/src/android/app/src/main/legacy/drawable/ic_icon_bg_orig.png
index a9fc55a4f5..5859c43f86 100644
Binary files a/src/android/app/src/main/legacy/drawable/ic_icon_bg_orig.png and b/src/android/app/src/main/legacy/drawable/ic_icon_bg_orig.png differ
diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg.png b/src/android/app/src/main/res/drawable/ic_icon_bg.png
index 7da2c6a1a3..db7e53410d 100644
Binary files a/src/android/app/src/main/res/drawable/ic_icon_bg.png and b/src/android/app/src/main/res/drawable/ic_icon_bg.png differ
diff --git a/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png
index 18325c031a..ddb43b349c 100644
Binary files a/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png and b/src/android/app/src/main/res/drawable/ic_icon_bg_orig.png differ
diff --git a/src/frontend_common/data_manager.cpp b/src/frontend_common/data_manager.cpp
index 4a63ed9e28..e83e3ada40 100644
--- a/src/frontend_common/data_manager.cpp
+++ b/src/frontend_common/data_manager.cpp
@@ -21,7 +21,9 @@ const std::string GetDataDir(DataDir dir)
case DataDir::UserNand:
return (nand_dir / "user" / "Contents" / "registered").string();
case DataDir::SysNand:
- return (nand_dir / "system").string();
+ // NB: do NOT delete save
+ // that contains profile data and other stuff
+ return (nand_dir / "system" / "Contents" / "registered").string();
case DataDir::Mods:
return Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir);
case DataDir::Shaders:
diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt
index fe728e0377..1f633fd894 100644
--- a/src/qt_common/CMakeLists.txt
+++ b/src/qt_common/CMakeLists.txt
@@ -35,9 +35,28 @@ if (ENABLE_QT)
target_link_libraries(qt_common PRIVATE Qt6::Widgets)
endif()
+target_compile_definitions(qt_common PUBLIC
+ # Use QStringBuilder for string concatenation to reduce
+ # the overall number of temporary strings created.
+ QT_USE_QSTRINGBUILDER
+
+ # Disable implicit conversions from/to C strings
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+
+ # Disable implicit type narrowing in signal/slot connect() calls.
+ QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
+
+ # Disable unsafe overloads of QProcess' start() function.
+ QT_NO_PROCESS_COMBINED_ARGUMENT_START
+
+ # Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
+ QT_NO_URL_CAST_FROM_STRING
+)
+
add_subdirectory(externals)
-target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip)
+target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent SimpleIni::SimpleIni QuaZip::QuaZip)
target_link_libraries(qt_common PUBLIC frozen::frozen)
if (NOT APPLE AND ENABLE_OPENGL)
diff --git a/src/qt_common/externals/CMakeLists.txt b/src/qt_common/externals/CMakeLists.txt
index 189a52c0a6..b998090d90 100644
--- a/src/qt_common/externals/CMakeLists.txt
+++ b/src/qt_common/externals/CMakeLists.txt
@@ -16,5 +16,4 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
AddJsonPackage(quazip)
# frozen
-# TODO(crueter): Qt String Lookup
AddJsonPackage(frozen)
diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp
index 9cbd1caf02..98ae9620c7 100644
--- a/src/qt_common/qt_content_util.cpp
+++ b/src/qt_common/qt_content_util.cpp
@@ -1,17 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-#include "qt_common/qt_game_util.h"
+#include "frontend_common/data_manager.h"
#include "qt_content_util.h"
#include "common/fs/fs.h"
#include "core/hle/service/acc/profile_manager.h"
#include "frontend_common/content_manager.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
+#include "qt_common/qt_game_util.h"
#include "qt_common/qt_progress_dialog.h"
#include "qt_frontend_util.h"
+#include
+#include
#include
+#include
namespace QtCommon::Content {
@@ -21,10 +25,10 @@ bool CheckGameFirmware(u64 program_id, QObject* parent)
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
auto result = QtCommon::Frontend::ShowMessage(
QMessageBox::Warning,
- "Game Requires Firmware",
- "The game you are trying to launch requires firmware to boot or to get past the "
- "opening menu. Please "
- "dump and install firmware, or press \"OK\" to launch anyways.",
+ tr("Game Requires Firmware"),
+ tr("The game you are trying to launch requires firmware to boot or to get past the "
+ "opening menu. Please "
+ "dump and install firmware, or press \"OK\" to launch anyways."),
QMessageBox::Ok | QMessageBox::Cancel,
parent);
@@ -60,8 +64,8 @@ void InstallFirmware(const QString& location, bool recursive)
const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon,
- failedTitle,
- GetFirmwareInstallResultString(result));
+ tr(failedTitle),
+ tr(GetFirmwareInstallResultString(result)));
};
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
@@ -125,8 +129,8 @@ void InstallFirmware(const QString& location, bool recursive)
i++;
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
FileSys::OpenMode::Read);
- auto firmware_dst_vfile = firmware_vdir
- ->CreateFileRelative(firmware_src_path.filename().string());
+ auto firmware_dst_vfile = firmware_vdir->CreateFileRelative(
+ firmware_src_path.filename().string());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend,
@@ -168,9 +172,9 @@ void InstallFirmware(const QString& location, bool recursive)
const auto failed_names = QString::fromStdString(
fmt::format("{}", fmt::join(results, "\n")));
progress.close();
- QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"),
- tr("Verification failed for the following files:\n\n%1")
- .arg(failed_names));
+ QtCommon::Frontend::Critical(
+ tr("Firmware integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1").arg(failed_names));
return;
}
@@ -181,10 +185,10 @@ void InstallFirmware(const QString& location, bool recursive)
const std::string display_version(firmware_data.display_version.data());
result = FirmwareInstallResult::Success;
- QtCommon::Frontend::Information(rootObject,
- tr(successTitle),
- tr(GetFirmwareInstallResultString(result))
- .arg(QString::fromStdString(display_version)));
+ QtCommon::Frontend::Information(
+ rootObject,
+ tr(successTitle),
+ tr(GetFirmwareInstallResultString(result)).arg(QString::fromStdString(display_version)));
}
QString UnzipFirmwareToTmp(const QString& location)
@@ -193,7 +197,7 @@ QString UnzipFirmwareToTmp(const QString& location)
fs::path tmp{fs::temp_directory_path()};
if (!fs::create_directories(tmp / "eden" / "firmware")) {
- return "";
+ return QString();
}
tmp /= "eden";
@@ -205,7 +209,7 @@ QString UnzipFirmwareToTmp(const QString& location)
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
if (result.isEmpty()) {
- return "";
+ return QString();
}
return qCacheDir;
@@ -264,9 +268,8 @@ void InstallKeys()
return;
}
- FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location
- .toStdString(),
- "keys");
+ FirmwareManager::KeyInstallResult result
+ = FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
@@ -282,9 +285,14 @@ void InstallKeys()
}
}
-void VerifyInstalledContents() {
+void VerifyInstalledContents()
+{
// Initialize a progress dialog.
- QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject);
+ QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
+ tr("Cancel"),
+ 0,
+ 100,
+ QtCommon::rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
@@ -296,16 +304,17 @@ void VerifyInstalledContents() {
return progress.wasCanceled();
};
- const std::vector result =
- ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
+ const std::vector result
+ = ContentManager::VerifyInstalledContents(*QtCommon::system,
+ *QtCommon::provider,
+ QtProgressCallback);
progress.close();
if (result.empty()) {
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
} else {
- const auto failed_names =
- QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
+ const auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QtCommon::Frontend::Critical(
tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
@@ -332,7 +341,7 @@ void FixProfiles()
qorphaned.reserve(8 * 33);
for (const std::string& s : orphaned) {
- qorphaned += "\n" + QString::fromStdString(s);
+ qorphaned = qorphaned % QStringLiteral("\n") % QString::fromStdString(s);
}
QtCommon::Frontend::Critical(
@@ -348,18 +357,19 @@ void FixProfiles()
QtCommon::Game::OpenSaveFolder();
}
-void ClearDataDir(FrontendCommon::DataManager::DataDir dir) {
- auto result = QtCommon::Frontend::Warning("Really clear data?",
- "Important data may be lost!",
+void ClearDataDir(FrontendCommon::DataManager::DataDir dir)
+{
+ auto result = QtCommon::Frontend::Warning(tr("Really clear data?"),
+ tr("Important data may be lost!"),
QMessageBox::Yes | QMessageBox::No);
if (result != QMessageBox::Yes)
return;
result = QtCommon::Frontend::Warning(
- "Are you REALLY sure?",
- "Once deleted, your data will NOT come back!\n"
- "Only do this if you're 100% sure you want to delete this data.",
+ tr("Are you REALLY sure?"),
+ tr("Once deleted, your data will NOT come back!\n"
+ "Only do this if you're 100% sure you want to delete this data."),
QMessageBox::Yes | QMessageBox::No);
if (result != QMessageBox::Yes)
@@ -373,4 +383,130 @@ void ClearDataDir(FrontendCommon::DataManager::DataDir dir) {
dialog.close();
}
+void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function callback)
+{
+ const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir);
+
+ const QString zip_dump_location
+ = QtCommon::Frontend::GetSaveFileName(tr("Select Export Location"),
+ QStringLiteral("export.zip"),
+ tr("Zipped Archives (*.zip)"));
+
+ if (zip_dump_location.isEmpty())
+ return;
+
+ QMetaObject::Connection* connection = new QMetaObject::Connection;
+ *connection = QObject::connect(qApp, &QGuiApplication::aboutToQuit, rootObject, [=]() mutable {
+ QtCommon::Frontend::Warning(tr("Still Exporting"),
+ tr("Eden is still exporting some data, and will continue "
+ "running in the background until it's done."));
+ });
+
+ QtCommon::Frontend::QtProgressDialog* progress = new QtCommon::Frontend::QtProgressDialog(
+ tr("Compressing, this may take a while..."), tr("Background"), 0, 0, rootObject);
+
+ progress->setWindowModality(Qt::WindowModal);
+ progress->show();
+ QGuiApplication::processEvents();
+
+ QFuture future = QtConcurrent::run([&]() {
+ return JlCompress::compressDir(zip_dump_location,
+ QString::fromStdString(dir),
+ true,
+ QDir::Hidden | QDir::Files | QDir::Dirs);
+ });
+
+ QFutureWatcher* watcher = new QFutureWatcher(rootObject);
+
+ QObject::connect(watcher, &QFutureWatcher::finished, rootObject, [=]() {
+ progress->close();
+ progress->deleteLater();
+ QObject::disconnect(*connection);
+ delete connection;
+
+ if (watcher->result()) {
+ QtCommon::Frontend::Information(tr("Exported Successfully"),
+ tr("Data was exported successfully."));
+ } else {
+ QtCommon::Frontend::Critical(
+ tr("Export Failed"),
+ tr("Ensure you have write permissions on the targeted directory and try again."));
+ }
+
+ watcher->deleteLater();
+ callback();
+ });
+
+ watcher->setFuture(future);
+}
+
+void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, std::function callback)
+{
+ const std::string dir = FrontendCommon::DataManager::GetDataDir(data_dir);
+
+ using namespace QtCommon::Frontend;
+
+ const QString zip_dump_location = GetOpenFileName(tr("Select Import Location"),
+ {},
+ tr("Zipped Archives (*.zip)"));
+
+ if (zip_dump_location.isEmpty())
+ return;
+
+ StandardButton button = Warning(
+ tr("Import Warning"),
+ tr("All previous data in this directory will be deleted. Are you sure you wish to "
+ "proceed?"),
+ StandardButton::Yes | StandardButton::No);
+
+ if (button != QMessageBox::Yes)
+ return;
+
+ FrontendCommon::DataManager::ClearDir(data_dir);
+
+ QMetaObject::Connection* connection = new QMetaObject::Connection;
+ *connection = QObject::connect(qApp, &QGuiApplication::aboutToQuit, rootObject, [=]() mutable {
+ Warning(tr("Still Importing"),
+ tr("Eden is still importing some data, and will continue "
+ "running in the background until it's done."));
+ });
+
+ QtProgressDialog* progress = new QtProgressDialog(tr("Decompressing, this may take a while..."),
+ tr("Background"),
+ 0,
+ 0,
+ rootObject);
+
+ progress->setWindowModality(Qt::WindowModal);
+ progress->show();
+ QGuiApplication::processEvents();
+
+ QFuture future = QtConcurrent::run([=]() {
+ return !JlCompress::extractDir(zip_dump_location,
+ QString::fromStdString(dir)).empty();
+ });
+
+ QFutureWatcher* watcher = new QFutureWatcher(rootObject);
+
+ QObject::connect(watcher, &QFutureWatcher::finished, rootObject, [=]() {
+ progress->close();
+ progress->deleteLater();
+ QObject::disconnect(*connection);
+ delete connection;
+
+ if (watcher->result()) {
+ Information(tr("Imported Successfully"), tr("Data was imported successfully."));
+ } else {
+ Critical(
+ tr("Import Failed"),
+ tr("Ensure you have read permissions on the targeted directory and try again."));
+ }
+
+ watcher->deleteLater();
+ callback();
+ });
+
+ watcher->setFuture(future);
+}
+
} // namespace QtCommon::Content
diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h
index b2443829ab..fb6d1b85fa 100644
--- a/src/qt_common/qt_content_util.h
+++ b/src/qt_common/qt_content_util.h
@@ -48,6 +48,8 @@ void VerifyGameContents(const std::string &game_path);
void VerifyInstalledContents();
void ClearDataDir(FrontendCommon::DataManager::DataDir dir);
+void ExportDataDir(FrontendCommon::DataManager::DataDir dir, std::function callback = {});
+void ImportDataDir(FrontendCommon::DataManager::DataDir dir, std::function callback = {});
// Profiles //
void FixProfiles();
diff --git a/src/qt_common/qt_frontend_util.cpp b/src/qt_common/qt_frontend_util.cpp
index d519669ad5..3fe0ba0a80 100644
--- a/src/qt_common/qt_frontend_util.cpp
+++ b/src/qt_common/qt_frontend_util.cpp
@@ -32,4 +32,15 @@ const QString GetOpenFileName(const QString &title,
#endif
}
+const QString GetSaveFileName(const QString &title,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options)
+{
+#ifdef YUZU_QT_WIDGETS
+ return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
+#endif
+}
+
} // namespace QtCommon::Frontend
diff --git a/src/qt_common/qt_frontend_util.h b/src/qt_common/qt_frontend_util.h
index f86b9e1357..59e8f15a4e 100644
--- a/src/qt_common/qt_frontend_util.h
+++ b/src/qt_common/qt_frontend_util.h
@@ -97,10 +97,10 @@ Q_ENUM_NS(Icon)
// TODO(crueter) widgets-less impl, choices et al.
StandardButton ShowMessage(Icon icon,
- const QString &title,
- const QString &text,
- StandardButtons buttons = StandardButton::NoButton,
- QObject *parent = nullptr);
+ const QString &title,
+ const QString &text,
+ StandardButtons buttons = StandardButton::NoButton,
+ QObject *parent = nullptr);
#define UTIL_OVERRIDES(level) \
inline StandardButton level(QObject *parent, \
@@ -110,21 +110,6 @@ StandardButton ShowMessage(Icon icon,
{ \
return ShowMessage(Icon::level, title, text, buttons, parent); \
} \
- inline StandardButton level(QObject *parent, \
- const char *title, \
- const char *text, \
- StandardButtons buttons \
- = StandardButton::Ok) \
- { \
- return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \
- } \
- inline StandardButton level(const char *title, \
- const char *text, \
- StandardButtons buttons \
- = StandardButton::Ok) \
- { \
- return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \
- } \
inline StandardButton level(const QString title, \
const QString &text, \
StandardButtons buttons \
@@ -144,5 +129,11 @@ const QString GetOpenFileName(const QString &title,
QString *selectedFilter = nullptr,
Options options = Options());
+const QString GetSaveFileName(const QString &title,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter = nullptr,
+ Options options = Options());
+
} // namespace QtCommon::Frontend
#endif // QT_FRONTEND_UTIL_H
diff --git a/src/qt_common/qt_game_util.cpp b/src/qt_common/qt_game_util.cpp
index ac922ea967..0eddd10fe1 100644
--- a/src/qt_common/qt_game_util.cpp
+++ b/src/qt_common/qt_game_util.cpp
@@ -220,8 +220,8 @@ void RemoveBaseContent(u64 program_id, InstalledEntryType type)
program_id);
if (res) {
QtCommon::Frontend::Information(rootObject,
- "Successfully Removed",
- "Successfully removed the installed base game.");
+ tr("Successfully Removed"),
+ tr("Successfully removed the installed base game."));
} else {
QtCommon::Frontend::Warning(
rootObject,
@@ -235,8 +235,8 @@ void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
if (res) {
QtCommon::Frontend::Information(rootObject,
- "Successfully Removed",
- "Successfully removed the installed update.");
+ tr("Successfully Removed"),
+ tr("Successfully removed the installed update."));
} else {
QtCommon::Frontend::Warning(rootObject,
GetGameListErrorRemoving(type),
diff --git a/src/qt_common/qt_path_util.cpp b/src/qt_common/qt_path_util.cpp
index 761e6e8405..632424e0ca 100644
--- a/src/qt_common/qt_path_util.cpp
+++ b/src/qt_common/qt_path_util.cpp
@@ -17,7 +17,12 @@ bool OpenShaderCache(u64 program_id, QObject *parent)
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
- QtCommon::Frontend::ShowMessage(QMessageBox::Warning, "Error Opening Shader Cache", "Failed to create or open shader cache for this title, ensure your app data directory has write permissions.", QMessageBox::Ok, parent);
+ QtCommon::Frontend::ShowMessage(QMessageBox::Warning,
+ tr("Error Opening Shader Cache"),
+ tr("Failed to create or open shader cache for this title, "
+ "ensure your app data directory has write permissions."),
+ QMessageBox::Ok,
+ parent);
}
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f4669d0914..010a3db174 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -393,7 +393,7 @@ endif()
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
-target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets)
+target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets Qt6::Concurrent)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (NOT WIN32)
@@ -408,25 +408,6 @@ if (UNIX AND NOT APPLE)
endif()
endif()
-target_compile_definitions(yuzu PRIVATE
- # Use QStringBuilder for string concatenation to reduce
- # the overall number of temporary strings created.
- QT_USE_QSTRINGBUILDER
-
- # Disable implicit conversions from/to C strings
- QT_NO_CAST_FROM_ASCII
- QT_NO_CAST_TO_ASCII
-
- # Disable implicit type narrowing in signal/slot connect() calls.
- QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
-
- # Disable unsafe overloads of QProcess' start() function.
- QT_NO_PROCESS_COMBINED_ARGUMENT_START
-
- # Disable implicit QString->QUrl conversions to enforce use of proper resolving functions.
- QT_NO_URL_CAST_FROM_STRING
-)
-
if (YUZU_ENABLE_COMPATIBILITY_REPORTING)
target_compile_definitions(yuzu PRIVATE YUZU_ENABLE_COMPATIBILITY_REPORTING)
endif()
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index 0e58f17405..6d5d263e16 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -1,10 +1,13 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include
#include
#include
-#include
+#include
#include "common/logging/log.h"
#include "ui_compatdb.h"
#include "yuzu/compatdb.h"
diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp
index 15a0029901..7a693be10c 100644
--- a/src/yuzu/configuration/configure_web.cpp
+++ b/src/yuzu/configuration/configure_web.cpp
@@ -14,7 +14,7 @@
#include
#endif
-#include
+#include
#include "common/settings.h"
#include "ui_configure_web.h"
#include "qt_common/uisettings.h"
diff --git a/src/yuzu/data_dialog.cpp b/src/yuzu/data_dialog.cpp
index 87d81e4f43..d7c937e7ed 100644
--- a/src/yuzu/data_dialog.cpp
+++ b/src/yuzu/data_dialog.cpp
@@ -4,12 +4,16 @@
#include "data_dialog.h"
#include "frontend_common/data_manager.h"
#include "qt_common/qt_content_util.h"
+#include "qt_common/qt_frontend_util.h"
+#include "qt_common/qt_progress_dialog.h"
#include "qt_common/qt_string_lookup.h"
#include "ui_data_dialog.h"
#include
+#include
#include
-#include
+#include
+#include
DataDialog::DataDialog(QWidget *parent)
: QDialog(parent)
@@ -49,25 +53,41 @@ DataWidget::DataWidget(FrontendCommon::DataManager::DataDir data_dir,
ui->tooltip->setText(QtCommon::StringLookup::Lookup(tooltip));
- ui->clear->setIcon(QIcon::fromTheme(QStringLiteral("trash")));
+ ui->clear->setIcon(QIcon::fromTheme(QStringLiteral("user-trash")));
ui->open->setIcon(QIcon::fromTheme(QStringLiteral("folder")));
+ ui->upload->setIcon(QIcon::fromTheme(QStringLiteral("upload")));
+ ui->download->setIcon(QIcon::fromTheme(QStringLiteral("download")));
connect(ui->clear, &QPushButton::clicked, this, &DataWidget::clear);
connect(ui->open, &QPushButton::clicked, this, &DataWidget::open);
+ connect(ui->upload, &QPushButton::clicked, this, &DataWidget::upload);
+ connect(ui->download, &QPushButton::clicked, this, &DataWidget::download);
scan();
}
-void DataWidget::clear() {
+void DataWidget::clear()
+{
QtCommon::Content::ClearDataDir(m_dir);
scan();
}
-void DataWidget::open() {
+void DataWidget::open()
+{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(FrontendCommon::DataManager::GetDataDir(m_dir))));
}
+void DataWidget::upload()
+{
+ QtCommon::Content::ExportDataDir(m_dir);
+}
+
+void DataWidget::download()
+{
+ QtCommon::Content::ImportDataDir(m_dir, std::bind(&DataWidget::scan, this));
+}
+
void DataWidget::scan() {
ui->size->setText(tr("Calculating..."));
diff --git a/src/yuzu/data_dialog.h b/src/yuzu/data_dialog.h
index 9f367d6049..8a64659965 100644
--- a/src/yuzu/data_dialog.h
+++ b/src/yuzu/data_dialog.h
@@ -37,6 +37,9 @@ public:
public slots:
void clear();
void open();
+ void upload();
+ void download();
+
void scan();
private:
diff --git a/src/yuzu/data_widget.ui b/src/yuzu/data_widget.ui
index ed67078fa1..99010d8856 100644
--- a/src/yuzu/data_widget.ui
+++ b/src/yuzu/data_widget.ui
@@ -13,8 +13,8 @@
Form
-
- -
+
+
-
-
@@ -50,14 +50,8 @@
- -
-
-
- QLayout::SizeConstraint::SetFixedSize
-
-
- 25
-
+
-
+
-
@@ -69,7 +63,7 @@
52
- 42
+ 47
@@ -77,14 +71,10 @@
QPushButton {
- border-style: solid;
- border-width:1px;
- border-radius:25px;
- border-color: transparent;
- max-width:50px;
- max-height:40px;
- min-width:50px;
- min-height:40px;
+max-width: 50px;
+max-height: 45px;
+min-width: 50px;
+min-height: 45px;
}
@@ -92,8 +82,8 @@
- 24
- 24
+ 28
+ 28
@@ -109,7 +99,7 @@
52
- 42
+ 47
@@ -117,14 +107,10 @@
QPushButton {
- border-style: solid;
- border-width:1px;
- border-radius:25px;
- border-color: transparent;
- max-width:50px;
- max-height:40px;
- min-width:50px;
- min-height:40px;
+max-width: 50px;
+max-height: 45px;
+min-width: 50px;
+min-height: 45px;
}
@@ -132,8 +118,80 @@
- 24
- 24
+ 28
+ 28
+
+
+
+
+ -
+
+
+
+ 1
+ 1
+
+
+
+
+ 52
+ 47
+
+
+
+ Export all data in this directory. This may take a while!
+
+
+ QPushButton {
+max-width: 50px;
+max-height: 45px;
+min-width: 50px;
+min-height: 45px;
+}
+
+
+
+
+
+
+ 28
+ 28
+
+
+
+
+ -
+
+
+
+ 1
+ 1
+
+
+
+
+ 52
+ 47
+
+
+
+ Import data for this directory. This may take a while, and will delete ALL EXISTING DATA!
+
+
+ QPushButton {
+max-width: 50px;
+max-height: 45px;
+min-width: 50px;
+min-height: 45px;
+}
+
+
+
+
+
+
+ 28
+ 28
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b6dded447c..21c92c495f 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3939,6 +3939,10 @@ void GMainWindow::OnEdenDependencies() {
void GMainWindow::OnDataDialog() {
DataDialog dataDialog(this);
dataDialog.exec();
+
+ // refresh stuff in case it was cleared
+ OnGameListRefresh();
+
}
void GMainWindow::OnToggleFilterBar() {
@@ -4481,11 +4485,15 @@ void GMainWindow::SetFirmwareVersion() {
if (result.IsError() || !CheckFirmwarePresence()) {
LOG_INFO(Frontend, "Installed firmware: No firmware available");
+ ui->menu_Applets->setEnabled(false);
+ ui->menu_Create_Shortcuts->setEnabled(false);
firmware_label->setVisible(false);
return;
}
firmware_label->setVisible(true);
+ ui->menu_Applets->setEnabled(true);
+ ui->menu_Create_Shortcuts->setEnabled(true);
const std::string display_version(firmware_data.display_version.data());
const std::string display_title(firmware_data.display_title.data());
diff --git a/src/yuzu/multiplayer/chat_room.cpp b/src/yuzu/multiplayer/chat_room.cpp
index 4c2c41ea2a..53beda0f8e 100644
--- a/src/yuzu/multiplayer/chat_room.cpp
+++ b/src/yuzu/multiplayer/chat_room.cpp
@@ -16,7 +16,7 @@
#include
#include
#include
-#include
+#include
#include "common/logging/log.h"
#include "network/announce_multiplayer_session.h"
#include "ui_chat_room.h"
diff --git a/src/yuzu/multiplayer/client_room.cpp b/src/yuzu/multiplayer/client_room.cpp
index 93d6662c1e..4e995c044f 100644
--- a/src/yuzu/multiplayer/client_room.cpp
+++ b/src/yuzu/multiplayer/client_room.cpp
@@ -10,7 +10,7 @@
#include
#include
#include
-#include
+#include
#include "common/logging/log.h"
#include "network/announce_multiplayer_session.h"
#include "ui_client_room.h"
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index deac3b9e59..6291979fe6 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -9,7 +9,7 @@
#include
#include
#include
-#include
+#include
#include "common/settings.h"
#include "core/core.h"
#include "core/internal_network/network_interface.h"
diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp
index 4dd3958550..cc163a5420 100644
--- a/src/yuzu/multiplayer/host_room.cpp
+++ b/src/yuzu/multiplayer/host_room.cpp
@@ -12,7 +12,7 @@
#include
#include
#include
-#include
+#include
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 84723041df..e8daa0c6eb 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -6,7 +6,7 @@
#include
#include
-#include
+#include
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"