Compare commits
10 commits
3409fe779e
...
6f5d7e0844
Author | SHA1 | Date | |
---|---|---|---|
6f5d7e0844 | |||
4b5a8e0621 | |||
39e27bc954 | |||
21c77bdcac | |||
1c3ca17cfb | |||
7ca197d900 | |||
3b4c1beb0c | |||
76de9d6c8c | |||
ab015bc730 | |||
f005f6a3ab |
18 changed files with 235 additions and 118 deletions
|
@ -1,6 +1,6 @@
|
||||||
AppRun
|
AppRun
|
||||||
eden.desktop
|
eden.desktop
|
||||||
org.eden_emu.eden.desktop
|
dev.eden_emu.eden.desktop
|
||||||
shared/bin/eden
|
shared/bin/eden
|
||||||
shared/lib/lib.path
|
shared/lib/lib.path
|
||||||
shared/lib/ld-linux-x86-64.so.2
|
shared/lib/ld-linux-x86-64.so.2
|
||||||
|
|
|
@ -59,15 +59,15 @@ VERSION="$(echo "$EDEN_TAG")"
|
||||||
mkdir -p ./AppDir
|
mkdir -p ./AppDir
|
||||||
cd ./AppDir
|
cd ./AppDir
|
||||||
|
|
||||||
cp ../dist/org.eden_emu.eden.desktop .
|
cp ../dist/dev.eden_emu.eden.desktop .
|
||||||
cp ../dist/org.eden_emu.eden.svg .
|
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'
|
UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync'
|
||||||
|
|
||||||
if [ "$DEVEL" = 'true' ]; then
|
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|')"
|
UPINFO="$(echo "$UPINFO" | sed 's|Releases|nightly|')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
which png2icns || [ which yay && yay libicns ] || exit
|
which png2icns || [ which yay && yay libicns ] || exit
|
||||||
which magick || 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
|
svgo --multipass $EDEN_SVG_ICO
|
||||||
|
|
||||||
magick -density 256x256 -background transparent $EDEN_SVG_ICO \
|
magick -density 256x256 -background transparent $EDEN_SVG_ICO \
|
||||||
|
|
|
@ -416,8 +416,10 @@ if (YUZU_USE_CPM)
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
# boost sucks
|
# boost sucks
|
||||||
if (NOT PLATFORM_LINUX AND NOT ANDROID)
|
# Solaris (and probably other NIXes) need explicit pthread definition
|
||||||
target_compile_definitions(boost_container INTERFACE BOOST_HAS_PTHREADS)
|
if (PLATFORM_SUN)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthreads")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(boost_heap INTERFACE -Wno-shadow)
|
target_compile_options(boost_heap INTERFACE -Wno-shadow)
|
||||||
|
@ -866,14 +868,14 @@ endif()
|
||||||
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||||
# https://www.freedesktop.org/software/appstream/docs/
|
# https://www.freedesktop.org/software/appstream/docs/
|
||||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
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")
|
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")
|
DESTINATION "share/icons/hicolor/scalable/apps")
|
||||||
|
|
||||||
# TODO: these files need to be updated.
|
# 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")
|
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")
|
DESTINATION "share/metainfo")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -10,7 +10,7 @@ Type=Application
|
||||||
Name=Eden
|
Name=Eden
|
||||||
GenericName=Switch Emulator
|
GenericName=Switch Emulator
|
||||||
Comment=Nintendo Switch video game console emulator
|
Comment=Nintendo Switch video game console emulator
|
||||||
Icon=org.eden_emu.eden
|
Icon=dev.eden_emu.eden
|
||||||
TryExec=eden
|
TryExec=eden
|
||||||
Exec=eden %f
|
Exec=eden %f
|
||||||
Categories=Game;Emulator;Qt;
|
Categories=Game;Emulator;Qt;
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
28
externals/ffmpeg/CMakeLists.txt
vendored
28
externals/ffmpeg/CMakeLists.txt
vendored
|
@ -63,20 +63,22 @@ if (NOT WIN32 AND NOT ANDROID)
|
||||||
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
|
||||||
set(FFmpeg_HWACCEL_LDFLAGS)
|
set(FFmpeg_HWACCEL_LDFLAGS)
|
||||||
|
|
||||||
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
|
if (NOT APPLE)
|
||||||
if(PLATFORM_SUN)
|
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
|
||||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
if(PLATFORM_SUN)
|
||||||
X11
|
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||||
"/usr/lib/xorg/amd64/libdrm.so")
|
X11
|
||||||
else()
|
"/usr/lib/xorg/amd64/libdrm.so")
|
||||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
else()
|
||||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||||
${LIBDRM_LIBRARIES})
|
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
${LIBDRM_LIBRARIES})
|
||||||
${LIBDRM_INCLUDE_DIRS})
|
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||||
|
${LIBDRM_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||||
|
--enable-libdrm)
|
||||||
endif()
|
endif()
|
||||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
|
||||||
--enable-libdrm)
|
|
||||||
|
|
||||||
if(LIBVA_FOUND)
|
if(LIBVA_FOUND)
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
|
|
|
@ -124,11 +124,16 @@ object CustomSettingsHandler {
|
||||||
|
|
||||||
// Check for driver requirements if activity and driverViewModel are provided
|
// Check for driver requirements if activity and driverViewModel are provided
|
||||||
if (activity != null && driverViewModel != null) {
|
if (activity != null && driverViewModel != null) {
|
||||||
val driverPath = extractDriverPath(customSettings)
|
val rawDriverPath = extractDriverPath(customSettings)
|
||||||
if (driverPath != null) {
|
if (rawDriverPath != null) {
|
||||||
Log.info("[CustomSettingsHandler] Custom settings specify driver: $driverPath")
|
// 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
|
// Check if driver exists in the driver storage
|
||||||
val driverFile = File(driverPath)
|
val driverFile = File(localDriverPath)
|
||||||
if (!driverFile.exists()) {
|
if (!driverFile.exists()) {
|
||||||
Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}")
|
Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}")
|
||||||
|
|
||||||
|
@ -182,7 +187,7 @@ object CustomSettingsHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to download and install the driver
|
// 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())
|
progressChannel.trySend(progress.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,12 +214,12 @@ object CustomSettingsHandler {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the downloaded driver
|
// Verify the downloaded driver (from normalized local path)
|
||||||
val installedFile = File(driverPath)
|
val installedFile = File(localDriverPath)
|
||||||
val metadata = GpuDriverHelper.getMetadataFromZip(installedFile)
|
val metadata = GpuDriverHelper.getMetadataFromZip(installedFile)
|
||||||
if (metadata.name == null) {
|
if (metadata.name == null) {
|
||||||
Log.error(
|
Log.error(
|
||||||
"[CustomSettingsHandler] Downloaded driver is invalid: $driverPath"
|
"[CustomSettingsHandler] Downloaded driver is invalid: $localDriverPath"
|
||||||
)
|
)
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
activity,
|
activity,
|
||||||
|
@ -232,7 +237,7 @@ object CustomSettingsHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to driver list
|
// Add to driver list
|
||||||
driverViewModel.onDriverAdded(Pair(driverPath, metadata))
|
driverViewModel.onDriverAdded(Pair(localDriverPath, metadata))
|
||||||
Log.info(
|
Log.info(
|
||||||
"[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}"
|
"[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}"
|
||||||
)
|
)
|
||||||
|
@ -268,7 +273,7 @@ object CustomSettingsHandler {
|
||||||
// Driver exists, verify it's valid
|
// Driver exists, verify it's valid
|
||||||
val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
|
val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
|
||||||
if (metadata.name == null) {
|
if (metadata.name == null) {
|
||||||
Log.error("[CustomSettingsHandler] Invalid driver file: $driverPath")
|
Log.error("[CustomSettingsHandler] Invalid driver file: $localDriverPath")
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
activity,
|
activity,
|
||||||
activity.getString(
|
activity.getString(
|
||||||
|
@ -459,6 +464,8 @@ object CustomSettingsHandler {
|
||||||
|
|
||||||
if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
|
if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
|
||||||
return trimmed.substringAfter("driver_path=")
|
return trimmed.substringAfter("driver_path=")
|
||||||
|
.trim()
|
||||||
|
.removeSurrounding("\"", "\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,48 @@ object DriverResolver {
|
||||||
val filename: String
|
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
|
* Resolve a driver download URL from its filename
|
||||||
* @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip")
|
* @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip")
|
||||||
|
@ -98,7 +140,7 @@ object DriverResolver {
|
||||||
async {
|
async {
|
||||||
searchRepository(repoPath, filename)
|
searchRepository(repoPath, filename)
|
||||||
}
|
}
|
||||||
}.mapNotNull { it.await() }.firstOrNull().also { resolved ->
|
}.firstNotNullOfOrNull { it.await() }.also { resolved ->
|
||||||
// Cache the result if found
|
// Cache the result if found
|
||||||
resolved?.let {
|
resolved?.let {
|
||||||
urlCache[filename] = it
|
urlCache[filename] = it
|
||||||
|
@ -119,22 +161,56 @@ object DriverResolver {
|
||||||
releaseCache[repoPath] = it
|
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 (release in releases) {
|
||||||
for (artifact in release.artifacts) {
|
for (artifact in release.artifacts) {
|
||||||
if (artifact.name == filename) {
|
if (artifact.name.equals(filename, ignoreCase = true) || artifact.name.lowercase() == target) {
|
||||||
Log.info(
|
Log.info("[DriverResolver] Found $filename in $repoPath/${release.tagName}")
|
||||||
"[DriverResolver] Found $filename in $repoPath/${release.tagName}"
|
|
||||||
)
|
|
||||||
return@withContext ResolvedDriver(
|
return@withContext ResolvedDriver(
|
||||||
downloadUrl = artifact.url.toString(),
|
downloadUrl = artifact.url.toString(),
|
||||||
repoPath = repoPath,
|
repoPath = repoPath,
|
||||||
releaseTag = release.tagName,
|
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
|
null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}")
|
Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}")
|
||||||
|
@ -296,8 +372,8 @@ object DriverResolver {
|
||||||
context: Context,
|
context: Context,
|
||||||
onProgress: ((Float) -> Unit)? = null
|
onProgress: ((Float) -> Unit)? = null
|
||||||
): Uri? {
|
): Uri? {
|
||||||
// Extract filename from path
|
// Extract filename from path (support both separators)
|
||||||
val filename = driverPath.substringAfterLast('/')
|
val filename = driverPath.substringAfterLast('/').substringAfterLast('\\')
|
||||||
|
|
||||||
// Check if driver already exists locally
|
// Check if driver already exists locally
|
||||||
val localPath = "${GpuDriverHelper.driverStoragePath}$filename"
|
val localPath = "${GpuDriverHelper.driverStoragePath}$filename"
|
||||||
|
|
|
@ -417,14 +417,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
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);
|
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;
|
return virtual_base;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
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.
|
/* This file is part of the dynarmic project.
|
||||||
* Copyright (c) 2018 MerryMage
|
* Copyright (c) 2018 MerryMage
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
@ -19,6 +22,16 @@
|
||||||
|
|
||||||
namespace Dynarmic::Common {
|
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>
|
template<typename Function, typename... Values>
|
||||||
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -34,4 +47,11 @@ inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
|
||||||
return MapT(pair_array.begin(), pair_array.end());
|
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
|
} // namespace Dynarmic::Common
|
||||||
|
|
|
@ -1378,13 +1378,13 @@ void Device::CollectPhysicalMemoryInfo() {
|
||||||
device_access_memory += mem_properties.memoryHeaps[element].size;
|
device_access_memory += mem_properties.memoryHeaps[element].size;
|
||||||
}
|
}
|
||||||
if (!is_integrated) {
|
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;
|
device_access_memory -= reserve_memory;
|
||||||
|
|
||||||
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
|
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
|
||||||
// Account for resolution scaling in memory limits
|
// Account for resolution scaling in memory limits
|
||||||
const size_t normal_memory = 8_GiB;
|
const size_t normal_memory = 6_GiB;
|
||||||
const size_t scaler_memory = 2_GiB * Settings::values.resolution_info.ScaleUp(1);
|
const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
|
||||||
device_access_memory =
|
device_access_memory =
|
||||||
std::min<u64>(device_access_memory, normal_memory + scaler_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);
|
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
|
||||||
device_access_memory = static_cast<u64>(std::max<s64>(
|
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() {
|
void Device::CollectToolingInfo() {
|
||||||
|
|
|
@ -553,9 +553,6 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
||||||
// Gen keys if necessary
|
// Gen keys if necessary
|
||||||
OnCheckFirmwareDecryption();
|
OnCheckFirmwareDecryption();
|
||||||
|
|
||||||
// Check firmware
|
|
||||||
OnCheckFirmware();
|
|
||||||
|
|
||||||
game_list->LoadCompatibilityList();
|
game_list->LoadCompatibilityList();
|
||||||
// force reload on first load to ensure add-ons get updated
|
// 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, false);
|
||||||
|
@ -3094,34 +3091,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
|
||||||
const std::filesystem::path& command,
|
const std::filesystem::path& command,
|
||||||
const std::string& arguments, const std::string& categories,
|
const std::string& arguments, const std::string& categories,
|
||||||
const std::string& keywords, const std::string& name) try {
|
const std::string& keywords, const std::string& name) try {
|
||||||
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
|
#ifdef _WIN32 // Windows
|
||||||
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);
|
HRESULT hr = CoInitialize(nullptr);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
LOG_ERROR(Frontend, "CoInitialize failed");
|
LOG_ERROR(Frontend, "CoInitialize failed");
|
||||||
|
@ -3183,7 +3153,34 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
#else // Unsupported platform
|
#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;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -3228,7 +3225,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
|
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
|
||||||
ico_extension = "ico";
|
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";
|
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
|
||||||
#endif
|
#endif
|
||||||
// Create icons directory if it doesn't exist
|
// Create icons directory if it doesn't exist
|
||||||
|
@ -4459,7 +4456,6 @@ void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
|
||||||
|
|
||||||
progress.close();
|
progress.close();
|
||||||
OnCheckFirmwareDecryption();
|
OnCheckFirmwareDecryption();
|
||||||
OnCheckFirmware();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnInstallFirmware() {
|
void GMainWindow::OnInstallFirmware() {
|
||||||
|
@ -4580,7 +4576,6 @@ void GMainWindow::OnInstallDecryptionKeys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
OnCheckFirmwareDecryption();
|
OnCheckFirmwareDecryption();
|
||||||
OnCheckFirmware();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnAbout() {
|
void GMainWindow::OnAbout() {
|
||||||
|
@ -4609,6 +4604,7 @@ void GMainWindow::OnToggleStatusBar() {
|
||||||
void GMainWindow::OnGameListRefresh() {
|
void GMainWindow::OnGameListRefresh() {
|
||||||
// force reload add-ons etc
|
// force reload add-ons etc
|
||||||
game_list->ForceRefreshGameDirectory();
|
game_list->ForceRefreshGameDirectory();
|
||||||
|
SetFirmwareVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnAlbum() {
|
void GMainWindow::OnAlbum() {
|
||||||
|
@ -4707,13 +4703,42 @@ void GMainWindow::OnOpenControllerMenu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnHomeMenu() {
|
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);
|
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||||
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
|
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);
|
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||||
if (!qlaunch_applet_nca) {
|
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
|
// Special case for AppImages
|
||||||
// Warn once if we are making a shortcut to a volatile AppImage
|
// Warn once if we are making a shortcut to a volatile AppImage
|
||||||
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
|
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;
|
UISettings::values.shortcut_already_warned = true;
|
||||||
}
|
}
|
||||||
#endif // __linux__
|
#endif
|
||||||
|
|
||||||
// Create shortcut
|
// Create shortcut
|
||||||
std::string arguments{arguments_};
|
std::string arguments{arguments_};
|
||||||
|
@ -5240,19 +5265,6 @@ void GMainWindow::OnCheckFirmwareDecryption() {
|
||||||
UpdateMenuState();
|
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() {
|
bool GMainWindow::CheckFirmwarePresence() {
|
||||||
return FirmwareManager::CheckFirmwarePresence(*system.get());
|
return FirmwareManager::CheckFirmwarePresence(*system.get());
|
||||||
}
|
}
|
||||||
|
@ -5730,17 +5742,13 @@ int main(int argc, char* argv[]) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Increases the maximum open file limit to 8192
|
// Increases the maximum open file limit to 8192
|
||||||
_setmaxstdio(8192);
|
_setmaxstdio(8192);
|
||||||
#endif
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
|
// 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
|
// 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
|
// the user folder in the Qt Frontend, we need to cd into that working directory
|
||||||
const auto bin_path = Common::FS::GetBundleDirectory() / "..";
|
const auto bin_path = Common::FS::GetBundleDirectory() / "..";
|
||||||
chdir(Common::FS::PathToUTF8String(bin_path).c_str());
|
chdir(Common::FS::PathToUTF8String(bin_path).c_str());
|
||||||
#endif
|
#elif defined(__unix__) && !defined(__ANDROID__)
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
// Set the DISPLAY variable in order to open web browsers
|
// Set the DISPLAY variable in order to open web browsers
|
||||||
// TODO (lat9nq): Find a better solution for AppImages to start external applications
|
// TODO (lat9nq): Find a better solution for AppImages to start external applications
|
||||||
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
|
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
|
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
|
||||||
// suffix.
|
// suffix.
|
||||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden"));
|
QGuiApplication::setDesktopFileName(QStringLiteral("dev.eden_emu.eden"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SetHighDPIAttributes();
|
SetHighDPIAttributes();
|
||||||
|
|
|
@ -424,7 +424,6 @@ private slots:
|
||||||
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
|
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
|
||||||
void OnCaptureScreenshot();
|
void OnCaptureScreenshot();
|
||||||
void OnCheckFirmwareDecryption();
|
void OnCheckFirmwareDecryption();
|
||||||
void OnCheckFirmware();
|
|
||||||
void OnLanguageChanged(const QString& locale);
|
void OnLanguageChanged(const QString& locale);
|
||||||
void OnMouseActivity();
|
void OnMouseActivity();
|
||||||
bool OnShutdownBegin();
|
bool OnShutdownBegin();
|
||||||
|
|
|
@ -212,6 +212,9 @@ struct Values {
|
||||||
// Play time
|
// Play time
|
||||||
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
|
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 configuration_applied;
|
||||||
bool reset_to_defaults;
|
bool reset_to_defaults;
|
||||||
bool shortcut_already_warned{false};
|
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-FileCopyrightText: 2015 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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();
|
icon_file.Close();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
|
||||||
// Convert and write the icon as a PNG
|
// Convert and write the icon as a PNG
|
||||||
if (!image.save(QString::fromStdString(icon_path.string()))) {
|
if (!image.save(QString::fromStdString(icon_path.string()))) {
|
||||||
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
|
LOG_ERROR(Frontend, "Could not write icon as PNG to file");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue