Compare commits

..

17 Commits

Author SHA1 Message Date
lizzie
8d40c3b7d4 [common] log modified settings first
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-12-05 22:43:20 +01:00
lizzie
90877dfc85 [cmake] only use MoltenVk on Apple platforms (#3146)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3146
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-05 02:57:39 +01:00
kleidis
f882ff72eb android: Rework setup fragment to use multiple buttons per-page (#2854)
Adapted from f771952e62 (diff-e59f69380a076aef2745f7ab65072ca25fc26c598e2ed177475a15fe44121b4d)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2854
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: kleidis <kleidis1@protonmail.com>
Co-committed-by: kleidis <kleidis1@protonmail.com>
2025-12-04 07:33:04 +01:00
lizzie
55cc4d5ede [experiment] mbedtls force ARM64 Neon even when macros say otherwise (#2793)
more of the mbedtls trying to make macro magic even through they do runtime checks

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2793
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:32:40 +01:00
lizzie
09f03f5640 [hle/kernel] mark invalid SVC paths as unreachable (#3023)
Should improve performance of SVC by a very, very tiny margin. Codegen seems to be better from the exclusion of all domains beyond >=0x92

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3023
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:31:30 +01:00
lizzie
7a98ee4ead [vk] attempt to continue even if unsuitable driver (#3087)
rationale:
- some drivers will not outright crash (and keeping a list of those who don't crash is tedious)
- if it does crash we can get a log line saying "hey, this driver? unsuitable"
- makes lfie with lavapipe a tad bit easier
Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3087
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:30:59 +01:00
lizzie
dfd042c809 [qt] remove unused config migrator (#3130)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3130
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:29:41 +01:00
lizzie
16ca7851c5 [qt, cmd] Document & fix some inconsistencies with command line arguments (#3104)
- documents command line arguments
- allows you to specify files starting with `-`
Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3104
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:26:56 +01:00
lizzie
0eed5100f9 [input_common/mouse, sdl_driver] increase latency of mouse and SDL_vibration threads to 4 "evals" per sec (#2982)
No need to have those nano-sleeps for these threads...

Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2982
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:26:20 +01:00
lizzie
8d3b33c3aa [android] remove (enhanced) text and just shorten to (e)nhanced FPS (#3048)
less visual clutter
Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3048
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:25:39 +01:00
MrPurple666
e3c942b209 [NCE] Fix cache invalidation and signal interrupt race condition (#3063)
Inspired by PR #3047

This should theoretically fix 3 bugs in NCE:

  - **Bug 1**: `ClearInstructionCache()` now properly invalidates L1 instruction cache using IC IALLU instead of only using memory barriers
  - **Bug 2**: `InvalidateCacheRange()` implements proper range-based cache invalidation instead of always flushing entire L1 cache
  - **Bug 3**: `SignalInterrupt()` adds acquire fence to guarantee memory visibility of the `is_running` flag, preventing lost signals

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3063
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: MrPurple666 <antoniosacramento666usa@gmail.com>
Co-committed-by: MrPurple666 <antoniosacramento666usa@gmail.com>
2025-12-04 07:25:21 +01:00
lizzie
1b1e186a58 [fs] fix paths not being created due to instance not existing yet (#3134)
basically a check runs that depends on the instance being created, but instance isnt created yet so check fails
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3134
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 07:18:36 +01:00
crueter
e75ecfd4a0 [externals] change bundled MVK to Ori's fork (#3141)
Fixes the weird Metal shader crash we saw previously, so now macOS works again. Hooray!

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3141
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2025-12-04 07:18:19 +01:00
crueter
18135424df [cmake] more modules, general "cleanup" (#3126)
Successor to that old MoltenVK PR. Does a lot of cleanups within root CMakeLists.txt, hands over MoltenVK and VulkanValidationLayers to CPMUtil, and separates out common operations into my modules.

Hopefully reduces the monstrosity that is root CMakeLists.txt. Please test:

- builds on all platforms
- VulkanValidationLayers

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3126
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2025-12-04 06:00:58 +01:00
lizzie
1d2b9de496 [docs/user] add server hosting section (#3093)
Authored-by: kaorukimura
Signed-off-by: lizzie lizzie@eden-emu.dev
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3093
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-12-04 01:03:19 +01:00
crueter
fe8017734b [cmake, externals] android x86_64 support (#3086)
Updates all of our bundled CI deps to support android x86_64, adds a
build flavor thereof (`chromeOS`), and also adds sirit mingw support.

The new FFmpeg package is built in a much better way that actually makes
it identically built to the other CI packages, meaning we now have real
8.0.0 support, no need for libvpx/cpu_features/all that other crap.
PLUS, we can now statically link it! Hooray! It's also built with
MediaCodec support so in the future we can work on that.

Rewrote the android build script too, plus added a copyFlavorTypeOutput
target that assembles and copies the APK. The code behind it sucks
because I'm not great with Gradle but hey, it works.

Testers: please test everything related to video decoding. VP9 and h264, games that normally suck with their prerendered stuff, make sure I didn't nuke it to oblivion, etc.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3086
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2025-12-03 06:40:11 +01:00
Caio Oliveira
33ee9de85a [fixup] settings: Set Overlay Applet to false (#3112)
* it's unlocking fps, should be investigate further

Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3112
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-12-03 03:32:11 +01:00
62 changed files with 1659 additions and 1242 deletions

View File

@@ -1,21 +1,121 @@
#!/bin/bash -e
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
export NDK_CCACHE=$(which ccache)
NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
export CMAKE_BUILD_PARALLEL_LEVEL="${NUM_JOBS}"
ARTIFACTS_DIR="$PWD/artifacts"
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
: "${CCACHE:=false}"
RETURN=0
usage() {
cat <<EOF
Usage: $0 [-t|--target FLAVOR] [-b|--build-type BUILD_TYPE]
[-h|--help] [-r|--release] [extra options]
Build script for Android.
Associated variables can be set outside the script,
and will apply both to this script and the packaging script.
bool values are "true" or "false"
Options:
-r, --release Enable update checker. If set, sets the DEVEL bool variable to false.
By default, DEVEL is true.
-t, --target <FLAVOR> Build flavor (variable: TARGET)
Valid values are: legacy, optimized, standard, chromeos
Default: standard
-b, --build-type <TYPE> Build type (variable: TYPE)
Valid values are: Release, RelWithDebInfo, Debug
Default: Debug
Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE)
Set the CCACHE variable to "true" to enable build caching.
The APK and AAB will be output into "artifacts".
EOF
exit "$RETURN"
}
die() {
echo "-- ! $*" >&2
RETURN=1 usage
}
target() {
[ -z "$1" ] && die "You must specify a valid target."
TARGET="$1"
}
type() {
[ -z "$1" ] && die "You must specify a valid type."
TYPE="$1"
}
while true; do
case "$1" in
-r|--release) DEVEL=false ;;
-t|--target) target "$2"; shift ;;
-b|--build-type) type "$2"; shift ;;
-h|--help) usage ;;
*) break ;;
esac
shift
done
: "${TARGET:=standard}"
: "${TYPE:=Release}"
: "${DEVEL:=true}"
TARGET_LOWER=$(echo "$TARGET" | tr '[:upper:]' '[:lower:]')
case "$TARGET_LOWER" in
legacy) FLAVOR=Legacy ;;
optimized) FLAVOR=GenshinSpoof ;;
standard) FLAVOR=Mainline ;;
chromeos) FLAVOR=ChromeOS ;;
*) die "Invalid build flavor $TARGET."
esac
case "$TYPE" in
RelWithDebInfo|Release|Debug) ;;
*) die "Invalid build type $TYPE."
esac
LOWER_FLAVOR=$(echo "$FLAVOR" | sed 's/./\L&/')
LOWER_TYPE=$(echo "$TYPE" | sed 's/./\L&/')
if [ -n "${ANDROID_KEYSTORE_B64}" ]; then
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
echo "${ANDROID_KEYSTORE_B64}" | base64 --decode > "${ANDROID_KEYSTORE_FILE}"
SHA1SUM=$(keytool -list -v -storepass "${ANDROID_KEYSTORE_PASS}" -keystore "${ANDROID_KEYSTORE_FILE}" | grep SHA1 | cut -d " " -f3)
echo "-- Keystore SHA1 is ${SHA1SUM}"
fi
cd src/android
chmod +x ./gradlew
./gradlew assembleMainlineRelease
./gradlew bundleMainlineRelease
set -- "$@" -DUSE_CCACHE="${CCACHE}"
[ "$DEVEL" != "true" ] && set -- "$@" -DENABLE_UPDATE_CHECKER=ON
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
echo "-- building..."
./gradlew "copy${FLAVOR}${TYPE}Outputs" \
-Dorg.gradle.caching="${CCACHE}" \
-Dorg.gradle.parallel="${CCACHE}" \
-Dorg.gradle.workers.max="${NUM_JOBS}" \
-PYUZU_ANDROID_ARGS="$*" \
--info
if [ -n "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi
echo "-- Done! APK and AAB artifacts are in ${ARTIFACTS_DIR}"
ls -l "${ARTIFACTS_DIR}/"

View File

@@ -1,22 +0,0 @@
#!/bin/sh
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
GITDATE="$(git show -s --date=short --format='%ad' | sed 's/-//g')"
GITREV="$(git show -s --format='%h')"
ARTIFACTS_DIR="$PWD/artifacts"
mkdir -p "${ARTIFACTS_DIR}/"
REV_NAME="eden-android-${GITDATE}-${GITREV}"
BUILD_FLAVOR="mainline"
BUILD_TYPE_LOWER="release"
BUILD_TYPE_UPPER="Release"
cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/${BUILD_TYPE_LOWER}/app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.apk" \
"${ARTIFACTS_DIR}/${REV_NAME}.apk" || echo "APK not found"
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}${BUILD_TYPE_UPPER}"/"app-${BUILD_FLAVOR}-${BUILD_TYPE_LOWER}.aab" \
"${ARTIFACTS_DIR}/${REV_NAME}.aab" || echo "AAB not found"
ls -la "${ARTIFACTS_DIR}/"

View File

@@ -4,7 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# specify full path if dupes may exist
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder externals/cmake-modules"
# license header constants, please change when needed :))))
YEAR=2025

View File

@@ -1,3 +1,16 @@
diff --git a/library/aesni.h b/library/aesni.h
index 754c984c79..59e27afd3e 100644
--- a/library/aesni.h
+++ b/library/aesni.h
@@ -35,7 +35,7 @@
/* GCC-like compilers: currently, we only support intrinsics if the requisite
* target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2`
* or `clang -maes -mpclmul`). */
-#if (defined(__GNUC__) || defined(__clang__)) && defined(__AES__) && defined(__PCLMUL__)
+#if defined(__GNUC__) || defined(__clang__)
#define MBEDTLS_AESNI_HAVE_INTRINSICS
#endif
/* For 32-bit, we only support intrinsics */
diff --git a/library/aesni.c b/library/aesni.c
index 2857068..3e104ab 100644
--- a/library/aesni.c

View File

@@ -1,10 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1811c42..bac9098 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 3.5)
if(TEST_CPP)
project("mbed TLS" C CXX)
else()

View File

@@ -1,13 +0,0 @@
diff --git a/library/aesni.h b/library/aesni.h
index 754c984c79..59e27afd3e 100644
--- a/library/aesni.h
+++ b/library/aesni.h
@@ -35,7 +35,7 @@
/* GCC-like compilers: currently, we only support intrinsics if the requisite
* target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2`
* or `clang -maes -mpclmul`). */
-#if (defined(__GNUC__) || defined(__clang__)) && defined(__AES__) && defined(__PCLMUL__)
+#if defined(__GNUC__) || defined(__clang__)
#define MBEDTLS_AESNI_HAVE_INTRINSICS
#endif
/* For 32-bit, we only support intrinsics */

View File

@@ -0,0 +1,20 @@
diff --git a/library/common.h b/library/common.h
index 50f2a29..c60d9dc 100644
--- a/library/common.h
+++ b/library/common.h
@@ -19,11 +19,11 @@
#include <stdint.h>
#include <stddef.h>
-#if defined(__ARM_NEON)
-#include <arm_neon.h>
+#if defined(MBEDTLS_PLATFORM_IS_WINDOWS_ON_ARM64)
+#include <arm64_neon.h.h>
#define MBEDTLS_HAVE_NEON_INTRINSICS
-#elif defined(MBEDTLS_PLATFORM_IS_WINDOWS_ON_ARM64)
-#include <arm64_neon.h>
+#elif defined(__ANDROID__) || defined(__ARM_NEON)
+#include <arm_neon.h>
#define MBEDTLS_HAVE_NEON_INTRINSICS
#endif

View File

@@ -5,73 +5,23 @@ cmake_minimum_required(VERSION 3.22)
project(yuzu)
if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
set(PLATFORM_SUN ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(PLATFORM_FREEBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(PLATFORM_OPENBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
set(PLATFORM_NETBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
set(PLATFORM_DRAGONFLYBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
set(CXX_CLANG_CL ON)
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CXX_CL ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CXX_ICC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CXX_APPLE ON)
endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
# https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11112
# This works totally fine on MinGW64, but not CLANG{,ARM}64
if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
# NB: this does not account for SPARC
# If you get Eden working on SPARC, please shoot crueter@crueter.xyz multiple emails
# and you will be hailed for eternity
if (PLATFORM_SUN)
# Terrific Solaris pkg shenanigans
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
include(DetectPlatform)
include(DetectArchitecture)
include(DefaultConfig)
include(DownloadExternals)
include(CMakeDependentOption)
include(CTest)
include(CPMUtil)
# Amazing - absolutely incredible
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
DetectArchitecture()
# For some mighty reason, doing a normal release build sometimes may not trigger
# the proper -O3 switch to materialize
if (CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
if (NOT DEFINED ARCHITECTURE)
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?")
endif()
# Needed for FFmpeg w/ VAAPI and DRM
@@ -90,34 +40,10 @@ if (PLATFORM_NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
# MSYS2 utilities
if (PLATFORM_MSYS)
include(FixMsysPaths)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# we (generally) want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# static stuff
option(YUZU_STATIC_BUILD "Use static libraries and executables if available" OFF)
# TODO: StaticBuild.cmake
if (YUZU_STATIC_BUILD)
include(StaticQtLibs)
@@ -177,92 +103,6 @@ if (YUZU_STATIC_BUILD)
endif()
endif()
# Detect current compilation architecture and create standard definitions
# =======================================================================
include(CheckSymbolExists)
function(detect_architecture symbol arch)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET 1)
check_symbol_exists("${symbol}" "" ARCHITECTURE_${arch})
unset(CMAKE_REQUIRED_QUIET)
# The output variable needs to be unique across invocations otherwise
# CMake's crazy scope rules will keep it defined
if (ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
add_definitions("-DARCHITECTURE_${arch}=1")
endif()
endif()
endfunction()
if (NOT ENABLE_GENERIC)
# https://sourceforge.net/p/predef/wiki/Architectures/
# TODO: THIS IS FUCKING FLAWED ONLY THE FIRST SYMBOL THAT APPEARS WILL BE CONSIDERED :(
if (MSVC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
detect_architecture("_M_ARM" arm)
detect_architecture("_M_ARM64" arm64)
else()
detect_architecture("__x86_64__" x86_64)
detect_architecture("__i386__" x86)
detect_architecture("__arm__" arm)
detect_architecture("__aarch64__" arm64)
endif()
detect_architecture("__ARM64__" arm64)
detect_architecture("__aarch64__" arm64)
detect_architecture("_M_ARM64" arm64)
detect_architecture("__arm__" arm)
detect_architecture("__TARGET_ARCH_ARM" arm)
detect_architecture("_M_ARM" arm)
detect_architecture("__x86_64" x86_64)
detect_architecture("__x86_64__" x86_64)
detect_architecture("__amd64" x86_64)
detect_architecture("_M_X64" x86_64)
detect_architecture("__i386" x86)
detect_architecture("__i386__" x86)
detect_architecture("_M_IX86" x86)
detect_architecture("__ia64" ia64)
detect_architecture("__ia64__" ia64)
detect_architecture("_M_IA64" ia64)
detect_architecture("__mips" mips)
detect_architecture("__mips__" mips)
detect_architecture("_M_MRX000" mips)
detect_architecture("__powerpc64__" ppc64)
detect_architecture("__ppc64__" ppc64)
detect_architecture("__PPC64__" ppc64)
detect_architecture("_ARCH_PPC64" ppc64)
detect_architecture("__ppc__" ppc)
detect_architecture("__ppc" ppc)
detect_architecture("__powerpc__" ppc)
detect_architecture("_ARCH_COM" ppc)
detect_architecture("_ARCH_PWR" ppc)
detect_architecture("_ARCH_PPC" ppc)
detect_architecture("_M_MPPC" ppc)
detect_architecture("_M_PPC" ppc)
detect_architecture("__riscv" riscv)
detect_architecture("__EMSCRIPTEN__" wasm)
endif()
if (NOT DEFINED ARCHITECTURE)
set(ARCHITECTURE "GENERIC")
set(ARCHITECTURE_GENERIC 1)
add_definitions(-DARCHITECTURE_GENERIC=1)
endif()
message(STATUS "Target architecture: ${ARCHITECTURE}")
if (MSVC AND ARCHITECTURE_x86)
message(FATAL_ERROR "Attempting to build with the x86 environment is not supported. \
This can typically happen if you used the Developer Command Prompt from the start menu; \
@@ -292,21 +132,11 @@ if (CXX_CLANG_CL)
endif()
endif()
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
include(DownloadExternals)
include(CMakeDependentOption)
include(CTest)
# Disable Warnings as Errors for MSVC
if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
endif()
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
# Set bundled sdl2/qt as dependent options.
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
cmake_dependent_option(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
@@ -358,13 +188,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
if(YUZU_ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT COMPILER_SUPPORTS_LTO)
if(NOT COMPILER_SUPPORTS_LTO)
message(FATAL_ERROR "Your compiler does not support interprocedural optimization (IPO). Re-run CMake with -DYUZU_ENABLE_LTO=OFF.")
endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${COMPILER_SUPPORTS_LTO})
include(UseLTO)
endif()
option(USE_CCACHE "Use ccache for compilation" OFF)
@@ -391,7 +215,6 @@ if(USE_CCACHE)
endif()
endif()
# TODO(crueter): CI this?
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF)
@@ -401,12 +224,12 @@ cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable"
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "ENABLE_SDL2;NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR PLATFORM_LINUX" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database")
cmake_dependent_option(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "LINUX" OFF)
cmake_dependent_option(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib" ON "APPLE" OFF)
@@ -436,37 +259,17 @@ if (ENABLE_OPENSSL)
option(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" ${DEFAULT_YUZU_USE_BUNDLED_OPENSSL})
endif()
# TODO(crueter): CPM this
if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
# TODO(crueter): CPM this
set(vvl_version "1.4.321.0")
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
if (NOT EXISTS "${vvl_zip_file}")
# Download and extract validation layer release to externals directory
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
file(DOWNLOAD "${vvl_base_url}/vulkan-sdk-${vvl_version}/android-binaries-${vvl_version}.zip"
"${vvl_zip_file}" SHOW_PROGRESS)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
AddJsonPackage(vulkan-validation-layers)
# Copy the arm64 binary to src/android/app/main/jniLibs
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
set(abi ${CMAKE_ANDROID_ARCH_ABI})
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/${abi}/")
file(COPY "${VVL_SOURCE_DIR}/${abi}/libVkLayer_khronos_validation.so"
DESTINATION "${vvl_lib_path}")
endif()
if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe
endif()
# Default to a Release build
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
message(STATUS "Defaulting to a Release build")
endif()
if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
if (EXISTS ${PROJECT_SOURCE_DIR}/.git/)
message(STATUS "Copying pre-commit hook")
@@ -474,25 +277,23 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
endif()
endif()
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
set(compat_base dist/compatibility_list/compatibility_list)
set(compat_qrc ${compat_base}.qrc)
set(compat_json ${compat_base}.json)
configure_file(${PROJECT_SOURCE_DIR}/${compat_qrc}
${PROJECT_BINARY_DIR}/${compat_qrc}
COPYONLY)
if (EXISTS ${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json)
configure_file("${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.json"
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json"
if (EXISTS ${PROJECT_SOURCE_DIR}/${compat_json})
configure_file("${PROJECT_SOURCE_DIR}/${compat_json}"
"${PROJECT_BINARY_DIR}/${compat_json}"
COPYONLY)
endif()
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
# TODO: Compat list download
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
endif()
if (YUZU_LEGACY)
@@ -574,8 +375,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# System imported libraries
# =======================================================================
include(CPMUtil)
# openssl funniness
if (ENABLE_OPENSSL)
if (YUZU_USE_BUNDLED_OPENSSL)
@@ -667,6 +466,7 @@ if (YUZU_USE_CPM)
add_library(Opus::opus ALIAS opus)
endif()
else()
# TODO: we can probably just use CPM for this... right?
# Enforce the search mode of non-required packages for better and shorter failure messages
find_package(fmt 8 REQUIRED)
@@ -731,10 +531,9 @@ endfunction()
if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
find_library(COCOA_LIBRARY Cocoa REQUIRED)
find_library(IOKIT_LIBRARY IOKit REQUIRED)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
# find_library(ICONV_LIBRARY iconv REQUIRED)
# list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
elseif (WIN32)
# Target Windows 10
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
@@ -875,11 +674,6 @@ if(ENABLE_QT)
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
endif()
if (UNIX AND NOT APPLE AND NOT ANDROID)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
endif()
if (NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
# Use system installed FFmpeg
find_package(FFmpeg REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
@@ -906,54 +700,6 @@ endif()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# Setup a custom clang-format target (if clang-format can be found) that will run
# against all the src files. This should be used before making a pull request.
# =======================================================================
set(CLANG_FORMAT_POSTFIX "-15")
find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format
PATHS ${PROJECT_BINARY_DIR}/externals)
# if find_program doesn't find it, try to download from externals
if (NOT CLANG_FORMAT)
if (WIN32 AND NOT CMAKE_CROSSCOMPILING)
message(STATUS "Clang format not found! Downloading...")
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
file(DOWNLOAD
https://github.com/eden-emulator/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
"${CLANG_FORMAT}" SHOW_PROGRESS
STATUS DOWNLOAD_SUCCESS)
if (NOT DOWNLOAD_SUCCESS EQUAL 0)
message(WARNING "Could not download clang format! Disabling the clang format target")
file(REMOVE ${CLANG_FORMAT})
unset(CLANG_FORMAT)
endif()
else()
message(WARNING "Clang format not found! Disabling the clang format target")
endif()
endif()
if (CLANG_FORMAT)
set(SRCS ${PROJECT_SOURCE_DIR}/src)
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32)
add_custom_target(clang-format
COMMAND powershell.exe -Command "Get-ChildItem '${SRCS}/*' -Include *.cpp,*.h -Recurse | Foreach {&'${CLANG_FORMAT}' -i $_.fullname}"
COMMENT ${CCOMMENT})
elseif(MINGW)
add_custom_target(clang-format
COMMAND find `cygpath -u ${SRCS}` -iname *.h -o -iname *.cpp | xargs `cygpath -u ${CLANG_FORMAT}` -i
COMMENT ${CCOMMENT})
else()
add_custom_target(clang-format
COMMAND find ${SRCS} -iname *.h -o -iname *.cpp | xargs ${CLANG_FORMAT} -i
COMMENT ${CCOMMENT})
endif()
unset(SRCS)
unset(CCOMMENT)
endif()
# Include source code
# ===================
@@ -987,47 +733,8 @@ if (MSVC AND CXX_CLANG)
link_libraries(llvm-mingw-runtime)
endif()
#[[
search order:
- gold (GCC only) - the best, generally, but unfortunately not packaged anymore
- mold (GCC only) - generally does well on GCC
- ldd - preferred on clang
- bfd - the final fallback
- If none are found (macOS uses ld.prime, etc) just use the default linker
]]
if (YUZU_USE_FASTER_LD)
find_program(LINKER_BFD bfd)
if (LINKER_BFD)
set(LINKER bfd)
endif()
find_program(LINKER_LLD lld)
if (LINKER_LLD)
set(LINKER lld)
endif()
if (CXX_GCC)
find_program(LINKER_MOLD mold)
if (LINKER_MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
set(LINKER mold)
endif()
find_program(LINKER_GOLD gold)
if (LINKER_GOLD)
set(LINKER gold)
endif()
endif()
if (LINKER)
message(NOTICE "Selecting ${LINKER} as linker")
add_link_options("-fuse-ld=${LINKER}")
else()
message(WARNING "No faster linker found--using default")
endif()
if (LINKER STREQUAL "lld" AND CXX_GCC)
message(WARNING "Using lld on GCC may cause issues with certain LTO settings. If the program fails to compile, disable YUZU_USE_FASTER_LD, or install mold or GNU gold.")
endif()
include(FasterLinker)
endif()
# Set runtime library to MD/MDd for all configurations
@@ -1049,14 +756,6 @@ if(MSVC)
)
endif()
if (MINGW)
# This saves a truly ridiculous amount of time during linking
# In my tests, without this it takes 2 mins, with it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
add_subdirectory(src)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not

View File

@@ -603,8 +603,12 @@ function(AddCIPackage)
add_ci_package(mingw-arm64)
endif()
if (ANDROID AND NOT "android" IN_LIST DISABLED_PLATFORMS)
add_ci_package(android)
if((ANDROID AND ARCHITECTURE_x86_64) AND NOT "android-x86_64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(android-x86_64)
endif()
if((ANDROID AND ARCHITECTURE_arm64) AND NOT "android-aarch64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(android-aarch64)
endif()
if(PLATFORM_SUN AND NOT "solaris-amd64" IN_LIST DISABLED_PLATFORMS)

View File

@@ -1,49 +0,0 @@
# SPDX-FileCopyrightText: 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
include(GetGitRevisionDescription)
function(trim var)
string(REGEX REPLACE "\n" "" new "${${var}}")
set(${var} ${new} PARENT_SCOPE)
endfunction()
set(TAG_FILE ${CMAKE_SOURCE_DIR}/GIT-TAG)
set(REF_FILE ${CMAKE_SOURCE_DIR}/GIT-REFSPEC)
set(COMMIT_FILE ${CMAKE_SOURCE_DIR}/GIT-COMMIT)
set(RELEASE_FILE ${CMAKE_SOURCE_DIR}/GIT-RELEASE)
if (EXISTS ${REF_FILE} AND EXISTS ${COMMIT_FILE})
file(READ ${REF_FILE} GIT_REFSPEC)
file(READ ${COMMIT_FILE} GIT_COMMIT)
else()
get_git_head_revision(GIT_REFSPEC GIT_COMMIT)
git_branch_name(GIT_REFSPEC)
if (GIT_REFSPEC MATCHES "NOTFOUND")
set(GIT_REFSPEC 1.0.0)
set(GIT_COMMIT stable)
endif()
endif()
if (EXISTS ${TAG_FILE})
file(READ ${TAG_FILE} GIT_TAG)
else()
git_describe(GIT_TAG --tags --abbrev=0)
if (GIT_TAG MATCHES "NOTFOUND")
set(GIT_TAG "${GIT_REFSPEC}")
endif()
endif()
if (EXISTS ${RELEASE_FILE})
file(READ ${RELEASE_FILE} GIT_RELEASE)
trim(GIT_RELEASE)
message(STATUS "Git release: ${GIT_RELEASE}")
endif()
trim(GIT_REFSPEC)
trim(GIT_COMMIT)
trim(GIT_TAG)
message(STATUS "Git commit: ${GIT_COMMIT}")
message(STATUS "Git tag: ${GIT_TAG}")
message(STATUS "Git refspec: ${GIT_REFSPEC}")

View File

@@ -4,7 +4,7 @@
"package": "OpenSSL",
"name": "openssl",
"repo": "crueter-ci/OpenSSL",
"version": "3.6.0-e3608d80df",
"version": "3.6.0-965d6279e8",
"min_version": "1.1.1"
},
"boost": {
@@ -25,9 +25,9 @@
"fmt": {
"repo": "fmtlib/fmt",
"tag": "%VERSION%",
"hash": "c4ab814c20fbad7e3f0ae169125a4988a2795631194703251481dc36b18da65c886c4faa9acd046b0a295005217b3689eb0126108a9ba5aac2ca909aae263c2f",
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
"version": "8",
"git_version": "12.0.0"
"git_version": "12.1.0"
},
"lz4": {
"name": "lz4",
@@ -91,5 +91,13 @@
"version": "20250828",
"artifact": "clang-rt-builtins.tar.zst",
"hash": "d902392caf94e84f223766e2cc51ca5fab6cae36ab8dc6ef9ef6a683ab1c483bfcfe291ef0bd38ab16a4ecc4078344fa8af72da2f225ab4c378dee23f6186181"
},
"vulkan-validation-layers": {
"package": "VVL",
"repo": "KhronosGroup/Vulkan-ValidationLayers",
"tag": "vulkan-sdk-%VERSION%",
"git_version": "1.4.328.1",
"artifact": "android-binaries-%VERSION%.zip",
"hash": "5ec895a453cb7c2f156830b9766953a0c2bd44dea99e6a3dac4160305041ccd3e87534b4ce0bd102392178d2a8eca48411856298f9395e60117cdfe89f72137e"
}
}

62
docs/build/Android.md vendored
View File

@@ -1,30 +1,37 @@
# Note: These build instructions are a work-in-progress.
# Android
## Dependencies
* [Android Studio](https://developer.android.com/studio)
* [NDK 27+ and CMake 3.22.1](https://developer.android.com/studio/projects/install-ndk#default-version)
* [Git](https://git-scm.com/download)
### WINDOWS ONLY - Additional Dependencies
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.**
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
- A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
## WINDOWS ONLY - Additional Dependencies
* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select "Desktop development with C++" support in the installer. Make sure to update to the latest version if already installed.**
* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
* A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
## Cloning Eden with Git
```
```sh
git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git
```
Eden by default will be cloned into -
Eden by default will be cloned into:
* `C:\Users\<user-name>\eden` on Windows
* `~/eden` on Linux and macOS
## Building
1. Start Android Studio, on the startup dialog select `Open`.
2. Navigate to the `eden/src/android` directory and click on `OK`.
3. In `Build > Select Build Variant`, select `release` or `relWithDebInfo` as the "Active build variant".
4. Build the project with `Build > Make Project` or run it on an Android device with `Run > Run 'app'`.
## Building with Terminal
1. Download the SDK and NDK from Android Studio.
2. Navigate to SDK and NDK paths.
3. Then set ANDROID_SDK_ROOT and ANDROID_NDK_ROOT in terminal via
@@ -38,7 +45,44 @@ Eden by default will be cloned into -
Remember to have a Java SDK installed if not already, on Debian and similar this is done with `sudo apt install openjdk-17-jdk`.
### Script
A convenience script for building is provided in `.ci/android/build.sh`. The built APK can be put into an `artifacts` directory via `.ci/android/package.sh`. On Windows, these must be done in the Git Bash or MinGW terminal.
A convenience script for building is provided in `.ci/android/build.sh`. On Windows, this must be run in Git Bash or MSYS2. This script provides the following options:
```txt
Usage: build.sh [-c|--chromeos] [-t|--target FLAVOR] [-b|--build-type BUILD_TYPE]
[-h|--help] [-r|--release] [extra options]
Build script for Android.
Associated variables can be set outside the script,
and will apply both to this script and the packaging script.
bool values are "true" or "false"
Options:
-c, --chromeos Build for ChromeOS (x86_64) (variable: CHROMEOS, bool)
Default: false
-r, --release Enable update checker. If set, sets the DEVEL bool variable to false.
By default, DEVEL is true.
-t, --target <FLAVOR> Build flavor (variable: TARGET)
Valid values are: legacy, optimized, standard
Default: standard
-b, --build-type <TYPE> Build type (variable: TYPE)
Valid values are: Release, RelWithDebInfo, Debug
Default: Debug
Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE)
Set the CCACHE variable to "true" to enable build caching.
The APK and AAB will be output into "artifacts".
```
Examples:
* Build legacy release with update checker for ChromeOS:
* `.ci/android/build.sh -c -r -t legacy`
* Build standard release with debug info without update checker for phones:
* `.ci/android/build.sh -b RelWithDebInfo`
* Build optimized release with update checker:
* `.ci/android/build.sh -r -t optimized`
### Additional Resources
https://developer.android.com/studio/intro
<https://developer.android.com/studio/intro>

22
docs/user/CommandLine.md Normal file
View File

@@ -0,0 +1,22 @@
# User Handbook - Command Line
There are two main applications, an SDL2 based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
## eden
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behaviour is similar to `eden-cli`; allows dragging and dropping games into the application.
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `-f`: Use fullscreen.
- `-u <number>`: Select the index of the user to load as.
- `-qlaunch`: Launch QLaunch.
- `-setup`: Launch setup applet.
## eden-cli
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `--config/-c`: Specify alternate configuration file.
- `--fullscreen/-f`: Set fullscreen.
- `--help/-h`: Display help.
- `--game/-g`: Specify the game to run.
- `--multiplayer/-m`: Specify multiplayer options.
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u`: Specify the user index.
- `--version/-v`: Display version and quit.

View File

@@ -6,9 +6,11 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e
- **[The Basics](Basics.md)**
- **[Audio](Audio.md)**
- **[Server hosting](ServerHosting.md)**
- **[Graphics](Graphics.md)**
- **[Platforms and Architectures](Architectures.md)**
- **[Testing](Testing.md)**
- **[Data, savefiles and storage](Storage.md)**
- **[Orphaned Profiles](Orphaned.md)**
- **[Command Line](CommandLine.md)**
- **[Native Application Development](Native.md)**

View File

@@ -0,0 +1,32 @@
# User Handbook - Server hosting
This guide explains how to set up a public/private self hosted Eden server/lobby.
## Using a Kamatera VPS and Docker on Ubuntu
- Firstly, head over to kamatera.com and create an account. Sign in and create a new server under "My cloud", then create a new server.
- Region: Choose a location that balances latency for both you and other players (example: New York for US-Europe connections if the host is based in the US).
- Next, under Server OS Images, select Ubuntu 24.04 LTS. Configure CPU/RAM/specs as desired for your server. Complete the creation process.
- Enable the Kamatera firewall and set default policy: IN: DROP, OUT: ACCEPT.
- After setting the default policy, add the three following rules: #1: SSH Access - Direction: IN, Interface: net0, Macro: SSH - Secure Shell Traffic, Source: ANY, Port: Blank/Auto (Handeld by SSH), Destination: Blank/Auto (Handeld by SSH), Policy: ACCEPT, leave a comment: SSH access.
- Then, after creating the first rule, add TCP & UDP Ports for Eden - Direction: IN, Interface: net0, Protocol: TCP or UDP (for respective rule), Source: ANY, Destination Port: 24872, Policy: ACCEPT, leave a comment: Eden server port.
- Note: Only UDP is required for Eden; opening TCP is optional.
- SSH into the server: `ssh root@YOUR_SERVER_IP`.
- Install Docker: `apt update`, `apt install -y docker.io`, `systemctl enable --now docker`. Verify Docker installation: `docker --version`
- (Optional) Install Eden AppImage: `chmod +x Eden-Linux*.AppImage` This step is optional and only needed if you want to manage Eden locally on a VPS.
- Now, we configure the lobby itself. Run the server: `docker run -d --name eden-lobby --restart unless-stopped -p 24872:24872/udp ikuzen/yuzu-hdr-multiplayer-dedicated --room-name "My Eden Room" --password "MySecurePass2025" --max-members 8 --preferred-game "Mario Kart 8 Deluxe" --preferred-game-id "01000ABF0C84C000" --web-api-url "api.ynet-fun.xyz"` This command starts the server in the background, maps the UDP port, and sets your room settings.
- Now you can try verifying the server: `docker ps` You should see: `eden-lobby Up 0.0.0.0:24872->24872/udp`
- Connect from the client: Open Eden on your PC, go to Multiplayer > Direct Connect to Room, enter the VPS IP address, use the room name and password you set above or alternatively you should see your lobby within the "Browse Public Game Lobby" section as well.
- Managing the Docker container: Stop the server: `docker stop eden-lobby`, Start it again: `docker start eden-lobby`, Remove it: `docker rm -f eden-lobby` if you want to change the name of your lobby, you must first stop the server via: docker stop eden-lobby, then remove it via: docker rm -f eden-lobby. Then edit the server name and just repaste the updated command.
- Notes: Only UDP port 24872 is strictly required. You can customize room name, password, max members, and preferred game. Optional parameters from the original Outcaster guide include: `--room-description` (Adds a room description in the lobby), `--allowed-name-suffix` (Restricts usernames), `--moderator-password` (Adds a moderator role), `--allow-non-preferred-game` (Allows games other than the preferred game)

View File

@@ -392,3 +392,19 @@ if (ANDROID)
add_library(oboe::oboe ALIAS oboe)
endif()
if (APPLE)
# moltenvk
if (NOT YUZU_USE_BUNDLED_MOLTENVK)
find_library(MOLTENVK_LIBRARY MoltenVK)
else()
unset(MOLTENVK_LIBRARY)
endif()
# TODO: kosmickrisp?
if (NOT MOLTENVK_LIBRARY)
AddJsonPackage(moltenvk)
set(MOLTENVK_LIBRARY "${moltenvk_SOURCE_DIR}/MoltenVK/dylib/macOS/libMoltenVK.dylib" CACHE STRING "" FORCE)
endif()
endif()

View File

@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## DefaultConfig ##
# Generally, you will always want "some" default configuration for your project.
# This module does nothing but enforce that. :)
set(CMAKE_BUILD_TYPE_DEFAULT "Release" CACHE STRING "Default build type")
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE_DEFAULT}"
CACHE STRING "Choose the type of build." FORCE)
message(STATUS "[DefaultConfig] Defaulting to a "
"${CMAKE_BUILD_TYPE_DEFAULT} build")
endif()

View File

@@ -0,0 +1,225 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## DetectArchitecture ##
#[[
Does exactly as it sounds. Detects common symbols defined for different architectures and
adds compile definitions thereof. Namely:
- arm64
- arm
- x86_64
- x86
- ia64
- mips64
- mips
- ppc64
- ppc
- riscv
- riscv64
- loongarch64
- wasm
Unsupported architectures:
- ARMv2-6
- m68k
- PIC
This file WILL NOT detect endian-ness for you.
This file is based off of Yuzu and Dynarmic.
]]
# multiarch builds are a special case and also very difficult
# this is what I have for now, but it's not ideal
# Do note that situations where multiple architectures are defined
# should NOT be too dependent on the architecture
# otherwise, you may end up with duplicate code
if (CMAKE_OSX_ARCHITECTURES)
set(MULTIARCH_BUILD 1)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
# hope and pray the architecture names match
foreach(ARCH IN ${CMAKE_OSX_ARCHITECTURES})
set(ARCHITECTURE_${ARCH} 1 PARENT_SCOPE)
add_definitions(-DARCHITECTURE_${ARCH}=1)
endforeach()
return()
endif()
include(CheckSymbolExists)
function(detect_architecture symbol arch)
# The output variable needs to be unset between invocations otherwise
# CMake's crazy scope rules will keep it defined
unset(SYMBOL_EXISTS CACHE)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET 1)
check_symbol_exists("${symbol}" "" SYMBOL_EXISTS)
unset(CMAKE_REQUIRED_QUIET)
if (SYMBOL_EXISTS)
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
add_definitions(-DARCHITECTURE_${arch}=1)
endif()
endif()
endfunction()
function(detect_architecture_symbols)
if (DEFINED ARCHITECTURE)
return()
endif()
set(oneValueArgs ARCH)
set(multiValueArgs SYMBOLS)
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
set(arch "${ARGS_ARCH}")
foreach(symbol ${ARGS_SYMBOLS})
detect_architecture("${symbol}" "${arch}")
if (ARCHITECTURE_${arch})
message(DEBUG "[DetectArchitecture] Found architecture symbol ${symbol} for ${arch}")
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
add_definitions(-DARCHITECTURE_${arch}=1)
return()
endif()
endforeach()
endfunction()
function(DetectArchitecture)
# arches here are put in a sane default order of importance
# notably, amd64, arm64, and riscv (in order) are BY FAR the most common
# mips is pretty popular in embedded
# ppc64 is pretty popular in supercomputing
# sparc is uh
# ia64 exists
# the rest exist, but are probably less popular than ia64
detect_architecture_symbols(
ARCH arm64
SYMBOLS
"__ARM64__"
"__aarch64__"
"_M_ARM64")
detect_architecture_symbols(
ARCH x86_64
SYMBOLS
"__x86_64"
"__x86_64__"
"__amd64"
"_M_X64"
"_M_AMD64")
# riscv is interesting since it generally does not define a riscv64-specific symbol
# We can, however, check for the rv32 zcf extension which is good enough of a heuristic on GCC
detect_architecture_symbols(
ARCH riscv
SYMBOLS
"__riscv_zcf")
# if zcf doesn't exist we can safely assume it's riscv64
detect_architecture_symbols(
ARCH riscv64
SYMBOLS
"__riscv")
detect_architecture_symbols(
ARCH x86
SYMBOLS
"__i386"
"__i386__"
"_M_IX86")
detect_architecture_symbols(
ARCH arm
SYMBOLS
"__arm__"
"__TARGET_ARCH_ARM"
"_M_ARM")
detect_architecture_symbols(
ARCH ia64
SYMBOLS
"__ia64"
"__ia64__"
"_M_IA64")
# mips is probably the least fun to detect due to microMIPS
# Because microMIPS is such cancer I'm considering it out of scope for now
detect_architecture_symbols(
ARCH mips64
SYMBOLS
"__mips64")
detect_architecture_symbols(
ARCH mips
SYMBOLS
"__mips"
"__mips__"
"_M_MRX000")
detect_architecture_symbols(
ARCH ppc64
SYMBOLS
"__ppc64__"
"__powerpc64__"
"_ARCH_PPC64"
"_M_PPC64")
detect_architecture_symbols(
ARCH ppc
SYMBOLS
"__ppc__"
"__ppc"
"__powerpc__"
"_ARCH_COM"
"_ARCH_PWR"
"_ARCH_PPC"
"_M_MPPC"
"_M_PPC")
detect_architecture_symbols(
ARCH sparc64
SYMBOLS
"__sparc_v9__")
detect_architecture_symbols(
ARCH sparc
SYMBOLS
"__sparc__"
"__sparc")
# I don't actually know about loongarch32 since crossdev does not support it, only 64
detect_architecture_symbols(
ARCH loongarch64
SYMBOLS
"__loongarch__"
"__loongarch64")
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
# "generic" target
# If you have reached this point, you're on some as-of-yet unsupported architecture.
# See the docs up above for known unsupported architectures
# If you're not in the list... I think you know what you're doing.
if (NOT DEFINED ARCHITECTURE)
set(ARCHITECTURE "GENERIC")
set(ARCHITECTURE_GENERIC 1)
add_definitions(-DARCHITECTURE_GENERIC=1)
endif()
message(STATUS "[DetectArchitecture] Target architecture: ${ARCHITECTURE}")
set(ARCHITECTURE "${ARCHITECTURE}" PARENT_SCOPE)
set(ARCHITECTURE_${ARCHITECTURE} 1 PARENT_SCOPE)
endfunction()

View File

@@ -0,0 +1,151 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## DetectPlatform ##
# This is a small helper that sets PLATFORM_<platform> variables for various
# operating systems and distributions. Note that Apple, Windows, Android, etc.
# are not covered, as CMake already does that for us.
# It also sets CXX_<compiler> for the C++ compiler.
# Furthermore, some platforms have really silly requirements/quirks, so this
# also does a few of those.
# This module contains contributions from the Eden Emulator Project,
# notably from crueter and Lizzie.
if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
set(PLATFORM_SUN ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(PLATFORM_FREEBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(PLATFORM_OPENBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
set(PLATFORM_NETBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
set(PLATFORM_DRAGONFLYBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
set(CXX_CLANG_CL ON)
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CXX_CL ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CXX_ICC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(CXX_APPLE ON)
endif()
# https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11112
# This works totally fine on MinGW64, but not CLANG{,ARM}64
if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
# NB: this does not account for SPARC
if (PLATFORM_SUN)
# Terrific OpenIndiana pkg shenanigans
list(APPEND CMAKE_PREFIX_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
# Amazing - absolutely incredible
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
# For some mighty reason, doing a normal release build sometimes
# may not trigger the proper -O3 switch to materialize
if (CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
endif()
# MSYS2 utilities
# Sometimes, PkgConfig modules will incorrectly reference / when CMake
# wants you to reference it as C:/msys64/. This function corrects that.
# Example in a Find module:
#[[
if (PLATFORM_MSYS)
FixMsysPath(PkgConfig::OPUS)
endif()
]]
function(FixMsysPath target)
get_target_property(include_dir ${target} INTERFACE_INCLUDE_DIRECTORIES)
if (NOT (include_dir MATCHES "^/"))
return()
endif()
set(root_default $ENV{MSYS2_LOCATION})
if (root_default STREQUAL "")
set(root_default "C:/msys64")
endif()
set(MSYS_ROOT_PATH ${root_default}
CACHE STRING "Location of the MSYS2 root")
set(include_dir "C:/msys64${include_dir}")
set_target_properties(${target} PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
endfunction()
# MSYSTEM handling + program_path
if (PLATFORM_MSYS)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# We generally want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# This saves a truly ridiculous amount of time during linking
# In my tests, without this, Eden takes 2 mins, with this, it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
if (MINGW)
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
# awesome
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()

View File

@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## FasterLinker ##
# This finds a faster linker for your compiler, if available.
# Only really tested on Linux. I would not recommend this on MSYS2.
#[[
search order:
- gold (GCC only) - the best, generally, but not packaged anymore
- mold (GCC only) - generally does well on GCC
- lld - preferred on clang
- bfd - the final fallback
- If none are found (macOS uses ld.prime, etc) just use the default linker
]]
# This module is based on the work of Yuzu, specifically Liam White,
# and later extended by crueter.
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
endif()
find_program(LINKER_BFD bfd)
if (LINKER_BFD)
set(LINKER bfd)
endif()
find_program(LINKER_LLD lld)
if (LINKER_LLD)
set(LINKER lld)
endif()
if (CXX_GCC)
find_program(LINKER_MOLD mold)
if (LINKER_MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
set(LINKER mold)
endif()
find_program(LINKER_GOLD gold)
if (LINKER_GOLD)
set(LINKER gold)
endif()
endif()
if (LINKER)
message(NOTICE "[FasterLinker] Selecting ${LINKER} as linker")
add_link_options("-fuse-ld=${LINKER}")
else()
message(WARNING "[FasterLinker] No faster linker found--using default")
endif()
if (LINKER STREQUAL "lld" AND CXX_GCC)
message(WARNING
"[FasterLinker] Using lld on GCC may cause issues "
"with certain LTO settings.")
endif()

View File

@@ -1,162 +0,0 @@
# SPDX-FileCopyrightText: 2009 Iowa State University
# SPDX-FileContributor: Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# SPDX-License-Identifier: BSL-1.0
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_branch_name _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
"${GIT_EXECUTABLE}"
rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
#get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
#if(NOT hash)
# set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
# return()
#endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
"${GIT_EXECUTABLE}"
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()

View File

@@ -1,45 +0,0 @@
# SPDX-FileCopyrightText: 2009 Iowa State University
# SPDX-FileContributor: Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# SPDX-License-Identifier: BSL-1.0
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}")
configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
set(HEAD_HASH "${HEAD_REF}")
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
if(EXISTS "@GIT_DATA@/head-ref")
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
else()
set(HEAD_HASH "Unknown")
endif()
endif()

85
externals/cmake-modules/GetSCMRev.cmake vendored Normal file
View File

@@ -0,0 +1,85 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## GetSCMRev ##
# Name is self explanatory. Gets revision information from files, OR from git.
# Prioritizes GIT-TAG, GIT-REFSPEC, GIT-COMMIT, GIT-RELEASE files within the root directory,
# otherwise grabs stuff from Git.
# loosely based on Ryan Pavlik's work
find_package(Git QUIET)
# commit: git rev-parse HEAD
# tag: git describe --tags --abbrev=0
# branch: git rev-parse --abbrev-ref=HEAD
function(run_git_command variable)
if(NOT GIT_FOUND)
set(${variable} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
"${GIT_EXECUTABLE}"
${ARGN}
WORKING_DIRECTORY
"${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${variable} "${out}" PARENT_SCOPE)
endfunction()
function(trim var)
string(REGEX REPLACE "\n" "" new "${${var}}")
set(${var} ${new} PARENT_SCOPE)
endfunction()
set(TAG_FILE ${CMAKE_SOURCE_DIR}/GIT-TAG)
set(REF_FILE ${CMAKE_SOURCE_DIR}/GIT-REFSPEC)
set(COMMIT_FILE ${CMAKE_SOURCE_DIR}/GIT-COMMIT)
set(RELEASE_FILE ${CMAKE_SOURCE_DIR}/GIT-RELEASE)
if (EXISTS ${REF_FILE} AND EXISTS ${COMMIT_FILE})
file(READ ${REF_FILE} GIT_REFSPEC)
file(READ ${COMMIT_FILE} GIT_COMMIT)
else()
run_git_command(GIT_COMMIT rev-parse HEAD)
run_git_command(GIT_REFSPEC rev-parse --abbrev-ref HEAD)
if (GIT_REFSPEC MATCHES "NOTFOUND")
set(GIT_REFSPEC 1.0.0)
set(GIT_COMMIT stable)
endif()
endif()
if (EXISTS ${TAG_FILE})
file(READ ${TAG_FILE} GIT_TAG)
else()
run_git_command(GIT_TAG describe --tags --abbrev=0)
if (GIT_TAG MATCHES "NOTFOUND")
set(GIT_TAG "${GIT_REFSPEC}")
endif()
endif()
if (EXISTS ${RELEASE_FILE})
file(READ ${RELEASE_FILE} GIT_RELEASE)
trim(GIT_RELEASE)
message(STATUS "[GetSCMRev] Git release: ${GIT_RELEASE}")
endif()
trim(GIT_REFSPEC)
trim(GIT_COMMIT)
trim(GIT_TAG)
message(STATUS "[GetSCMRev] Git commit: ${GIT_COMMIT}")
message(STATUS "[GetSCMRev] Git tag: ${GIT_TAG}")
message(STATUS "[GetSCMRev] Git refspec: ${GIT_REFSPEC}")

34
externals/cmake-modules/UseCcache.cmake vendored Normal file
View File

@@ -0,0 +1,34 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## UseCcache ##
# Adds an option to enable CCache and uses it if provided.
# Also does some debug info downgrading to make it easier.
# Credit to DraVee for his work on this
option(USE_CCACHE "Use ccache for compilation" OFF)
set(CCACHE_PATH "ccache" CACHE STRING "Path to ccache binary")
if(USE_CCACHE)
find_program(CCACHE_BINARY ${CCACHE_PATH})
if(CCACHE_BINARY)
message(STATUS "[UseCcache] Found ccache at: ${CCACHE_BINARY}")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_BINARY})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_BINARY})
else()
message(FATAL_ERROR "[UseCcache] USE_CCACHE enabled, but no "
"executable found at: ${CCACHE_PATH}")
endif()
# Follow SCCache recommendations:
# <https://github.com/mozilla/sccache/blob/main/README.md?plain=1#L144>
if(WIN32)
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG
"${CMAKE_C_FLAGS_DEBUG}")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO
"${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif()
endif()

17
externals/cmake-modules/UseLTO.cmake vendored Normal file
View File

@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
## UseLTO ##
# Enable Interprocedural Optimization (IPO).
# Self-explanatory.
include(CheckIPOSupported)
check_ipo_supported(RESULT COMPILER_SUPPORTS_LTO)
if(NOT COMPILER_SUPPORTS_LTO)
message(FATAL_ERROR
"Your compiler does not support interprocedural optimization"
" (IPO).")
endif()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${COMPILER_SUPPORTS_LTO})

View File

@@ -9,7 +9,7 @@
},
"sirit": {
"repo": "eden-emulator/sirit",
"git_version": "1.0.2",
"git_version": "1.0.3",
"tag": "v%VERSION%",
"artifact": "sirit-source-%VERSION%.tar.zst",
"hash_suffix": "sha512sum",
@@ -23,17 +23,13 @@
"package": "sirit",
"name": "sirit",
"repo": "eden-emulator/sirit",
"version": "1.0.2",
"disabled_platforms": [
"mingw-amd64",
"mingw-arm64"
]
"version": "1.0.3"
},
"httplib": {
"repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%",
"hash": "b364500f76e2ecb0fe21b032d831272e3f1dfeea71af74e325f8fc4ce9dcdb3c941b97a5b422bdeafb9facd058597b90f8bfc284fb9afe3c33fefa15dd5a010b",
"git_version": "0.26.0",
"hash": "e7a8877d489c97669a8ee536e1498575be921e558ed947253013fe6b67a49d4569eedd01f543caa70183b92d8ac0e8687d662a70d880954412e387317008a239",
"git_version": "0.28.0",
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch"
@@ -93,9 +89,9 @@
"package": "unordered_dense",
"repo": "martinus/unordered_dense",
"tag": "v%VERSION%",
"hash": "f9c819e28e1c1a387acfee09277d6af5e366597a0d39acf1c687acf0608a941ba966af8aaebdb8fba0126c7360269c4a51754ef4cab17c35c01a30215f953368",
"hash": "b98b5d4d96f8e0081b184d6c4c1181fae4e41723b54bed4296717d7f417348b48fad0bbcc664cac142b8c8a47e95aa57c1eb1cf6caa855fd782fad3e3ab99e5e",
"find_args": "CONFIG",
"git_version": "4.5.0"
"git_version": "4.8.1"
},
"mbedtls": {
"package": "MbedTLS",
@@ -107,8 +103,8 @@
"artifact": "%TAG%.tar.bz2",
"skip_updates": true,
"patches": [
"0002-aesni-fix.patch",
"0003-aesni-fix.patch"
"0001-aesni-fix.patch",
"0002-arm64-aes-fix.patch"
]
},
"enet": {
@@ -167,7 +163,7 @@
"package": "SDL2",
"name": "SDL2",
"repo": "crueter-ci/SDL2",
"version": "2.32.10-38e0094637",
"version": "2.32.10-a65111bd2d",
"min_version": "2.26.4"
},
"catch2": {
@@ -192,9 +188,9 @@
"package": "SimpleIni",
"repo": "brofield/simpleini",
"tag": "v%VERSION%",
"hash": "6c198636816a0018adbf7f735d402c64245c6fcd540b7360d4388d46f007f3a520686cdaec4705cb8cb31401b2cb4797a80b42ea5d08a6a5807c0848386f7ca1",
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
"find_args": "MODULE",
"git_version": "4.22"
"git_version": "4.25"
},
"sdl2_generic": {
"package": "SDL2",
@@ -214,5 +210,13 @@
"key": "steamdeck",
"bundled": true,
"skip_updates": "true"
},
"moltenvk": {
"repo": "V380-Ori/Ryujinx.MoltenVK",
"tag": "v%VERSION%-ryujinx",
"git_version": "1.4.1",
"artifact": "MoltenVK-macOS.tar",
"hash": "5695b36ca5775819a71791557fcb40a4a5ee4495be6b8442e0b666d0c436bec02aae68cc6210183f7a5c986bdbec0e117aecfad5396e496e9c2fd5c89133a347",
"bundled": true
}
}

View File

@@ -114,16 +114,6 @@ if (UNIX AND NOT ANDROID)
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
# MSVC conflicts with ksuser otherwise
# MinGW has the funny quirk of requiring avutil after avcodec
# Android needs some deps to be compiled with PIC (TODO)
# TODO(crueter) fix
if (ANDROID)
set(BUILD_SHARED_LIBS ON)
else()
set(BUILD_SHARED_LIBS OFF)
endif()
AddJsonPackage(ffmpeg-ci)
set(FFmpeg_INCLUDE_DIR

View File

@@ -1,8 +1,8 @@
{
"ffmpeg": {
"repo": "FFmpeg/FFmpeg",
"sha": "c2184b65d2",
"hash": "007b1ccdd4d3ea3324835258d9a255103253bd66edb442b12d9c60dca85149cad52136a3b3120e5094115b6a3d9e80eeacbf9c07e5ffafc9ac459614d5fa3b22",
"sha": "ddf443f1e9",
"hash": "ded1c313843f23805102565bd3ca92602fb9c2951e059ca5e1a486ab3ef7d589acccf3cde05c5ff0cfc5199c3a261dccb4d2a93254e585824850696fb41a292e",
"bundled": true
},
"ffmpeg-ci": {
@@ -10,7 +10,7 @@
"package": "FFmpeg",
"name": "ffmpeg",
"repo": "crueter-ci/FFmpeg",
"version": "8.0-be99d2c0b2",
"version": "8.0-ddf443f1e9",
"min_version": "4.1"
}
}

View File

@@ -4,10 +4,11 @@
// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
import android.annotation.SuppressLint
// import android.annotation.SuppressLint
import kotlin.collections.setOf
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
import org.gradle.api.tasks.Copy
plugins {
id("com.android.application")
@@ -63,11 +64,6 @@ android {
versionName = getGitVersion()
versionCode = autoVersion
ndk {
@SuppressLint("ChromeOsAbiSupport")
abiFilters += listOf("arm64-v8a")
}
externalNativeBuild {
cmake {
val extraCMakeArgs =
@@ -127,7 +123,7 @@ android {
isMinifyEnabled = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
@@ -139,7 +135,7 @@ android {
signingConfig = signingConfigs.getByName("default")
isDebuggable = true
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
versionNameSuffix = "-relWithDebInfo"
@@ -163,12 +159,20 @@ android {
create("mainline") {
dimension = "version"
resValue("string", "app_name_suffixed", "Eden")
ndk {
abiFilters += listOf("arm64-v8a")
}
}
create("genshinSpoof") {
dimension = "version"
resValue("string", "app_name_suffixed", "Eden Optimized")
applicationId = "com.miHoYo.Yuanshen"
ndk {
abiFilters += listOf("arm64-v8a")
}
}
create("legacy") {
@@ -187,6 +191,25 @@ android {
res.srcDirs("src/main/legacy")
}
}
ndk {
abiFilters += listOf("arm64-v8a")
}
}
create("chromeOS") {
dimension = "version"
resValue("string", "app_name_suffixed", "Eden")
ndk {
abiFilters += listOf("x86_64")
}
externalNativeBuild {
cmake {
abiFilters("x86_64")
}
}
}
}
@@ -321,3 +344,35 @@ fun getGitVersion(): String {
}
return versionName.ifEmpty { "0.0" }
}
afterEvaluate {
val artifactsDir = layout.projectDirectory.dir("../../../artifacts")
val outputsDir = layout.buildDirectory.dir("outputs").get()
android.applicationVariants.forEach { variant ->
val variantName = variant.name
val variantTask = variantName.replaceFirstChar { it.uppercaseChar() }
val flavor = variant.flavorName
val type = variant.buildType.name
val baseName = "app-$flavor-$type"
val apkFile = outputsDir.file("apk/$flavor/$type/$baseName.apk")
val aabFile = outputsDir.file("bundle/$variantName/$baseName.aab")
val taskName = "copy${variantTask}Outputs"
tasks.register<Copy>(taskName) {
group = "publishing"
description = "Copy APK and AAB for $variantName to $artifactsDir"
from(apkFile)
from(aabFile)
into(artifactsDir)
dependsOn("assemble${variantTask}")
dependsOn("bundle${variantTask}")
}
}
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,16 +11,16 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.PageState
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import android.content.res.ColorStateList
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.model.ButtonState
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
@@ -29,9 +32,40 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
AbstractViewHolder<SetupPage>(binding), SetupCallback {
override fun bind(model: SetupPage) {
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.setVisible(visible = false, gone = false)
binding.textConfirmation.setVisible(true)
if (model.pageSteps.invoke() == PageState.COMPLETE) {
onStepCompleted(0, pageFullyCompleted = true)
}
if (model.pageButtons != null && model.pageSteps.invoke() != PageState.COMPLETE) {
for (pageButton in model.pageButtons) {
val pageButtonView = LayoutInflater.from(activity)
.inflate(
R.layout.page_button,
binding.pageButtonContainer,
false
) as MaterialButton
pageButtonView.apply {
id = pageButton.titleId
icon = ResourcesCompat.getDrawable(
activity.resources,
pageButton.iconId,
activity.theme
)
text = activity.resources.getString(pageButton.titleId)
}
pageButtonView.setOnClickListener {
pageButton.buttonAction.invoke(this@SetupPageViewHolder)
}
binding.pageButtonContainer.addView(pageButtonView)
// Disable buton add if its already completed
if (pageButton.buttonState.invoke() == ButtonState.BUTTON_ACTION_COMPLETE) {
onStepCompleted(pageButton.titleId, pageFullyCompleted = false)
}
}
}
binding.icon.setImageDrawable(
@@ -44,32 +78,26 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
binding.textTitle.text = activity.resources.getString(model.titleId)
binding.textDescription.text =
Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
binding.buttonAction.apply {
text = activity.resources.getString(model.buttonTextId)
if (model.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable(
activity.resources,
model.buttonIconId,
activity.theme
)
}
iconGravity =
if (model.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START
} else {
MaterialButton.ICON_GRAVITY_END
}
setOnClickListener {
model.buttonAction.invoke(this@SetupPageViewHolder)
}
}
}
override fun onStepCompleted() {
ViewUtils.hideView(binding.buttonAction, 200)
ViewUtils.showView(binding.textConfirmation, 200)
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
override fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean) {
val button = binding.pageButtonContainer.findViewById<MaterialButton>(pageButtonId)
if (pageFullyCompleted) {
ViewUtils.hideView(binding.pageButtonContainer, 200)
ViewUtils.showView(binding.textConfirmation, 200)
}
if (button != null) {
button.isEnabled = false
button.animate()
.alpha(0.38f)
.setDuration(200)
.start()
button.setTextColor(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
button.iconTint =
ColorStateList.valueOf(button.context.getColor(com.google.android.material.R.color.material_on_surface_disabled))
}
}
}
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -32,12 +35,14 @@ class AddGameFolderDialogFragment : DialogFragment() {
.setTitle(R.string.add_game_folder)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
homeViewModel.setGamesDirSelected(true)
val calledFromGameFragment = requireArguments().getBoolean(
"calledFromGameFragment",
false
)
gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
val job = gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
job.invokeOnCompletion {
homeViewModel.setGamesDirSelected(true)
}
}
.setNegativeButton(android.R.string.cancel, null)
.setView(binding.root)

View File

@@ -1132,15 +1132,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// val enableFrameSkipping = BooleanSetting.FRAME_SKIPPING.getBoolean()
var fpsText = String.format("FPS: %.1f", actualFps)
if (enableFrameInterpolation) {
fpsText += " " + getString(R.string.enhanced_fps_suffix)
fpsText = String.format("eFPS: %.1f", actualFps)
}
// if (enableFrameSkipping) {
// fpsText += " " + getString(R.string.skipping_fps_suffix)
// }
sb.append(fpsText)
}

View File

@@ -26,7 +26,6 @@ import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.File
import org.yuzu.yuzu_emu.R
@@ -34,10 +33,13 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.SetupAdapter
import org.yuzu.yuzu_emu.databinding.FragmentSetupBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.ButtonState
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.PageButton
import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.model.PageState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.NativeConfig
@@ -50,11 +52,16 @@ class SetupFragment : Fragment() {
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val gamesViewModel: GamesViewModel by activityViewModels()
private lateinit var mainActivity: MainActivity
private lateinit var hasBeenWarned: BooleanArray
private lateinit var pages: MutableList<SetupPage>
private lateinit var pageButtonCallback: SetupCallback
companion object {
const val KEY_NEXT_VISIBILITY = "NextButtonVisibility"
const val KEY_BACK_VISIBILITY = "BackButtonVisibility"
@@ -94,124 +101,142 @@ class SetupFragment : Fragment() {
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
val pages = mutableListOf<SetupPage>()
pages = mutableListOf<SetupPage>()
pages.apply {
add(
SetupPage(
R.drawable.ic_yuzu_title,
R.string.welcome,
R.string.welcome_description,
0,
true,
R.string.get_started,
{ pageForward() },
false
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
add(
SetupPage(
R.drawable.ic_notification,
R.string.notifications,
R.string.notifications_description,
0,
false,
R.string.give_permission,
{
notificationCallback = it
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
},
true,
R.string.notification_warning,
R.string.notification_warning_description,
0,
{
if (NotificationManagerCompat.from(requireContext())
R.drawable.ic_permission,
R.string.permissions,
R.string.permissions_description,
mutableListOf<PageButton>().apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
add(
PageButton(
R.drawable.ic_notification,
R.string.notifications,
R.string.notifications_description,
{
pageButtonCallback = it
permissionLauncher.launch(
Manifest.permission.POST_NOTIFICATIONS
)
},
{
if (NotificationManagerCompat.from(requireContext())
.areNotificationsEnabled()
) {
ButtonState.BUTTON_ACTION_COMPLETE
} else {
ButtonState.BUTTON_ACTION_INCOMPLETE
}
},
false,
false,
)
)
}
},
{
if (NotificationManagerCompat.from(requireContext())
.areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
)
)
}
add(
SetupPage(
R.drawable.ic_key,
R.string.keys,
R.string.keys_description,
R.drawable.ic_add,
true,
R.string.select_keys,
{
keyCallback = it
getProdKey.launch(arrayOf("*/*"))
},
true,
R.string.install_prod_keys_warning,
R.string.install_prod_keys_warning_description,
R.string.install_prod_keys_warning_help,
{
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
if (file.exists() && NativeLibrary.areKeysPresent()) {
StepState.COMPLETE
) {
PageState.COMPLETE
} else {
StepState.INCOMPLETE
PageState.INCOMPLETE
}
}
)
)
add(
SetupPage(
R.drawable.ic_firmware,
R.string.firmware,
R.string.firmware_description,
R.drawable.ic_add,
true,
R.string.select_firmware,
{
firmwareCallback = it
getFirmware.launch(arrayOf("application/zip"))
R.drawable.ic_folder_open,
R.string.emulator_data,
R.string.emulator_data_description,
mutableListOf<PageButton>().apply {
add(
PageButton(
R.drawable.ic_key,
R.string.keys,
R.string.keys_description,
{
pageButtonCallback = it
getProdKey.launch(arrayOf("*/*"))
},
{
val file = File(
DirectoryInitialization.userDirectory + "/keys/prod.keys"
)
if (file.exists() && NativeLibrary.areKeysPresent()) {
ButtonState.BUTTON_ACTION_COMPLETE
} else {
ButtonState.BUTTON_ACTION_INCOMPLETE
}
},
false,
true,
R.string.install_prod_keys_warning,
R.string.install_prod_keys_warning_description,
R.string.install_prod_keys_warning_help,
)
)
add(
PageButton(
R.drawable.ic_firmware,
R.string.firmware,
R.string.firmware_description,
{
pageButtonCallback = it
getFirmware.launch(arrayOf("application/zip"))
},
{
if (NativeLibrary.isFirmwareAvailable()) {
ButtonState.BUTTON_ACTION_COMPLETE
} else {
ButtonState.BUTTON_ACTION_INCOMPLETE
}
},
false,
true,
R.string.install_firmware_warning,
R.string.install_firmware_warning_description,
R.string.install_firmware_warning_help,
)
)
add(
PageButton(
R.drawable.ic_controller,
R.string.games,
R.string.games_description,
{
pageButtonCallback = it
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
},
{
if (NativeConfig.getGameDirs().isNotEmpty()) {
ButtonState.BUTTON_ACTION_COMPLETE
} else {
ButtonState.BUTTON_ACTION_INCOMPLETE
}
},
false,
true,
R.string.add_games_warning,
R.string.add_games_warning_description,
R.string.add_games_warning_help,
)
)
},
true,
R.string.install_firmware_warning,
R.string.install_firmware_warning_description,
R.string.install_firmware_warning_help,
{
if (NativeLibrary.isFirmwareAvailable()) {
StepState.COMPLETE
val file = File(
DirectoryInitialization.userDirectory + "/keys/prod.keys"
)
if (file.exists() && NativeLibrary.areKeysPresent() &&
NativeLibrary.isFirmwareAvailable() && NativeConfig.getGameDirs()
.isNotEmpty()
) {
PageState.COMPLETE
} else {
StepState.INCOMPLETE
}
}
)
)
add(
SetupPage(
R.drawable.ic_controller,
R.string.games,
R.string.games_description,
R.drawable.ic_add,
true,
R.string.add_games,
{
gamesDirCallback = it
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
},
true,
R.string.add_games_warning,
R.string.add_games_warning_description,
R.string.add_games_warning_help,
{
if (NativeConfig.getGameDirs().isNotEmpty()) {
StepState.COMPLETE
} else {
StepState.INCOMPLETE
PageState.INCOMPLETE
}
}
)
@@ -221,12 +246,22 @@ class SetupFragment : Fragment() {
R.drawable.ic_check,
R.string.done,
R.string.done_description,
R.drawable.ic_arrow_forward,
false,
R.string.text_continue,
{ finishSetup() },
false
)
mutableListOf<PageButton>().apply {
add(
PageButton(
R.drawable.ic_arrow_forward,
R.string.get_started,
0,
buttonAction = {
finishSetup()
},
buttonState = {
ButtonState.BUTTON_ACTION_UNDEFINED
},
)
)
}
) { PageState.UNDEFINED }
)
}
@@ -237,7 +272,7 @@ class SetupFragment : Fragment() {
homeViewModel.gamesDirSelected.collect(
viewLifecycleOwner,
resetState = { homeViewModel.setGamesDirSelected(false) }
) { if (it) gamesDirCallback.onStepCompleted() }
) { if (it) checkForButtonState.invoke() }
binding.viewPager2.apply {
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
@@ -251,15 +286,18 @@ class SetupFragment : Fragment() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position == 1 && previousPosition == 0) {
ViewUtils.showView(binding.buttonNext)
ViewUtils.showView(binding.buttonBack)
} else if (position == 0 && previousPosition == 1) {
val isFirstPage = position == 0
val isLastPage = position == pages.size - 1
if (isFirstPage) {
ViewUtils.hideView(binding.buttonBack)
} else {
ViewUtils.showView(binding.buttonBack)
}
if (isLastPage) {
ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
} else {
ViewUtils.showView(binding.buttonNext)
}
@@ -271,35 +309,63 @@ class SetupFragment : Fragment() {
val index = binding.viewPager2.currentItem
val currentPage = pages[index]
// Checks if the user has completed the task on the current page
if (currentPage.hasWarning) {
val stepState = currentPage.stepCompleted.invoke()
if (stepState != StepState.INCOMPLETE) {
pageForward()
return@setOnClickListener
}
val warningMessages =
mutableListOf<Triple<Int, Int, Int>>() // title, description, helpLink
if (!hasBeenWarned[index]) {
SetupWarningDialogFragment.newInstance(
currentPage.warningTitleId,
currentPage.warningDescriptionId,
currentPage.warningHelpLinkId,
index
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
return@setOnClickListener
currentPage.pageButtons?.forEach { button ->
if (button.hasWarning || button.isUnskippable) {
val buttonState = button.buttonState()
if (buttonState == ButtonState.BUTTON_ACTION_COMPLETE) {
return@forEach
}
if (button.isUnskippable) {
MessageDialogFragment.newInstance(
activity = requireActivity(),
titleId = button.warningTitleId,
descriptionId = button.warningDescriptionId,
helpLinkId = button.warningHelpLinkId
).show(childFragmentManager, MessageDialogFragment.TAG)
return@setOnClickListener
}
if (!hasBeenWarned[index]) {
warningMessages.add(
Triple(
button.warningTitleId,
button.warningDescriptionId,
button.warningHelpLinkId
)
)
}
}
}
if (warningMessages.isNotEmpty()) {
SetupWarningDialogFragment.newInstance(
warningMessages.map { it.first }.toIntArray(),
warningMessages.map { it.second }.toIntArray(),
warningMessages.map { it.third }.toIntArray(),
index
).show(childFragmentManager, SetupWarningDialogFragment.TAG)
return@setOnClickListener
}
pageForward()
}
binding.buttonBack.setOnClickListener { pageBackward() }
if (savedInstanceState != null) {
val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
binding.buttonNext.setVisible(nextIsVisible)
binding.buttonBack.setVisible(backIsVisible)
if (nextIsVisible) {
binding.buttonNext.visibility = View.VISIBLE
}
if (backIsVisible) {
binding.buttonBack.visibility = View.VISIBLE
}
} else {
hasBeenWarned = BooleanArray(pages.size)
}
@@ -307,6 +373,7 @@ class SetupFragment : Fragment() {
setInsets()
}
override fun onStop() {
super.onStop()
NativeConfig.saveGlobalConfig()
@@ -314,10 +381,8 @@ class SetupFragment : Fragment() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
if (_binding != null) {
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
}
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
}
@@ -326,13 +391,27 @@ class SetupFragment : Fragment() {
_binding = null
}
private lateinit var notificationCallback: SetupCallback
private val checkForButtonState: () -> Unit = {
val page = pages[binding.viewPager2.currentItem]
page.pageButtons?.forEach {
if (it.buttonState() == ButtonState.BUTTON_ACTION_COMPLETE) {
pageButtonCallback.onStepCompleted(
it.titleId,
pageFullyCompleted = false
)
}
if (page.pageSteps() == PageState.COMPLETE) {
pageButtonCallback.onStepCompleted(0, pageFullyCompleted = true)
}
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
notificationCallback.onStepCompleted()
checkForButtonState.invoke()
}
if (!it &&
@@ -345,15 +424,13 @@ class SetupFragment : Fragment() {
}
}
private lateinit var keyCallback: SetupCallback
private lateinit var firmwareCallback: SetupCallback
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
mainActivity.processKey(result, "keys")
if (NativeLibrary.areKeysPresent()) {
keyCallback.onStepCompleted()
checkForButtonState.invoke()
}
}
}
@@ -363,14 +440,12 @@ class SetupFragment : Fragment() {
if (result != null) {
mainActivity.processFirmware(result) {
if (NativeLibrary.isFirmwareAvailable()) {
firmwareCallback.onStepCompleted()
checkForButtonState.invoke()
}
}
}
}
private lateinit var gamesDirCallback: SetupCallback
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
@@ -379,9 +454,13 @@ class SetupFragment : Fragment() {
}
private fun finishSetup() {
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.edit()
.putBoolean(Settings.PREF_FIRST_APP_LAUNCH, false)
.apply()
gamesViewModel.reloadGames(directoriesChanged = true, firstStartup = false)
mainActivity.finishSetup(binding.root.findNavController())
}
@@ -405,8 +484,10 @@ class SetupFragment : Fragment() {
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val barInsets =
windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets =
windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftPadding = barInsets.left + cutoutInsets.left
val topPadding = barInsets.top + cutoutInsets.top
@@ -415,11 +496,22 @@ class SetupFragment : Fragment() {
if (resources.getBoolean(R.bool.small_layout)) {
binding.viewPager2
.updatePadding(left = leftPadding, top = topPadding, right = rightPadding)
.updatePadding(
left = leftPadding,
top = topPadding,
right = rightPadding
)
binding.constraintButtons
.updatePadding(left = leftPadding, right = rightPadding, bottom = bottomPadding)
.updatePadding(
left = leftPadding,
right = rightPadding,
bottom = bottomPadding
)
} else {
binding.viewPager2.updatePadding(top = topPadding, bottom = bottomPadding)
binding.viewPager2.updatePadding(
top = topPadding,
bottom = bottomPadding
)
binding.constraintButtons
.updatePadding(
left = leftPadding,

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,20 +14,21 @@ import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import androidx.core.net.toUri
class SetupWarningDialogFragment : DialogFragment() {
private var titleId: Int = 0
private var descriptionId: Int = 0
private var helpLinkId: Int = 0
private var titleIds: IntArray = intArrayOf()
private var descriptionIds: IntArray = intArrayOf()
private var helpLinkIds: IntArray = intArrayOf()
private var page: Int = 0
private lateinit var setupFragment: SetupFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
titleId = requireArguments().getInt(TITLE)
descriptionId = requireArguments().getInt(DESCRIPTION)
helpLinkId = requireArguments().getInt(HELP_LINK)
titleIds = requireArguments().getIntArray(TITLES) ?: intArrayOf()
descriptionIds = requireArguments().getIntArray(DESCRIPTIONS) ?: intArrayOf()
helpLinkIds = requireArguments().getIntArray(HELP_LINKS) ?: intArrayOf()
page = requireArguments().getInt(PAGE)
setupFragment = requireParentFragment() as SetupFragment
@@ -38,18 +42,24 @@ class SetupWarningDialogFragment : DialogFragment() {
}
.setNegativeButton(R.string.warning_cancel, null)
if (titleId != 0) {
builder.setTitle(titleId)
} else {
builder.setTitle("")
val messageBuilder = StringBuilder()
for (i in titleIds.indices) {
if (titleIds[i] != 0) {
messageBuilder.append(getString(titleIds[i])).append("\n\n")
}
if (descriptionIds[i] != 0) {
messageBuilder.append(getString(descriptionIds[i])).append("\n\n")
}
}
if (descriptionId != 0) {
builder.setMessage(descriptionId)
}
if (helpLinkId != 0) {
builder.setTitle("Warning")
builder.setMessage(messageBuilder.toString().trim())
if (helpLinkIds.any { it != 0 }) {
builder.setNeutralButton(R.string.warning_help) { _: DialogInterface?, _: Int ->
val helpLink = resources.getString(R.string.install_prod_keys_warning_help)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
val helpLinkId = helpLinkIds.first { it != 0 }
val helpLink = resources.getString(helpLinkId)
val intent = Intent(Intent.ACTION_VIEW, helpLink.toUri())
startActivity(intent)
}
}
@@ -60,27 +70,27 @@ class SetupWarningDialogFragment : DialogFragment() {
companion object {
const val TAG = "SetupWarningDialogFragment"
private const val TITLE = "Title"
private const val DESCRIPTION = "Description"
private const val HELP_LINK = "HelpLink"
private const val TITLES = "Titles"
private const val DESCRIPTIONS = "Descriptions"
private const val HELP_LINKS = "HelpLinks"
private const val PAGE = "Page"
fun newInstance(
titleId: Int,
descriptionId: Int,
helpLinkId: Int,
titleIds: IntArray,
descriptionIds: IntArray,
helpLinkIds: IntArray,
page: Int
): SetupWarningDialogFragment {
val dialog = SetupWarningDialogFragment()
val bundle = Bundle()
bundle.apply {
putInt(TITLE, titleId)
putInt(DESCRIPTION, descriptionId)
putInt(HELP_LINK, helpLinkId)
putIntArray(TITLES, titleIds)
putIntArray(DESCRIPTIONS, descriptionIds)
putIntArray(HELP_LINKS, helpLinkIds)
putInt(PAGE, page)
}
dialog.arguments = bundle
return dialog
}
}
}
}

View File

@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
@@ -145,7 +145,10 @@ class GamesViewModel : ViewModel() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeConfig.addGameDir(gameDir)
getGameDirs(true)
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
getGameDirs(!isFirstTimeSetup)
}
if (savedFromGameFragment) {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -7,23 +10,36 @@ data class SetupPage(
val iconId: Int,
val titleId: Int,
val descriptionId: Int,
val buttonIconId: Int,
val leftAlignedIcon: Boolean,
val buttonTextId: Int,
val pageButtons: List<PageButton>? = null,
val pageSteps: () -> PageState = { PageState.COMPLETE },
)
data class PageButton(
val iconId: Int,
val titleId: Int,
val descriptionId: Int,
val buttonAction: (callback: SetupCallback) -> Unit,
val hasWarning: Boolean,
val buttonState: () -> ButtonState = { ButtonState.BUTTON_ACTION_UNDEFINED },
val isUnskippable: Boolean = false,
val hasWarning: Boolean = false,
val warningTitleId: Int = 0,
val warningDescriptionId: Int = 0,
val warningHelpLinkId: Int = 0,
val stepCompleted: () -> StepState = { StepState.UNDEFINED }
val warningHelpLinkId: Int = 0
)
interface SetupCallback {
fun onStepCompleted()
fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean)
}
enum class StepState {
enum class PageState {
COMPLETE,
INCOMPLETE,
UNDEFINED
}
enum class ButtonState {
BUTTON_ACTION_COMPLETE,
BUTTON_ACTION_INCOMPLETE,
BUTTON_ACTION_UNDEFINED
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M12,1L3,5v6c0,5.55 3.84,10.74 9,12 5.16,-1.26 9,-6.45 9,-12V5l-9,-4zM12,11.99h7c-0.53,4.12 -3.28,7.79 -7,8.94V12H5V6.3l7,-3.11v8.8z"/>
</vector>

View File

@@ -27,7 +27,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
android:visibility="invisible"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View File

@@ -1,97 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/left_content"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"
android:gravity="center">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/right_content"
app:layout_constraintHorizontal_weight="2">
<ImageView
android:id="@+id/icon"
android:layout_width="260dp"
android:layout_height="260dp"
android:layout_gravity="center" />
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="32dp"
app:layout_constraintBottom_toTopOf="@+id/text_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="160dp"
app:layout_constraintHeight_min="80dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="160dp"
app:layout_constraintWidth_min="80dp"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintVertical_weight="3"
tools:src="@drawable/ic_notification" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_title"
style="@style/TextAppearance.Material3.DisplaySmall"
style="@style/SynthwaveText.Header"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:textColor="?attr/colorOnSurface"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/text_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="2"
app:layout_constraintTop_toBottomOf="@+id/icon"
tools:text="@string/welcome" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_description"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:textSize="20sp"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_action"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title"
app:layout_constraintVertical_weight="2"
app:lineHeight="30sp"
tools:text="@string/welcome_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/TextAppearance.Material3.TitleLarge"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingHorizontal="16dp"
android:paddingBottom="20dp"
android:gravity="center"
android:textSize="30sp"
android:visibility="invisible"
android:text="@string/step_complete"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_action"
style="@style/EdenButton.Primary"
style="@style/SynthwaveText.Accent"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:textSize="20sp"
app:iconGravity="end"
app:iconSize="24sp"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:text="@string/step_complete"
android:textAlignment="center"
android:textSize="30sp"
android:textStyle="bold"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_description"
tools:text="Get started" />
app:lineHeight="30sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/right_content"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/left_content"
app:layout_constraintHorizontal_weight="1">
<LinearLayout
android:id="@+id/page_button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -27,7 +27,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
android:visibility="invisible"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View File

@@ -0,0 +1,8 @@
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="170dp"
android:layout_height="55dp"
android:layout_marginBottom="16dp"
app:iconTint="?attr/colorOnPrimary"
app:iconSize="24dp"
style="@style/Widget.Material3.Button.UnelevatedButton" />

View File

@@ -11,7 +11,6 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="64dp"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toTopOf="@+id/text_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="220dp"
@@ -45,7 +44,7 @@
android:textAlignment="center"
android:textSize="20sp"
android:paddingHorizontal="16dp"
app:layout_constraintBottom_toTopOf="@+id/button_action"
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title"
@@ -56,8 +55,8 @@
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_confirmation"
style="@style/SynthwaveText.Accent"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_width="213dp"
android:layout_height="226dp"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:textAlignment="center"
@@ -71,20 +70,16 @@
app:layout_constraintVertical_weight="1"
app:lineHeight="30sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_action"
style="@style/EdenButton.Primary"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="48dp"
android:textSize="20sp"
app:iconGravity="end"
app:iconSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
<LinearLayout
android:id="@+id/page_button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@+id/text_description"
tools:text="Get started" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -298,6 +298,10 @@
<string name="install_prod_keys_warning_description">Valid keys are required to emulate retail games. Only homebrew apps will function if you continue.</string>
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="install_firmware_warning">Skip adding firmware?</string>
<string name="emulator_data">Setup Emulator Data</string>
<string name="emulator_data_description">Keys are required in order for the emulator to work and firmware is recommended and required for using the QLaunch applet</string>
<string name="permissions">Grant Permissions</string>
<string name="permissions_description">Grant optional permissions to use specific features of the emulator</string>
<string name="install_firmware_warning_description">Many games require access to firmware to run properly.</string>
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="notifications">Notifications</string>

View File

@@ -294,17 +294,15 @@ std::string GetLegacyPathString(EmuPath legacy_path) {
}
void SetEdenPath(EdenPath eden_path, const fs::path& new_path) {
if (!FS::IsDir(new_path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory",
PathToUTF8String(new_path));
return;
auto& instance = PathManagerImpl::GetInstance();
if (FS::IsDir(new_path)) {
instance.SetEdenPathImpl(eden_path, new_path);
} else {
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory", PathToUTF8String(new_path));
}
PathManagerImpl::GetInstance().SetEdenPathImpl(eden_path, new_path);
}
void CreateEdenPaths()
{
void CreateEdenPaths() {
PathManagerImpl::GetInstance().CreateEdenPaths();
}

View File

@@ -110,35 +110,37 @@ std::string GetTimeZoneString(TimeZone time_zone) {
}
void LogSettings() {
const auto log_setting = [](std::string_view name, const auto& value) {
LOG_INFO(Config, "{}: {}", name, value);
};
const auto log_path = [](std::string_view name, const std::filesystem::path& path) {
LOG_INFO(Config, "{}: {}", name, Common::FS::PathToUTF8String(path));
};
LOG_INFO(Config, "Eden Configuration:");
std::deque<std::string> settings_list;
for (auto& [category, settings] : values.linkage.by_category) {
for (const auto& setting : settings) {
if (setting->Id() == values.eden_token.Id()) {
// Hide the token secret, for security reasons.
continue;
// Hide the token secret, for security reasons.
if (setting->Id() != values.eden_token.Id()) {
auto const is_default = setting->ToString() == setting->DefaultToString();
auto const name = fmt::format(
"{:c}{:c} {}.{}",
is_default ? '-' : 'M',
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
setting->GetLabel());
if (is_default)
settings_list.push_back(fmt::format("{}: {}\n", name, setting->Canonicalize()));
else
settings_list.push_front(fmt::format("{}: {}\n", name, setting->Canonicalize()));
}
const auto name = fmt::format(
"{:c}{:c} {}.{}", setting->ToString() == setting->DefaultToString() ? '-' : 'M',
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
setting->GetLabel());
log_setting(name, setting->Canonicalize());
}
}
log_path("DataStorage_CacheDir", Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir));
log_path("DataStorage_ConfigDir", Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir));
log_path("DataStorage_LoadDir", Common::FS::GetEdenPath(Common::FS::EdenPath::LoadDir));
log_path("DataStorage_NANDDir", Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir));
std::string settings_str{};
for (auto const& e : settings_list)
settings_str += e;
LOG_INFO(Config, "Eden Configuration:\n{}", settings_str);
#define LOG_PATH(NAME) \
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
LOG_PATH(CacheDir);
LOG_PATH(ConfigDir);
LOG_PATH(LoadDir);
LOG_PATH(NANDDir);
LOG_PATH(SDMCDir);
#undef LOG_PATH
}
bool getDebugKnobAt(u8 i) {
@@ -167,12 +169,10 @@ bool IsDMALevelSafe() {
}
bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging) {
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
return bool(values.cpuopt_fastmem);
}
if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe) {
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
return bool(values.cpuopt_unsafe_host_mmu);
}
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
return false;
#else
@@ -338,13 +338,10 @@ void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info)
info.down_shift = 0;
break;
default:
ASSERT(false);
info.up_scale = 1;
info.down_shift = 0;
break;
UNREACHABLE();
}
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
info.up_factor = f32(info.up_scale) / (1U << info.down_shift);
info.down_factor = f32(1U << info.down_shift) / info.up_scale;
info.active = info.up_scale != 1 || info.down_shift != 0;
}

View File

@@ -775,7 +775,7 @@ struct Values {
// Per-game overrides
bool use_squashed_iterated_blend;
Setting<bool> enable_overlay{linkage, true, "enable_overlay", Category::Core};
Setting<bool> enable_overlay{linkage, false, "enable_overlay", Category::Core};
};
extern Values values;

View File

@@ -371,10 +371,12 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
// Add break loop condition.
m_guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
// Lock the thread context.
auto* params = &thread->GetNativeExecutionParameters();
LockThreadParameters(params);
// Ensure visibility of is_running after lock acquire
std::atomic_thread_fence(std::memory_order_acquire);
if (params->is_running) {
// We should signal to the running thread.
// The running thread will unlock the thread context.
@@ -389,15 +391,28 @@ const std::size_t CACHE_PAGE_SIZE = 4096;
void ArmNce::ClearInstructionCache() {
#ifdef __aarch64__
// Ensure all previous memory operations complete
asm volatile("dmb ish" ::: "memory");
asm volatile("dsb ish" ::: "memory");
asm volatile("isb" ::: "memory");
// Use IC IALLU to actually invalidate L1 instruction cache
asm volatile("dsb ish\n"
"ic iallu\n"
"dsb ish\n"
"isb" ::: "memory");
#endif
}
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
this->ClearInstructionCache();
#ifdef ARCHITECTURE_arm64
// Invalidate instruction cache for specific range instead of full flush
constexpr u64 cache_line_size = 64;
const u64 aligned_addr = addr & ~(cache_line_size - 1);
const u64 end_addr = (addr + size + cache_line_size - 1) & ~(cache_line_size - 1);
asm volatile("dsb ish" ::: "memory");
for (u64 i = aligned_addr; i < end_addr; i += cache_line_size) {
asm volatile("ic ivau, %0" :: "r"(i) : "memory");
}
asm volatile("dsb ish\n"
"isb" ::: "memory");
#endif
}
} // namespace Core

View File

@@ -2364,9 +2364,7 @@ static void Call32(Core::System& system, u32 imm, std::span<uint64_t, 8> args) {
case SvcId::CallSecureMonitor: return SvcWrap_CallSecureMonitor64From32(system, args);
case SvcId::MapInsecureMemory: return SvcWrap_MapInsecureMemory64From32(system, args);
case SvcId::UnmapInsecureMemory: return SvcWrap_UnmapInsecureMemory64From32(system, args);
default:
LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
break;
default: UNREACHABLE_MSG("Unhandled SVC {:#x}!", imm);
}
}
@@ -2495,9 +2493,7 @@ static void Call64(Core::System& system, u32 imm, std::span<uint64_t, 8> args) {
case SvcId::CallSecureMonitor: return SvcWrap_CallSecureMonitor64(system, args);
case SvcId::MapInsecureMemory: return SvcWrap_MapInsecureMemory64(system, args);
case SvcId::UnmapInsecureMemory: return SvcWrap_UnmapInsecureMemory64(system, args);
default:
LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
break;
default: UNREACHABLE_MSG("Unhandled SVC {:#x}!", imm);
}
}
void Call(Core::System& system, u32 imm) {

View File

@@ -33,12 +33,6 @@ option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
# Default to a Release build
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
message(STATUS "Defaulting to a Release build")
endif()
# Set hard requirements for C++
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -54,12 +48,6 @@ endif()
# Add the module directory to the list of paths
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
# Arch detection
if (NOT DEFINED ARCHITECTURE)
message(FATAL_ERROR "Unsupported architecture encountered. Ending CMake generation.")
endif()
message(STATUS "Target architecture: ${ARCHITECTURE}")
# Compiler flags
if (MSVC)
set(DYNARMIC_CXX_FLAGS
@@ -119,7 +107,7 @@ else()
endif()
find_package(Boost 1.57 REQUIRED)
find_package(fmt 9 CONFIG)
find_package(fmt 8 CONFIG)
# Pull in externals CMakeLists for libs where available
add_subdirectory(externals)

View File

@@ -1,5 +1,8 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
function(target_architecture_specific_sources project arch)
if (NOT DYNARMIC_MULTIARCH_BUILD)
if (NOT MULTIARCH_BUILD)
target_sources("${project}" PRIVATE ${ARGN})
return()
endif()

View File

@@ -56,7 +56,7 @@ if ("x86_64" IN_LIST ARCHITECTURE)
native/preserve_xmm.cpp
)
if (NOT MSVC AND NOT DYNARMIC_MULTIARCH_BUILD)
if (NOT MSVC AND NOT MULTIARCH_BUILD)
target_sources(dynarmic_tests PRIVATE
rsqrt_test.cpp
rsqrt_test_fn.s

View File

@@ -14,7 +14,7 @@
#include "input_common/drivers/mouse.h"
namespace InputCommon {
constexpr int update_time = 10;
constexpr int update_time = 250; // 4 TPS
constexpr float default_panning_sensitivity = 0.0010f;
constexpr float default_stick_sensitivity = 0.0006f;
constexpr float default_deadzone_counterweight = 0.01f;
@@ -73,18 +73,14 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_))
last_mouse_change = {};
last_motion_change = {};
update_thread = std::jthread([this](std::stop_token stop_token) { UpdateThread(stop_token); });
}
void Mouse::UpdateThread(std::stop_token stop_token) {
Common::SetCurrentThreadName("Mouse");
while (!stop_token.stop_requested()) {
UpdateStickInput();
UpdateMotionInput();
std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
}
update_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("Mouse");
while (!stop_token.stop_requested()) {
UpdateStickInput();
UpdateMotionInput();
std::this_thread::sleep_for(std::chrono::milliseconds(update_time));
}
});
}
void Mouse::UpdateStickInput() {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -95,10 +98,8 @@ public:
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
private:
void UpdateThread(std::stop_token stop_token);
void UpdateStickInput();
void UpdateMotionInput();
bool IsMousePanningEnabled();
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;

View File

@@ -548,7 +548,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
using namespace std::chrono_literals;
while (initialized) {
SendVibrations();
std::this_thread::sleep_for(10ms);
std::this_thread::sleep_for(250ms); // 4 TPS
}
});
}

View File

@@ -274,10 +274,18 @@ void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
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);
const auto target_file = shader_cache_folder_path / target_file_name;
if (!Common::FS::Exists(target_file) || Common::FS::RemoveFile(target_file)) {
QtCommon::Frontend::Information(tr("Successfully Removed"), tr("Successfully removed the shader cache."));
if (!Common::FS::Exists(target_file)) {
QtCommon::Frontend::Warning(tr("Error Removing Transferable Shader Cache"),
tr("A shader cache for this title does not exist."));
return;
}
if (Common::FS::RemoveFile(target_file)) {
QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully removed the transferable shader cache."));
} else {
QtCommon::Frontend::Warning(tr("Error Removing Shader Cache"), tr("Failed to remove the shader cache."));
QtCommon::Frontend::Warning(tr("Error Removing Transferable Shader Cache"),
tr("Failed to remove the transferable shader cache."));
}
}

View File

@@ -428,13 +428,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
const bool is_s8gen2 = device_id == 0x43050a01;
const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
//const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) {
LOG_WARNING(Render_Vulkan, "Unsuitable driver, continuing anyway");
} else if (!is_suitable) {
throw vk::Exception(VK_ERROR_INCOMPATIBLE_DRIVER);
}
if (!is_suitable)
LOG_WARNING(Render_Vulkan, "Unsuitable driver - continuing anyways");
if (is_nvidia) {
nvidia_arch = GetNvidiaArchitecture(physical, supported_extensions);

View File

@@ -375,12 +375,6 @@ if (APPLE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE TRUE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
if (YUZU_USE_BUNDLED_MOLTENVK)
set(MOLTENVK_PLATFORM "macOS")
set(MOLTENVK_VERSION "v1.4.0")
download_moltenvk(${MOLTENVK_PLATFORM} ${MOLTENVK_VERSION})
endif()
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib")
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")

View File

@@ -545,40 +545,44 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
// TODO(crueter): Refactor this and make it less bad
QAction* favorite = context_menu.addAction(tr("Favorite"));
context_menu.addSeparator();
QAction* start_game = context_menu.addAction(tr("Start"));
QAction* start_game_global = context_menu.addAction(tr("Start with Global Settings"));
QAction* start_game = context_menu.addAction(tr("Start Game"));
QAction* start_game_global =
context_menu.addAction(tr("Start Game without Custom Configuration"));
context_menu.addSeparator();
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
QAction* properties = context_menu.addAction(tr("Configure Game"));
context_menu.addSeparator();
QMenu* shader_cache_menu = context_menu.addMenu(tr("Shader Cache"));
QAction* open_transferable_shader_cache = shader_cache_menu->addAction(tr("Open Pipeline Cache"));
QAction* remove_shader_cache = shader_cache_menu->addAction(tr("Remove All Pipeline Caches"));
QAction* remove_gl_shader_cache = shader_cache_menu->addAction(tr("Remove OpenGL Pipeline Cache"));
QAction* remove_vk_shader_cache = shader_cache_menu->addAction(tr("Remove Vulkan Pipeline Cache"));
QMenu* storage_menu = context_menu.addMenu(tr("Storage"));
QAction* remove_all_content = storage_menu->addAction(tr("Remove All Installed Contents"));
QAction* remove_cache_storage = storage_menu->addAction(tr("Remove Cache Storage"));
QAction* remove_update = storage_menu->addAction(tr("Remove Installed Update"));
QAction* remove_dlc = storage_menu->addAction(tr("Remove All Installed DLC"));
QAction* remove_custom_config = storage_menu->addAction(tr("Remove Custom Settings"));
QAction* dump_romfs = storage_menu->addAction(tr("Dump RomFS"));
QAction* dump_romfs_sdmc = storage_menu->addAction(tr("Dump RomFS to SDMC"));
QAction* open_transferable_shader_cache =
context_menu.addAction(tr("Open Transferable Pipeline Cache"));
QAction* ryujinx = context_menu.addAction(tr("Link to Ryujinx"));
QMenu* play_time_menu = context_menu.addMenu(tr("Play Time"));
QAction* set_play_time = play_time_menu->addAction(tr("Edit"));
QAction* remove_play_time_data = play_time_menu->addAction(tr("Clear"));
context_menu.addSeparator();
QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
QAction* remove_cache_storage = remove_menu->addAction(tr("Remove Cache Storage"));
QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache"));
QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache"));
remove_menu->addSeparator();
QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
QMenu* play_time_menu = context_menu.addMenu(tr("Manage Play Time"));
QAction* set_play_time = play_time_menu->addAction(tr("Edit Play Time Data"));
QAction* remove_play_time_data = play_time_menu->addAction(tr("Remove Play Time Data"));
QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC"));
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
// TODO: Implement shortcut creation for macOS
#if !defined(__APPLE__)
QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
QAction* create_applications_menu_shortcut = shortcut_menu->addAction(tr("Add to Applications Menu"));
QAction* create_applications_menu_shortcut =
shortcut_menu->addAction(tr("Add to Applications Menu"));
#endif
context_menu.addSeparator();
QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID"));
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("View on GameDB"));
QAction* properties = context_menu.addAction(tr("Configure Game"));
favorite->setVisible(program_id != 0);
favorite->setCheckable(true);
@@ -599,12 +603,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
});
connect(start_game, &QAction::triggered, [this, path]() {
emit BootGame(QString::fromStdString(path), StartGameType::Normal);
});
connect(start_game_global, &QAction::triggered, [this, path]() {
emit BootGame(QString::fromStdString(path), StartGameType::Global);
});
connect(start_game, &QAction::triggered,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); });
connect(start_game_global, &QAction::triggered,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); });
connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
});
@@ -631,12 +633,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path);
});
connect(set_play_time, &QAction::triggered, [this, program_id]() {
emit SetPlayTimeRequested(program_id);
});
connect(remove_play_time_data, &QAction::triggered, [this, program_id]() {
emit RemovePlayTimeRequested(program_id);
});
connect(set_play_time, &QAction::triggered,
[this, program_id]() { emit SetPlayTimeRequested(program_id); });
connect(remove_play_time_data, &QAction::triggered,
[this, program_id]() { emit RemovePlayTimeRequested(program_id); });
connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] {
emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path);
});

View File

@@ -571,8 +571,6 @@ MainWindow::MainWindow(bool has_broken_vulkan)
connect(&update_input_timer, &QTimer::timeout, this, &MainWindow::UpdateInputDrivers);
update_input_timer.start();
MigrateConfigFiles();
if (has_broken_vulkan) {
UISettings::values.has_broken_vulkan = true;
@@ -627,75 +625,43 @@ MainWindow::MainWindow(bool has_broken_vulkan)
bool has_gamepath = false;
bool is_fullscreen = false;
for (int i = 1; i < args.size(); ++i) {
// Preserves drag/drop functionality
if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
game_path = args[1];
has_gamepath = true;
break;
}
// Launch game in fullscreen mode
// Preserves drag/drop functionality
if (args.size() == 2 && !args[1].startsWith(QChar::fromLatin1('-'))) {
game_path = args[1];
has_gamepath = true;
} else for (int i = 1; i < args.size(); ++i) {
if (args[i] == QStringLiteral("-f")) {
// Launch game in fullscreen mode
is_fullscreen = true;
continue;
}
// Launch game with a specific user
if (args[i] == QStringLiteral("-u")) {
if (i >= args.size() - 1) {
continue;
}
if (args[i + 1].startsWith(QChar::fromLatin1('-'))) {
continue;
}
} else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) {
// Launch game with a specific user
int user_arg_idx = ++i;
bool argument_ok;
std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
if (!argument_ok) {
// try to look it up by username, only finds the first username that matches.
const std::string user_arg_str = args[user_arg_idx].toStdString();
const auto user_idx = QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str);
if (user_idx == std::nullopt) {
LOG_ERROR(Frontend, "Invalid user argument");
std::string const user_arg_str = args[user_arg_idx].toStdString();
auto const user_idx = QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str);
if (user_idx != std::nullopt) {
selected_user = user_idx.value();
} else {
LOG_ERROR(Frontend, "Invalid user argument '{}'", user_arg_str);
continue;
}
selected_user = user_idx.value();
}
if (!QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
LOG_ERROR(Frontend, "Selected user doesn't exist");
continue;
if (QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
Settings::values.current_user = s32(selected_user);
user_flag_cmd_line = true;
} else {
LOG_ERROR(Frontend, "Selected user {} doesn't exist", selected_user);
}
Settings::values.current_user = static_cast<s32>(selected_user);
user_flag_cmd_line = true;
continue;
}
// Launch game at path
if (args[i] == QStringLiteral("-g")) {
if (i >= args.size() - 1) {
continue;
}
if (args[i + 1].startsWith(QChar::fromLatin1('-'))) {
continue;
}
} else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) {
// Launch game at path
game_path = args[++i];
has_gamepath = true;
}
if (args[i] == QStringLiteral("-qlaunch"))
} else if (args[i] == QStringLiteral("-qlaunch"))
should_launch_qlaunch = true;
if (args[i] == QStringLiteral("-setup"))
else if (args[i] == QStringLiteral("-setup"))
should_launch_setup = true;
}
@@ -2536,11 +2502,11 @@ void MainWindow::OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListRe
const QString question = [target] {
switch (target) {
case QtCommon::Game::GameListRemoveTarget::GlShaderCache:
return tr("Delete OpenGL Shader Cache?");
return tr("Delete OpenGL Transferable Shader Cache?");
case QtCommon::Game::GameListRemoveTarget::VkShaderCache:
return tr("Delete Vulkan Shader Cache?");
return tr("Delete Vulkan Transferable Shader Cache?");
case QtCommon::Game::GameListRemoveTarget::AllShaderCache:
return tr("Delete All Shader Caches?");
return tr("Delete All Transferable Shader Caches?");
case QtCommon::Game::GameListRemoveTarget::CustomConfiguration:
return tr("Remove Custom Game Configuration?");
case QtCommon::Game::GameListRemoveTarget::CacheStorage:
@@ -4154,33 +4120,6 @@ void MainWindow::OnCaptureScreenshot() {
render_window->CaptureScreenshot(filename);
}
// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
void MainWindow::MigrateConfigFiles() {
const auto config_dir_fs_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir);
const QDir config_dir =
QDir(QString::fromStdString(Common::FS::PathToUTF8String(config_dir_fs_path)));
const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
if (!Common::FS::CreateDirs(config_dir_fs_path / "custom")) {
LOG_ERROR(Frontend, "Failed to create new config file directory");
}
for (auto it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) {
const auto filename = it->toStdString();
if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) {
continue;
}
const auto origin = config_dir_fs_path / filename;
const auto destination = config_dir_fs_path / "custom" / filename;
LOG_INFO(Frontend, "Migrating config file from {} to {}", origin.string(),
destination.string());
if (!Common::FS::RenameFile(origin, destination)) {
// Delete the old config file if one already exists in the new location.
Common::FS::RemoveFile(origin);
}
}
}
#ifdef ENABLE_UPDATE_CHECKER
void MainWindow::OnEmulatorUpdateAvailable() {
QString version_string = update_future.result();

View File

@@ -8,10 +8,14 @@
RETURN=0
filter() {
filter_out() {
TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\") | not)]")
}
filter_in() {
TAGS=$(echo "$TAGS" | jq "[.[] | select(.name | test(\"$1\"; \"i\"))]")
}
usage() {
cat << EOF
Usage: $0 [uf] [PACKAGE]...
@@ -83,19 +87,27 @@ while true; do
# filter out some commonly known annoyances
# TODO add more
filter vulkan-sdk # vulkan
filter yotta # mbedtls
if [ "$PACKAGE" = "vulkan-validation-layers" ]; then
filter_in vulkan-sdk
else
filter_out vulkan-sdk
fi
[ "$CI" = "true" ] && filter_in "-"
filter_out yotta # mbedtls
# ignore betas/alphas (remove if needed)
filter alpha
filter beta
filter rc
filter_out alpha
filter_out beta
filter_out rc
# Add package-specific overrides here, e.g. here for fmt:
[ "$PACKAGE" = fmt ] && filter v0.11
[ "$PACKAGE" = fmt ] && filter_out v0.11
LATEST=$(echo "$TAGS" | jq -r '.[0].name')
[ "$LATEST" = "null" ] && echo "-- * Up-to-date" && continue
[ "$LATEST" = "$TAG" ] && [ "$FORCE" != "true" ] && echo "-- * Up-to-date" && continue
RETURN=1

View File

@@ -20,10 +20,13 @@ download_package() {
ACTUAL_HASH=$("${HASH_ALGO}"sum "$OUTFILE" | cut -d" " -f1)
[ "$ACTUAL_HASH" != "$HASH" ] && echo "!! $FILENAME did not match expected hash; expected $HASH but got $ACTUAL_HASH" && exit 1
TMPDIR="$TMP/extracted"
mkdir -p "$OUTDIR"
mkdir -p "$TMPDIR"
PREVDIR="$PWD"
cd "$OUTDIR"
mkdir -p "$TMPDIR"
cd "$TMPDIR"
case "$FILENAME" in
(*.7z)
@@ -44,11 +47,16 @@ download_package() {
# thanks gnu
if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
mv "$SUBDIR"/* .
mv "$SUBDIR"/.* . 2>/dev/null || true
mv "$SUBDIR"/* "$OUTDIR"
mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true
rmdir "$SUBDIR"
else
mv ./* "$OUTDIR"
mv ./.* "$OUTDIR" 2>/dev/null || true
fi
cd "$OUTDIR"
if echo "$JSON" | grep -e "patches" > /dev/null; then
PATCHES=$(echo "$JSON" | jq -r '.patches | join(" ")')
for patch in $PATCHES; do
@@ -65,7 +73,12 @@ ci_package() {
echo "-- CI package $PACKAGE_NAME"
for platform in windows-amd64 windows-arm64 mingw-amd64 mingw-arm64 android solaris-amd64 freebsd-amd64 openbsd-amd64 linux-amd64 linux-aarch64 macos-universal; do
for platform in windows-amd64 windows-arm64 \
mingw-amd64 mingw-arm64 \
android-aarch64 android-x86_64 \
solaris-amd64 freebsd-amd64 openbsd-amd64 \
linux-amd64 linux-aarch64 \
macos-universal; do
echo "-- * platform $platform"
case $DISABLED in

View File

@@ -597,10 +597,7 @@ def emit_call(bitness, names, suffix):
for _, name in names:
lines.append(f"{indent}case SvcId::{name}: return SvcWrap_{name}{suffix}(system, args);")
lines.append(f"{indent}default:")
lines.append(
f"{indent*2}LOG_CRITICAL(Kernel_SVC, \"Unknown SVC {{:x}}!\", imm);")
lines.append(f"{indent*2}break;")
lines.append(f"{indent}default: UNREACHABLE_MSG(\"Unhandled SVC {{:#x}}\", imm);")
lines.append(f"{indent}}}")
lines.append("}")