Compare commits

...
Sign in to create a new pull request.

10 commits

Author SHA1 Message Date
490758c2ce
Fix license headers 2025-07-23 21:12:22 -04:00
aafd2b7437
[qt] frontend abstraction and message box early handling
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 21:11:27 -04:00
0f28ecbe71 thank you Qt Creator, very cool
Signed-off-by: crueter <crueter@crueter.xyz>
2025-07-23 19:21:35 +02:00
b080a8192b Fix license headers 2025-07-23 19:21:35 +02:00
3198770ffb more common funcs
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
1da87f01f7 explicitly check write status for dir
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
6c48a82c83 debug: log user/save id
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
6b13fd566d Fix license headers
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
916d01242e move fw install
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
8d0b35e75d qt_common init
Signed-off-by: crueter <crueter@eden-emu.dev>
2025-07-23 19:21:35 +02:00
65 changed files with 939 additions and 505 deletions

View file

@ -4,16 +4,22 @@ HEADER="$(cat "$PWD/.ci/license/header.txt")"
echo "Getting branch changes"
BRANCH=`git rev-parse --abbrev-ref HEAD`
COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
# BRANCH=`git rev-parse --abbrev-ref HEAD`
# COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
# FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
BASE=`git merge-base master HEAD`
FILES=`git diff --name-only $BASE`
#FILES=$(git diff --name-only master)
echo $FILES
echo "Done"
for file in $FILES; do
[ -f "$file" ] || continue
EXTENSION="${file##*.}"
case "$EXTENSION" in
kts|kt|cpp|h)

View file

@ -212,6 +212,8 @@ if (YUZU_ROOM_STANDALONE)
endif()
if (ENABLE_QT)
add_definitions(-DYUZU_QT_WIDGETS)
add_subdirectory(qt_common)
add_subdirectory(yuzu)
endif()

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -126,6 +129,10 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
std::string out = GetSaveDataSpaceIdPath(space);
LOG_INFO(Common_Filesystem, "Save ID: {:016X}", save_id);
LOG_INFO(Common_Filesystem, "User ID[1]: {:016X}", user_id[1]);
LOG_INFO(Common_Filesystem, "User ID[0]: {:016X}", user_id[0]);
switch (type) {
case SaveDataType::System:
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
find_package(Qt6 REQUIRED COMPONENTS Core)
add_library(qt_common STATIC
qt_common.h
qt_common.cpp
uisettings.cpp
uisettings.h
qt_config.cpp
qt_config.h
shared_translation.cpp
shared_translation.h
qt_path_util.h qt_path_util.cpp
qt_game_util.h qt_game_util.cpp
qt_frontend_util.h qt_frontend_util.cpp
)
create_target_directory_groups(qt_common)
target_link_libraries(qt_common PUBLIC core Qt6::Widgets SimpleIni::SimpleIni QuaZip::QuaZip)
target_link_libraries(qt_common PRIVATE Qt6::Core)
if (NOT WIN32)
target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()

242
src/qt_common/qt_common.cpp Normal file
View file

@ -0,0 +1,242 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "frontend_common/firmware_manager.h"
#include "uisettings.h"
#include <QGuiApplication>
#include <QStringLiteral>
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include <QFile>
#include <QMessageBox>
#include "qt_frontend_util.h"
#include <JlCompress.h>
#if !defined(WIN32) && !defined(__APPLE__)
#include <qpa/qplatformnativeinterface.h>
#elif defined(__APPLE__)
#include <objc/message.h>
#endif
namespace QtCommon {
MetadataResult ResetMetadata()
{
if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir)
/ "game_list/")) {
return MetadataResult::Empty;
} else if (Common::FS::RemoveDirRecursively(
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
return MetadataResult::Success;
UISettings::values.is_game_list_reload_pending.exchange(true);
} else {
return MetadataResult::Failure;
}
}
Core::Frontend::WindowSystemType GetWindowSystemType()
{
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
return Core::Frontend::WindowSystemType::Windows;
else if (platform_name == QStringLiteral("xcb"))
return Core::Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("wayland-egl"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa"))
return Core::Frontend::WindowSystemType::Cocoa;
else if (platform_name == QStringLiteral("android"))
return Core::Frontend::WindowSystemType::Android;
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
{
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
#if defined(WIN32)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__)
wsi.render_surface = reinterpret_cast<void* (*) (id, SEL)>(
objc_msgSend)(reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
else
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
return wsi;
}
FirmwareInstallResult InstallFirmware(
const QString& location,
bool recursive,
std::function<bool(std::size_t, std::size_t)> QtProgressCallback,
Core::System* system,
FileSys::VfsFilesystem* vfs,
QWidget* parent)
{
static constexpr const char* failedTitle = "Firmware Install Failed";
static constexpr const char* successTitle = "Firmware Install Failed";
static constexpr QMessageBox::StandardButtons buttons = QMessageBox::Ok;
QMessageBox::Icon icon;
FirmwareInstallResult result;
const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon,
failedTitle,
GetFirmwareInstallResultString(result),
buttons,
parent);
};
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
// Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
// there.)
std::filesystem::path firmware_source_path = location.toStdString();
if (!Common::FS::IsDir(firmware_source_path)) {
return FirmwareInstallResult::NoOp;
}
std::vector<std::filesystem::path> out;
const Common::FS::DirEntryCallable callback =
[&out](const std::filesystem::directory_entry& entry) {
if (entry.path().has_extension() && entry.path().extension() == ".nca") {
out.emplace_back(entry.path());
}
return true;
};
QtProgressCallback(100, 10);
if (recursive) {
Common::FS::IterateDirEntriesRecursively(firmware_source_path,
callback,
Common::FS::DirEntryFilter::File);
} else {
Common::FS::IterateDirEntries(firmware_source_path,
callback,
Common::FS::DirEntryFilter::File);
}
if (out.size() <= 0) {
result = FirmwareInstallResult::NoNCAs;
icon = QMessageBox::Warning;
ShowMessage();
return result;
}
// Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (sysnand_content_vdir->IsWritable()
&& !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
result = FirmwareInstallResult::FailedDelete;
icon = QMessageBox::Critical;
ShowMessage();
return result;
}
LOG_INFO(Frontend,
"Cleaned nand/system/Content/registered folder in preparation for new firmware.");
QtProgressCallback(100, 20);
auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
bool success = true;
int i = 0;
for (const auto& firmware_src_path : out) {
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());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend,
"Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(),
firmware_src_path.filename().string());
success = false;
}
if (QtProgressCallback(100,
20
+ static_cast<int>(((i) / static_cast<float>(out.size()))
* 70.0))) {
result = FirmwareInstallResult::FailedCorrupted;
icon = QMessageBox::Warning;
ShowMessage();
return result;
}
}
if (!success) {
result = FirmwareInstallResult::FailedCopy;
icon = QMessageBox::Critical;
ShowMessage();
return result;
}
// Re-scan VFS for the newly placed firmware files.
system->GetFileSystemController().CreateFactories(*vfs);
const auto pair = FirmwareManager::GetFirmwareVersion(*system);
const auto firmware_data = pair.first;
const std::string display_version(firmware_data.display_version.data());
result = FirmwareInstallResult::Success;
QtCommon::Frontend::ShowMessage(QMessageBox::Information,
qApp->tr(successTitle),
qApp->tr(GetFirmwareInstallResultString(result))
.arg(QString::fromStdString(display_version)),
buttons,
parent);
return result;
}
QString UnzipFirmwareToTmp(const QString& location)
{
namespace fs = std::filesystem;
fs::path tmp{fs::temp_directory_path()};
if (!fs::create_directories(tmp / "eden" / "firmware")) {
return "";
}
tmp /= "eden";
tmp /= "firmware";
QString qCacheDir = QString::fromStdString(tmp.string());
QFile zip(location);
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
if (result.isEmpty()) {
return "";
}
return qCacheDir;
}
} // namespace QtCommon

80
src/qt_common/qt_common.h Normal file
View file

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_COMMON_H
#define QT_COMMON_H
#include <array>
#include <QWindow>
#include "core/core.h"
#include <core/frontend/emu_window.h>
#include <core/file_sys/vfs/vfs_real.h>
namespace QtCommon {
static constexpr std::array<const char *, 3> METADATA_RESULTS = {
"The operation completed successfully.",
"The metadata cache couldn't be deleted. It might be in use or non-existent.",
"The metadata cache is already empty.",
};
enum class MetadataResult {
Success,
Failure,
Empty,
};
/**
* @brief ResetMetadata Reset game list metadata.
* @return A result code.
*/
MetadataResult ResetMetadata();
/**
* \brief Get a string representation of a result from ResetMetadata.
* \param result The result code.
* \return A string representation of the passed result code.
*/
inline constexpr const char *GetResetMetadataResultString(MetadataResult result)
{
return METADATA_RESULTS.at(static_cast<std::size_t>(result));
}
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 {
Success,
NoOp,
NoNCAs,
FailedDelete,
FailedCopy,
FailedCorrupted,
};
FirmwareInstallResult InstallFirmware(
const QString &location,
bool recursive,
std::function<bool(std::size_t, std::size_t)> QtProgressCallback,
Core::System *system,
FileSys::VfsFilesystem *vfs,
QWidget *parent = nullptr);
QString UnzipFirmwareToTmp(const QString &location);
inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result)
{
return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result));
}
Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window);
} // namespace QtCommon
#endif

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_frontend_util.h"
namespace QtCommon::Frontend {
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
const QString &title,
const QString &text,
QMessageBox::StandardButtons buttons,
QWidget *parent)
{
#ifdef YUZU_QT_WIDGETS
QMessageBox *box = new QMessageBox(icon, title, text, buttons, parent);
return static_cast<QMessageBox::StandardButton>(box->exec());
#endif
}
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
const char *title,
const char *text,
QMessageBox::StandardButtons buttons,
QWidget *parent)
{
return ShowMessage(icon, qApp->tr(title), qApp->tr(text), buttons, parent);
}
} // namespace QtCommon::Frontend

View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_FRONTEND_UTIL_H
#define QT_FRONTEND_UTIL_H
#include <QGuiApplication>
#include <QMessageBox>
#ifdef YUZU_QT_WIDGETS
#include <QWidget>
#endif
/**
* manages common functionality e.g. message boxes and such for Qt/QML
*/
namespace QtCommon::Frontend {
Q_NAMESPACE
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
const QString &title,
const QString &text,
QMessageBox::StandardButtons buttons = QMessageBox::NoButton,
QWidget *parent = nullptr);
QMessageBox::StandardButton ShowMessage(QMessageBox::Icon icon,
const char *title,
const char *text,
QMessageBox::StandardButtons buttons = QMessageBox::NoButton,
QWidget *parent = nullptr);
} // namespace QtCommon::Frontend
#endif // QT_FRONTEND_UTIL_H

View file

@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_game_util.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include <QDesktopServices>
#include <QUrl>
#ifdef _WIN32
#include <shlobj.h>
#include <windows.h>
#include "common/scope_exit.h"
#include "common/string_util.h"
#else
#include "fmt/ostream.h"
#include <fstream>
#endif
namespace QtCommon {
bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command,
const std::string& arguments, const std::string& categories,
const std::string& keywords, const std::string& name) try {
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#elif defined(_WIN32) // Windows
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
SCOPE_EXIT {
CoUninitialize();
};
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
SCOPE_EXIT {
if (persist_file != nullptr) {
persist_file->Release();
}
if (ps1 != nullptr) {
ps1->Release();
}
};
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
return false;
}
hres = ps1->SetPath(command.c_str());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set path");
return false;
}
if (!arguments.empty()) {
hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set arguments");
return false;
}
}
if (!comment.empty()) {
hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set description");
return false;
}
}
if (std::filesystem::is_regular_file(icon_path)) {
hres = ps1->SetIconLocation(icon_path.c_str(), 0);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set icon location");
return false;
}
}
hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
return false;
}
hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to save shortcut");
return false;
}
return true;
#else // Unsupported platform
return false;
#endif
} catch (const std::exception& e) {
LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
return false;
}
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path) {
// Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif
// Create icons directory if it doesn't exist
if (!Common::FS::CreateDirs(out_icon_path)) {
out_icon_path.clear();
return false;
}
// Create icon file path
out_icon_path /= (program_id == 0 ? fmt::format("eden-{}.{}", game_file_name, ico_extension)
: fmt::format("eden-{:016X}.{}", program_id, ico_extension));
return true;
}
void OpenRootDataFolder() {
QDesktopServices::openUrl(QUrl(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir))));
}
void OpenNANDFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir))));
}
void OpenSDMCFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir))));
}
void OpenModFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir))));
}
void OpenLogFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir))));
}
}

View file

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_GAME_UTIL_H
#define QT_GAME_UTIL_H
#include "frontend_common/content_manager.h"
#include <array>
namespace QtCommon {
static constexpr std::array<const char *, 3> GAME_VERIFICATION_RESULTS = {
"The operation completed successfully.",
"File contents may be corrupt or missing..",
"Firmware installation cancelled, firmware may be in a bad state or corrupted."
"File contents could not be checked for validity."
};
inline constexpr const char *GetGameVerificationResultString(ContentManager::GameVerificationResult result)
{
return GAME_VERIFICATION_RESULTS.at(static_cast<std::size_t>(result));
}
bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command,
const std::string& arguments,
const std::string& categories,
const std::string& keywords,
const std::string& name);
bool MakeShortcutIcoPath(const u64 program_id,
const std::string_view game_file_name,
std::filesystem::path& out_icon_path);
void OpenRootDataFolder();
void OpenNANDFolder();
void OpenSDMCFolder();
void OpenModFolder();
void OpenLogFolder();
}
#endif // QT_GAME_UTIL_H

View file

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_path_util.h"
#include <QDesktopServices>
#include <QString>
#include <QUrl>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include <fmt/format.h>
bool QtCommon::PathUtil::OpenShaderCache(u64 program_id)
{
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)) {
return false;
}
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
const auto qt_shader_cache_path = QString::fromStdString(shader_path_string);
return QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
}

View file

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_PATH_UTIL_H
#define QT_PATH_UTIL_H
#include "common/common_types.h"
namespace QtCommon::PathUtil { bool OpenShaderCache(u64 program_id); }
#endif // QT_PATH_UTIL_H

View file

@ -7,23 +7,21 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "yuzu/configuration/shared_translation.h"
#include "shared_translation.h"
#include <QCoreApplication>
#include <QWidget>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include <map>
#include <memory>
#include <tuple>
#include <utility>
namespace ConfigurationShared {
std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent)
std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
{
std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
@ -459,7 +457,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent)
return translations;
}
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent)
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
{
std::unique_ptr<ComboboxTranslationMap> translations = std::make_unique<ComboboxTranslationMap>();
const auto& tr = [&](const char* text, const char* context = "") {

View file

@ -11,23 +11,20 @@
#include <map>
#include <memory>
#include <typeindex>
#include <utility>
#include <vector>
#include <QString>
#include "common/common_types.h"
#include "common/settings.h"
class QWidget;
#include "common/settings_enums.h"
namespace ConfigurationShared {
using TranslationMap = std::map<u32, std::pair<QString, QString>>;
using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent);
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},

View file

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QSettings>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
namespace Settings {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -14,7 +17,7 @@
#include "common/common_types.h"
#include "common/settings.h"
#include "common/settings_enums.h"
#include "configuration/qt_config.h"
#include "qt_common/qt_config.h"
using Settings::Category;
using Settings::ConfirmStop;

View file

@ -150,12 +150,8 @@ add_executable(yuzu
configuration/configure_web.ui
configuration/input_profiles.cpp
configuration/input_profiles.h
configuration/shared_translation.cpp
configuration/shared_translation.h
configuration/shared_widget.cpp
configuration/shared_widget.h
configuration/qt_config.cpp
configuration/qt_config.h
debugger/console.cpp
debugger/console.h
debugger/controller.cpp
@ -207,12 +203,8 @@ add_executable(yuzu
play_time_manager.cpp
play_time_manager.h
precompiled_headers.h
qt_common.cpp
qt_common.h
startup_checks.cpp
startup_checks.h
uisettings.cpp
uisettings.h
util/clickable_label.cpp
util/clickable_label.h
util/controller_navigation.cpp
@ -394,14 +386,12 @@ elseif(WIN32)
endif()
endif()
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core)
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 ${PLATFORM_LIBRARIES} Threads::Threads)
target_link_libraries(yuzu PRIVATE Vulkan::Headers)
if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (UNIX AND NOT APPLE)
target_link_libraries(yuzu PRIVATE Qt6::DBus)
endif()

View file

@ -12,7 +12,7 @@
#include <QtCore/qglobal.h>
#include "common/settings_enums.h"
#include "uisettings.h"
#include "qt_common/uisettings.h"
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#include <QCamera>
#include <QCameraImageCapture>
@ -59,7 +59,7 @@
#include "video_core/renderer_base.h"
#include "yuzu/bootmanager.h"
#include "yuzu/main.h"
#include "yuzu/qt_common.h"
#include "qt_common/qt_common.h"
class QObject;
class QPaintEngine;
@ -240,7 +240,7 @@ class DummyContext : public Core::Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
public:
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
if (QtCommon::GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
@ -253,9 +253,6 @@ public:
QPaintEngine* paintEngine() const override {
return nullptr;
}
private:
GRenderWindow* render_window;
};
struct OpenGLRenderWidget : public RenderWidget {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -16,9 +19,9 @@
#include "ui_configure_audio.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_audio.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureAudio::ConfigureAudio(const Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -7,7 +10,7 @@
#include <vector>
#include <QWidget>
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
class QComboBox;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -12,7 +15,7 @@
#include "ui_configure_debug.h"
#include "yuzu/configuration/configure_debug.h"
#include "yuzu/debugger/console.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
: QScrollArea(parent), ui{std::make_unique<Ui::ConfigureDebug>()}, system{system_} {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -27,7 +30,7 @@
#include "yuzu/configuration/configure_ui.h"
#include "yuzu/configuration/configure_web.h"
#include "yuzu/hotkeys.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
InputCommon::InputSubsystem* input_subsystem,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include <QDialog>
#include "configuration/shared_widget.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
#include "yuzu/vk_device_info.h"
namespace Core {

View file

@ -1,14 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "yuzu/configuration/configure_filesystem.h"
#include <QFileDialog>
#include <QMessageBox>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "qt_common/qt_common.h"
#include "qt_common/uisettings.h"
#include "ui_configure_filesystem.h"
#include "yuzu/configuration/configure_filesystem.h"
#include "yuzu/uisettings.h"
ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
@ -126,19 +130,16 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
}
void ConfigureFilesystem::ResetMetadata() {
if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) /
"game_list/")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The metadata cache is already empty."));
} else if (Common::FS::RemoveDirRecursively(
Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
QMessageBox::information(this, tr("Reset Metadata Cache"),
tr("The operation completed successfully."));
UISettings::values.is_game_list_reload_pending.exchange(true);
} else {
QMessageBox::warning(
this, tr("Reset Metadata Cache"),
tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
auto result = QtCommon::ResetMetadata();
const QString resultMessage = tr(QtCommon::GetResetMetadataResultString(result));
const QString title = tr("Reset Metadata Cache");
switch (result) {
case QtCommon::MetadataResult::Failure:
QMessageBox::warning(this, title, resultMessage);
break;
default:
QMessageBox::information(this, title, resultMessage);
}
}

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -11,7 +14,7 @@
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_general.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureGeneral::ConfigureGeneral(const Core::System& system_,
std::shared_ptr<std::vector<ConfigurationShared::Tab*>> group_,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -40,8 +43,8 @@
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#include "yuzu/configuration/shared_widget.h"
#include "yuzu/qt_common.h"
#include "yuzu/uisettings.h"
#include "qt_common/qt_common.h"
#include "qt_common/uisettings.h"
#include "yuzu/vk_device_info.h"
static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -15,7 +18,7 @@
#include <vulkan/vulkan_core.h>
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
#include "vk_device_info.h"
#include "yuzu/configuration/configuration_shared.h"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +12,7 @@
#include "ui_configure_graphics_advanced.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics_advanced.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
#include "yuzu/configuration/shared_widget.h"
ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -11,7 +14,7 @@
#include "ui_configure_graphics_extensions.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics_extensions.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
#include "yuzu/configuration/shared_widget.h"
ConfigureGraphicsExtensions::ConfigureGraphicsExtensions(

View file

@ -1,3 +1,6 @@
// 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
@ -13,7 +16,7 @@
#include "ui_configure_hotkeys.h"
#include "yuzu/configuration/configure_hotkeys.h"
#include "yuzu/hotkeys.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/sequence_dialog/sequence_dialog.h"
constexpr int name_column = 0;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +12,7 @@
#include "ui_configure_input_per_game.h"
#include "yuzu/configuration/input_profiles.h"
#include "yuzu/configuration/qt_config.h"
#include "qt_common/qt_config.h"
class QComboBox;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -12,7 +15,7 @@
#include <QTimer>
#include "common/assert.h"
#include "common/param_package.h"
#include "configuration/qt_config.h"
#include "qt_common/qt_config.h"
#include "frontend_common/config.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -38,7 +41,7 @@
#include "yuzu/configuration/configure_per_game.h"
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/configuration/configure_system.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/util.h"
#include "yuzu/vk_device_info.h"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -15,8 +18,8 @@
#include "frontend_common/config.h"
#include "vk_device_info.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/qt_config.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/qt_config.h"
#include "qt_common/shared_translation.h"
namespace Core {
class System;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -21,7 +24,7 @@
#include "ui_configure_per_game_addons.h"
#include "yuzu/configuration/configure_input.h"
#include "yuzu/configuration/configure_per_game_addons.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigurePerGameAddons>()}, system{system_} {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include <QTimer>
#include <fmt/ranges.h>
#include "configuration/qt_config.h"
#include "qt_common/qt_config.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "input_common/drivers/keyboard.h"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include "common/settings.h"
#include "ui_configure_tas.h"
#include "yuzu/configuration/configure_tas.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureTas>()) {

View file

@ -27,7 +27,7 @@
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
#include "ui_configure_ui.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
namespace {
constexpr std::array default_game_icon_sizes{

View file

@ -17,7 +17,7 @@
#include <QtConcurrent/QtConcurrentRun>
#include "common/settings.h"
#include "ui_configure_web.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
ConfigureWeb::ConfigureWeb(QWidget* parent)
: QWidget(parent)

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -6,7 +9,7 @@
#include <string>
#include <unordered_map>
#include "configuration/qt_config.h"
#include "qt_common/qt_config.h"
namespace Core {
class System;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -42,7 +45,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/settings_common.h"
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
namespace ConfigurationShared {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -11,7 +14,7 @@
#include <QStringLiteral>
#include <QWidget>
#include <qobjectdefs.h>
#include "yuzu/configuration/shared_translation.h"
#include "qt_common/shared_translation.h"
class QCheckBox;
class QComboBox;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +12,7 @@
#include "common/logging/backend.h"
#include "yuzu/debugger/console.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
namespace Debugger {
void ToggleConsole() {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -5,7 +8,7 @@
#include <fmt/ranges.h>
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "core/arm/debug.h"
#include "core/core.h"

View file

@ -24,7 +24,7 @@
#include "yuzu/game_list_p.h"
#include "yuzu/game_list_worker.h"
#include "yuzu/main.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/controller_navigation.h"
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent)
@ -820,7 +820,7 @@ QStandardItemModel* GameList::GetModel() const {
return item_model;
}
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs, const bool cached)
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs)
{
tree_view->setEnabled(false);
@ -843,8 +843,7 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs, const bool
game_dirs,
compatibility_list,
play_time_manager,
system,
cached);
system);
// Get events from the worker as data becomes available
connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent,
@ -873,14 +872,6 @@ const QStringList GameList::supported_file_extensions = {
QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
void GameList::ForceRefreshGameDirectory()
{
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Force-reloading game list per user request.");
PopulateAsync(UISettings::values.game_dirs, false);
}
}
void GameList::RefreshGameDirectory()
{
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {

View file

@ -20,7 +20,7 @@
#include "common/common_types.h"
#include "core/core.h"
#include "uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/play_time_manager.h"
@ -97,7 +97,7 @@ public:
bool IsEmpty() const;
void LoadCompatibilityList();
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs, const bool cached = true);
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
void SaveInterfaceLayout();
void LoadInterfaceLayout();
@ -110,7 +110,6 @@ public:
static const QStringList supported_file_extensions;
public slots:
void ForceRefreshGameDirectory();
void RefreshGameDirectory();
signals:

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -19,7 +22,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "yuzu/play_time_manager.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/util.h"
enum class GameListItemType {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -27,7 +30,7 @@
#include "yuzu/game_list.h"
#include "yuzu/game_list_p.h"
#include "yuzu/game_list_worker.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
namespace {
@ -199,8 +202,7 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path,
u64 program_id,
const CompatibilityList& compatibility_list,
const PlayTime::PlayTimeManager& play_time_manager,
const FileSys::PatchManager& patch,
const bool cached)
const FileSys::PatchManager& patch)
{
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@ -224,14 +226,10 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path,
QString patch_versions;
if (cached) {
patch_versions = GetGameListCachedObject(
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
});
} else {
patch_versions = FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
}
patch_versions = GetGameListCachedObject(
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
});
list.insert(2, new GameListItem(patch_versions));
@ -244,15 +242,13 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_,
QVector<UISettings::GameDir>& game_dirs_,
const CompatibilityList& compatibility_list_,
const PlayTime::PlayTimeManager& play_time_manager_,
Core::System& system_,
const bool cached_)
Core::System& system_)
: vfs{std::move(vfs_)}
, provider{provider_}
, game_dirs{game_dirs_}
, compatibility_list{compatibility_list_}
, play_time_manager{play_time_manager_}
, system{system_}
, cached{cached_}
{
// We want the game list to manage our lifetime.
setAutoDelete(false);
@ -355,8 +351,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
program_id,
compatibility_list,
play_time_manager,
patch,
cached);
patch);
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
}
}
@ -439,8 +434,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
id,
compatibility_list,
play_time_manager,
patch,
cached);
patch);
RecordEvent(
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
@ -463,8 +457,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
program_id,
compatibility_list,
play_time_manager,
patch,
cached);
patch);
RecordEvent(
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -15,7 +18,7 @@
#include "common/thread.h"
#include "core/file_sys/registered_cache.h"
#include "uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/play_time_manager.h"
@ -45,8 +48,7 @@ public:
QVector<UISettings::GameDir>& game_dirs_,
const CompatibilityList& compatibility_list_,
const PlayTime::PlayTimeManager& play_time_manager_,
Core::System& system_,
const bool cached = true);
Core::System& system_);
~GameListWorker() override;
/// Starts the processing of directory tree information.
@ -95,6 +97,4 @@ private:
Common::Event processing_completed;
Core::System& system;
const bool cached;
};

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include "hid_core/frontend/emulated_controller.h"
#include "yuzu/hotkeys.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
HotkeyRegistry::HotkeyRegistry() = default;
HotkeyRegistry::~HotkeyRegistry() = default;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,7 @@
#include <QListWidget>
#include <QVBoxLayout>
#include "yuzu/install_dialog.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) {
file_list = new QListWidget(this);

View file

@ -11,8 +11,9 @@
#include "core/loader/nca.h"
#include "core/tools/renderdoc.h"
#include "frontend_common/firmware_manager.h"
#include <JlCompress.h>
#include "qt_common/qt_common.h"
#include "qt_common/qt_game_util.h"
#include "qt_common/qt_path_util.h"
#ifdef __APPLE__
#include <unistd.h> // for chdir
@ -116,7 +117,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#ifdef _WIN32
#include <shlobj.h>
#include "common/windows/timer_resolution.h"
#endif
#ifdef ARCHITECTURE_x86_64
@ -154,7 +154,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/compatibility_list.h"
#include "yuzu/configuration/configure_dialog.h"
#include "yuzu/configuration/configure_input_per_game.h"
#include "yuzu/configuration/qt_config.h"
#include "qt_common/qt_config.h"
#include "yuzu/debugger/console.h"
#include "yuzu/debugger/controller.h"
#include "yuzu/debugger/profiler.h"
@ -168,7 +168,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/main.h"
#include "yuzu/play_time_manager.h"
#include "yuzu/startup_checks.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/clickable_label.h"
#include "yuzu/vk_device_info.h"
@ -470,7 +470,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
game_list->LoadCompatibilityList();
// force reload on first load to ensure add-ons get updated
game_list->PopulateAsync(UISettings::values.game_dirs, false);
game_list->PopulateAsync(UISettings::values.game_dirs);
// make sure menubar has the arrow cursor instead of inheriting from this
ui->menubar->setCursor(QCursor());
@ -1076,7 +1076,7 @@ void GMainWindow::InitializeWidgets() {
loading_screen = new LoadingScreen(this);
loading_screen->hide();
ui->horizontalLayout->addWidget(loading_screen);
connect(loading_screen, &LoadingScreen::Hidden, [&] {
connect(loading_screen, &LoadingScreen::Hidden, this, [&] {
loading_screen->Clear();
if (emulation_running) {
render_window->show();
@ -2518,17 +2518,11 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
}
// TODO(crueter): Transfer ts to showmessage
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
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)) {
QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
tr("Failed to create the shader cache directory for this title."));
return;
if (!QtCommon::PathUtil::OpenShaderCache(program_id)) {
QMessageBox::warning(this, tr("Error Opening Shader Cache"), tr("Failed to create or open shader cache for this title, ensure your app data directory has write permissions."));
}
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
const auto qt_shader_cache_path = QString::fromStdString(shader_path_string);
QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
}
static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
@ -2593,6 +2587,9 @@ static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog&
return true;
}
// TODO(crueter): All this can be transfered to qt_common
// Aldoe I need to decide re: message boxes for QML
// translations_common? strings_common? qt_strings? who knows
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
switch (type) {
case InstalledEntryType::Game:
@ -2949,12 +2946,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
}
}
// END
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
const auto NotImplemented = [this] {
QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"),
tr("File contents were not checked for validity."));
};
QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
@ -2967,18 +2960,20 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
};
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
const QString resultString = tr(QtCommon::GetGameVerificationResultString(result));
progress.close();
switch (result) {
case ContentManager::GameVerificationResult::Success:
QMessageBox::information(this, tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
resultString);
break;
case ContentManager::GameVerificationResult::Failed:
QMessageBox::critical(this, tr("Integrity verification failed!"),
tr("File contents may be corrupt."));
resultString);
break;
case ContentManager::GameVerificationResult::NotImplemented:
NotImplemented();
QMessageBox::warning(this, tr("Integrity verification couldn't be performed"),
resultString);
}
}
@ -2999,109 +2994,8 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QDesktopServices::openUrl(QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory));
}
bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command,
const std::string& arguments, const std::string& categories,
const std::string& keywords, const std::string& name) try {
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#elif defined(_WIN32) // Windows
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
SCOPE_EXIT {
CoUninitialize();
};
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
SCOPE_EXIT {
if (persist_file != nullptr) {
persist_file->Release();
}
if (ps1 != nullptr) {
ps1->Release();
}
};
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
return false;
}
hres = ps1->SetPath(command.c_str());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set path");
return false;
}
if (!arguments.empty()) {
hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set arguments");
return false;
}
}
if (!comment.empty()) {
hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set description");
return false;
}
}
if (std::filesystem::is_regular_file(icon_path)) {
hres = ps1->SetIconLocation(icon_path.c_str(), 0);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to set icon location");
return false;
}
}
hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
return false;
}
hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to save shortcut");
return false;
}
return true;
#else // Unsupported platform
return false;
#endif
} catch (const std::exception& e) {
LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
return false;
}
// Messages in pre-defined message boxes for less code spaghetti
// TODO(crueter): Still need to decide what to do re: message boxes w/ qml
bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
int result = 0;
QMessageBox::StandardButtons buttons;
@ -3132,33 +3026,6 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt
}
}
bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path) {
// Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
ico_extension = "ico";
#elif defined(__linux__) || defined(__FreeBSD__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif
// Create icons directory if it doesn't exist
if (!Common::FS::CreateDirs(out_icon_path)) {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
out_icon_path.clear();
return false;
}
// Create icon file path
out_icon_path /= (program_id == 0 ? fmt::format("eden-{}.{}", game_file_name, ico_extension)
: fmt::format("eden-{:016X}.{}", program_id, ico_extension));
return true;
}
void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target) {
// Create shortcut
@ -4185,32 +4052,27 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
}
void GMainWindow::OnOpenRootDataFolder() {
QDesktopServices::openUrl(QUrl(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir))));
QtCommon::OpenRootDataFolder();
}
void GMainWindow::OnOpenNANDFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir))));
QtCommon::OpenNANDFolder();
}
void GMainWindow::OnOpenSDMCFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir))));
QtCommon::OpenSDMCFolder();
}
void GMainWindow::OnOpenModFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir))));
QtCommon::OpenModFolder();
}
void GMainWindow::OnOpenLogFolder()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir))));
QtCommon::OpenLogFolder();
}
void GMainWindow::OnVerifyInstalledContents() {
@ -4258,104 +4120,23 @@ void GMainWindow::InstallFirmware(const QString &location, bool recursive)
return progress.wasCanceled();
};
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
auto result = QtCommon::InstallFirmware(location, recursive, QtProgressCallback, system.get(), vfs.get(), this);
// Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
// there.)
std::filesystem::path firmware_source_path = location.toStdString();
if (!Common::FS::IsDir(firmware_source_path)) {
progress.close();
return;
}
progress.close();
std::vector<std::filesystem::path> out;
const Common::FS::DirEntryCallable callback =
[&out](const std::filesystem::directory_entry& entry) {
if (entry.path().has_extension() && entry.path().extension() == ".nca") {
out.emplace_back(entry.path());
}
return true;
};
QtProgressCallback(100, 10);
if (recursive) {
Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
} else {
Common::FS::IterateDirEntries(firmware_source_path, callback, Common::FS::DirEntryFilter::File);
}
if (out.size() <= 0) {
progress.close();
QMessageBox::warning(this, tr("Firmware install failed"),
tr("Unable to locate potential firmware NCA files"));
return;
}
// Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
progress.close();
QMessageBox::critical(this, tr("Firmware install failed"),
tr("Failed to delete one or more firmware file."));
return;
}
LOG_INFO(Frontend,
"Cleaned nand/system/Content/registered folder in preparation for new firmware.");
QtProgressCallback(100, 20);
auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
bool success = true;
int i = 0;
for (const auto& firmware_src_path : out) {
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());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(), firmware_src_path.filename().string());
success = false;
}
if (QtProgressCallback(
100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
progress.close();
QMessageBox::warning(
this, tr("Firmware install failed"),
tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. "
"Restart Eden or re-install firmware."));
return;
}
}
if (!success) {
progress.close();
QMessageBox::critical(this, tr("Firmware install failed"),
tr("One or more firmware files failed to copy into NAND."));
return;
}
// Re-scan VFS for the newly placed firmware files.
system->GetFileSystemController().CreateFactories(*vfs);
if (result != QtCommon::FirmwareInstallResult::Success) return;
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress.wasCanceled();
};
auto result =
auto results =
ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true);
if (result.size() > 0) {
if (results.size() > 0) {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QString::fromStdString(fmt::format("{}", fmt::join(results, "\n")));
progress.close();
QMessageBox::critical(
this, tr("Firmware integrity verification failed!"),
@ -4412,32 +4193,14 @@ void GMainWindow::OnInstallFirmwareFromZIP()
return;
}
namespace fs = std::filesystem;
fs::path tmp{std::filesystem::temp_directory_path()};
const QString qCacheDir = QtCommon::UnzipFirmwareToTmp(firmware_zip_location);
if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) {
goto unzipFailed;
}
{
tmp /= "eden";
tmp /= "firmware";
QString qCacheDir = QString::fromStdString(tmp.string());
QFile zip(firmware_zip_location);
QStringList result = JlCompress::extractDir(&zip, qCacheDir);
if (result.isEmpty()) {
goto unzipFailed;
}
// In this case, it has to be done recursively, since sometimes people
// will pack it into a subdirectory after dumping
// In this case, it has to be done recursively, since sometimes people
// will pack it into a subdirectory after dumping
if (!qCacheDir.isEmpty()) {
InstallFirmware(qCacheDir, true);
std::error_code ec;
std::filesystem::remove_all(tmp, ec);
std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", ec);
if (ec) {
QMessageBox::warning(this, tr("Firmware cleanup failed"),
@ -4445,14 +4208,7 @@ void GMainWindow::OnInstallFirmwareFromZIP()
"Check write permissions in the system temp directory and try again.\nOS reported error: %1")
.arg(QString::fromStdString(ec.message())));
}
return;
}
unzipFailed:
QMessageBox::critical(this, tr("Firmware unzip failed"),
tr("Check write permissions in the system temp directory and try again."));
return;
}
void GMainWindow::OnInstallDecryptionKeys() {
@ -4509,8 +4265,9 @@ void GMainWindow::OnToggleStatusBar() {
void GMainWindow::OnGameListRefresh()
{
// force reload add-ons etc
game_list->ForceRefreshGameDirectory();
// Resets metadata cache and reloads
QtCommon::ResetMetadata();
game_list->RefreshGameDirectory();
}
void GMainWindow::OnAlbum() {
@ -4518,7 +4275,7 @@ void GMainWindow::OnAlbum() {
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Album applet."));
tr("Please install firmware to use the Album applet."));
return;
}
@ -4541,7 +4298,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Cabinet applet."));
tr("Please install firmware to use the Cabinet applet."));
return;
}
@ -4565,7 +4322,7 @@ void GMainWindow::OnMiiEdit() {
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Mii editor."));
tr("Please install firmware to use the Mii editor."));
return;
}
@ -4588,7 +4345,7 @@ void GMainWindow::OnOpenControllerMenu() {
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Controller Menu."));
tr("Please install firmware to use the Controller Menu."));
return;
}
@ -4613,7 +4370,7 @@ void GMainWindow::OnHomeMenu() {
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Home Menu."));
tr("Please install firmware to use the Home Menu."));
return;
}
@ -4637,7 +4394,7 @@ void GMainWindow::OnInitialSetup()
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use Starter."));
tr("Please install firmware to use Starter."));
return;
}
@ -4700,7 +4457,7 @@ std::filesystem::path GMainWindow::GetShortcutPath(GameListShortcutTarget target
}
void GMainWindow::CreateShortcut(const std::string &game_path, const u64 program_id, const std::string& game_title_, GameListShortcutTarget target, std::string arguments_, const bool needs_title) {
// Get path to yuzu executable
// Get path to Eden executable
std::filesystem::path command = GetEdenCommand();
// Shortcut path
@ -4752,10 +4509,16 @@ void GMainWindow::CreateShortcut(const std::string &game_path, const u64 program
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path;
if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (QtCommon::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file");
}
} else {
QMessageBox::critical(
this, tr("Create Icon"),
tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
.arg(QString::fromStdString(out_icon_path.string())),
QMessageBox::StandardButton::Ok);
}
#if defined(__linux__)
@ -4776,11 +4539,11 @@ void GMainWindow::CreateShortcut(const std::string &game_path, const u64 program
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) {
arguments = "-f " + arguments;
}
const std::string comment = fmt::format("Start {:s} with the eden Emulator", game_title);
const std::string comment = fmt::format("Start {:s} with the Eden Emulator", game_title);
const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;";
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
if (QtCommon::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
arguments, categories, keywords, game_title)) {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
qgame_title);
@ -4810,6 +4573,7 @@ void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target)
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
const auto game_path = qlaunch_applet_nca->GetFullPath();
// TODO(crueter): Make this use the Eden icon
CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
}

View file

@ -17,7 +17,7 @@
#include <QTranslator>
#include "common/common_types.h"
#include "configuration/qt_config.h"
#include "qt_common/qt_config.h"
#include "frontend_common/content_manager.h"
#include "input_common/drivers/tas_input.h"
#include "user_data_migration.h"
@ -478,13 +478,7 @@ private:
QString GetTasStateDescription() const;
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path);
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
const std::filesystem::path& icon_path,
const std::filesystem::path& command, const std::string& arguments,
const std::string& categories, const std::string& keywords,
const std::string& name);
/**
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
* The only difference is that it returns a boolean.

View file

@ -21,7 +21,7 @@
#include "yuzu/multiplayer/message.h"
#include "yuzu/multiplayer/state.h"
#include "yuzu/multiplayer/validation.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
enum class ConnectionType : u8 { TraversalServer, IP };

View file

@ -25,7 +25,7 @@
#include "yuzu/multiplayer/message.h"
#include "yuzu/multiplayer/state.h"
#include "yuzu/multiplayer/validation.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/verify_user_jwt.h"
#endif

View file

@ -22,7 +22,7 @@
#include "yuzu/multiplayer/message.h"
#include "yuzu/multiplayer/state.h"
#include "yuzu/multiplayer/validation.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/web_backend.h"
#endif

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -19,7 +22,7 @@
#include "yuzu/multiplayer/lobby.h"
#include "yuzu/multiplayer/message.h"
#include "yuzu/multiplayer/state.h"
#include "yuzu/uisettings.h"
#include "qt_common/uisettings.h"
#include "yuzu/util/clickable_label.h"
MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_,

View file

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -9,8 +12,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/polyfill_thread.h"
#include <common/polyfill_thread.h>
// TODO(crueter): Extract this into frontend_common
namespace Service::Account {
class ProfileManager;
}

View file

@ -1,60 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QGuiApplication>
#include <QStringLiteral>
#include <QWindow>
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include "yuzu/qt_common.h"
#if !defined(WIN32) && !defined(__APPLE__)
#include <qpa/qplatformnativeinterface.h>
#elif defined(__APPLE__)
#include <objc/message.h>
#endif
namespace QtCommon {
Core::Frontend::WindowSystemType GetWindowSystemType() {
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
return Core::Frontend::WindowSystemType::Windows;
else if (platform_name == QStringLiteral("xcb"))
return Core::Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("wayland-egl"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa"))
return Core::Frontend::WindowSystemType::Cocoa;
else if (platform_name == QStringLiteral("android"))
return Core::Frontend::WindowSystemType::Android;
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
#if defined(WIN32)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__)
wsi.render_surface = reinterpret_cast<void* (*)(id, SEL)>(objc_msgSend)(
reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
else
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
return wsi;
}
} // namespace QtCommon

View file

@ -1,15 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QWindow>
#include "core/frontend/emu_window.h"
namespace QtCommon {
Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window);
} // namespace QtCommon

View file

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include <vector>
#include "yuzu/qt_common.h"
#include "qt_common/qt_common.h"
#include "common/dynamic_library.h"
#include "common/logging/log.h"