Compare commits
10 commits
8e387dc156
...
7c19195e49
Author | SHA1 | Date | |
---|---|---|---|
7c19195e49 | |||
4b5a8e0621 | |||
39e27bc954 | |||
21c77bdcac | |||
1c3ca17cfb | |||
7ca197d900 | |||
3b4c1beb0c | |||
76de9d6c8c | |||
ab015bc730 | |||
f005f6a3ab |
18 changed files with 235 additions and 118 deletions
|
@ -1,6 +1,6 @@
|
|||
AppRun
|
||||
eden.desktop
|
||||
org.eden_emu.eden.desktop
|
||||
dev.eden_emu.eden.desktop
|
||||
shared/bin/eden
|
||||
shared/lib/lib.path
|
||||
shared/lib/ld-linux-x86-64.so.2
|
||||
|
|
|
@ -59,15 +59,15 @@ VERSION="$(echo "$EDEN_TAG")"
|
|||
mkdir -p ./AppDir
|
||||
cd ./AppDir
|
||||
|
||||
cp ../dist/org.eden_emu.eden.desktop .
|
||||
cp ../dist/org.eden_emu.eden.svg .
|
||||
cp ../dist/dev.eden_emu.eden.desktop .
|
||||
cp ../dist/dev.eden_emu.eden.svg .
|
||||
|
||||
ln -sf ./org.eden_emu.eden.svg ./.DirIcon
|
||||
ln -sf ./dev.eden_emu.eden.svg ./.DirIcon
|
||||
|
||||
UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync'
|
||||
|
||||
if [ "$DEVEL" = 'true' ]; then
|
||||
sed -i 's|Name=Eden|Name=Eden Nightly|' ./org.eden_emu.eden.desktop
|
||||
sed -i 's|Name=Eden|Name=Eden Nightly|' ./dev.eden_emu.eden.desktop
|
||||
UPINFO="$(echo "$UPINFO" | sed 's|Releases|nightly|')"
|
||||
fi
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
which png2icns || [ which yay && yay libicns ] || exit
|
||||
which magick || exit
|
||||
|
||||
export EDEN_SVG_ICO="dist/org.eden_emu.eden.svg"
|
||||
export EDEN_SVG_ICO="dist/dev.eden_emu.eden.svg"
|
||||
svgo --multipass $EDEN_SVG_ICO
|
||||
|
||||
magick -density 256x256 -background transparent $EDEN_SVG_ICO \
|
||||
|
|
|
@ -406,8 +406,10 @@ if (YUZU_USE_CPM)
|
|||
|
||||
if (NOT MSVC)
|
||||
# boost sucks
|
||||
if (NOT PLATFORM_LINUX AND NOT ANDROID)
|
||||
target_compile_definitions(boost_container INTERFACE BOOST_HAS_PTHREADS)
|
||||
# Solaris (and probably other NIXes) need explicit pthread definition
|
||||
if (PLATFORM_SUN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthreads")
|
||||
endif()
|
||||
|
||||
target_compile_options(boost_heap INTERFACE -Wno-shadow)
|
||||
|
@ -856,14 +858,14 @@ endif()
|
|||
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
# https://www.freedesktop.org/software/appstream/docs/
|
||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "dist/org.eden_emu.eden.desktop"
|
||||
install(FILES "dist/dev.eden_emu.eden.desktop"
|
||||
DESTINATION "share/applications")
|
||||
install(FILES "dist/org.eden_emu.eden.svg"
|
||||
install(FILES "dist/dev.eden_emu.eden.svg"
|
||||
DESTINATION "share/icons/hicolor/scalable/apps")
|
||||
|
||||
# TODO: these files need to be updated.
|
||||
install(FILES "dist/org.eden_emu.eden.xml"
|
||||
install(FILES "dist/dev.eden_emu.eden.xml"
|
||||
DESTINATION "share/mime/packages")
|
||||
install(FILES "dist/org.eden_emu.eden.metainfo.xml"
|
||||
install(FILES "dist/dev.eden_emu.eden.metainfo.xml"
|
||||
DESTINATION "share/metainfo")
|
||||
endif()
|
||||
|
|
|
@ -10,7 +10,7 @@ Type=Application
|
|||
Name=Eden
|
||||
GenericName=Switch Emulator
|
||||
Comment=Nintendo Switch video game console emulator
|
||||
Icon=org.eden_emu.eden
|
||||
Icon=dev.eden_emu.eden
|
||||
TryExec=eden
|
||||
Exec=eden %f
|
||||
Categories=Game;Emulator;Qt;
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
2
externals/ffmpeg/CMakeLists.txt
vendored
2
externals/ffmpeg/CMakeLists.txt
vendored
|
@ -63,6 +63,7 @@ if (NOT WIN32 AND NOT ANDROID)
|
|||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||
|
||||
if (NOT APPLE)
|
||||
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
|
||||
if(PLATFORM_SUN)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
|
@ -77,6 +78,7 @@ if (NOT WIN32 AND NOT ANDROID)
|
|||
endif()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-libdrm)
|
||||
endif()
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
find_package(X11 REQUIRED)
|
||||
|
|
|
@ -124,11 +124,16 @@ object CustomSettingsHandler {
|
|||
|
||||
// Check for driver requirements if activity and driverViewModel are provided
|
||||
if (activity != null && driverViewModel != null) {
|
||||
val driverPath = extractDriverPath(customSettings)
|
||||
if (driverPath != null) {
|
||||
Log.info("[CustomSettingsHandler] Custom settings specify driver: $driverPath")
|
||||
val rawDriverPath = extractDriverPath(customSettings)
|
||||
if (rawDriverPath != null) {
|
||||
// Normalize to local storage path (we only store drivers under driverStoragePath)
|
||||
val driverFilename = rawDriverPath.substringAfterLast('/')
|
||||
.substringAfterLast('\\')
|
||||
val localDriverPath = "${GpuDriverHelper.driverStoragePath}$driverFilename"
|
||||
Log.info("[CustomSettingsHandler] Custom settings specify driver: $rawDriverPath (normalized: $localDriverPath)")
|
||||
|
||||
// Check if driver exists in the driver storage
|
||||
val driverFile = File(driverPath)
|
||||
val driverFile = File(localDriverPath)
|
||||
if (!driverFile.exists()) {
|
||||
Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}")
|
||||
|
||||
|
@ -182,7 +187,7 @@ object CustomSettingsHandler {
|
|||
}
|
||||
|
||||
// Attempt to download and install the driver
|
||||
val driverUri = DriverResolver.ensureDriverAvailable(driverPath, activity) { progress ->
|
||||
val driverUri = DriverResolver.ensureDriverAvailable(driverFilename, activity) { progress ->
|
||||
progressChannel.trySend(progress.toInt())
|
||||
}
|
||||
|
||||
|
@ -209,12 +214,12 @@ object CustomSettingsHandler {
|
|||
return null
|
||||
}
|
||||
|
||||
// Verify the downloaded driver
|
||||
val installedFile = File(driverPath)
|
||||
// Verify the downloaded driver (from normalized local path)
|
||||
val installedFile = File(localDriverPath)
|
||||
val metadata = GpuDriverHelper.getMetadataFromZip(installedFile)
|
||||
if (metadata.name == null) {
|
||||
Log.error(
|
||||
"[CustomSettingsHandler] Downloaded driver is invalid: $driverPath"
|
||||
"[CustomSettingsHandler] Downloaded driver is invalid: $localDriverPath"
|
||||
)
|
||||
Toast.makeText(
|
||||
activity,
|
||||
|
@ -232,7 +237,7 @@ object CustomSettingsHandler {
|
|||
}
|
||||
|
||||
// Add to driver list
|
||||
driverViewModel.onDriverAdded(Pair(driverPath, metadata))
|
||||
driverViewModel.onDriverAdded(Pair(localDriverPath, metadata))
|
||||
Log.info(
|
||||
"[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}"
|
||||
)
|
||||
|
@ -268,7 +273,7 @@ object CustomSettingsHandler {
|
|||
// Driver exists, verify it's valid
|
||||
val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
|
||||
if (metadata.name == null) {
|
||||
Log.error("[CustomSettingsHandler] Invalid driver file: $driverPath")
|
||||
Log.error("[CustomSettingsHandler] Invalid driver file: $localDriverPath")
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.getString(
|
||||
|
@ -459,6 +464,8 @@ object CustomSettingsHandler {
|
|||
|
||||
if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
|
||||
return trimmed.substringAfter("driver_path=")
|
||||
.trim()
|
||||
.removeSurrounding("\"", "\"")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,48 @@ object DriverResolver {
|
|||
val filename: String
|
||||
)
|
||||
|
||||
// Matching helpers
|
||||
private val KNOWN_SUFFIXES = listOf(
|
||||
".adpkg.zip",
|
||||
".zip",
|
||||
".7z",
|
||||
".tar.gz",
|
||||
".tar.xz",
|
||||
".rar"
|
||||
)
|
||||
|
||||
private fun stripKnownSuffixes(name: String): String {
|
||||
var result = name
|
||||
var changed: Boolean
|
||||
do {
|
||||
changed = false
|
||||
for (s in KNOWN_SUFFIXES) {
|
||||
if (result.endsWith(s, ignoreCase = true)) {
|
||||
result = result.dropLast(s.length)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
} while (changed)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun normalizeName(name: String): String {
|
||||
val base = stripKnownSuffixes(name.lowercase())
|
||||
// Remove non-alphanumerics to make substring checks resilient
|
||||
return base.replace(Regex("[^a-z0-9]+"), " ").trim()
|
||||
}
|
||||
|
||||
private fun tokenize(name: String): Set<String> =
|
||||
normalizeName(name).split(Regex("\\s+")).filter { it.isNotBlank() }.toSet()
|
||||
|
||||
// Jaccard similarity between two sets
|
||||
private fun jaccard(a: Set<String>, b: Set<String>): Double {
|
||||
if (a.isEmpty() || b.isEmpty()) return 0.0
|
||||
val inter = a.intersect(b).size.toDouble()
|
||||
val uni = a.union(b).size.toDouble()
|
||||
return if (uni == 0.0) 0.0 else inter / uni
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a driver download URL from its filename
|
||||
* @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip")
|
||||
|
@ -98,7 +140,7 @@ object DriverResolver {
|
|||
async {
|
||||
searchRepository(repoPath, filename)
|
||||
}
|
||||
}.mapNotNull { it.await() }.firstOrNull().also { resolved ->
|
||||
}.firstNotNullOfOrNull { it.await() }.also { resolved ->
|
||||
// Cache the result if found
|
||||
resolved?.let {
|
||||
urlCache[filename] = it
|
||||
|
@ -119,22 +161,56 @@ object DriverResolver {
|
|||
releaseCache[repoPath] = it
|
||||
}
|
||||
|
||||
// Search through all releases and artifacts
|
||||
// First pass: exact name (case-insensitive) against asset filenames
|
||||
val target = filename.lowercase()
|
||||
for (release in releases) {
|
||||
for (artifact in release.artifacts) {
|
||||
if (artifact.name == filename) {
|
||||
Log.info(
|
||||
"[DriverResolver] Found $filename in $repoPath/${release.tagName}"
|
||||
)
|
||||
if (artifact.name.equals(filename, ignoreCase = true) || artifact.name.lowercase() == target) {
|
||||
Log.info("[DriverResolver] Found $filename in $repoPath/${release.tagName}")
|
||||
return@withContext ResolvedDriver(
|
||||
downloadUrl = artifact.url.toString(),
|
||||
repoPath = repoPath,
|
||||
releaseTag = release.tagName,
|
||||
filename = filename
|
||||
filename = artifact.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: fuzzy match by asset filenames only
|
||||
val reqNorm = normalizeName(filename)
|
||||
val reqTokens = tokenize(filename)
|
||||
var best: ResolvedDriver? = null
|
||||
var bestScore = 0.0
|
||||
|
||||
for (release in releases) {
|
||||
for (artifact in release.artifacts) {
|
||||
val artNorm = normalizeName(artifact.name)
|
||||
val artTokens = tokenize(artifact.name)
|
||||
|
||||
var score = jaccard(reqTokens, artTokens)
|
||||
// Boost if one normalized name contains the other
|
||||
if (artNorm.contains(reqNorm) || reqNorm.contains(artNorm)) {
|
||||
score = maxOf(score, 0.92)
|
||||
}
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score
|
||||
best = ResolvedDriver(
|
||||
downloadUrl = artifact.url.toString(),
|
||||
repoPath = repoPath,
|
||||
releaseTag = release.tagName,
|
||||
filename = artifact.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Threshold to avoid bad guesses, this worked fine in testing but might need tuning
|
||||
if (best != null && bestScore >= 0.6) {
|
||||
Log.info("[DriverResolver] Fuzzy matched $filename -> ${best.filename} in ${best.repoPath} (score=%.2f)".format(bestScore))
|
||||
return@withContext best
|
||||
}
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}")
|
||||
|
@ -296,8 +372,8 @@ object DriverResolver {
|
|||
context: Context,
|
||||
onProgress: ((Float) -> Unit)? = null
|
||||
): Uri? {
|
||||
// Extract filename from path
|
||||
val filename = driverPath.substringAfterLast('/')
|
||||
// Extract filename from path (support both separators)
|
||||
val filename = driverPath.substringAfterLast('/').substringAfterLast('\\')
|
||||
|
||||
// Check if driver already exists locally
|
||||
val localPath = "${GpuDriverHelper.driverStoragePath}$filename"
|
||||
|
|
|
@ -417,14 +417,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
|||
#else
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__)
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
|
||||
if (virtual_base != MAP_FAILED) {
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
}
|
||||
#endif
|
||||
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
|
@ -19,6 +22,16 @@
|
|||
|
||||
namespace Dynarmic::Common {
|
||||
|
||||
// prevents this function from printing 56,000 character warning messages
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wno-stack-usage"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wno-stack-usage"
|
||||
#endif
|
||||
|
||||
template<typename Function, typename... Values>
|
||||
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
||||
#ifdef _MSC_VER
|
||||
|
@ -34,4 +47,11 @@ inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
|||
return MapT(pair_array.begin(), pair_array.end());
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace Dynarmic::Common
|
||||
|
|
|
@ -1378,13 +1378,13 @@ void Device::CollectPhysicalMemoryInfo() {
|
|||
device_access_memory += mem_properties.memoryHeaps[element].size;
|
||||
}
|
||||
if (!is_integrated) {
|
||||
const u64 reserve_memory = std::min<u64>(device_access_memory / 4, 2_GiB);
|
||||
const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
|
||||
device_access_memory -= reserve_memory;
|
||||
|
||||
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
|
||||
// Account for resolution scaling in memory limits
|
||||
const size_t normal_memory = 8_GiB;
|
||||
const size_t scaler_memory = 2_GiB * Settings::values.resolution_info.ScaleUp(1);
|
||||
const size_t normal_memory = 6_GiB;
|
||||
const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
|
||||
device_access_memory =
|
||||
std::min<u64>(device_access_memory, normal_memory + scaler_memory);
|
||||
}
|
||||
|
@ -1393,7 +1393,7 @@ void Device::CollectPhysicalMemoryInfo() {
|
|||
}
|
||||
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
|
||||
device_access_memory = static_cast<u64>(std::max<s64>(
|
||||
std::min<s64>(available_memory - 4_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB)));
|
||||
std::min<s64>(available_memory - 8_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB)));
|
||||
}
|
||||
|
||||
void Device::CollectToolingInfo() {
|
||||
|
|
|
@ -553,9 +553,6 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
|||
// Gen keys if necessary
|
||||
OnCheckFirmwareDecryption();
|
||||
|
||||
// Check firmware
|
||||
OnCheckFirmware();
|
||||
|
||||
game_list->LoadCompatibilityList();
|
||||
// force reload on first load to ensure add-ons get updated
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs, false);
|
||||
|
@ -3094,34 +3091,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_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
|
||||
#ifdef _WIN32 // Windows
|
||||
HRESULT hr = CoInitialize(nullptr);
|
||||
if (FAILED(hr)) {
|
||||
LOG_ERROR(Frontend, "CoInitialize failed");
|
||||
|
@ -3183,6 +3153,33 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
|
|||
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
|
||||
|
@ -3228,7 +3225,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
|
|||
#if defined(_WIN32)
|
||||
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
|
||||
ico_extension = "ico";
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
#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
|
||||
|
@ -4459,7 +4456,6 @@ void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
|
|||
|
||||
progress.close();
|
||||
OnCheckFirmwareDecryption();
|
||||
OnCheckFirmware();
|
||||
}
|
||||
|
||||
void GMainWindow::OnInstallFirmware() {
|
||||
|
@ -4580,7 +4576,6 @@ void GMainWindow::OnInstallDecryptionKeys() {
|
|||
}
|
||||
|
||||
OnCheckFirmwareDecryption();
|
||||
OnCheckFirmware();
|
||||
}
|
||||
|
||||
void GMainWindow::OnAbout() {
|
||||
|
@ -4609,6 +4604,7 @@ void GMainWindow::OnToggleStatusBar() {
|
|||
void GMainWindow::OnGameListRefresh() {
|
||||
// force reload add-ons etc
|
||||
game_list->ForceRefreshGameDirectory();
|
||||
SetFirmwareVersion();
|
||||
}
|
||||
|
||||
void GMainWindow::OnAlbum() {
|
||||
|
@ -4707,13 +4703,42 @@ void GMainWindow::OnOpenControllerMenu() {
|
|||
}
|
||||
|
||||
void GMainWindow::OnHomeMenu() {
|
||||
auto result = FirmwareManager::VerifyFirmware(*system.get());
|
||||
|
||||
switch (result) {
|
||||
case FirmwareManager::ErrorFirmwareMissing:
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Home Menu."));
|
||||
return;
|
||||
case FirmwareManager::ErrorFirmwareCorrupted:
|
||||
QMessageBox::warning(this, tr("Firmware Corrupted"),
|
||||
tr(FirmwareManager::GetFirmwareCheckString(result)));
|
||||
return;
|
||||
case FirmwareManager::ErrorFirmwareTooNew: {
|
||||
if (!UISettings::values.show_fw_warning.GetValue()) break;
|
||||
|
||||
QMessageBox box(QMessageBox::Warning,
|
||||
tr("Firmware Too New"),
|
||||
tr(FirmwareManager::GetFirmwareCheckString(result)) + tr("\nContinue anyways?"),
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
this);
|
||||
|
||||
QCheckBox *checkbox = new QCheckBox(tr("Don't show again"));
|
||||
box.setCheckBox(checkbox);
|
||||
|
||||
int button = box.exec();
|
||||
if (checkbox->isChecked()) {
|
||||
UISettings::values.show_fw_warning.SetValue(false);
|
||||
}
|
||||
|
||||
if (button == static_cast<int>(QMessageBox::No)) return;
|
||||
break;
|
||||
} default:
|
||||
break;
|
||||
}
|
||||
|
||||
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||
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."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||
if (!qlaunch_applet_nca) {
|
||||
|
@ -4853,7 +4878,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
|
||||
// Special case for AppImages
|
||||
// Warn once if we are making a shortcut to a volatile AppImage
|
||||
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
|
||||
|
@ -4863,7 +4888,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
|
|||
}
|
||||
UISettings::values.shortcut_already_warned = true;
|
||||
}
|
||||
#endif // __linux__
|
||||
#endif
|
||||
|
||||
// Create shortcut
|
||||
std::string arguments{arguments_};
|
||||
|
@ -5240,19 +5265,6 @@ void GMainWindow::OnCheckFirmwareDecryption() {
|
|||
UpdateMenuState();
|
||||
}
|
||||
|
||||
void GMainWindow::OnCheckFirmware() {
|
||||
auto result = FirmwareManager::VerifyFirmware(*system.get());
|
||||
|
||||
switch (result) {
|
||||
case FirmwareManager::FirmwareGood:
|
||||
break;
|
||||
default:
|
||||
QMessageBox::warning(this, tr("Firmware Read Error"),
|
||||
tr(FirmwareManager::GetFirmwareCheckString(result)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool GMainWindow::CheckFirmwarePresence() {
|
||||
return FirmwareManager::CheckFirmwarePresence(*system.get());
|
||||
}
|
||||
|
@ -5730,17 +5742,13 @@ int main(int argc, char* argv[]) {
|
|||
#ifdef _WIN32
|
||||
// Increases the maximum open file limit to 8192
|
||||
_setmaxstdio(8192);
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
|
||||
// But since we require the working directory to be the executable path for the location of
|
||||
// the user folder in the Qt Frontend, we need to cd into that working directory
|
||||
const auto bin_path = Common::FS::GetBundleDirectory() / "..";
|
||||
chdir(Common::FS::PathToUTF8String(bin_path).c_str());
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#elif defined(__unix__) && !defined(__ANDROID__)
|
||||
// Set the DISPLAY variable in order to open web browsers
|
||||
// TODO (lat9nq): Find a better solution for AppImages to start external applications
|
||||
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
|
||||
|
@ -5749,7 +5757,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
|
||||
// suffix.
|
||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden"));
|
||||
QGuiApplication::setDesktopFileName(QStringLiteral("dev.eden_emu.eden"));
|
||||
#endif
|
||||
|
||||
SetHighDPIAttributes();
|
||||
|
|
|
@ -424,7 +424,6 @@ private slots:
|
|||
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
|
||||
void OnCaptureScreenshot();
|
||||
void OnCheckFirmwareDecryption();
|
||||
void OnCheckFirmware();
|
||||
void OnLanguageChanged(const QString& locale);
|
||||
void OnMouseActivity();
|
||||
bool OnShutdownBegin();
|
||||
|
|
|
@ -212,6 +212,9 @@ struct Values {
|
|||
// Play time
|
||||
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
|
||||
|
||||
// misc
|
||||
Setting<bool> show_fw_warning{linkage, true, "show_fw_warning", Category::Miscellaneous};
|
||||
|
||||
bool configuration_applied;
|
||||
bool reset_to_defaults;
|
||||
bool shortcut_already_warned{false};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -138,7 +141,7 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
|
|||
icon_file.Close();
|
||||
|
||||
return true;
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
|
||||
// Convert and write the icon as a PNG
|
||||
if (!image.save(QString::fromStdString(icon_path.string()))) {
|
||||
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue