[desktop] feat: import/export data
All checks were successful
eden-license / license-header (pull_request) Successful in 30s

Currently not the ideal solution. Can't be cancelled due to JlCompress
currently lacking a method of cancellation, but for now this is a good
prototype.

Signed-off-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
crueter 2025-10-09 15:19:32 -04:00
parent 1c186d9280
commit f60e99915a
45 changed files with 380 additions and 129 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -18,7 +18,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/trash.png">icons/48x48/trash.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>

View file

@ -11,7 +11,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/trash.png">../colorful/icons/48x48/trash.png</file>
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>

View file

@ -14,7 +14,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/trash.png">icons/48x48/trash.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="48x48/star.png">icons/48x48/star.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View file

@ -13,7 +13,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/trash.png">../colorful/icons/48x48/trash.png</file>
<file alias="48x48/user-trash.png">../colorful/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../colorful/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../colorful/icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">../colorful/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

View file

@ -9,7 +9,9 @@
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/trash.png">icons/48x48/trash.png</file>
<file alias="48x48/user-trash.png">icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">icons/48x48/download.png</file>
<file alias="48x48/upload.png">icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>

View file

@ -6,6 +6,9 @@
<file alias="48x48/bad_folder.png">../qdarkstyle/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../qdarkstyle/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../qdarkstyle/icons/48x48/folder.png</file>
<file alias="48x48/user-trash.png">../qdarkstyle/icons/48x48/user-trash.png</file>
<file alias="48x48/download.png">../qdarkstyle/icons/48x48/download.png</file>
<file alias="48x48/upload.png">../qdarkstyle/icons/48x48/upload.png</file>
<file alias="48x48/no_avatar.png">../qdarkstyle/icons/48x48/no_avatar.png</file>
<file alias="48x48/list-add.png">../qdarkstyle/icons/48x48/list-add.png</file>
<file alias="48x48/sd_card.png">../qdarkstyle/icons/48x48/sd_card.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Before After
Before After

View file

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

View file

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

View file

@ -16,5 +16,4 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
AddJsonPackage(quazip)
# frozen
# TODO(crueter): Qt String Lookup
AddJsonPackage(frozen)

View file

@ -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 <QFuture>
#include <QtConcurrentRun>
#include <JlCompress.h>
#include <qfuturewatcher.h>
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 "
tr("Game Requires Firmware"),
tr("The game you are trying to launch requires firmware to boot or to get past the "
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
"dump and install firmware</a>, or press \"OK\" to launch anyways.",
"dump and install firmware</a>, 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,
QtCommon::Frontend::Information(
rootObject,
tr(successTitle),
tr(GetFirmwareInstallResultString(result))
.arg(QString::fromStdString(display_version)));
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<std::string> result =
ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
const std::vector<std::string> 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<void()> 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<bool> future = QtConcurrent::run([&]() {
return JlCompress::compressDir(zip_dump_location,
QString::fromStdString(dir),
true,
QDir::Hidden | QDir::Files | QDir::Dirs);
});
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
QObject::connect(watcher, &QFutureWatcher<bool>::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<void()> 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<bool> future = QtConcurrent::run([=]() {
return !JlCompress::extractDir(zip_dump_location,
QString::fromStdString(dir)).empty();
});
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
QObject::connect(watcher, &QFutureWatcher<bool>::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

View file

@ -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<void()> callback = {});
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, std::function<void()> callback = {});
// Profiles //
void FixProfiles();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 <QButtonGroup>
#include <QMessageBox>
#include <QPushButton>
#include <QtConcurrent/qtconcurrentrun.h>
#include <qtconcurrentrun.h>
#include "common/logging/log.h"
#include "ui_compatdb.h"
#include "yuzu/compatdb.h"

View file

@ -14,7 +14,7 @@
#include <QRegExpValidator>
#endif
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/settings.h"
#include "ui_configure_web.h"
#include "qt_common/uisettings.h"

View file

@ -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 <QDesktopServices>
#include <QFileDialog>
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun>
#include <QProgressDialog>
#include <QtConcurrentRun>
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..."));

View file

@ -37,6 +37,9 @@ public:
public slots:
void clear();
void open();
void upload();
void download();
void scan();
private:

View file

@ -13,8 +13,8 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2">
<item>
<widget class="QLabel" name="tooltip">
@ -50,14 +50,8 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
</property>
<property name="bottomMargin">
<number>25</number>
</property>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="open">
<property name="sizePolicy">
@ -69,7 +63,7 @@
<property name="minimumSize">
<size>
<width>52</width>
<height>42</height>
<height>47</height>
</size>
</property>
<property name="toolTip">
@ -77,14 +71,10 @@
</property>
<property name="styleSheet">
<string notr="true">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;
}</string>
</property>
<property name="text">
@ -92,8 +82,8 @@
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
<width>28</width>
<height>28</height>
</size>
</property>
</widget>
@ -109,7 +99,7 @@
<property name="minimumSize">
<size>
<width>52</width>
<height>42</height>
<height>47</height>
</size>
</property>
<property name="toolTip">
@ -117,14 +107,10 @@
</property>
<property name="styleSheet">
<string notr="true">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;
}</string>
</property>
<property name="text">
@ -132,8 +118,80 @@
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
<width>28</width>
<height>28</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="upload">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>52</width>
<height>47</height>
</size>
</property>
<property name="toolTip">
<string>Export all data in this directory. This may take a while!</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
max-width: 50px;
max-height: 45px;
min-width: 50px;
min-height: 45px;
}</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="download">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>52</width>
<height>47</height>
</size>
</property>
<property name="toolTip">
<string>Import data for this directory. This may take a while, and will delete ALL EXISTING DATA!</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
max-width: 50px;
max-height: 45px;
min-width: 50px;
min-height: 45px;
}</string>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>28</width>
<height>28</height>
</size>
</property>
</widget>

View file

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

View file

@ -16,7 +16,7 @@
#include <QMetaType>
#include <QTime>
#include <QUrl>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/logging/log.h"
#include "network/announce_multiplayer_session.h"
#include "ui_chat_room.h"

View file

@ -10,7 +10,7 @@
#include <QLocale>
#include <QMetaType>
#include <QTime>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/logging/log.h"
#include "network/announce_multiplayer_session.h"
#include "ui_client_room.h"

View file

@ -9,7 +9,7 @@
#include <QIntValidator>
#include <QRegularExpressionValidator>
#include <QString>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/settings.h"
#include "core/core.h"
#include "core/internal_network/network_interface.h"

View file

@ -12,7 +12,7 @@
#include <QMessageBox>
#include <QMetaType>
#include <QTime>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"

View file

@ -6,7 +6,7 @@
#include <QInputDialog>
#include <QList>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrentRun>
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"