Compare commits
1 Commits
display-mo
...
rem-dup-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72bcc4c43a |
@@ -1,121 +1,21 @@
|
||||
#!/bin/sh -e
|
||||
#!/bin/bash -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
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"
|
||||
export NDK_CCACHE=$(which ccache)
|
||||
|
||||
: "${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
|
||||
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
|
||||
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}"
|
||||
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
|
||||
fi
|
||||
|
||||
cd src/android
|
||||
chmod +x ./gradlew
|
||||
|
||||
set -- "$@" -DUSE_CCACHE="${CCACHE}"
|
||||
[ "$DEVEL" != "true" ] && set -- "$@" -DENABLE_UPDATE_CHECKER=ON
|
||||
./gradlew assembleMainlineRelease
|
||||
./gradlew bundleMainlineRelease
|
||||
|
||||
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
|
||||
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
rm "${ANDROID_KEYSTORE_FILE}"
|
||||
fi
|
||||
|
||||
echo "-- Done! APK and AAB artifacts are in ${ARTIFACTS_DIR}"
|
||||
|
||||
ls -l "${ARTIFACTS_DIR}/"
|
||||
|
||||
22
.ci/android/package.sh
Executable file
22
.ci/android/package.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/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}/"
|
||||
@@ -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 externals/cmake-modules"
|
||||
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"
|
||||
|
||||
# license header constants, please change when needed :))))
|
||||
YEAR=2025
|
||||
|
||||
10
.patch/mbedtls/0001-cmake-version.patch
Normal file
10
.patch/mbedtls/0001-cmake-version.patch
Normal file
@@ -0,0 +1,10 @@
|
||||
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()
|
||||
13
.patch/mbedtls/0002-aesni-fix.patch
Normal file
13
.patch/mbedtls/0002-aesni-fix.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
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 */
|
||||
@@ -1,20 +0,0 @@
|
||||
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
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
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
|
||||
375
CMakeLists.txt
375
CMakeLists.txt
@@ -5,23 +5,73 @@ 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")
|
||||
|
||||
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
|
||||
# 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()
|
||||
|
||||
include(DetectPlatform)
|
||||
include(DetectArchitecture)
|
||||
include(DefaultConfig)
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(CPMUtil)
|
||||
# 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")
|
||||
|
||||
DetectArchitecture()
|
||||
# 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")
|
||||
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.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()
|
||||
|
||||
# Needed for FFmpeg w/ VAAPI and DRM
|
||||
@@ -40,10 +90,34 @@ 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)
|
||||
|
||||
@@ -103,6 +177,92 @@ 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; \
|
||||
@@ -132,11 +292,21 @@ 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)
|
||||
@@ -188,7 +358,13 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
|
||||
|
||||
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
||||
if(YUZU_ENABLE_LTO)
|
||||
include(UseLTO)
|
||||
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})
|
||||
endif()
|
||||
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
@@ -215,6 +391,7 @@ 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)
|
||||
@@ -224,12 +401,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 PLATFORM_LINUX" OFF)
|
||||
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR 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 "PLATFORM_LINUX" OFF)
|
||||
cmake_dependent_option(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "LINUX" OFF)
|
||||
|
||||
cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib" ON "APPLE" OFF)
|
||||
|
||||
@@ -259,17 +436,37 @@ 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)
|
||||
AddJsonPackage(vulkan-validation-layers)
|
||||
# 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()
|
||||
|
||||
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"
|
||||
# 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"
|
||||
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")
|
||||
@@ -277,23 +474,25 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
|
||||
endif()
|
||||
endif()
|
||||
|
||||
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}
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
|
||||
if (EXISTS ${PROJECT_SOURCE_DIR}/${compat_json})
|
||||
configure_file("${PROJECT_SOURCE_DIR}/${compat_json}"
|
||||
"${PROJECT_BINARY_DIR}/${compat_json}"
|
||||
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"
|
||||
COPYONLY)
|
||||
endif()
|
||||
|
||||
# TODO: Compat list download
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
|
||||
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 "")
|
||||
endif()
|
||||
|
||||
if (YUZU_LEGACY)
|
||||
@@ -375,6 +574,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
# System imported libraries
|
||||
# =======================================================================
|
||||
|
||||
include(CPMUtil)
|
||||
|
||||
# openssl funniness
|
||||
if (ENABLE_OPENSSL)
|
||||
if (YUZU_USE_BUNDLED_OPENSSL)
|
||||
@@ -466,7 +667,6 @@ 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)
|
||||
|
||||
@@ -531,9 +731,10 @@ endfunction()
|
||||
|
||||
if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa REQUIRED)
|
||||
find_library(IOKIT_LIBRARY IOKit REQUIRED)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
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)
|
||||
@@ -674,6 +875,11 @@ 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})
|
||||
@@ -700,6 +906,54 @@ 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
|
||||
# ===================
|
||||
|
||||
@@ -733,8 +987,47 @@ 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)
|
||||
include(FasterLinker)
|
||||
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()
|
||||
endif()
|
||||
|
||||
# Set runtime library to MD/MDd for all configurations
|
||||
@@ -756,6 +1049,14 @@ 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
|
||||
|
||||
@@ -603,12 +603,8 @@ function(AddCIPackage)
|
||||
add_ci_package(mingw-arm64)
|
||||
endif()
|
||||
|
||||
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)
|
||||
if (ANDROID AND NOT "android" IN_LIST DISABLED_PLATFORMS)
|
||||
add_ci_package(android)
|
||||
endif()
|
||||
|
||||
if(PLATFORM_SUN AND NOT "solaris-amd64" IN_LIST DISABLED_PLATFORMS)
|
||||
|
||||
49
CMakeModules/GetSCMRev.cmake
Normal file
49
CMakeModules/GetSCMRev.cmake
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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}")
|
||||
14
cpmfile.json
14
cpmfile.json
@@ -4,7 +4,7 @@
|
||||
"package": "OpenSSL",
|
||||
"name": "openssl",
|
||||
"repo": "crueter-ci/OpenSSL",
|
||||
"version": "3.6.0-965d6279e8",
|
||||
"version": "3.6.0-e3608d80df",
|
||||
"min_version": "1.1.1"
|
||||
},
|
||||
"boost": {
|
||||
@@ -25,9 +25,9 @@
|
||||
"fmt": {
|
||||
"repo": "fmtlib/fmt",
|
||||
"tag": "%VERSION%",
|
||||
"hash": "f0da82c545b01692e9fd30fdfb613dbb8dd9716983dcd0ff19ac2a8d36f74beb5540ef38072fdecc1e34191b3682a8542ecbf3a61ef287dbba0a2679d4e023f2",
|
||||
"hash": "c4ab814c20fbad7e3f0ae169125a4988a2795631194703251481dc36b18da65c886c4faa9acd046b0a295005217b3689eb0126108a9ba5aac2ca909aae263c2f",
|
||||
"version": "8",
|
||||
"git_version": "12.1.0"
|
||||
"git_version": "12.0.0"
|
||||
},
|
||||
"lz4": {
|
||||
"name": "lz4",
|
||||
@@ -91,13 +91,5 @@
|
||||
"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
62
docs/build/Android.md
vendored
@@ -1,37 +1,30 @@
|
||||
# Android
|
||||
# Note: These build instructions are a work-in-progress.
|
||||
|
||||
## 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
|
||||
@@ -45,44 +38,7 @@ 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`. 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`
|
||||
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.
|
||||
|
||||
### Additional Resources
|
||||
|
||||
<https://developer.android.com/studio/intro>
|
||||
https://developer.android.com/studio/intro
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# 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.
|
||||
@@ -6,11 +6,9 @@ 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)**
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# 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)
|
||||
16
externals/CMakeLists.txt
vendored
16
externals/CMakeLists.txt
vendored
@@ -392,19 +392,3 @@ 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()
|
||||
|
||||
17
externals/cmake-modules/DefaultConfig.cmake
vendored
17
externals/cmake-modules/DefaultConfig.cmake
vendored
@@ -1,17 +0,0 @@
|
||||
# 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()
|
||||
225
externals/cmake-modules/DetectArchitecture.cmake
vendored
225
externals/cmake-modules/DetectArchitecture.cmake
vendored
@@ -1,225 +0,0 @@
|
||||
# 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()
|
||||
151
externals/cmake-modules/DetectPlatform.cmake
vendored
151
externals/cmake-modules/DetectPlatform.cmake
vendored
@@ -1,151 +0,0 @@
|
||||
# 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()
|
||||
58
externals/cmake-modules/FasterLinker.cmake
vendored
58
externals/cmake-modules/FasterLinker.cmake
vendored
@@ -1,58 +0,0 @@
|
||||
# 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()
|
||||
162
externals/cmake-modules/GetGitRevisionDescription.cmake
vendored
Normal file
162
externals/cmake-modules/GetGitRevisionDescription.cmake
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
# 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()
|
||||
45
externals/cmake-modules/GetGitRevisionDescription.cmake.in
vendored
Normal file
45
externals/cmake-modules/GetGitRevisionDescription.cmake.in
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# 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
85
externals/cmake-modules/GetSCMRev.cmake
vendored
@@ -1,85 +0,0 @@
|
||||
# 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
34
externals/cmake-modules/UseCcache.cmake
vendored
@@ -1,34 +0,0 @@
|
||||
# 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
17
externals/cmake-modules/UseLTO.cmake
vendored
@@ -1,17 +0,0 @@
|
||||
# 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})
|
||||
34
externals/cpmfile.json
vendored
34
externals/cpmfile.json
vendored
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"sirit": {
|
||||
"repo": "eden-emulator/sirit",
|
||||
"git_version": "1.0.3",
|
||||
"git_version": "1.0.2",
|
||||
"tag": "v%VERSION%",
|
||||
"artifact": "sirit-source-%VERSION%.tar.zst",
|
||||
"hash_suffix": "sha512sum",
|
||||
@@ -23,13 +23,17 @@
|
||||
"package": "sirit",
|
||||
"name": "sirit",
|
||||
"repo": "eden-emulator/sirit",
|
||||
"version": "1.0.3"
|
||||
"version": "1.0.2",
|
||||
"disabled_platforms": [
|
||||
"mingw-amd64",
|
||||
"mingw-arm64"
|
||||
]
|
||||
},
|
||||
"httplib": {
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "e7a8877d489c97669a8ee536e1498575be921e558ed947253013fe6b67a49d4569eedd01f543caa70183b92d8ac0e8687d662a70d880954412e387317008a239",
|
||||
"git_version": "0.28.0",
|
||||
"hash": "b364500f76e2ecb0fe21b032d831272e3f1dfeea71af74e325f8fc4ce9dcdb3c941b97a5b422bdeafb9facd058597b90f8bfc284fb9afe3c33fefa15dd5a010b",
|
||||
"git_version": "0.26.0",
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"patches": [
|
||||
"0001-mingw.patch"
|
||||
@@ -89,9 +93,9 @@
|
||||
"package": "unordered_dense",
|
||||
"repo": "martinus/unordered_dense",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b98b5d4d96f8e0081b184d6c4c1181fae4e41723b54bed4296717d7f417348b48fad0bbcc664cac142b8c8a47e95aa57c1eb1cf6caa855fd782fad3e3ab99e5e",
|
||||
"hash": "f9c819e28e1c1a387acfee09277d6af5e366597a0d39acf1c687acf0608a941ba966af8aaebdb8fba0126c7360269c4a51754ef4cab17c35c01a30215f953368",
|
||||
"find_args": "CONFIG",
|
||||
"git_version": "4.8.1"
|
||||
"git_version": "4.5.0"
|
||||
},
|
||||
"mbedtls": {
|
||||
"package": "MbedTLS",
|
||||
@@ -103,8 +107,8 @@
|
||||
"artifact": "%TAG%.tar.bz2",
|
||||
"skip_updates": true,
|
||||
"patches": [
|
||||
"0001-aesni-fix.patch",
|
||||
"0002-arm64-aes-fix.patch"
|
||||
"0002-aesni-fix.patch",
|
||||
"0003-aesni-fix.patch"
|
||||
]
|
||||
},
|
||||
"enet": {
|
||||
@@ -163,7 +167,7 @@
|
||||
"package": "SDL2",
|
||||
"name": "SDL2",
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"version": "2.32.10-a65111bd2d",
|
||||
"version": "2.32.10-38e0094637",
|
||||
"min_version": "2.26.4"
|
||||
},
|
||||
"catch2": {
|
||||
@@ -188,9 +192,9 @@
|
||||
"package": "SimpleIni",
|
||||
"repo": "brofield/simpleini",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b937c18a7b6277d77ca7ebfb216af4984810f77af4c32d101b7685369a4bd5eb61406223f82698e167e6311a728d07415ab59639fdf19eff71ad6dc2abfda989",
|
||||
"hash": "6c198636816a0018adbf7f735d402c64245c6fcd540b7360d4388d46f007f3a520686cdaec4705cb8cb31401b2cb4797a80b42ea5d08a6a5807c0848386f7ca1",
|
||||
"find_args": "MODULE",
|
||||
"git_version": "4.25"
|
||||
"git_version": "4.22"
|
||||
},
|
||||
"sdl2_generic": {
|
||||
"package": "SDL2",
|
||||
@@ -210,13 +214,5 @@
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
10
externals/ffmpeg/CMakeLists.txt
vendored
10
externals/ffmpeg/CMakeLists.txt
vendored
@@ -114,6 +114,16 @@ 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
|
||||
|
||||
6
externals/ffmpeg/cpmfile.json
vendored
6
externals/ffmpeg/cpmfile.json
vendored
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"ffmpeg": {
|
||||
"repo": "FFmpeg/FFmpeg",
|
||||
"sha": "ddf443f1e9",
|
||||
"hash": "ded1c313843f23805102565bd3ca92602fb9c2951e059ca5e1a486ab3ef7d589acccf3cde05c5ff0cfc5199c3a261dccb4d2a93254e585824850696fb41a292e",
|
||||
"sha": "c2184b65d2",
|
||||
"hash": "007b1ccdd4d3ea3324835258d9a255103253bd66edb442b12d9c60dca85149cad52136a3b3120e5094115b6a3d9e80eeacbf9c07e5ffafc9ac459614d5fa3b22",
|
||||
"bundled": true
|
||||
},
|
||||
"ffmpeg-ci": {
|
||||
@@ -10,7 +10,7 @@
|
||||
"package": "FFmpeg",
|
||||
"name": "ffmpeg",
|
||||
"repo": "crueter-ci/FFmpeg",
|
||||
"version": "8.0-ddf443f1e9",
|
||||
"version": "8.0-be99d2c0b2",
|
||||
"min_version": "4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
// 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")
|
||||
@@ -64,6 +63,11 @@ android {
|
||||
versionName = getGitVersion()
|
||||
versionCode = autoVersion
|
||||
|
||||
ndk {
|
||||
@SuppressLint("ChromeOsAbiSupport")
|
||||
abiFilters += listOf("arm64-v8a")
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
val extraCMakeArgs =
|
||||
@@ -123,7 +127,7 @@ android {
|
||||
isMinifyEnabled = true
|
||||
isDebuggable = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
getDefaultProguardFile("proguard-android.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
@@ -135,7 +139,7 @@ android {
|
||||
signingConfig = signingConfigs.getByName("default")
|
||||
isDebuggable = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
getDefaultProguardFile("proguard-android.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
versionNameSuffix = "-relWithDebInfo"
|
||||
@@ -159,20 +163,12 @@ 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") {
|
||||
@@ -191,25 +187,6 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,35 +321,3 @@ 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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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,16 +8,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.PageState
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
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) {
|
||||
@@ -32,40 +29,9 @@ 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.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)
|
||||
}
|
||||
}
|
||||
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
|
||||
binding.buttonAction.setVisible(visible = false, gone = false)
|
||||
binding.textConfirmation.setVisible(true)
|
||||
}
|
||||
|
||||
binding.icon.setImageDrawable(
|
||||
@@ -78,26 +44,32 @@ 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(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))
|
||||
}
|
||||
override fun onStepCompleted() {
|
||||
ViewUtils.hideView(binding.buttonAction, 200)
|
||||
ViewUtils.showView(binding.textConfirmation, 200)
|
||||
ViewModelProvider(activity)[HomeViewModel::class.java].setShouldPageForward(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -35,14 +32,12 @@ 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
|
||||
)
|
||||
val job = gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
|
||||
job.invokeOnCompletion {
|
||||
homeViewModel.setGamesDirSelected(true)
|
||||
}
|
||||
gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setView(binding.root)
|
||||
|
||||
@@ -1132,9 +1132,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
// val enableFrameSkipping = BooleanSetting.FRAME_SKIPPING.getBoolean()
|
||||
|
||||
var fpsText = String.format("FPS: %.1f", actualFps)
|
||||
|
||||
if (enableFrameInterpolation) {
|
||||
fpsText = String.format("eFPS: %.1f", actualFps)
|
||||
fpsText += " " + getString(R.string.enhanced_fps_suffix)
|
||||
}
|
||||
|
||||
// if (enableFrameSkipping) {
|
||||
// fpsText += " " + getString(R.string.skipping_fps_suffix)
|
||||
// }
|
||||
|
||||
sb.append(fpsText)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ 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
|
||||
@@ -33,13 +34,10 @@ 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.PageState
|
||||
import org.yuzu.yuzu_emu.model.StepState
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
@@ -52,16 +50,11 @@ 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"
|
||||
@@ -101,142 +94,124 @@ class SetupFragment : Fragment() {
|
||||
requireActivity().window.navigationBarColor =
|
||||
ContextCompat.getColor(requireContext(), android.R.color.transparent)
|
||||
|
||||
pages = mutableListOf<SetupPage>()
|
||||
val pages = mutableListOf<SetupPage>()
|
||||
pages.apply {
|
||||
add(
|
||||
SetupPage(
|
||||
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())
|
||||
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())
|
||||
.areNotificationsEnabled()
|
||||
) {
|
||||
PageState.COMPLETE
|
||||
) {
|
||||
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
|
||||
} else {
|
||||
PageState.INCOMPLETE
|
||||
StepState.INCOMPLETE
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
SetupPage(
|
||||
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,
|
||||
)
|
||||
)
|
||||
},
|
||||
R.drawable.ic_firmware,
|
||||
R.string.firmware,
|
||||
R.string.firmware_description,
|
||||
R.drawable.ic_add,
|
||||
true,
|
||||
R.string.select_firmware,
|
||||
{
|
||||
val file = File(
|
||||
DirectoryInitialization.userDirectory + "/keys/prod.keys"
|
||||
)
|
||||
if (file.exists() && NativeLibrary.areKeysPresent() &&
|
||||
NativeLibrary.isFirmwareAvailable() && NativeConfig.getGameDirs()
|
||||
.isNotEmpty()
|
||||
) {
|
||||
PageState.COMPLETE
|
||||
firmwareCallback = it
|
||||
getFirmware.launch(arrayOf("application/zip"))
|
||||
},
|
||||
true,
|
||||
R.string.install_firmware_warning,
|
||||
R.string.install_firmware_warning_description,
|
||||
R.string.install_firmware_warning_help,
|
||||
{
|
||||
if (NativeLibrary.isFirmwareAvailable()) {
|
||||
StepState.COMPLETE
|
||||
} else {
|
||||
PageState.INCOMPLETE
|
||||
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
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -246,22 +221,12 @@ class SetupFragment : Fragment() {
|
||||
R.drawable.ic_check,
|
||||
R.string.done,
|
||||
R.string.done_description,
|
||||
mutableListOf<PageButton>().apply {
|
||||
add(
|
||||
PageButton(
|
||||
R.drawable.ic_arrow_forward,
|
||||
R.string.get_started,
|
||||
0,
|
||||
buttonAction = {
|
||||
finishSetup()
|
||||
},
|
||||
buttonState = {
|
||||
ButtonState.BUTTON_ACTION_UNDEFINED
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
) { PageState.UNDEFINED }
|
||||
R.drawable.ic_arrow_forward,
|
||||
false,
|
||||
R.string.text_continue,
|
||||
{ finishSetup() },
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -272,7 +237,7 @@ class SetupFragment : Fragment() {
|
||||
homeViewModel.gamesDirSelected.collect(
|
||||
viewLifecycleOwner,
|
||||
resetState = { homeViewModel.setGamesDirSelected(false) }
|
||||
) { if (it) checkForButtonState.invoke() }
|
||||
) { if (it) gamesDirCallback.onStepCompleted() }
|
||||
|
||||
binding.viewPager2.apply {
|
||||
adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
|
||||
@@ -286,18 +251,15 @@ class SetupFragment : Fragment() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
|
||||
val isFirstPage = position == 0
|
||||
val isLastPage = position == pages.size - 1
|
||||
|
||||
if (isFirstPage) {
|
||||
ViewUtils.hideView(binding.buttonBack)
|
||||
} else {
|
||||
if (position == 1 && previousPosition == 0) {
|
||||
ViewUtils.showView(binding.buttonNext)
|
||||
ViewUtils.showView(binding.buttonBack)
|
||||
}
|
||||
|
||||
if (isLastPage) {
|
||||
} else if (position == 0 && previousPosition == 1) {
|
||||
ViewUtils.hideView(binding.buttonBack)
|
||||
ViewUtils.hideView(binding.buttonNext)
|
||||
} else {
|
||||
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
|
||||
ViewUtils.hideView(binding.buttonNext)
|
||||
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
|
||||
ViewUtils.showView(binding.buttonNext)
|
||||
}
|
||||
|
||||
@@ -309,63 +271,35 @@ class SetupFragment : Fragment() {
|
||||
val index = binding.viewPager2.currentItem
|
||||
val currentPage = pages[index]
|
||||
|
||||
val warningMessages =
|
||||
mutableListOf<Triple<Int, Int, Int>>() // title, description, helpLink
|
||||
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (!hasBeenWarned[index]) {
|
||||
SetupWarningDialogFragment.newInstance(
|
||||
currentPage.warningTitleId,
|
||||
currentPage.warningDescriptionId,
|
||||
currentPage.warningHelpLinkId,
|
||||
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)!!
|
||||
|
||||
if (nextIsVisible) {
|
||||
binding.buttonNext.visibility = View.VISIBLE
|
||||
}
|
||||
if (backIsVisible) {
|
||||
binding.buttonBack.visibility = View.VISIBLE
|
||||
}
|
||||
binding.buttonNext.setVisible(nextIsVisible)
|
||||
binding.buttonBack.setVisible(backIsVisible)
|
||||
} else {
|
||||
hasBeenWarned = BooleanArray(pages.size)
|
||||
}
|
||||
@@ -373,7 +307,6 @@ class SetupFragment : Fragment() {
|
||||
setInsets()
|
||||
}
|
||||
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
NativeConfig.saveGlobalConfig()
|
||||
@@ -381,8 +314,10 @@ class SetupFragment : Fragment() {
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
|
||||
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
|
||||
if (_binding != null) {
|
||||
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
|
||||
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
|
||||
}
|
||||
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
|
||||
}
|
||||
|
||||
@@ -391,27 +326,13 @@ class SetupFragment : Fragment() {
|
||||
_binding = null
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
private lateinit var notificationCallback: SetupCallback
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
private val permissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
||||
if (it) {
|
||||
checkForButtonState.invoke()
|
||||
notificationCallback.onStepCompleted()
|
||||
}
|
||||
|
||||
if (!it &&
|
||||
@@ -424,13 +345,15 @@ 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()) {
|
||||
checkForButtonState.invoke()
|
||||
keyCallback.onStepCompleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,12 +363,14 @@ class SetupFragment : Fragment() {
|
||||
if (result != null) {
|
||||
mainActivity.processFirmware(result) {
|
||||
if (NativeLibrary.isFirmwareAvailable()) {
|
||||
checkForButtonState.invoke()
|
||||
firmwareCallback.onStepCompleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var gamesDirCallback: SetupCallback
|
||||
|
||||
val getGamesDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
@@ -454,13 +379,9 @@ 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())
|
||||
}
|
||||
|
||||
@@ -484,10 +405,8 @@ 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
|
||||
@@ -496,22 +415,11 @@ 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,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -14,21 +11,20 @@ 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 titleIds: IntArray = intArrayOf()
|
||||
private var descriptionIds: IntArray = intArrayOf()
|
||||
private var helpLinkIds: IntArray = intArrayOf()
|
||||
private var titleId: Int = 0
|
||||
private var descriptionId: Int = 0
|
||||
private var helpLinkId: Int = 0
|
||||
private var page: Int = 0
|
||||
|
||||
private lateinit var setupFragment: SetupFragment
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
titleIds = requireArguments().getIntArray(TITLES) ?: intArrayOf()
|
||||
descriptionIds = requireArguments().getIntArray(DESCRIPTIONS) ?: intArrayOf()
|
||||
helpLinkIds = requireArguments().getIntArray(HELP_LINKS) ?: intArrayOf()
|
||||
titleId = requireArguments().getInt(TITLE)
|
||||
descriptionId = requireArguments().getInt(DESCRIPTION)
|
||||
helpLinkId = requireArguments().getInt(HELP_LINK)
|
||||
page = requireArguments().getInt(PAGE)
|
||||
|
||||
setupFragment = requireParentFragment() as SetupFragment
|
||||
@@ -42,24 +38,18 @@ class SetupWarningDialogFragment : DialogFragment() {
|
||||
}
|
||||
.setNegativeButton(R.string.warning_cancel, null)
|
||||
|
||||
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 (titleId != 0) {
|
||||
builder.setTitle(titleId)
|
||||
} else {
|
||||
builder.setTitle("")
|
||||
}
|
||||
|
||||
builder.setTitle("Warning")
|
||||
builder.setMessage(messageBuilder.toString().trim())
|
||||
|
||||
if (helpLinkIds.any { it != 0 }) {
|
||||
if (descriptionId != 0) {
|
||||
builder.setMessage(descriptionId)
|
||||
}
|
||||
if (helpLinkId != 0) {
|
||||
builder.setNeutralButton(R.string.warning_help) { _: DialogInterface?, _: Int ->
|
||||
val helpLinkId = helpLinkIds.first { it != 0 }
|
||||
val helpLink = resources.getString(helpLinkId)
|
||||
val intent = Intent(Intent.ACTION_VIEW, helpLink.toUri())
|
||||
val helpLink = resources.getString(R.string.install_prod_keys_warning_help)
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(helpLink))
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -70,27 +60,27 @@ class SetupWarningDialogFragment : DialogFragment() {
|
||||
companion object {
|
||||
const val TAG = "SetupWarningDialogFragment"
|
||||
|
||||
private const val TITLES = "Titles"
|
||||
private const val DESCRIPTIONS = "Descriptions"
|
||||
private const val HELP_LINKS = "HelpLinks"
|
||||
private const val TITLE = "Title"
|
||||
private const val DESCRIPTION = "Description"
|
||||
private const val HELP_LINK = "HelpLink"
|
||||
private const val PAGE = "Page"
|
||||
|
||||
fun newInstance(
|
||||
titleIds: IntArray,
|
||||
descriptionIds: IntArray,
|
||||
helpLinkIds: IntArray,
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
helpLinkId: Int,
|
||||
page: Int
|
||||
): SetupWarningDialogFragment {
|
||||
val dialog = SetupWarningDialogFragment()
|
||||
val bundle = Bundle()
|
||||
bundle.apply {
|
||||
putIntArray(TITLES, titleIds)
|
||||
putIntArray(DESCRIPTIONS, descriptionIds)
|
||||
putIntArray(HELP_LINKS, helpLinkIds)
|
||||
putInt(TITLE, titleId)
|
||||
putInt(DESCRIPTION, descriptionId)
|
||||
putInt(HELP_LINK, helpLinkId)
|
||||
putInt(PAGE, page)
|
||||
}
|
||||
dialog.arguments = bundle
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.model
|
||||
@@ -145,10 +145,7 @@ class GamesViewModel : ViewModel() {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
NativeConfig.addGameDir(gameDir)
|
||||
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||
|
||||
getGameDirs(!isFirstTimeSetup)
|
||||
getGameDirs(true)
|
||||
}
|
||||
|
||||
if (savedFromGameFragment) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -10,36 +7,23 @@ data class SetupPage(
|
||||
val iconId: Int,
|
||||
val titleId: Int,
|
||||
val descriptionId: Int,
|
||||
val pageButtons: List<PageButton>? = null,
|
||||
val pageSteps: () -> PageState = { PageState.COMPLETE },
|
||||
|
||||
)
|
||||
|
||||
data class PageButton(
|
||||
val iconId: Int,
|
||||
val titleId: Int,
|
||||
val descriptionId: Int,
|
||||
val buttonIconId: Int,
|
||||
val leftAlignedIcon: Boolean,
|
||||
val buttonTextId: Int,
|
||||
val buttonAction: (callback: SetupCallback) -> Unit,
|
||||
val buttonState: () -> ButtonState = { ButtonState.BUTTON_ACTION_UNDEFINED },
|
||||
val isUnskippable: Boolean = false,
|
||||
val hasWarning: Boolean = false,
|
||||
val hasWarning: Boolean,
|
||||
val warningTitleId: Int = 0,
|
||||
val warningDescriptionId: Int = 0,
|
||||
val warningHelpLinkId: Int = 0
|
||||
val warningHelpLinkId: Int = 0,
|
||||
val stepCompleted: () -> StepState = { StepState.UNDEFINED }
|
||||
)
|
||||
|
||||
interface SetupCallback {
|
||||
fun onStepCompleted(pageButtonId: Int, pageFullyCompleted: Boolean)
|
||||
fun onStepCompleted()
|
||||
}
|
||||
|
||||
enum class PageState {
|
||||
enum class StepState {
|
||||
COMPLETE,
|
||||
INCOMPLETE,
|
||||
UNDEFINED
|
||||
}
|
||||
|
||||
enum class ButtonState {
|
||||
BUTTON_ACTION_COMPLETE,
|
||||
BUTTON_ACTION_INCOMPLETE,
|
||||
BUTTON_ACTION_UNDEFINED
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<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>
|
||||
@@ -27,7 +27,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/next"
|
||||
android:visibility="visible"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
|
||||
@@ -1,101 +1,97 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<LinearLayout
|
||||
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">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/left_content"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/right_content"
|
||||
app:layout_constraintHorizontal_weight="2">
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
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" />
|
||||
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">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_title"
|
||||
style="@style/SynthwaveText.Header"
|
||||
style="@style/TextAppearance.Material3.DisplaySmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/text_description"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_weight="2"
|
||||
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="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textAlignment="center"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
|
||||
android:paddingHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button_action"
|
||||
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/SynthwaveText.Accent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.Material3.TitleLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:text="@string/step_complete"
|
||||
android:textAlignment="center"
|
||||
android:paddingBottom="20dp"
|
||||
android:gravity="center"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
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"
|
||||
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"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_description"
|
||||
tools:text="Get started" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<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>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/next"
|
||||
android:visibility="visible"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<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" />
|
||||
@@ -11,6 +11,7 @@
|
||||
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"
|
||||
@@ -44,7 +45,7 @@
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
android:paddingHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/text_confirmation"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button_action"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_title"
|
||||
@@ -55,8 +56,8 @@
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/text_confirmation"
|
||||
style="@style/SynthwaveText.Accent"
|
||||
android:layout_width="213dp"
|
||||
android:layout_height="226dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="24dp"
|
||||
android:textAlignment="center"
|
||||
@@ -70,16 +71,20 @@
|
||||
app:layout_constraintVertical_weight="1"
|
||||
app:lineHeight="30sp" />
|
||||
|
||||
<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"
|
||||
<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"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/text_description"
|
||||
tools:text="Get started" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -298,10 +298,6 @@
|
||||
<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>
|
||||
|
||||
@@ -294,15 +294,17 @@ std::string GetLegacyPathString(EmuPath legacy_path) {
|
||||
}
|
||||
|
||||
void SetEdenPath(EdenPath eden_path, const fs::path& new_path) {
|
||||
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));
|
||||
if (!FS::IsDir(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory",
|
||||
PathToUTF8String(new_path));
|
||||
return;
|
||||
}
|
||||
|
||||
PathManagerImpl::GetInstance().SetEdenPathImpl(eden_path, new_path);
|
||||
}
|
||||
|
||||
void CreateEdenPaths() {
|
||||
void CreateEdenPaths()
|
||||
{
|
||||
PathManagerImpl::GetInstance().CreateEdenPaths();
|
||||
}
|
||||
|
||||
|
||||
@@ -110,37 +110,35 @@ std::string GetTimeZoneString(TimeZone time_zone) {
|
||||
}
|
||||
|
||||
void LogSettings() {
|
||||
std::deque<std::string> settings_list;
|
||||
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:");
|
||||
for (auto& [category, settings] : values.linkage.by_category) {
|
||||
for (const auto& setting : settings) {
|
||||
// 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()));
|
||||
if (setting->Id() == values.eden_token.Id()) {
|
||||
// Hide the token secret, for security reasons.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto name = fmt::format(
|
||||
"{:c}{:c} {}.{}", setting->ToString() == setting->DefaultToString() ? '-' : 'M',
|
||||
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
||||
setting->GetLabel());
|
||||
|
||||
log_setting(name, setting->Canonicalize());
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
bool getDebugKnobAt(u8 i) {
|
||||
@@ -169,10 +167,12 @@ 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);
|
||||
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
|
||||
}
|
||||
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,10 +338,13 @@ void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info)
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
ASSERT(false);
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
}
|
||||
info.up_factor = f32(info.up_scale) / (1U << info.down_shift);
|
||||
info.down_factor = f32(1U << info.down_shift) / info.up_scale;
|
||||
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.active = info.up_scale != 1 || info.down_shift != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -775,7 +775,7 @@ struct Values {
|
||||
// Per-game overrides
|
||||
bool use_squashed_iterated_blend;
|
||||
|
||||
Setting<bool> enable_overlay{linkage, false, "enable_overlay", Category::Core};
|
||||
Setting<bool> enable_overlay{linkage, true, "enable_overlay", Category::Core};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -371,12 +371,10 @@ 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.
|
||||
@@ -391,28 +389,15 @@ const std::size_t CACHE_PAGE_SIZE = 4096;
|
||||
|
||||
void ArmNce::ClearInstructionCache() {
|
||||
#ifdef __aarch64__
|
||||
// Use IC IALLU to actually invalidate L1 instruction cache
|
||||
asm volatile("dsb ish\n"
|
||||
"ic iallu\n"
|
||||
"dsb ish\n"
|
||||
"isb" ::: "memory");
|
||||
// Ensure all previous memory operations complete
|
||||
asm volatile("dmb ish" ::: "memory");
|
||||
asm volatile("dsb ish" ::: "memory");
|
||||
asm volatile("isb" ::: "memory");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
|
||||
#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
|
||||
this->ClearInstructionCache();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -2364,7 +2364,9 @@ 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: UNREACHABLE_MSG("Unhandled SVC {:#x}!", imm);
|
||||
default:
|
||||
LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2493,7 +2495,9 @@ 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: UNREACHABLE_MSG("Unhandled SVC {:#x}!", imm);
|
||||
default:
|
||||
LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void Call(Core::System& system, u32 imm) {
|
||||
|
||||
@@ -33,6 +33,12 @@ 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)
|
||||
@@ -48,6 +54,12 @@ 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
|
||||
@@ -107,7 +119,7 @@ else()
|
||||
endif()
|
||||
|
||||
find_package(Boost 1.57 REQUIRED)
|
||||
find_package(fmt 8 CONFIG)
|
||||
find_package(fmt 9 CONFIG)
|
||||
|
||||
# Pull in externals CMakeLists for libs where available
|
||||
add_subdirectory(externals)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
function(target_architecture_specific_sources project arch)
|
||||
if (NOT MULTIARCH_BUILD)
|
||||
if (NOT DYNARMIC_MULTIARCH_BUILD)
|
||||
target_sources("${project}" PRIVATE ${ARGN})
|
||||
return()
|
||||
endif()
|
||||
|
||||
@@ -56,7 +56,7 @@ if ("x86_64" IN_LIST ARCHITECTURE)
|
||||
native/preserve_xmm.cpp
|
||||
)
|
||||
|
||||
if (NOT MSVC AND NOT MULTIARCH_BUILD)
|
||||
if (NOT MSVC AND NOT DYNARMIC_MULTIARCH_BUILD)
|
||||
target_sources(dynarmic_tests PRIVATE
|
||||
rsqrt_test.cpp
|
||||
rsqrt_test_fn.s
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "input_common/drivers/mouse.h"
|
||||
|
||||
namespace InputCommon {
|
||||
constexpr int update_time = 250; // 4 TPS
|
||||
constexpr int update_time = 10;
|
||||
constexpr float default_panning_sensitivity = 0.0010f;
|
||||
constexpr float default_stick_sensitivity = 0.0006f;
|
||||
constexpr float default_deadzone_counterweight = 0.01f;
|
||||
@@ -73,14 +73,18 @@ 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) {
|
||||
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) { 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));
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::UpdateStickInput() {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// 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
|
||||
|
||||
@@ -98,8 +95,10 @@ 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;
|
||||
|
||||
@@ -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(250ms); // 4 TPS
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -428,10 +428,13 @@ 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_suitable)
|
||||
LOG_WARNING(Render_Vulkan, "Unsuitable driver - continuing anyways");
|
||||
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_nvidia) {
|
||||
nvidia_arch = GetNvidiaArchitecture(physical, supported_extensions);
|
||||
|
||||
@@ -375,6 +375,12 @@ 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}.")
|
||||
|
||||
@@ -160,20 +160,20 @@
|
||||
<property name="title">
|
||||
<string>Am&iibo</string>
|
||||
</property>
|
||||
<addaction name="action_Load_Cabinet_Nickname_Owner"/>
|
||||
<addaction name="action_Load_Cabinet_Eraser"/>
|
||||
<addaction name="action_Load_Cabinet_Restorer"/>
|
||||
<addaction name="action_Load_Cabinet_Formatter"/>
|
||||
<addaction name="action_Launch_Cabinet_Nickname_Owner"/>
|
||||
<addaction name="action_Launch_Cabinet_Eraser"/>
|
||||
<addaction name="action_Launch_Cabinet_Restorer"/>
|
||||
<addaction name="action_Launch_Cabinet_Formatter"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_Applets">
|
||||
<property name="title">
|
||||
<string>&Applets</string>
|
||||
<string>Launch &Applet</string>
|
||||
</property>
|
||||
<addaction name="action_Load_Home_Menu"/>
|
||||
<addaction name="action_Load_Album"/>
|
||||
<addaction name="action_Load_Mii_Edit"/>
|
||||
<addaction name="action_Open_Controller_Menu"/>
|
||||
<addaction name="action_Open_Setup"/>
|
||||
<addaction name="action_Launch_QLaunch"/>
|
||||
<addaction name="action_Launch_MiiEdit"/>
|
||||
<addaction name="action_Launch_Controller"/>
|
||||
<addaction name="action_Launch_Setup"/>
|
||||
<addaction name="action_Launch_PhotoViewer"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTAS">
|
||||
<property name="title">
|
||||
@@ -420,34 +420,34 @@
|
||||
<string>&Capture Screenshot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Album">
|
||||
<action name="action_Launch_PhotoViewer">
|
||||
<property name="text">
|
||||
<string>Open &Album</string>
|
||||
<string>&Album</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Cabinet_Nickname_Owner">
|
||||
<action name="action_Launch_Cabinet_Nickname_Owner">
|
||||
<property name="text">
|
||||
<string>&Set Nickname and Owner</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Cabinet_Eraser">
|
||||
<action name="action_Launch_Cabinet_Eraser">
|
||||
<property name="text">
|
||||
<string>&Delete Game Data</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Cabinet_Restorer">
|
||||
<action name="action_Launch_Cabinet_Restorer">
|
||||
<property name="text">
|
||||
<string>&Restore Amiibo</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Cabinet_Formatter">
|
||||
<action name="action_Launch_Cabinet_Formatter">
|
||||
<property name="text">
|
||||
<string>&Format Amiibo</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Mii_Edit">
|
||||
<action name="action_Launch_MiiEdit">
|
||||
<property name="text">
|
||||
<string>Open &Mii Editor</string>
|
||||
<string>&Mii Editor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Configure_Tas">
|
||||
@@ -493,7 +493,7 @@
|
||||
<string>R&ecord</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_Controller_Menu">
|
||||
<action name="action_Launch_Controller">
|
||||
<property name="text">
|
||||
<string>Open &Controller Menu</string>
|
||||
</property>
|
||||
@@ -503,14 +503,14 @@
|
||||
<string>Install Decryption &Keys</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Load_Home_Menu">
|
||||
<action name="action_Launch_QLaunch">
|
||||
<property name="text">
|
||||
<string>Open &Home Menu</string>
|
||||
<string>&Home Menu</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Open_Setup">
|
||||
<action name="action_Launch_Setup">
|
||||
<property name="text">
|
||||
<string>Open &Setup</string>
|
||||
<string>&Setup</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::TextHeuristicRole</enum>
|
||||
|
||||
@@ -571,6 +571,8 @@ 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;
|
||||
|
||||
@@ -625,43 +627,75 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
||||
bool has_gamepath = false;
|
||||
bool is_fullscreen = false;
|
||||
|
||||
// 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) {
|
||||
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
|
||||
if (args[i] == QStringLiteral("-f")) {
|
||||
// Launch game in fullscreen mode
|
||||
is_fullscreen = true;
|
||||
} else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) {
|
||||
// Launch game with a specific user
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
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);
|
||||
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");
|
||||
continue;
|
||||
}
|
||||
|
||||
selected_user = user_idx.value();
|
||||
}
|
||||
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);
|
||||
|
||||
if (!QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
|
||||
LOG_ERROR(Frontend, "Selected user doesn't exist");
|
||||
continue;
|
||||
}
|
||||
} else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) {
|
||||
// Launch game at path
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
game_path = args[++i];
|
||||
has_gamepath = true;
|
||||
} else if (args[i] == QStringLiteral("-qlaunch"))
|
||||
}
|
||||
|
||||
if (args[i] == QStringLiteral("-qlaunch"))
|
||||
should_launch_qlaunch = true;
|
||||
else if (args[i] == QStringLiteral("-setup"))
|
||||
if (args[i] == QStringLiteral("-setup"))
|
||||
should_launch_setup = true;
|
||||
}
|
||||
|
||||
@@ -671,13 +705,13 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
||||
}
|
||||
|
||||
if (should_launch_setup) {
|
||||
OnInitialSetup();
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Starter, std::nullopt);
|
||||
} else {
|
||||
if (!game_path.isEmpty()) {
|
||||
BootGame(game_path, ApplicationAppletParameters());
|
||||
} else {
|
||||
if (should_launch_qlaunch) {
|
||||
OnHomeMenu();
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::QLaunch, std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1627,19 +1661,35 @@ void MainWindow::ConnectMenuEvents() {
|
||||
connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &MainWindow::OnSaveConfig);
|
||||
|
||||
// Tools
|
||||
connect_menu(ui->action_Load_Album, &MainWindow::OnAlbum);
|
||||
connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
|
||||
[this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
|
||||
connect_menu(ui->action_Load_Cabinet_Eraser,
|
||||
[this]() { OnCabinet(Service::NFP::CabinetMode::StartGameDataEraser); });
|
||||
connect_menu(ui->action_Load_Cabinet_Restorer,
|
||||
[this]() { OnCabinet(Service::NFP::CabinetMode::StartRestorer); });
|
||||
connect_menu(ui->action_Load_Cabinet_Formatter,
|
||||
[this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
|
||||
connect_menu(ui->action_Load_Mii_Edit, &MainWindow::OnMiiEdit);
|
||||
connect_menu(ui->action_Open_Controller_Menu, &MainWindow::OnOpenControllerMenu);
|
||||
connect_menu(ui->action_Load_Home_Menu, &MainWindow::OnHomeMenu);
|
||||
connect_menu(ui->action_Open_Setup, &MainWindow::OnInitialSetup);
|
||||
connect_menu(ui->action_Launch_PhotoViewer, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::PhotoViewer, std::nullopt);
|
||||
});
|
||||
connect_menu(ui->action_Launch_MiiEdit, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::MiiEdit, std::nullopt);
|
||||
});
|
||||
connect_menu(ui->action_Launch_Controller, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Controller, std::nullopt);
|
||||
});
|
||||
connect_menu(ui->action_Launch_QLaunch, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::QLaunch, std::nullopt);
|
||||
});
|
||||
connect_menu(ui->action_Launch_Setup, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Starter, std::nullopt);
|
||||
});
|
||||
// Tools (cabinet)
|
||||
connect_menu(ui->action_Launch_Cabinet_Nickname_Owner, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartNicknameAndOwnerSettings});
|
||||
});
|
||||
connect_menu(ui->action_Launch_Cabinet_Eraser, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartGameDataEraser});
|
||||
});
|
||||
connect_menu(ui->action_Launch_Cabinet_Restorer, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartRestorer});
|
||||
});
|
||||
connect_menu(ui->action_Launch_Cabinet_Formatter, [this]{
|
||||
LaunchFirmwareApplet(Service::AM::AppletProgramId::Cabinet, {Service::NFP::CabinetMode::StartFormatter});
|
||||
});
|
||||
|
||||
connect_menu(ui->action_Desktop, &MainWindow::OnCreateHomeMenuDesktopShortcut);
|
||||
connect_menu(ui->action_Application_Menu,
|
||||
&MainWindow::OnCreateHomeMenuApplicationMenuShortcut);
|
||||
@@ -1680,14 +1730,16 @@ void MainWindow::UpdateMenuState() {
|
||||
ui->action_Pause,
|
||||
};
|
||||
|
||||
const std::array applet_actions{ui->action_Load_Album,
|
||||
ui->action_Load_Cabinet_Nickname_Owner,
|
||||
ui->action_Load_Cabinet_Eraser,
|
||||
ui->action_Load_Cabinet_Restorer,
|
||||
ui->action_Load_Cabinet_Formatter,
|
||||
ui->action_Load_Mii_Edit,
|
||||
ui->action_Load_Home_Menu,
|
||||
ui->action_Open_Controller_Menu};
|
||||
const std::array applet_actions{
|
||||
ui->action_Launch_PhotoViewer,
|
||||
ui->action_Launch_Cabinet_Nickname_Owner,
|
||||
ui->action_Launch_Cabinet_Eraser,
|
||||
ui->action_Launch_Cabinet_Restorer,
|
||||
ui->action_Launch_Cabinet_Formatter,
|
||||
ui->action_Launch_MiiEdit,
|
||||
ui->action_Launch_QLaunch,
|
||||
ui->action_Launch_Controller
|
||||
};
|
||||
|
||||
for (QAction* action : running_actions) {
|
||||
action->setEnabled(emulation_running);
|
||||
@@ -3926,157 +3978,61 @@ void MainWindow::OnGameListRefresh()
|
||||
SetFirmwareVersion();
|
||||
}
|
||||
|
||||
void MainWindow::OnAlbum() {
|
||||
constexpr u64 AlbumId = static_cast<u64>(Service::AM::AppletProgramId::PhotoViewer);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Album applet."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto album_nca = bis_system->GetEntry(AlbumId, FileSys::ContentRecordType::Program);
|
||||
if (!album_nca) {
|
||||
QMessageBox::warning(this, tr("Album Applet"),
|
||||
tr("Album applet is not available. Please reinstall firmware."));
|
||||
return;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer);
|
||||
|
||||
const auto filename = QString::fromStdString(album_nca->GetFullPath());
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename, LibraryAppletParameters(AlbumId, Service::AM::AppletId::PhotoViewer));
|
||||
}
|
||||
|
||||
void MainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
|
||||
constexpr u64 CabinetId = static_cast<u64>(Service::AM::AppletProgramId::Cabinet);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Cabinet applet."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto cabinet_nca = bis_system->GetEntry(CabinetId, FileSys::ContentRecordType::Program);
|
||||
if (!cabinet_nca) {
|
||||
QMessageBox::warning(this, tr("Cabinet Applet"),
|
||||
tr("Cabinet applet is not available. Please reinstall firmware."));
|
||||
return;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet);
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCabinetMode(mode);
|
||||
|
||||
const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename, LibraryAppletParameters(CabinetId, Service::AM::AppletId::Cabinet));
|
||||
}
|
||||
|
||||
void MainWindow::OnMiiEdit() {
|
||||
constexpr u64 MiiEditId = static_cast<u64>(Service::AM::AppletProgramId::MiiEdit);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Mii editor."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto mii_applet_nca = bis_system->GetEntry(MiiEditId, FileSys::ContentRecordType::Program);
|
||||
if (!mii_applet_nca) {
|
||||
QMessageBox::warning(this, tr("Mii Edit Applet"),
|
||||
tr("Mii editor is not available. Please reinstall firmware."));
|
||||
return;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit);
|
||||
|
||||
const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename, LibraryAppletParameters(MiiEditId, Service::AM::AppletId::MiiEdit));
|
||||
}
|
||||
|
||||
void MainWindow::OnOpenControllerMenu() {
|
||||
constexpr u64 ControllerAppletId = static_cast<u64>(Service::AM::AppletProgramId::Controller);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use the Controller Menu."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto controller_applet_nca =
|
||||
bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
|
||||
if (!controller_applet_nca) {
|
||||
QMessageBox::warning(this, tr("Controller Applet"),
|
||||
tr("Controller Menu is not available. Please reinstall firmware."));
|
||||
return;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller);
|
||||
|
||||
const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename,
|
||||
LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller));
|
||||
}
|
||||
|
||||
void MainWindow::OnHomeMenu() {
|
||||
void MainWindow::LaunchFirmwareApplet(Service::AM::AppletProgramId program_id, std::optional<Service::NFP::CabinetMode> cabinet_mode) {
|
||||
auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get());
|
||||
|
||||
using namespace QtCommon::StringLookup;
|
||||
|
||||
switch (result) {
|
||||
case FirmwareManager::ErrorFirmwareMissing:
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
Lookup(FwCheckErrorFirmwareMissing));
|
||||
QMessageBox::warning(this, tr("No firmware available"), Lookup(FwCheckErrorFirmwareMissing));
|
||||
return;
|
||||
case FirmwareManager::ErrorFirmwareCorrupted:
|
||||
QMessageBox::warning(this, tr("Firmware Corrupted"),
|
||||
Lookup(FwCheckErrorFirmwareCorrupted));
|
||||
QMessageBox::warning(this, tr("Firmware Corrupted"), Lookup(FwCheckErrorFirmwareCorrupted));
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
|
||||
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
|
||||
if (!qlaunch_applet_nca) {
|
||||
QMessageBox::warning(this, tr("Home Menu Applet"),
|
||||
tr("Home Menu is not available. Please reinstall firmware."));
|
||||
return;
|
||||
if (auto applet_nca = bis_system->GetEntry(u64(program_id), FileSys::ContentRecordType::Program); applet_nca) {
|
||||
if (auto const applet_id = [program_id] {
|
||||
using namespace Service::AM;
|
||||
switch (program_id) {
|
||||
case AppletProgramId::OverlayDisplay: return AppletId::OverlayDisplay;
|
||||
case AppletProgramId::QLaunch: return AppletId::QLaunch;
|
||||
case AppletProgramId::Starter: return AppletId::Starter;
|
||||
case AppletProgramId::Auth: return AppletId::Auth;
|
||||
case AppletProgramId::Cabinet: return AppletId::Cabinet;
|
||||
case AppletProgramId::Controller: return AppletId::Controller;
|
||||
case AppletProgramId::DataErase: return AppletId::DataErase;
|
||||
case AppletProgramId::Error: return AppletId::Error;
|
||||
case AppletProgramId::NetConnect: return AppletId::NetConnect;
|
||||
case AppletProgramId::ProfileSelect: return AppletId::ProfileSelect;
|
||||
case AppletProgramId::SoftwareKeyboard: return AppletId::SoftwareKeyboard;
|
||||
case AppletProgramId::MiiEdit: return AppletId::MiiEdit;
|
||||
case AppletProgramId::Web: return AppletId::Web;
|
||||
case AppletProgramId::Shop: return AppletId::Shop;
|
||||
case AppletProgramId::PhotoViewer: return AppletId::PhotoViewer;
|
||||
case AppletProgramId::Settings: return AppletId::Settings;
|
||||
case AppletProgramId::OfflineWeb: return AppletId::OfflineWeb;
|
||||
case AppletProgramId::LoginShare: return AppletId::LoginShare;
|
||||
case AppletProgramId::WebAuth: return AppletId::WebAuth;
|
||||
case AppletProgramId::MyPage: return AppletId::MyPage;
|
||||
default: return AppletId::None;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch);
|
||||
|
||||
const auto filename = QString::fromStdString((qlaunch_applet_nca->GetFullPath()));
|
||||
}(); applet_id != Service::AM::AppletId::None) {
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(applet_id);
|
||||
if (cabinet_mode)
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCabinetMode(*cabinet_mode);
|
||||
// ?
|
||||
auto const filename = QString::fromStdString((applet_nca->GetFullPath()));
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename, LibraryAppletParameters(QLaunchId, Service::AM::AppletId::QLaunch));
|
||||
}
|
||||
|
||||
void MainWindow::OnInitialSetup() {
|
||||
constexpr u64 Starter = static_cast<u64>(Service::AM::AppletProgramId::Starter);
|
||||
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
QMessageBox::warning(this, tr("No firmware available"),
|
||||
tr("Please install firmware to use Starter."));
|
||||
return;
|
||||
BootGame(filename, LibraryAppletParameters(u64(program_id), applet_id));
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Unknown applet"), tr("Applet doesn't map to a known value."));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Record not found"), tr("Applet not found. Please reinstall firmware."));
|
||||
}
|
||||
|
||||
auto qlaunch_nca = bis_system->GetEntry(Starter, FileSys::ContentRecordType::Program);
|
||||
if (!qlaunch_nca) {
|
||||
QMessageBox::warning(this, tr("Starter Applet"),
|
||||
tr("Starter is not available. Please reinstall firmware."));
|
||||
return;
|
||||
}
|
||||
|
||||
QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Starter);
|
||||
|
||||
const auto filename = QString::fromStdString((qlaunch_nca->GetFullPath()));
|
||||
UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
|
||||
BootGame(filename, LibraryAppletParameters(Starter, Service::AM::AppletId::Starter));
|
||||
}
|
||||
|
||||
void MainWindow::OnCreateHomeMenuDesktopShortcut() {
|
||||
@@ -4120,6 +4076,33 @@ 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();
|
||||
|
||||
@@ -101,6 +101,7 @@ class InputSubsystem;
|
||||
namespace Service::AM {
|
||||
struct FrontendAppletParameters;
|
||||
enum class AppletId : u32;
|
||||
enum class AppletProgramId : u64;
|
||||
} // namespace Service::AM
|
||||
|
||||
namespace Service::AM::Frontend {
|
||||
@@ -399,12 +400,7 @@ private slots:
|
||||
void ResetWindowSize720();
|
||||
void ResetWindowSize900();
|
||||
void ResetWindowSize1080();
|
||||
void OnAlbum();
|
||||
void OnCabinet(Service::NFP::CabinetMode mode);
|
||||
void OnMiiEdit();
|
||||
void OnOpenControllerMenu();
|
||||
void OnHomeMenu();
|
||||
void OnInitialSetup();
|
||||
void LaunchFirmwareApplet(Service::AM::AppletProgramId program_id, std::optional<Service::NFP::CabinetMode> mode);
|
||||
void OnCreateHomeMenuDesktopShortcut();
|
||||
void OnCreateHomeMenuApplicationMenuShortcut();
|
||||
void OnCaptureScreenshot();
|
||||
@@ -428,7 +424,6 @@ private:
|
||||
bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id,
|
||||
u64* selected_title_id, u8* selected_content_record_type);
|
||||
ContentManager::InstallResult InstallNCA(const QString& filename);
|
||||
void MigrateConfigFiles();
|
||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
|
||||
std::string_view gpu_vendor = {});
|
||||
void UpdateDockedButton();
|
||||
|
||||
@@ -8,14 +8,10 @@
|
||||
|
||||
RETURN=0
|
||||
|
||||
filter_out() {
|
||||
filter() {
|
||||
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]...
|
||||
@@ -87,27 +83,19 @@ while true; do
|
||||
# filter out some commonly known annoyances
|
||||
# TODO add more
|
||||
|
||||
if [ "$PACKAGE" = "vulkan-validation-layers" ]; then
|
||||
filter_in vulkan-sdk
|
||||
else
|
||||
filter_out vulkan-sdk
|
||||
fi
|
||||
|
||||
[ "$CI" = "true" ] && filter_in "-"
|
||||
|
||||
filter_out yotta # mbedtls
|
||||
filter vulkan-sdk # vulkan
|
||||
filter yotta # mbedtls
|
||||
|
||||
# ignore betas/alphas (remove if needed)
|
||||
filter_out alpha
|
||||
filter_out beta
|
||||
filter_out rc
|
||||
filter alpha
|
||||
filter beta
|
||||
filter rc
|
||||
|
||||
# Add package-specific overrides here, e.g. here for fmt:
|
||||
[ "$PACKAGE" = fmt ] && filter_out v0.11
|
||||
[ "$PACKAGE" = fmt ] && filter 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
|
||||
|
||||
@@ -20,13 +20,10 @@ 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"
|
||||
mkdir -p "$TMPDIR"
|
||||
cd "$TMPDIR"
|
||||
cd "$OUTDIR"
|
||||
|
||||
case "$FILENAME" in
|
||||
(*.7z)
|
||||
@@ -47,16 +44,11 @@ download_package() {
|
||||
# thanks gnu
|
||||
if [ "$(echo "$DIRS" | wc -l)" -eq 2 ]; then
|
||||
SUBDIR=$(find . -maxdepth 1 -type d -not -name ".")
|
||||
mv "$SUBDIR"/* "$OUTDIR"
|
||||
mv "$SUBDIR"/.* "$OUTDIR" 2>/dev/null || true
|
||||
mv "$SUBDIR"/* .
|
||||
mv "$SUBDIR"/.* . 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
|
||||
@@ -73,12 +65,7 @@ ci_package() {
|
||||
|
||||
echo "-- CI package $PACKAGE_NAME"
|
||||
|
||||
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
|
||||
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
|
||||
echo "-- * platform $platform"
|
||||
|
||||
case $DISABLED in
|
||||
|
||||
@@ -597,7 +597,10 @@ 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: UNREACHABLE_MSG(\"Unhandled SVC {{:#x}}\", imm);")
|
||||
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}}}")
|
||||
lines.append("}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user