[qt] fix translations (#2773)

Linguist strongly dislikes lookup tables of this sort due to the fact
that it looks for tr(), qsTr(), etc. when determining what strings need
translations. However, it does provide QT_TR_NOOP which marks the
string for translation *without* running the translation, which is designed
to allow for static or constexpr lookup tables. So let's use that.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2773
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
This commit is contained in:
crueter 2025-10-18 22:19:06 +02:00
parent b330117a14
commit a53823646c
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
11 changed files with 234 additions and 193 deletions

View file

@ -15,21 +15,12 @@
#include <core/hle/service/am/frontend/applet_mii_edit.h> #include <core/hle/service/am/frontend/applet_mii_edit.h>
#include <string> #include <string>
#include "core/hle/result.h"
#include "core/hle/service/set/settings_types.h" #include "core/hle/service/set/settings_types.h"
#include "core/hle/service/set/system_settings_server.h" #include "core/hle/service/set/system_settings_server.h"
#include "core/hle/result.h"
namespace FirmwareManager { namespace FirmwareManager {
static constexpr std::array<const char *, 5> KEY_INSTALL_RESULT_STRINGS = {
"Decryption Keys were successfully installed",
"Unable to read key directory, aborting",
"One or more keys failed to copy.",
"Verify your keys file has a .keys extension and try again.",
"Decryption Keys failed to initialize. Check that your dumping tools are up to date and "
"re-dump keys.",
};
static constexpr std::array<u64, 1> FIRMWARE_REQUIRED_GAMES = { static constexpr std::array<u64, 1> FIRMWARE_REQUIRED_GAMES = {
0x0100152000022000ULL, // MK8DX 0x0100152000022000ULL, // MK8DX
}; };
@ -50,16 +41,6 @@ enum KeyInstallResult {
*/ */
KeyInstallResult InstallKeys(std::string location, std::string expected_extension); KeyInstallResult InstallKeys(std::string location, std::string expected_extension);
/**
* \brief Get a string representation of a result from InstallKeys.
* \param result The result code.
* \return A string representation of the passed result code.
*/
inline constexpr const char *GetKeyInstallResultString(KeyInstallResult result)
{
return KEY_INSTALL_RESULT_STRINGS.at(static_cast<std::size_t>(result));
}
/** /**
* \brief Check if the specified program requires firmware to run properly. * \brief Check if the specified program requires firmware to run properly.
* It is the responsibility of the frontend to properly expose this to the user. * It is the responsibility of the frontend to properly expose this to the user.

View file

@ -66,7 +66,8 @@ target_compile_definitions(qt_common PUBLIC
add_subdirectory(externals) add_subdirectory(externals)
find_package(frozen REQUIRED) # pass targets
find_package(frozen)
target_link_libraries(qt_common PRIVATE core Qt6::Core Qt6::Concurrent 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-headers) target_link_libraries(qt_common PUBLIC frozen::frozen-headers)

View file

@ -13,7 +13,6 @@
"package": "frozen", "package": "frozen",
"repo": "serge-sans-paille/frozen", "repo": "serge-sans-paille/frozen",
"sha": "61dce5ae18", "sha": "61dce5ae18",
"hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d", "hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d"
"bundled": true
} }
} }

View file

@ -3,11 +3,10 @@
#pragma once #pragma once
#include <QObject>
#include <QString> #include <QString>
#include <frozen/string.h> #include "frozen/map.h"
#include <frozen/unordered_map.h> #include "frozen/string.h"
#include <qobjectdefs.h>
#include <qtmetamacros.h>
namespace QtCommon::StringLookup { namespace QtCommon::StringLookup {
@ -20,19 +19,85 @@ enum StringKey {
UserNandTooltip, UserNandTooltip,
SysNandTooltip, SysNandTooltip,
ModsTooltip, ModsTooltip,
// Key install results
KeyInstallSuccess,
KeyInstallInvalidDir,
KeyInstallErrorFailedCopy,
KeyInstallErrorWrongFilename,
KeyInstallErrorFailedInit,
// Firmware install results
FwInstallSuccess,
FwInstallNoNCAs,
FwInstallFailedDelete,
FwInstallFailedCopy,
FwInstallFailedCorrupted,
// user data migrator
MigrationPromptPrefix,
MigrationPrompt,
MigrationTooltipClearShader,
MigrationTooltipKeepOld,
MigrationTooltipClearOld,
MigrationTooltipLinkOld,
}; };
static constexpr const frozen::unordered_map<StringKey, frozen::string, 5> strings = { static const frozen::map<StringKey, frozen::string, 21> strings = {
{SavesTooltip, "Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING!"}, {SavesTooltip,
{ShadersTooltip, "Contains Vulkan and OpenGL pipeline caches. Generally safe to remove."}, QT_TR_NOOP("Contains game save data. DO NOT REMOVE UNLESS YOU KNOW WHAT YOU'RE DOING!")},
{UserNandTooltip, "Contains updates and DLC for games."}, {ShadersTooltip,
{SysNandTooltip, "Contains firmware and applet data."}, QT_TR_NOOP("Contains Vulkan and OpenGL pipeline caches. Generally safe to remove.")},
{ModsTooltip, "Contains game mods, patches, and cheats."}, {UserNandTooltip, QT_TR_NOOP("Contains updates and DLC for games.")},
{SysNandTooltip, QT_TR_NOOP("Contains firmware and applet data.")},
{ModsTooltip, QT_TR_NOOP("Contains game mods, patches, and cheats.")},
// Key install
{KeyInstallSuccess, QT_TR_NOOP("Decryption Keys were successfully installed")},
{KeyInstallInvalidDir, QT_TR_NOOP("Unable to read key directory, aborting")},
{KeyInstallErrorFailedCopy, QT_TR_NOOP("One or more keys failed to copy.")},
{KeyInstallErrorWrongFilename,
QT_TR_NOOP("Verify your keys file has a .keys extension and try again.")},
{KeyInstallErrorFailedInit,
QT_TR_NOOP(
"Decryption Keys failed to initialize. Check that your dumping tools are up to date and "
"re-dump keys.")},
// fw install
{FwInstallSuccess, QT_TR_NOOP("Successfully installed firmware version %1")},
{FwInstallNoNCAs, QT_TR_NOOP("Unable to locate potential firmware NCA files")},
{FwInstallFailedDelete, QT_TR_NOOP("Failed to delete one or more firmware files.")},
{FwInstallFailedCopy, QT_TR_NOOP("One or more firmware files failed to copy into NAND.")},
{FwInstallFailedCorrupted,
QT_TR_NOOP(
"Firmware installation cancelled, firmware may be in a bad state or corrupted. Restart "
"Eden or re-install firmware.")},
// migrator
{MigrationPromptPrefix, QT_TR_NOOP("Eden has detected user data for the following emulators:")},
{MigrationPrompt,
QT_TR_NOOP("Would you like to migrate your data for use in Eden?\n"
"Select the corresponding button to migrate data from that emulator.\n"
"This may take a while.")},
{MigrationTooltipClearShader,
QT_TR_NOOP("Clearing shader cache is recommended for all "
"users.\nDo not uncheck unless you know what "
"you're doing.")},
{MigrationTooltipKeepOld,
QT_TR_NOOP("Keeps the old data directory. This is recommended if you aren't\n"
"space-constrained and want to keep separate data for the old emulator.")},
{MigrationTooltipClearOld,
QT_TR_NOOP("Deletes the old data directory.\nThis is recommended on "
"devices with space constraints.")},
{MigrationTooltipLinkOld,
QT_TR_NOOP("Creates a filesystem link between the old directory and Eden directory.\n"
"This is recommended if you want to share data between emulators.")},
}; };
static inline const QString Lookup(StringKey key) static inline const QString Lookup(StringKey key)
{ {
return QString::fromStdString(strings.at(key).data()); return QObject::tr(strings.at(key).data());
} }
} } // namespace QtCommon::StringLookup

View file

@ -10,15 +10,15 @@
#include "frontend_common/data_manager.h" #include "frontend_common/data_manager.h"
#include "frontend_common/firmware_manager.h" #include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h"
#include "compress.h" #include "compress.h"
#include "qt_common/abstract/qt_progress_dialog.h"
#include "qt_common/abstract/qt_frontend_util.h" #include "qt_common/abstract/qt_frontend_util.h"
#include "qt_common/abstract/qt_progress_dialog.h"
#include "qt_common/qt_common.h"
#include <QFuture> #include <QFuture>
#include <QFutureWatcher>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <JlCompress.h> #include <JlCompress.h>
#include <QFutureWatcher>
namespace QtCommon::Content { namespace QtCommon::Content {
@ -60,15 +60,15 @@ void InstallFirmware(const QString& location, bool recursive)
return progress.wasCanceled(); return progress.wasCanceled();
}; };
static constexpr const char* failedTitle = "Firmware Install Failed"; QString failedTitle = tr("Firmware Install Failed");
static constexpr const char* successTitle = "Firmware Install Succeeded"; QString successTitle = tr("Firmware Install Succeeded");
QMessageBox::Icon icon; QMessageBox::Icon icon;
FirmwareInstallResult result; FirmwareInstallResult result;
const auto ShowMessage = [&]() { const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon, QtCommon::Frontend::ShowMessage(icon,
tr(failedTitle), failedTitle,
tr(GetFirmwareInstallResultString(result))); GetFirmwareInstallResultString(result));
}; };
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
@ -188,10 +188,9 @@ void InstallFirmware(const QString& location, bool recursive)
const std::string display_version(firmware_data.display_version.data()); const std::string display_version(firmware_data.display_version.data());
result = FirmwareInstallResult::Success; result = FirmwareInstallResult::Success;
QtCommon::Frontend::Information( QtCommon::Frontend::Information(successTitle,
rootObject, GetFirmwareInstallResultString(result).arg(
tr(successTitle), QString::fromStdString(display_version)));
tr(GetFirmwareInstallResultString(result)).arg(QString::fromStdString(display_version)));
} }
QString UnzipFirmwareToTmp(const QString& location) QString UnzipFirmwareToTmp(const QString& location)
@ -277,14 +276,13 @@ void InstallKeys()
system->GetFileSystemController().CreateFactories(*QtCommon::vfs); system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
const QString resMsg = GetKeyInstallResultString(result);
switch (result) { switch (result) {
case FirmwareManager::KeyInstallResult::Success: case FirmwareManager::KeyInstallResult::Success:
QtCommon::Frontend::Information(tr("Decryption Keys install succeeded"), QtCommon::Frontend::Information(tr("Decryption Keys install succeeded"), resMsg);
tr("Decryption Keys were successfully installed"));
break; break;
default: default:
QtCommon::Frontend::Critical(tr("Decryption Keys install failed"), QtCommon::Frontend::Critical(tr("Decryption Keys install failed"), resMsg);
tr(FirmwareManager::GetKeyInstallResultString(result)));
break; break;
} }
} }
@ -296,7 +294,7 @@ void VerifyInstalledContents()
tr("Cancel"), tr("Cancel"),
0, 0,
100, 100,
QtCommon::rootObject); rootObject);
progress.setWindowModality(Qt::WindowModal); progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100); progress.setMinimumDuration(100);
progress.setAutoClose(false); progress.setAutoClose(false);
@ -415,7 +413,9 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir,
QGuiApplication::processEvents(); QGuiApplication::processEvents();
auto progress_callback = [=](size_t total_size, size_t processed_size) { auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod(progress, "setValue", Qt::DirectConnection, QMetaObject::invokeMethod(progress,
"setValue",
Qt::DirectConnection,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size))); Q_ARG(int, static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled(); return !progress->wasCanceled();
}; };
@ -499,8 +499,11 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir,
QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() { QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
auto progress_callback = [=](size_t total_size, size_t processed_size) { auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod(progress, "setValue", Qt::DirectConnection, QMetaObject::invokeMethod(progress,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size))); "setValue",
Qt::DirectConnection,
Q_ARG(int,
static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled(); return !progress->wasCanceled();
}; };

View file

@ -7,33 +7,36 @@
#include <QObject> #include <QObject>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend_common/data_manager.h" #include "frontend_common/data_manager.h"
#include "frontend_common/firmware_manager.h"
#include "qt_common/qt_string_lookup.h"
namespace QtCommon::Content { namespace QtCommon::Content {
// //
bool CheckGameFirmware(u64 program_id, QObject *parent); bool CheckGameFirmware(u64 program_id, QObject *parent);
static constexpr std::array<const char *, 6> FIRMWARE_RESULTS
= {"Successfully installed firmware version %1",
"",
"Unable to locate potential firmware NCA files",
"Failed to delete one or more firmware files.",
"One or more firmware files failed to copy into NAND.",
"Firmware installation cancelled, firmware may be in a bad state or corrupted."
"Restart Eden or re-install firmware."};
enum class FirmwareInstallResult { enum class FirmwareInstallResult {
Success, Success,
NoOp,
NoNCAs, NoNCAs,
FailedDelete, FailedDelete,
FailedCopy, FailedCopy,
FailedCorrupted, FailedCorrupted,
}; };
inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result) inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result)
{ {
return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result)); return QtCommon::StringLookup::Lookup(static_cast<StringLookup::StringKey>((int) result + (int) QtCommon::StringLookup::FwInstallSuccess));
}
/**
* \brief Get a string representation of a result from InstallKeys.
* \param result The result code.
* \return A string representation of the passed result code.
*/
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));
} }
void InstallFirmware(const QString &location, bool recursive); void InstallFirmware(const QString &location, bool recursive);

View file

@ -8,9 +8,9 @@
#include "core/file_sys/savedata_factory.h" #include "core/file_sys/savedata_factory.h"
#include "core/hle/service/am/am_types.h" #include "core/hle/service/am/am_types.h"
#include "frontend_common/content_manager.h" #include "frontend_common/content_manager.h"
#include "qt_common/qt_common.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/abstract/qt_frontend_util.h" #include "qt_common/abstract/qt_frontend_util.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
#include "yuzu/util/util.h" #include "yuzu/util/util.h"
#include <QDesktopServices> #include <QDesktopServices>
@ -166,7 +166,8 @@ bool MakeShortcutIcoPath(const u64 program_id,
void OpenEdenFolder(const Common::FS::EdenPath& path) void OpenEdenFolder(const Common::FS::EdenPath& path)
{ {
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path)))); QDesktopServices::openUrl(
QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path))));
} }
void OpenRootDataFolder() void OpenRootDataFolder()
@ -181,7 +182,8 @@ void OpenNANDFolder()
void OpenSaveFolder() void OpenSaveFolder()
{ {
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000"; const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string()))); QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string())));
} }
@ -220,12 +222,11 @@ void RemoveBaseContent(u64 program_id, InstalledEntryType type)
const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(), const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(),
program_id); program_id);
if (res) { if (res) {
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed the installed base game.")); tr("Successfully removed the installed base game."));
} else { } else {
QtCommon::Frontend::Warning( QtCommon::Frontend::Warning(
rootObject,
GetGameListErrorRemoving(type), GetGameListErrorRemoving(type),
tr("The base game is not installed in the NAND and cannot be removed.")); tr("The base game is not installed in the NAND and cannot be removed."));
} }
@ -235,12 +236,10 @@ void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
{ {
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id); const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
if (res) { if (res) {
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed the installed update.")); tr("Successfully removed the installed update."));
} else { } else {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(GetGameListErrorRemoving(type),
GetGameListErrorRemoving(type),
tr("There is no update installed for this title.")); tr("There is no update installed for this title."));
} }
} }
@ -249,14 +248,12 @@ void RemoveAddOnContent(u64 program_id, InstalledEntryType type)
{ {
const size_t count = ContentManager::RemoveAllDLC(*system, program_id); const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
if (count == 0) { if (count == 0) {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(GetGameListErrorRemoving(type),
GetGameListErrorRemoving(type),
tr("There are no DLCs installed for this title.")); tr("There are no DLCs installed for this title."));
return; return;
} }
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed %1 installed DLC.").arg(count)); tr("Successfully removed %1 installed DLC.").arg(count));
} }
@ -279,18 +276,15 @@ void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
const auto target_file = shader_cache_folder_path / target_file_name; const auto target_file = shader_cache_folder_path / target_file_name;
if (!Common::FS::Exists(target_file)) { if (!Common::FS::Exists(target_file)) {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Transferable Shader Cache"),
tr("Error Removing Transferable Shader Cache"),
tr("A shader cache for this title does not exist.")); tr("A shader cache for this title does not exist."));
return; return;
} }
if (Common::FS::RemoveFile(target_file)) { if (Common::FS::RemoveFile(target_file)) {
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed the transferable shader cache.")); tr("Successfully removed the transferable shader cache."));
} else { } else {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Transferable Shader Cache"),
tr("Error Removing Transferable Shader Cache"),
tr("Failed to remove the transferable shader cache.")); tr("Failed to remove the transferable shader cache."));
} }
} }
@ -307,8 +301,7 @@ void RemoveVulkanDriverPipelineCache(u64 program_id)
return; return;
} }
if (!Common::FS::RemoveFile(target_file)) { if (!Common::FS::RemoveFile(target_file)) {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Vulkan Driver Pipeline Cache"),
tr("Error Removing Vulkan Driver Pipeline Cache"),
tr("Failed to remove the driver pipeline cache.")); tr("Failed to remove the driver pipeline cache."));
} }
} }
@ -319,18 +312,16 @@ void RemoveAllTransferableShaderCaches(u64 program_id)
const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
if (!Common::FS::Exists(program_shader_cache_dir)) { if (!Common::FS::Exists(program_shader_cache_dir)) {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Transferable Shader Caches"),
tr("Error Removing Transferable Shader Caches"),
tr("A shader cache for this title does not exist.")); tr("A shader cache for this title does not exist."));
return; return;
} }
if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) { if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) {
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed the transferable shader caches.")); tr("Successfully removed the transferable shader caches."));
} else { } else {
QtCommon::Frontend::Warning( QtCommon::Frontend::Warning(
rootObject,
tr("Error Removing Transferable Shader Caches"), tr("Error Removing Transferable Shader Caches"),
tr("Failed to remove the transferable shader cache directory.")); tr("Failed to remove the transferable shader cache directory."));
} }
@ -339,27 +330,23 @@ void RemoveAllTransferableShaderCaches(u64 program_id)
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) void RemoveCustomConfiguration(u64 program_id, const std::string& game_path)
{ {
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
const auto config_file_name = program_id == 0 const auto config_file_name
? Common::FS::PathToUTF8String(file_path.filename()) = program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
.append(".ini")
: fmt::format("{:016X}.ini", program_id); : fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir)
/ "custom" / config_file_name; / "custom" / config_file_name;
if (!Common::FS::Exists(custom_config_file_path)) { if (!Common::FS::Exists(custom_config_file_path)) {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Custom Configuration"),
tr("Error Removing Custom Configuration"),
tr("A custom configuration for this title does not exist.")); tr("A custom configuration for this title does not exist."));
return; return;
} }
if (Common::FS::RemoveFile(custom_config_file_path)) { if (Common::FS::RemoveFile(custom_config_file_path)) {
QtCommon::Frontend::Information(rootObject, QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully Removed"),
tr("Successfully removed the custom game configuration.")); tr("Successfully removed the custom game configuration."));
} else { } else {
QtCommon::Frontend::Warning(rootObject, QtCommon::Frontend::Warning(tr("Error Removing Custom Configuration"),
tr("Error Removing Custom Configuration"),
tr("Failed to remove the custom game configuration.")); tr("Failed to remove the custom game configuration."));
} }
} }
@ -392,16 +379,21 @@ void ResetMetadata(bool show_message)
if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir)
/ "game_list/")) { / "game_list/")) {
if (show_message) QtCommon::Frontend::Warning(rootObject, title, tr("The metadata cache is already empty.")); if (show_message)
QtCommon::Frontend::Warning(rootObject,
title,
tr("The metadata cache is already empty."));
} else if (Common::FS::RemoveDirRecursively( } else if (Common::FS::RemoveDirRecursively(
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) { Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
if (show_message) QtCommon::Frontend::Information(rootObject, if (show_message)
QtCommon::Frontend::Information(rootObject,
title, title,
tr("The operation completed successfully.")); tr("The operation completed successfully."));
UISettings::values.is_game_list_reload_pending.exchange(true); UISettings::values.is_game_list_reload_pending.exchange(true);
} else { } else {
if (show_message) QtCommon::Frontend::Warning( if (show_message)
rootObject, QtCommon::Frontend::Warning(
title, title,
tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
} }
@ -463,11 +455,13 @@ void CreateShortcut(const std::string& game_path,
return; return;
} }
const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(), const FileSys::PatchManager pm{program_id,
QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider()}; QtCommon::system->GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
const auto loader = const auto loader = Loader::GetLoader(*QtCommon::system,
Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read)); QtCommon::vfs->OpenFile(game_path,
FileSys::OpenMode::Read));
std::string game_title{game_title_}; std::string game_title{game_title_};
@ -498,8 +492,8 @@ void CreateShortcut(const std::string& game_path,
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
} }
QImage icon_data = QImage icon_data = QImage::fromData(icon_image_file.data(),
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path; std::filesystem::path out_icon_path;
if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) { if (!SaveIconToFile(out_icon_path, icon_data)) {
@ -532,33 +526,39 @@ void CreateShortcut(const std::string& game_path,
const std::string categories = "Game;Emulator;Qt;"; const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;"; const std::string keywords = "Switch;Nintendo;";
if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command, if (QtCommon::Game::CreateShortcutLink(shortcut_path,
arguments, categories, keywords, game_title)) { comment,
CreateShortcutMessagesGUI(ShortcutMessages::Success, out_icon_path,
qgame_title); command,
arguments,
categories,
keywords,
game_title)) {
CreateShortcutMessagesGUI(ShortcutMessages::Success, qgame_title);
return; return;
} }
CreateShortcutMessagesGUI(ShortcutMessages::Failed, CreateShortcutMessagesGUI(ShortcutMessages::Failed, qgame_title);
qgame_title);
} }
// TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string literal // TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string literal
std::string GetShortcutPath(ShortcutTarget target) { std::string GetShortcutPath(ShortcutTarget target)
{
{ {
std::string shortcut_path{}; std::string shortcut_path{};
if (target == ShortcutTarget::Desktop) { if (target == ShortcutTarget::Desktop) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) shortcut_path
.toStdString(); = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == ShortcutTarget::Applications) { } else if (target == ShortcutTarget::Applications) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) shortcut_path
.toStdString(); = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
} }
return shortcut_path; return shortcut_path;
} }
} }
void CreateHomeMenuShortcut(ShortcutTarget target) { void CreateHomeMenuShortcut(ShortcutTarget target)
{
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch); constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {

View file

@ -406,7 +406,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
#define MIGRATE_DIR(type) \ #define MIGRATE_DIR(type) \
std::string type##path = Common::FS::GetEdenPathString(Common::FS::EdenPath::type##Dir); \ std::string type##path = Common::FS::GetEdenPathString(Common::FS::EdenPath::type##Dir); \
if (type##path.starts_with(user_data_migrator.selected_emu.get_user_dir())) { \ if (type##path.starts_with(user_data_migrator.selected_emu.get_user_dir())) { \
boost::replace_all(type##path, user_data_migrator.selected_emu.lower_name(), "eden"); \ boost::replace_all(type##path, user_data_migrator.selected_emu.lower_name().toStdString(), "eden"); \
Common::FS::SetEdenPath(Common::FS::EdenPath::type##Dir, type##path); \ Common::FS::SetEdenPath(Common::FS::EdenPath::type##Dir, type##path); \
} }

View file

@ -68,7 +68,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(tr(selected_legacy_emu.name), .arg(selected_legacy_emu.name(),
QString::fromStdString(eden_dir.string()), QString::fromStdString(eden_dir.string()),
QString::fromStdString(config_dir.string()), QString::fromStdString(config_dir.string()),
QString::fromStdString(cache_dir.string()))); QString::fromStdString(cache_dir.string())));

View file

@ -10,7 +10,7 @@
using namespace Common::FS; using namespace Common::FS;
typedef struct Emulator { typedef struct Emulator {
const char *name; const char *m_name;
LegacyPath e_user_dir; LegacyPath e_user_dir;
LegacyPath e_config_dir; LegacyPath e_config_dir;
@ -28,23 +28,19 @@ typedef struct Emulator {
return Common::FS::GetLegacyPath(e_cache_dir).string(); return Common::FS::GetLegacyPath(e_cache_dir).string();
} }
const std::string lower_name() const { const QString name() const { return QObject::tr(m_name);
std::string lower_name{name}; }
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
[](unsigned char c){ return std::tolower(c); });
return lower_name; const QString lower_name() const { return name().toLower();
} }
} Emulator; } Emulator;
#define EMU(name) Emulator{#name, name##Dir, name##ConfigDir, name##CacheDir}
static constexpr std::array<Emulator, 4> legacy_emus = { static constexpr std::array<Emulator, 4> legacy_emus = {
EMU(Citron), Emulator{QT_TR_NOOP("Citron"), CitronDir, CitronConfigDir, CitronCacheDir},
EMU(Sudachi), Emulator{QT_TR_NOOP("Sudachi"), SudachiDir, SudachiConfigDir, SudachiCacheDir},
EMU(Suyu), Emulator{QT_TR_NOOP("Suyu"), SuyuDir, SuyuConfigDir, SuyuCacheDir},
EMU(Yuzu), Emulator{QT_TR_NOOP("Yuzu"), YuzuDir, YuzuConfigDir, YuzuCacheDir},
}; };
#undef EMU
class MigrationWorker : public QObject class MigrationWorker : public QObject
{ {

View file

@ -11,12 +11,13 @@
#include <QString> #include <QString>
#include <QTranslator> #include <QTranslator>
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "../yuzu/migration_dialog.h" #include "qt_common/qt_string_lookup.h"
#include "yuzu/migration_dialog.h"
// Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263 // Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263
#include <QGuiApplication>
#include <QButtonGroup> #include <QButtonGroup>
#include <QCheckBox> #include <QCheckBox>
#include <QGuiApplication>
#include <QProgressDialog> #include <QProgressDialog>
#include <QRadioButton> #include <QRadioButton>
#include <QThread> #include <QThread>
@ -41,30 +42,19 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
namespace fs = std::filesystem; namespace fs = std::filesystem;
// define strings here for easy access // define strings here for easy access
static constexpr const char *prompt_prefix_text
= "Eden has detected user data for the following emulators:";
static constexpr const char *migration_prompt_message QString prompt_prefix_text = QtCommon::StringLookup::Lookup(
= "Would you like to migrate your data for use in Eden?\n" QtCommon::StringLookup::MigrationPromptPrefix);
"Select the corresponding button to migrate data from that emulator.\n" QString migration_prompt_message = QtCommon::StringLookup::Lookup(
"This may take a while."; QtCommon::StringLookup::MigrationPrompt);
QString clear_shader_tooltip = QtCommon::StringLookup::Lookup(
static constexpr const char *clear_shader_tooltip QtCommon::StringLookup::MigrationTooltipClearShader);
= "Clearing shader cache is recommended for all " QString keep_old_data_tooltip = QtCommon::StringLookup::Lookup(
"users.\nDo not uncheck unless you know what " QtCommon::StringLookup::MigrationTooltipKeepOld);
"you're doing."; QString clear_old_data_tooltip = QtCommon::StringLookup::Lookup(
QtCommon::StringLookup::MigrationTooltipClearOld);
static constexpr const char *keep_old_data_tooltip QString link_old_dir_tooltip = QtCommon::StringLookup::Lookup(
= "Keeps the old data directory. This is recommended if you aren't\n" QtCommon::StringLookup::MigrationTooltipLinkOld);
"space-constrained and want to keep separate data for the old emulator.";
static constexpr const char *clear_old_data_tooltip
= "Deletes the old data directory.\nThis is recommended on "
"devices with space constraints.";
static constexpr const char *link_old_dir_tooltip
= "Creates a filesystem link between the old directory and Eden directory.\n"
"This is recommended if you want to share data between emulators.";
// actual migration code // actual migration code
@ -78,12 +68,12 @@ 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(QObject::tr(text)); \ name->setText(text); \
name->setToolTip(QObject::tr(tooltip)); \ name->setToolTip(tooltip); \
name->setChecked(checkState); \ name->setChecked(checkState); \
migration_prompt.addBox(name); migration_prompt.addBox(name);
BUTTON(QCheckBox, clear_shaders, "Clear Shader Cache", clear_shader_tooltip, true) BUTTON(QCheckBox, clear_shaders, QObject::tr("Clear Shader Cache"), clear_shader_tooltip, true)
u32 id = 0; u32 id = 0;
@ -91,9 +81,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, "Keep Old Data", keep_old_data_tooltip, true) RADIO(keep_old, QObject::tr("Keep Old Data"), keep_old_data_tooltip, true)
RADIO(clear_old, "Clear Old Data", clear_old_data_tooltip, false) RADIO(clear_old, QObject::tr("Clear Old Data"), clear_old_data_tooltip, false)
RADIO(link_old, "Link Old Directory", link_old_dir_tooltip, false) RADIO(link_old, QObject::tr("Link Old Directory"), link_old_dir_tooltip, false)
#undef RADIO #undef RADIO
#undef BUTTON #undef BUTTON
@ -111,20 +101,20 @@ void UserDataMigrator::ShowMigrationPrompt(QMainWindow *main_window)
// makes my life easier // makes my life easier
qRegisterMetaType<Emulator>(); qRegisterMetaType<Emulator>();
QString prompt_text = QObject::tr(prompt_prefix_text); QString prompt_text = prompt_prefix_text;
// natural language processing is a nightmare // natural language processing is a nightmare
for (const Emulator &emu : found) { for (const Emulator &emu : found) {
prompt_text.append(QStringLiteral("\n- %1").arg(QObject::tr(emu.name))); prompt_text = prompt_text % QStringLiteral("\n ") % emu.name();
QAbstractButton *button = migration_prompt.addButton(QObject::tr(emu.name)); QAbstractButton *button = migration_prompt.addButton(emu.name());
// This is cursed, but it's actually the most efficient way by a mile // This is cursed, but it's actually the most efficient way by a mile
button->setProperty("emulator", QVariant::fromValue(emu)); button->setProperty("emulator", QVariant::fromValue(emu));
} }
prompt_text.append(QObject::tr("\n\n")); prompt_text.append(QObject::tr("\n\n"));
prompt_text.append(QObject::tr(migration_prompt_message)); prompt_text = prompt_text % QStringLiteral("\n\n") % migration_prompt_message;
migration_prompt.setText(prompt_text); migration_prompt.setText(prompt_text);
migration_prompt.addButton(QObject::tr("No"), true); migration_prompt.addButton(QObject::tr("No"), true);
@ -191,7 +181,10 @@ void UserDataMigrator::MigrateUserData(QMainWindow *main_window,
thread->connect(thread, &QThread::started, worker, &MigrationWorker::process); thread->connect(thread, &QThread::started, worker, &MigrationWorker::process);
thread->connect(worker, &MigrationWorker::finished, progress, [=, this](const QString &success_text, const std::string &path) { thread->connect(worker,
&MigrationWorker::finished,
progress,
[=, this](const QString &success_text, const std::string &path) {
progress->close(); progress->close();
QMessageBox::information(main_window, QMessageBox::information(main_window,
QObject::tr("Migration"), QObject::tr("Migration"),