more common funcs

Signed-off-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
crueter 2025-07-22 23:23:14 -04:00
parent edadf5e8c8
commit b2967171ea
11 changed files with 331 additions and 220 deletions

View file

@ -10,10 +10,8 @@ echo "Getting branch changes"
# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}" # RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
# FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r` # FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
CURRENT=`git rev-parse --short=10 HEAD` BASE=`git merge-base master HEAD`
BASE=`git merge-base master $CURRENT` FILES=`git diff --name-only $BASE`
RANGE="$CURRENT^..$BASE"
FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
#FILES=$(git diff --name-only master) #FILES=$(git diff --name-only master)

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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -13,10 +13,12 @@ add_library(qt_common STATIC
shared_translation.cpp shared_translation.cpp
shared_translation.h shared_translation.h
qt_path_util.h qt_path_util.cpp
qt_game_util.h qt_game_util.cpp
) )
create_target_directory_groups(qt_common) create_target_directory_groups(qt_common)
target_link_libraries(qt_common PUBLIC core Qt6::Core Qt6::Gui SimpleIni::SimpleIni) target_link_libraries(qt_common PUBLIC core Qt6::Core Qt6::Gui SimpleIni::SimpleIni QuaZip::QuaZip)
if (NOT WIN32) if (NOT WIN32)
target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})

View file

@ -15,6 +15,11 @@
#if !defined(WIN32) && !defined(__APPLE__) #if !defined(WIN32) && !defined(__APPLE__)
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <QFile>
#include <JlCompress.h>
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <objc/message.h> #include <objc/message.h>
#endif #endif
@ -80,9 +85,10 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
return wsi; return wsi;
} }
FirmwareInstallResult InstallFirmware(const QString& location, FirmwareInstallResult InstallFirmware(
const QString& location,
bool recursive, bool recursive,
std::function<bool(size_t, size_t)> QtProgressCallback, std::function<bool(std::size_t, std::size_t)> QtProgressCallback,
Core::System* system, Core::System* system,
FileSys::VfsFilesystem* vfs) FileSys::VfsFilesystem* vfs)
{ {
@ -123,7 +129,8 @@ FirmwareInstallResult InstallFirmware(const QString& location,
// Locate and erase the content of nand/system/Content/registered/*.nca, if any. // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (sysnand_content_vdir->IsWritable() && !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { if (sysnand_content_vdir->IsWritable()
&& !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
return FirmwareInstallResult::FailedDelete; return FirmwareInstallResult::FailedDelete;
} }
@ -168,4 +175,28 @@ FirmwareInstallResult InstallFirmware(const QString& location,
return FirmwareInstallResult::Success; return FirmwareInstallResult::Success;
} }
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

View file

@ -62,10 +62,12 @@ enum class FirmwareInstallResult {
FirmwareInstallResult InstallFirmware(const QString &location, FirmwareInstallResult InstallFirmware(const QString &location,
bool recursive, bool recursive,
std::function<bool(size_t, size_t)> QtProgressCallback, std::function<bool(std::size_t, std::size_t)> QtProgressCallback,
Core::System *system, Core::System *system,
FileSys::VfsFilesystem *vfs); FileSys::VfsFilesystem *vfs);
QString UnzipFirmwareToTmp(const QString &location);
inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result) inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result)
{ {
return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result)); return FIRMWARE_RESULTS.at(static_cast<std::size_t>(result));

View file

@ -0,0 +1,171 @@
#include "qt_game_util.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include <QDesktopServices>
#include <QUrl>
#include "fmt/ostream.h"
#include <fstream>
#ifdef _WIN32
#include <windows.h>
#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 {
#ifdef _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;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX
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;
#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,41 @@
#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,20 @@
#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,7 @@
#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

@ -12,6 +12,8 @@
#include "core/tools/renderdoc.h" #include "core/tools/renderdoc.h"
#include "frontend_common/firmware_manager.h" #include "frontend_common/firmware_manager.h"
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
#include "qt_common/qt_game_util.h"
#include "qt_common/qt_path_util.h"
#include <JlCompress.h> #include <JlCompress.h>
@ -1166,7 +1168,7 @@ void GMainWindow::InitializeWidgets() {
loading_screen = new LoadingScreen(this); loading_screen = new LoadingScreen(this);
loading_screen->hide(); loading_screen->hide();
ui->horizontalLayout->addWidget(loading_screen); ui->horizontalLayout->addWidget(loading_screen);
connect(loading_screen, &LoadingScreen::Hidden, [&] { connect(loading_screen, &LoadingScreen::Hidden, this, [&] {
loading_screen->Clear(); loading_screen->Clear();
if (emulation_running) { if (emulation_running) {
render_window->show(); render_window->show();
@ -2613,16 +2615,9 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
} }
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); if (!QtCommon::PathUtil::OpenShaderCache(program_id)) {
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", 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."));
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;
} }
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, static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog,
@ -2687,6 +2682,7 @@ static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog&
return true; return true;
} }
// TODO(crueter): All this can be transfered to qt_common
QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const {
switch (type) { switch (type) {
case InstalledEntryType::Game: case InstalledEntryType::Game:
@ -3043,12 +3039,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
} }
} }
// END
void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { 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); QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this);
progress.setWindowModality(Qt::WindowModal); progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100); progress.setMinimumDuration(100);
@ -3061,18 +3053,20 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
}; };
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback); const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
const QString resultString = tr(QtCommon::GetGameVerificationResultString(result));
progress.close(); progress.close();
switch (result) { switch (result) {
case ContentManager::GameVerificationResult::Success: case ContentManager::GameVerificationResult::Success:
QMessageBox::information(this, tr("Integrity verification succeeded!"), QMessageBox::information(this, tr("Integrity verification succeeded!"),
tr("The operation completed successfully.")); resultString);
break; break;
case ContentManager::GameVerificationResult::Failed: case ContentManager::GameVerificationResult::Failed:
QMessageBox::critical(this, tr("Integrity verification failed!"), QMessageBox::critical(this, tr("Integrity verification failed!"),
tr("File contents may be corrupt.")); resultString);
break; break;
case ContentManager::GameVerificationResult::NotImplemented: case ContentManager::GameVerificationResult::NotImplemented:
NotImplemented(); QMessageBox::warning(this, tr("Integrity verification couldn't be performed"),
resultString);
} }
} }
@ -3094,109 +3088,8 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory)); 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 {
#ifdef _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;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX
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;
#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 // 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) { bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
int result = 0; int result = 0;
QMessageBox::StandardButtons buttons; QMessageBox::StandardButtons buttons;
@ -3227,33 +3120,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(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
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, void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
GameListShortcutTarget target) { GameListShortcutTarget target) {
// Create shortcut // Create shortcut
@ -4288,28 +4154,27 @@ void GMainWindow::LoadAmiibo(const QString& filename) {
} }
void GMainWindow::OnOpenRootDataFolder() { void GMainWindow::OnOpenRootDataFolder() {
QDesktopServices::openUrl( QtCommon::OpenRootDataFolder();
QUrl(QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir))));
} }
void GMainWindow::OnOpenNANDFolder() { void GMainWindow::OnOpenNANDFolder()
QDesktopServices::openUrl(QUrl::fromLocalFile( {
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)))); QtCommon::OpenNANDFolder();
} }
void GMainWindow::OnOpenSDMCFolder() { void GMainWindow::OnOpenSDMCFolder()
QDesktopServices::openUrl(QUrl::fromLocalFile( {
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)))); QtCommon::OpenSDMCFolder();
} }
void GMainWindow::OnOpenModFolder() { void GMainWindow::OnOpenModFolder()
QDesktopServices::openUrl(QUrl::fromLocalFile( {
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir)))); QtCommon::OpenModFolder();
} }
void GMainWindow::OnOpenLogFolder() { void GMainWindow::OnOpenLogFolder()
QDesktopServices::openUrl(QUrl::fromLocalFile( {
QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir)))); QtCommon::OpenLogFolder();
} }
void GMainWindow::OnVerifyInstalledContents() { void GMainWindow::OnVerifyInstalledContents() {
@ -4444,32 +4309,14 @@ void GMainWindow::OnInstallFirmwareFromZIP() {
return; return;
} }
namespace fs = std::filesystem; const QString qCacheDir = QtCommon::UnzipFirmwareToTmp(firmware_zip_location);
fs::path tmp{std::filesystem::temp_directory_path()};
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 // In this case, it has to be done recursively, since sometimes people
// will pack it into a subdirectory after dumping // will pack it into a subdirectory after dumping
if (!qCacheDir.isEmpty()) {
InstallFirmware(qCacheDir, true); InstallFirmware(qCacheDir, true);
std::error_code ec; std::error_code ec;
std::filesystem::remove_all(tmp, ec); std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", ec);
if (ec) { if (ec) {
QMessageBox::warning(this, tr("Firmware cleanup failed"), QMessageBox::warning(this, tr("Firmware cleanup failed"),
@ -4478,14 +4325,7 @@ void GMainWindow::OnInstallFirmwareFromZIP() {
"again.\nOS reported error: %1") "again.\nOS reported error: %1")
.arg(QString::fromStdString(ec.message()))); .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() { void GMainWindow::OnInstallDecryptionKeys() {
@ -4764,10 +4604,8 @@ std::filesystem::path GMainWindow::GetShortcutPath(GameListShortcutTarget target
return shortcut_path; return shortcut_path;
} }
void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program_id, 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) {
const std::string& game_title_, GameListShortcutTarget target, // Get path to Eden executable
std::string arguments_, const bool needs_title) {
// Get path to yuzu executable
std::filesystem::path command = GetEdenCommand(); std::filesystem::path command = GetEdenCommand();
// Shortcut path // Shortcut path
@ -4819,10 +4657,16 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
QImage icon_data = QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path; 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)) { if (!SaveIconToFile(out_icon_path, icon_data)) {
LOG_ERROR(Frontend, "Could not write icon to file"); 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(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) #if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
@ -4843,12 +4687,12 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) { this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) {
arguments = "-f " + arguments; 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 categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;"; const std::string keywords = "Switch;Nintendo;";
if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, command, arguments, if (QtCommon::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
categories, keywords, game_title)) { arguments, categories, keywords, game_title)) {
GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
qgame_title); qgame_title);
return; return;
@ -4876,6 +4720,7 @@ void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target) {
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
const auto game_path = qlaunch_applet_nca->GetFullPath(); 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); CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
} }

View file

@ -478,15 +478,6 @@ private:
QString GetTasStateDescription() const; QString GetTasStateDescription() const;
bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); 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);
bool OnCheckNcaVerification();
/** /**
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog