Compare commits

..

No commits in common. "master" and "v0.1.0" have entirely different histories.

473 changed files with 29945 additions and 40154 deletions

View File

@ -1,6 +1,6 @@
#!/bin/sh -e
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# 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)
@ -29,7 +29,6 @@ Options:
-b, --build-type <TYPE> Build type (variable: TYPE)
Valid values are: Release, RelWithDebInfo, Debug
Default: Debug
-n, --nightly Create a nightly build.
Extra arguments are passed to CMake (e.g. -DCMAKE_OPTION_NAME=VALUE)
Set the CCACHE variable to "true" to enable build caching.
@ -62,7 +61,6 @@ while true; do
-r|--release) DEVEL=false ;;
-t|--target) target "$2"; shift ;;
-b|--build-type) type "$2"; shift ;;
-n|--nightly) NIGHTLY=true ;;
-h|--help) usage ;;
*) break ;;
esac
@ -103,20 +101,7 @@ cd src/android
chmod +x ./gradlew
set -- "$@" -DUSE_CCACHE="${CCACHE}"
nightly() {
[ "$NIGHTLY" = "true" ]
}
if nightly || [ "$DEVEL" != "true" ]; then
set -- "$@" -DENABLE_UPDATE_CHECKER=ON
fi
if nightly; then
NIGHTLY=true
else
NIGHTLY=false
fi
[ "$DEVEL" != "true" ] && set -- "$@" -DENABLE_UPDATE_CHECKER=ON
echo "-- building..."
@ -125,7 +110,6 @@ echo "-- building..."
-Dorg.gradle.parallel="${CCACHE}" \
-Dorg.gradle.workers.max="${NUM_JOBS}" \
-PYUZU_ANDROID_ARGS="$*" \
-Pnightly="$NIGHTLY" \
--info
if [ -n "${ANDROID_KEYSTORE_B64}" ]; then

View File

@ -41,8 +41,9 @@ EOF
while true; do
case "$1" in
(-uc) UPDATE=true; COMMIT=true ;;
(-u|--update) UPDATE=true ;;
(-c|--commit) UPDATE=true; COMMIT=true ;;
(-c|--commit) COMMIT=true ;;
("$0") break ;;
("") break ;;
(*) usage ;;

View File

@ -1,6 +1,6 @@
#!/bin/bash -e
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
case "$1" in
@ -104,7 +104,8 @@ cmake .. -G Ninja \
-DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \
-DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \
-DYUZU_USE_FASTER_LD=ON \
-DENABLE_LTO=ON \
-DYUZU_ENABLE_LTO=ON \
-DDYNARMIC_ENABLE_LTO=ON \
"${EXTRA_CMAKE_FLAGS[@]}"
ninja -j${NPROC}

5
.ci/windows/build.sh Executable file → Normal file
View File

@ -1,6 +1,6 @@
#!/bin/bash -ex
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
if [ "$COMPILER" == "clang" ]
@ -32,8 +32,9 @@ cmake .. -G Ninja \
-DYUZU_ROOM_STANDALONE=OFF \
-DYUZU_USE_QT_MULTIMEDIA=${USE_MULTIMEDIA:-false} \
-DYUZU_USE_QT_WEB_ENGINE=${USE_WEBENGINE:-false} \
-DENABLE_LTO=ON \
-DYUZU_ENABLE_LTO=ON \
-DCMAKE_EXE_LINKER_FLAGS=" /LTCG" \
-DDYNARMIC_ENABLE_LTO=ON \
-DYUZU_USE_BUNDLED_QT=${BUNDLE_QT:-false} \
-DUSE_CCACHE=${CCACHE:-false} \
-DENABLE_UPDATE_CHECKER=${DEVEL:-true} \

View File

@ -0,0 +1,11 @@
--- a/libs/context/CMakeLists.txt 2025-09-08 00:42:31.303651800 -0400
+++ b/libs/context/CMakeLists.txt 2025-09-08 00:42:40.592184300 -0400
@@ -146,7 +146,7 @@
set(ASM_LANGUAGE ASM)
endif()
elseif(BOOST_CONTEXT_ASSEMBLER STREQUAL armasm)
- set(ASM_LANGUAGE ASM_ARMASM)
+ set(ASM_LANGUAGE ASM_MARMASM)
else()
set(ASM_LANGUAGE ASM_MASM)
endif()

View File

@ -0,0 +1,14 @@
diff --git a/libs/context/CMakeLists.txt b/libs/context/CMakeLists.txt
index 8210f65..0e59dd7 100644
--- a/libs/context/CMakeLists.txt
+++ b/libs/context/CMakeLists.txt
@@ -186,7 +186,8 @@ if(BOOST_CONTEXT_IMPLEMENTATION STREQUAL "fcontext")
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "/safeseh")
endif()
- else() # masm
+ # armasm doesn't support most of these options
+ elseif(NOT BOOST_CONTEXT_ASSEMBLER STREQUAL armasm) # masm
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")

View File

@ -1,25 +0,0 @@
From ce992811fe8eb5ea7ad37e5b255bfecb0c313928 Mon Sep 17 00:00:00 2001
From: crueter <crueter@crueter.xyz>
Date: Sun, 7 Sep 2025 23:43:57 -0400
Subject: [PATCH] [algorithm] fix missing declaration error
Projects with restrictive error options won't compile without this
Signed-off-by: crueter <crueter@crueter.xyz>
---
include/jwt/algorithm.hpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/jwt/algorithm.hpp b/include/jwt/algorithm.hpp
index 0e3b843..35347fe 100644
--- a/include/jwt/algorithm.hpp
+++ b/include/jwt/algorithm.hpp
@@ -63,6 +63,8 @@ using sign_func_t = sign_result_t (*) (const jwt::string_view key,
using verify_func_t = verify_result_t (*) (const jwt::string_view key,
const jwt::string_view head,
const jwt::string_view jwt_sign);
+
+verify_result_t is_secret_a_public_key(const jwt::string_view secret);
namespace algo {

View File

@ -1,62 +1,52 @@
From 436fc1978c78edd085d99b33275b24be0ac96aa0 Mon Sep 17 00:00:00 2001
From e1a946ffb79022d38351a0623f819a5419965c3e Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sun, 1 Feb 2026 16:21:10 -0500
Subject: [PATCH] Fix build on MinGW
Date: Fri, 24 Oct 2025 23:41:09 -0700
Subject: [PATCH] [build] Fix MinGW missing GetAddrInfoExCancel definition
MinGW doesn't define GetAddrInfoExCancel.
MinGW does not define GetAddrInfoExCancel in its wstcpi whatever header,
so to get around this we can just load it with GetProcAddress et al.
Signed-off-by: crueter <crueter@eden-emu.dev>
---
httplib.h | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
httplib.h | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/httplib.h b/httplib.h
index ec8d2a2..5f9a510 100644
index e15ba44..90a76dc 100644
--- a/httplib.h
+++ b/httplib.h
@@ -203,14 +203,17 @@
@@ -203,11 +203,13 @@
#error Sorry, Visual Studio versions prior to 2015 are not supported
#endif
-#pragma comment(lib, "ws2_32.lib")
-
#ifndef _SSIZE_T_DEFINED
using ssize_t = __int64;
#define _SSIZE_T_DEFINED
#endif
#endif // _MSC_VER
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#pragma comment(lib, "ws2_32.lib")
+#endif
+
+
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
#endif // S_ISREG
@@ -4528,7 +4531,17 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
@@ -3557,7 +3559,15 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
auto wait_result =
::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
if (wait_result == WAIT_TIMEOUT) {
+#ifdef __MINGW32__
+ typedef INT(WSAAPI * PFN_GETADDRINFOEXCANCEL)(HANDLE * CancelHandle);
+ auto wsdll = LoadLibraryW((wchar_t *)"ws2_32.lib");
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel =
+ (PFN_GETADDRINFOEXCANCEL)GetProcAddress(wsdll, "GetAddrInfoExCancel");
+ typedef INT (WSAAPI *PFN_GETADDRINFOEXCANCEL)(HANDLE *CancelHandle);
+ auto wsdll = LoadLibraryW((wchar_t*) "ws2_32.lib");
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel = (PFN_GETADDRINFOEXCANCEL) GetProcAddress(wsdll, "GetAddrInfoExCancel");
+
+ if (cancel_handle) { GetAddrInfoExCancel(&cancel_handle); }
+#else
if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
+#endif
+
::CloseHandle(event);
return EAI_AGAIN;
}
@@ -13952,3 +13965,4 @@ inline SSL_CTX *Client::ssl_context() const {
} // namespace httplib
#endif // CPPHTTPLIB_HTTPLIB_H
+
--
2.51.2
2.51.0

View File

@ -1,28 +0,0 @@
From cc15da16e533b2a801934eab2dfeaf3c3949a1dc Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 8 Sep 2025 12:28:55 -0400
Subject: [PATCH] [cmake] disable NEON runtime check on clang-cl
When enabling runtime NEON checking for clang-cl, the linker would error out with `undefined symbol: __emit`, since clang doesn't actually implement this instruction. Therefore it makes sense to disable the runtime check by default on this platform, until either this is fixed or a clang-cl compatible intrinsic check is added (I don't have enough knowledge of MSVC to do this)
---
cmake/OpusConfig.cmake | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/cmake/OpusConfig.cmake b/cmake/OpusConfig.cmake
index e9319fbad..d0f459e88 100644
--- a/cmake/OpusConfig.cmake
+++ b/cmake/OpusConfig.cmake
@@ -71,7 +71,12 @@ elseif(OPUS_CPU_ARM AND NOT OPUS_DISABLE_INTRINSICS)
opus_detect_neon(COMPILER_SUPPORT_NEON)
if(COMPILER_SUPPORT_NEON)
option(OPUS_USE_NEON "Option to enable NEON" ON)
- option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ON)
+ if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(NEON_RUNTIME_CHECK_DEFAULT OFF)
+ else()
+ set(NEON_RUNTIME_CHECK_DEFAULT ON)
+ endif()
+ option(OPUS_MAY_HAVE_NEON "Does runtime check for neon support" ${NEON_RUNTIME_CHECK_DEFAULT})
option(OPUS_PRESUME_NEON "Assume target CPU has NEON support" OFF)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
set(OPUS_PRESUME_NEON ON)

View File

@ -1,153 +0,0 @@
From bf455b67b4eaa446ffae5d25410b141b7b1b1082 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 8 Sep 2025 12:08:20 -0400
Subject: [PATCH] [cmake] `OPUS_INSTALL` option; only default install if root
project
Signed-off-by: crueter <crueter@eden-emu.dev>
---
CMakeLists.txt | 112 ++++++++++++++++++++++++++++---------------------
1 file changed, 64 insertions(+), 48 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fcf034b19..08b5e16f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,6 +4,13 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(OpusPackageVersion)
get_package_version(PACKAGE_VERSION PROJECT_VERSION)
+# root project detection
+if(DEFINED PROJECT_NAME)
+ set(root_project OFF)
+else()
+ set(root_project ON)
+endif()
+
project(Opus LANGUAGES C VERSION ${PROJECT_VERSION})
include(OpusFunctions)
@@ -83,12 +90,16 @@ set(OPUS_DNN_FLOAT_DEBUG_HELP_STR "Run DNN computations as float for debugging p
option(OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR} OFF)
add_feature_info(OPUS_DNN_FLOAT_DEBUG OPUS_DNN_FLOAT_DEBUG ${OPUS_DNN_FLOAT_DEBUG_HELP_STR})
+set(OPUS_INSTALL_HELP_STR "Install Opus targets")
+option(OPUS_INSTALL ${OPUS_INSTALL_HELP_STR} ${root_project})
+add_feature_info(OPUS_INSTALL OPUS_INSTALL ${OPUS_INSTALL_HELP_STR})
+
set(OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR "install pkg-config module.")
-option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ON)
+option(OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
add_feature_info(OPUS_INSTALL_PKG_CONFIG_MODULE OPUS_INSTALL_PKG_CONFIG_MODULE ${OPUS_INSTALL_PKG_CONFIG_MODULE_HELP_STR})
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR "install CMake package config module.")
-option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ON)
+option(OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR} ${OPUS_INSTALL})
add_feature_info(OPUS_INSTALL_CMAKE_CONFIG_MODULE OPUS_INSTALL_CMAKE_CONFIG_MODULE ${OPUS_INSTALL_CMAKE_CONFIG_MODULE_HELP_STR})
set(OPUS_DRED_HELP_STR "enable DRED.")
@@ -613,53 +624,58 @@ if(OPUS_BUILD_FRAMEWORK)
OUTPUT_NAME Opus)
endif()
-install(TARGETS opus
- EXPORT OpusTargets
- ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
- LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
- RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
- FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
-
-if(OPUS_INSTALL_PKG_CONFIG_MODULE)
- set(prefix ${CMAKE_INSTALL_PREFIX})
- set(exec_prefix ${CMAKE_INSTALL_PREFIX})
- set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
- set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
- set(VERSION ${PACKAGE_VERSION})
- if(HAVE_LIBM)
- set(LIBM "-lm")
+if (OPUS_INSTALL)
+ install(TARGETS opus
+ EXPORT OpusTargets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ FRAMEWORK DESTINATION ${CMAKE_INSTALL_PREFIX}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/opus)
+
+ if(OPUS_INSTALL_PKG_CONFIG_MODULE)
+ set(prefix ${CMAKE_INSTALL_PREFIX})
+ set(exec_prefix ${CMAKE_INSTALL_PREFIX})
+ set(libdir ${CMAKE_INSTALL_FULL_LIBDIR})
+ set(includedir ${CMAKE_INSTALL_FULL_INCLUDEDIR})
+ set(VERSION ${PACKAGE_VERSION})
+ if(HAVE_LIBM)
+ set(LIBM "-lm")
+ endif()
+ configure_file(opus.pc.in opus.pc)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+ endif()
+
+ if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
+ set(CPACK_GENERATOR TGZ)
+ include(CPack)
+ set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+ install(EXPORT OpusTargets
+ NAMESPACE Opus::
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
+
+ include(CMakePackageConfigHelpers)
+
+ set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
+ configure_package_config_file(
+ ${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
+ OpusConfig.cmake
+ INSTALL_DESTINATION
+ ${CMAKE_INSTALL_PACKAGEDIR}
+ PATH_VARS
+ INCLUDE_INSTALL_DIR
+ INSTALL_PREFIX
+ ${CMAKE_INSTALL_PREFIX})
+
+ write_basic_package_version_file(OpusConfigVersion.cmake
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion)
+
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
endif()
- configure_file(opus.pc.in opus.pc)
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/opus.pc
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-endif()
-
-if(OPUS_INSTALL_CMAKE_CONFIG_MODULE)
- set(CPACK_GENERATOR TGZ)
- include(CPack)
- set(CMAKE_INSTALL_PACKAGEDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
- install(EXPORT OpusTargets
- NAMESPACE Opus::
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
-
- include(CMakePackageConfigHelpers)
-
- set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR})
- configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/OpusConfig.cmake.in
- OpusConfig.cmake
- INSTALL_DESTINATION
- ${CMAKE_INSTALL_PACKAGEDIR}
- PATH_VARS
- INCLUDE_INSTALL_DIR
- INSTALL_PREFIX
- ${CMAKE_INSTALL_PREFIX})
- write_basic_package_version_file(OpusConfigVersion.cmake
- VERSION ${PROJECT_VERSION}
- COMPATIBILITY SameMajorVersion)
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/OpusConfig.cmake
- ${CMAKE_CURRENT_BINARY_DIR}/OpusConfigVersion.cmake
- DESTINATION ${CMAKE_INSTALL_PACKAGEDIR})
endif()
if(OPUS_BUILD_PROGRAMS)

View File

@ -1,287 +0,0 @@
From 67bf3d1381b1faf59e87001d6156ba4e21cada14 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Mon, 29 Dec 2025 21:22:36 -0500
Subject: [PATCH] [cmake] refactor: shared/static handling
This significantly redoes the way shared and static libraries are
handled. Now, it's controlled by two options: `SPIRV_TOOLS_BUILD_STATIC`
and `SPIRV_TOOLS_BUILD_SHARED`.
The default configuration (no `BUILD_SHARED_LIBS` set, options left at
default) is to build shared ONLY if this is the master project, or
static ONLY if this is a subproject (e.g. FetchContent, CPM.cmake). Also
I should note that static-only (i.e. no shared) is now a supported
target, this is done because projects including it as a submodule e.g.
on Android or Windows may prefer this.
Now the shared/static handling:
- static ON, shared OFF: Only generates `.a` libraries.
- static ON, shared ON: Generates `.a` libraries, but also
`libSPIRV-Tools.so`
- static OFF, shared ON: Only generates `.so` libraries.
Notable TODOs:
- SPIRV-Tools-shared.pc seems redundant--how should we handle which one
to use in the case of distributions that distribute both types (MSYS2
for instance)?
* *Note: pkgconfig sucks at this and usually just leaves it up to the
user, so the optimal solution may indeed be doing absolutely
nothing.* CMake is unaffected :)
- use namespaces in the CMake config files pleaaaaase
This is going to change things a good bit for package maintainers, but
cest la vie. It's for the greater good, I promise.
Signed-off-by: crueter <crueter@eden-emu.dev>
---
CMakeLists.txt | 108 +++++++++++++++++++++++++-----------------
source/CMakeLists.txt | 62 ++++++++++++------------
2 files changed, 94 insertions(+), 76 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d843b4d2f..07201f690f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,15 @@
cmake_minimum_required(VERSION 3.22.1)
+# master project detection--useful for FetchContent/submodule inclusion
+set(master_project OFF)
+set(subproject ON)
+
+if (NOT DEFINED PROJECT_NAME)
+ set(master_project ON)
+ set(subproject OFF)
+endif()
+
project(spirv-tools)
# Avoid a bug in CMake 3.22.1. By default it will set -std=c++11 for
@@ -135,46 +144,49 @@ if (DEFINED SPIRV_TOOLS_EXTRA_DEFINITIONS)
add_definitions(${SPIRV_TOOLS_EXTRA_DEFINITIONS})
endif()
-# Library build setting definitions:
-#
-# * SPIRV_TOOLS_BUILD_STATIC - ON or OFF - Defaults to ON.
-# If enabled the following targets will be created:
-# ${SPIRV_TOOLS}-static - STATIC library.
-# Has full public symbol visibility.
-# ${SPIRV_TOOLS}-shared - SHARED library.
-# Has default-hidden symbol visibility.
-# ${SPIRV_TOOLS} - will alias to one of above, based on BUILD_SHARED_LIBS.
-# If disabled the following targets will be created:
-# ${SPIRV_TOOLS} - either STATIC or SHARED based on SPIRV_TOOLS_LIBRARY_TYPE.
-# Has full public symbol visibility.
-# ${SPIRV_TOOLS}-shared - SHARED library.
-# Has default-hidden symbol visibility.
-#
-# * SPIRV_TOOLS_LIBRARY_TYPE - SHARED or STATIC.
-# Specifies the library type used for building SPIRV-Tools libraries.
-# Defaults to SHARED when BUILD_SHARED_LIBS=1, otherwise STATIC.
-#
-# * SPIRV_TOOLS_FULL_VISIBILITY - "${SPIRV_TOOLS}-static" or "${SPIRV_TOOLS}"
-# Evaluates to the SPIRV_TOOLS target library name that has no hidden symbols.
-# This is used by internal targets for accessing symbols that are non-public.
-# Note this target provides no API stability guarantees.
-#
-# Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909.
-option(ENABLE_EXCEPTIONS_ON_MSVC "Build SPIRV-TOOLS with c++ exceptions enabled in MSVC" ON)
-option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON)
-if(SPIRV_TOOLS_BUILD_STATIC)
- set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static)
+# If BUILD_SHARED_LIBS is undefined, set it based on whether we are
+# the master project or a subproject
+if (NOT DEFINED BUILD_SHARED_LIBS)
+ set(BUILD_SHARED_LIBS ${master_project})
+endif()
+
+if (BUILD_SHARED_LIBS)
+ set(static_default OFF)
+else()
+ set(static_default ON)
+endif()
+
+option(SPIRV_TOOLS_BUILD_SHARED "Build ${SPIRV_TOOLS} as a shared library"
+ ${BUILD_SHARED_LIBS})
+option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS} as a static library"
+ ${static_default})
+
+# Avoid conflict between the dll import library and
+# the static library (thanks microsoft)
+if(CMAKE_STATIC_LIBRARY_PREFIX STREQUAL "" AND
+ CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL ".lib")
+ set(SPIRV_TOOLS_STATIC_LIBNAME "${SPIRV_TOOLS}-static")
+else()
+ set(SPIRV_TOOLS_STATIC_LIBNAME "${SPIRV_TOOLS}")
+endif()
+
+if (SPIRV_TOOLS_BUILD_STATIC)
+ # If building a static library at all, always build other libraries as static,
+ # and link to the static SPIRV-Tools library.
set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC")
-else(SPIRV_TOOLS_BUILD_STATIC)
- set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS})
- if (NOT DEFINED SPIRV_TOOLS_LIBRARY_TYPE)
- if(BUILD_SHARED_LIBS)
- set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED")
- else()
- set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC")
- endif()
- endif()
-endif(SPIRV_TOOLS_BUILD_STATIC)
+ set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static)
+elseif (SPIRV_TOOLS_BUILD_SHARED)
+ # If only building a shared library, link other libraries to the
+ # shared library. Also, other libraries should be shared
+ set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED")
+ set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-shared)
+else()
+ message(FATAL_ERROR "You must set one of "
+ "SPIRV_TOOLS_BUILD_STATIC or SPIRV_TOOLS_BUILD_SHARED!")
+endif()
+
+option(ENABLE_EXCEPTIONS_ON_MSVC
+ "Build SPIRV-TOOLS with C++ exceptions enabled in MSVC" ON)
function(spvtools_default_compile_options TARGET)
target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS})
@@ -372,7 +384,7 @@ if (NOT "${SPIRV_SKIP_TESTS}")
endif()
set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link")
-set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared")
+set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools")
# Build pkg-config file
# Use a first-class target so it's regenerated when relevant files are updated.
@@ -388,7 +400,12 @@ add_custom_command(
-DSPIRV_LIBRARIES=${SPIRV_LIBRARIES}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake")
-add_custom_command(
+
+set(pc_files ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc)
+
+# TODO(crueter): remove?
+if (SPIRV_TOOLS_BUILD_SHARED)
+ add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc
COMMAND ${CMAKE_COMMAND}
-DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES
@@ -400,9 +417,12 @@ add_custom_command(
-DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake
DEPENDS "CHANGES" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake")
-add_custom_target(spirv-tools-pkg-config
- ALL
- DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc)
+ set(pc_files ${pc_files} ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc)
+endif()
+
+add_custom_target(spirv-tools-pkg-config
+ ALL
+ DEPENDS ${pc_files})
# Install pkg-config file
if (ENABLE_SPIRV_TOOLS_INSTALL)
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index bfa1e661bc..fd3712c70c 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -337,49 +337,44 @@ function(spirv_tools_default_target_options target)
)
set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries")
spvtools_check_symbol_exports(${target})
- add_dependencies(${target} spirv-tools-build-version core_tables extinst_tables)
+ add_dependencies(${target}
+ spirv-tools-build-version core_tables extinst_tables)
endfunction()
-# Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and
-# unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility.
-add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
-if (SPIRV_TOOLS_USE_MIMALLOC)
- target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+if (SPIRV_TOOLS_BUILD_SHARED)
+ add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES})
+ if (SPIRV_TOOLS_USE_MIMALLOC)
+ target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
+ endif()
+
+ set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES
+ OUTPUT_NAME "${SPIRV_TOOLS}")
+ spirv_tools_default_target_options(${SPIRV_TOOLS}-shared)
+
+ target_compile_definitions(${SPIRV_TOOLS}-shared
+ PRIVATE SPIRV_TOOLS_IMPLEMENTATION
+ PUBLIC SPIRV_TOOLS_SHAREDLIB)
+
+ list(APPEND SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-shared)
endif()
-spirv_tools_default_target_options(${SPIRV_TOOLS}-shared)
-set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden)
-target_compile_definitions(${SPIRV_TOOLS}-shared
- PRIVATE SPIRV_TOOLS_IMPLEMENTATION
- PUBLIC SPIRV_TOOLS_SHAREDLIB
-)
if(SPIRV_TOOLS_BUILD_STATIC)
add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES})
if (SPIRV_TOOLS_USE_MIMALLOC AND SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD)
target_link_libraries(${SPIRV_TOOLS}-shared PRIVATE mimalloc-static)
endif()
+
spirv_tools_default_target_options(${SPIRV_TOOLS}-static)
- # The static target does not have the '-static' suffix.
- set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}")
-
- # Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static"
- # or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS.
- if(BUILD_SHARED_LIBS)
- add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared)
- else()
- add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static)
- endif()
+ set_target_properties(${SPIRV_TOOLS}-static PROPERTIES
+ OUTPUT_NAME "${SPIRV_TOOLS_STATIC_LIBNAME}")
- set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared)
-else()
- add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES})
- if (SPIRV_TOOLS_USE_MIMALLOC)
- target_link_libraries(${SPIRV_TOOLS} PRIVATE mimalloc-static)
- endif()
- spirv_tools_default_target_options(${SPIRV_TOOLS})
- set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared)
+ list(APPEND SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static)
endif()
+# Create the "SPIRV-Tools" target as an alias to either "SPIRV-Tools-static"
+# or "SPIRV-Tools-shared" depending on the value of SPIRV_TOOLS_BUILD_SHARED.
+add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS_FULL_VISIBILITY})
+
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
find_library(LIBRT rt)
if(LIBRT)
@@ -390,14 +385,17 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
endif()
if(ENABLE_SPIRV_TOOLS_INSTALL)
- if (SPIRV_TOOLS_USE_MIMALLOC AND (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
+ if (SPIRV_TOOLS_USE_MIMALLOC AND
+ (NOT SPIRV_TOOLS_BUILD_STATIC OR SPIRV_TOOLS_USE_MIMALLOC_IN_STATIC_BUILD))
list(APPEND SPIRV_TOOLS_TARGETS mimalloc-static)
endif()
install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets)
export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake)
spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR)
- install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR})
+ install(EXPORT ${SPIRV_TOOLS}Targets
+ FILE ${SPIRV_TOOLS}Target.cmake
+ DESTINATION ${PACKAGE_DIR})
# Special config file for root library compared to other libs.
file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake

View File

@ -1,26 +0,0 @@
From b3622608433c183ba868a1dc8dd9cf285eb3b916 Mon Sep 17 00:00:00 2001
From: Dario Petrillo <dario.pk1@gmail.com>
Date: Thu, 27 Nov 2025 23:12:38 +0100
Subject: [PATCH] avoid extra memset when clearing an empty table
---
include/ankerl/unordered_dense.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h
index 0835342..4938212 100644
--- a/include/ankerl/unordered_dense.h
+++ b/include/ankerl/unordered_dense.h
@@ -1490,8 +1490,10 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
// modifiers //////////////////////////////////////////////////////////////
void clear() {
- m_values.clear();
- clear_buckets();
+ if (!empty()) {
+ m_values.clear();
+ clear_buckets();
+ }
}
auto insert(value_type const& value) -> std::pair<iterator, bool> {

View File

@ -9,7 +9,6 @@ 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)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include(DetectPlatform)
include(DetectArchitecture)
@ -32,8 +31,8 @@ if (PLATFORM_OPENBSD)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
elseif (PLATFORM_NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
endif()
@ -42,37 +41,6 @@ if (PLATFORM_NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "PLATFORM_LINUX" OFF)
if (YUZU_STATIC_ROOM)
set(YUZU_ROOM ON)
set(YUZU_ROOM_STANDALONE ON)
# disable e v e r y t h i n g
set(ENABLE_QT OFF)
set(ENABLE_SDL2 OFF)
set(YUZU_CMD OFF)
set(ENABLE_CUBEB OFF)
set(ENABLE_UPDATE_CHECKER OFF)
set(USE_DISCORD_PRESENCE OFF)
set(BUILD_TESTING OFF)
set(ENABLE_OPENSSL OFF)
set(ENABLE_WEB_SERVICE OFF)
set(ENABLE_LIBUSB OFF)
# allow static libs for boost and mbedtls though
set(Boost_USE_STATIC_LIBS ON)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(MBEDTLS_LIB_SUFFIX "_static")
set(YUZU_USE_CPM ON)
set(zstd_FORCE_BUNDLED ON)
set(fmt_FORCE_BUNDLED ON)
endif()
# common network mbedtls
# common: xbyak? booost fmt lz4 zstd
# network: enet boost
# qt stuff
option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
@ -178,9 +146,7 @@ endif()
# Disable Warnings as Errors for MSVC
if (MSVC AND NOT CXX_CLANG)
# This was dripping into spirv, being overriden, and causing cl flag override warning
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /W3 /WX-")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
endif()
# Set bundled sdl2/qt as dependent options.
@ -229,8 +195,6 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.g. Snapdragon 865) at the cost of performance" OFF)
option(NIGHTLY_BUILD "Use Nightly qualifiers in the update checker and build metadata" OFF)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
@ -246,12 +210,11 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_ENABLE_OPENSSL ON)
if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN OR PLATFORM_OPENBSD)
if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN)
# - Windows defaults to the Schannel backend.
# - macOS defaults to the SecureTransport backend.
# - Android currently has no SSL backend as the NDK doesn't include any SSL
# library; a proper 'native' backend would have to go through Java.
# - Solaris and OpenBSD have too old backends
# But you can force builds for those platforms to use OpenSSL if you have
# your own copy of it.
set(DEFAULT_ENABLE_OPENSSL OFF)
@ -263,9 +226,11 @@ endif()
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR PLATFORM_SUN OR PLATFORM_OPENBSD)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
if (ENABLE_OPENSSL)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR PLATFORM_SUN)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
endif()
endif()
cmake_dependent_option(YUZU_USE_BUNDLED_OPENSSL "Download bundled OpenSSL build" ${DEFAULT_YUZU_USE_BUNDLED_OPENSSL} "ENABLE_OPENSSL" OFF)
@ -455,6 +420,16 @@ if (YUZU_USE_CPM)
add_library(lz4::lz4 ALIAS lz4_static)
endif()
# nlohmann
AddJsonPackage(nlohmann)
# zlib
AddJsonPackage(zlib)
if (ZLIB_ADDED)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
endif()
# zstd
AddJsonPackage(zstd)
@ -463,31 +438,19 @@ if (YUZU_USE_CPM)
add_library(zstd::libzstd ALIAS libzstd_static)
endif()
if (NOT YUZU_STATIC_ROOM)
# nlohmann
AddJsonPackage(nlohmann)
# Opus
AddJsonPackage(opus)
# zlib
AddJsonPackage(zlib)
if (ZLIB_ADDED)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
if (Opus_ADDED)
if (MSVC AND CXX_CLANG)
target_compile_options(opus PRIVATE
-Wno-implicit-function-declaration
)
endif()
endif()
# Opus
AddJsonPackage(opus)
if (Opus_ADDED)
if (MSVC AND CXX_CLANG)
target_compile_options(opus PRIVATE
-Wno-implicit-function-declaration
)
endif()
endif()
if (NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS opus)
endif()
if (NOT TARGET Opus::opus)
add_library(Opus::opus ALIAS opus)
endif()
else()
# TODO: we can probably just use CPM for this... right?
@ -574,52 +537,47 @@ message(STATUS "Platform Libraries: ${PLATFORM_LIBRARIES}")
add_subdirectory(externals)
# pass targets from externals
find_package(libusb)
find_package(VulkanMemoryAllocator)
find_package(enet)
find_package(MbedTLS)
find_package(unordered_dense REQUIRED)
find_package(VulkanUtilityLibraries)
find_package(SimpleIni)
find_package(SPIRV-Tools)
find_package(sirit)
find_package(gamemode)
find_package(mcl)
if (ARCHITECTURE_riscv64)
find_package(biscuit)
endif()
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
find_package(xbyak)
endif()
if (NOT YUZU_STATIC_ROOM)
find_package(libusb)
find_package(VulkanMemoryAllocator)
find_package(VulkanUtilityLibraries)
find_package(SimpleIni)
find_package(SPIRV-Tools)
find_package(sirit)
find_package(gamemode)
find_package(mcl)
find_package(frozen)
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
find_package(cpp-jwt)
endif()
if (ARCHITECTURE_riscv64)
find_package(biscuit)
endif()
if (ARCHITECTURE_arm64 OR DYNARMIC_TESTS)
find_package(oaknut)
endif()
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
find_package(cpp-jwt)
endif()
if (ENABLE_SDL2)
find_package(SDL2)
endif()
if (ARCHITECTURE_arm64 OR DYNARMIC_TESTS)
find_package(oaknut)
endif()
if (USE_DISCORD_PRESENCE)
find_package(DiscordRPC)
endif()
if (ENABLE_SDL2)
find_package(SDL2)
endif()
if (ENABLE_CUBEB)
find_package(cubeb)
endif()
if (USE_DISCORD_PRESENCE)
find_package(DiscordRPC)
endif()
if (ENABLE_CUBEB)
find_package(cubeb)
endif()
if (YUZU_TESTS OR DYNARMIC_TESTS)
find_package(Catch2)
endif()
if (YUZU_TESTS OR DYNARMIC_TESTS)
find_package(Catch2)
endif()
# Qt stuff
@ -641,7 +599,7 @@ if (ENABLE_QT)
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
endif()
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Concurrent)
if (YUZU_USE_QT_MULTIMEDIA)
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
@ -680,7 +638,7 @@ if (ENABLE_QT)
## Components ##
# Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
set(YUZU_QT_COMPONENTS Core Widgets Concurrent)
if (PLATFORM_LINUX)
list(APPEND YUZU_QT_COMPONENTS DBus)
endif()
@ -698,14 +656,9 @@ if (ENABLE_QT)
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
## Qt Externals ##
# QuaZip
AddJsonPackage(quazip)
endif()
if (NOT YUZU_STATIC_ROOM AND NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
if (NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
# Use system installed FFmpeg
find_package(FFmpeg REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})

View File

@ -41,11 +41,6 @@ function(cpm_utils_message level name message)
message(${level} "[CPMUtil] ${name}: ${message}")
endfunction()
# propagate a variable to parent scope
macro(Propagate var)
set(${var} ${${var}} PARENT_SCOPE)
endmacro()
function(array_to_list array length out)
math(EXPR range "${length} - 1")
@ -77,163 +72,6 @@ function(get_json_element object out member default)
set("${out}" "${outvar}" PARENT_SCOPE)
endfunction()
# Determine whether or not a package has a viable system candidate.
function(SystemPackageViable JSON_NAME)
string(JSON object GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
parse_object(${object})
string(REPLACE " " ";" find_args "${find_args}")
if (${package}_FORCE_BUNDLED)
set(${package}_FOUND OFF)
else()
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
endif()
set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
endfunction()
# Add several packages such that if one is bundled,
# all the rest must also be bundled.
function(AddDependentPackages)
set(_some_system OFF)
set(_some_bundled OFF)
foreach(pkg ${ARGN})
SystemPackageViable(${pkg})
if (${pkg}_VIABLE)
set(_some_system ON)
list(APPEND _system_pkgs ${${pkg}_PACKAGE})
else()
set(_some_bundled ON)
list(APPEND _bundled_pkgs ${${pkg}_PACKAGE})
endif()
endforeach()
if (_some_system AND _some_bundled)
foreach(pkg ${ARGN})
list(APPEND package_names ${${pkg}_PACKAGE})
endforeach()
string(REPLACE ";" ", " package_names "${package_names}")
string(REPLACE ";" ", " bundled_names "${_bundled_pkgs}")
foreach(sys ${_system_pkgs})
list(APPEND system_names ${sys}_FORCE_BUNDLED)
endforeach()
string(REPLACE ";" ", " system_names "${system_names}")
message(FATAL_ERROR "Partial dependency installation detected "
"for the following packages:\n${package_names}\n"
"You can solve this in one of two ways:\n"
"1. Install the following packages to your system if available:"
"\n\t${bundled_names}\n"
"2. Set the following variables to ON:"
"\n\t${system_names}\n"
"This may also be caused by a version mismatch, "
"such as one package being newer than the other.")
endif()
foreach(pkg ${ARGN})
AddJsonPackage(${pkg})
endforeach()
endfunction()
# json util
macro(parse_object object)
get_json_element("${object}" package package ${JSON_NAME})
get_json_element("${object}" repo repo "")
get_json_element("${object}" ci ci OFF)
get_json_element("${object}" version version "")
if(ci)
get_json_element("${object}" name name "${JSON_NAME}")
get_json_element("${object}" extension extension "tar.zst")
get_json_element("${object}" min_version min_version "")
get_json_element("${object}" raw_disabled disabled_platforms "")
if(raw_disabled)
array_to_list("${raw_disabled}"
${raw_disabled_LENGTH} disabled_platforms)
else()
set(disabled_platforms "")
endif()
else()
get_json_element("${object}" hash hash "")
get_json_element("${object}" hash_suffix hash_suffix "")
get_json_element("${object}" sha sha "")
get_json_element("${object}" url url "")
get_json_element("${object}" key key "")
get_json_element("${object}" tag tag "")
get_json_element("${object}" artifact artifact "")
get_json_element("${object}" git_version git_version "")
get_json_element("${object}" git_host git_host "")
get_json_element("${object}" source_subdir source_subdir "")
get_json_element("${object}" bundled bundled "unset")
get_json_element("${object}" find_args find_args "")
get_json_element("${object}" raw_patches patches "")
# okay here comes the fun part: REPLACEMENTS!
# first: tag gets %VERSION% replaced if applicable,
# with either git_version (preferred) or version
# second: artifact gets %VERSION% and %TAG% replaced
# accordingly (same rules for VERSION)
if(git_version)
set(version_replace ${git_version})
else()
set(version_replace ${version})
endif()
# TODO(crueter): fmt module for cmake
if(tag)
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
endif()
if(artifact)
string(REPLACE "%VERSION%" "${version_replace}"
artifact ${artifact})
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
endif()
# format patchdir
if(raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
foreach(IDX RANGE ${range})
string(JSON _patch GET "${raw_patches}" "${IDX}")
set(full_patch
"${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
if(NOT EXISTS ${full_patch})
cpm_utils_message(FATAL_ERROR ${JSON_NAME}
"specifies patch ${full_patch} which does not exist")
endif()
list(APPEND patches "${full_patch}")
endforeach()
endif()
# end format patchdir
# options
get_json_element("${object}" raw_options options "")
if(raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
set(options ${options} ${JSON_OPTIONS})
# end options
# system/bundled
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE})
endif()
endif()
endmacro()
# The preferred usage
function(AddJsonPackage)
set(oneValueArgs
@ -242,8 +80,7 @@ function(AddJsonPackage)
# these are overrides that can be generated at runtime,
# so can be defined separately from the json
DOWNLOAD_ONLY
BUNDLED_PACKAGE
FORCE_BUNDLED_PACKAGE)
BUNDLED_PACKAGE)
set(multiValueArgs OPTIONS)
@ -274,9 +111,24 @@ function(AddJsonPackage)
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
endif()
parse_object(${object})
get_json_element("${object}" package package ${JSON_NAME})
get_json_element("${object}" repo repo "")
get_json_element("${object}" ci ci OFF)
get_json_element("${object}" version version "")
if(ci)
get_json_element("${object}" name name "${JSON_NAME}")
get_json_element("${object}" extension extension "tar.zst")
get_json_element("${object}" min_version min_version "")
get_json_element("${object}" raw_disabled disabled_platforms "")
if(raw_disabled)
array_to_list("${raw_disabled}"
${raw_disabled_LENGTH} disabled_platforms)
else()
set(disabled_platforms "")
endif()
AddCIPackage(
VERSION ${version}
NAME ${name}
@ -286,38 +138,116 @@ function(AddJsonPackage)
MIN_VERSION ${min_version}
DISABLED_PLATFORMS ${disabled_platforms})
else()
if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE)
set(JSON_FORCE_BUNDLED_PACKAGE OFF)
endif()
# pass stuff to parent scope
set(${package}_ADDED "${${package}_ADDED}"
PARENT_SCOPE)
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}"
PARENT_SCOPE)
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
PARENT_SCOPE)
AddPackage(
NAME "${package}"
VERSION "${version}"
URL "${url}"
HASH "${hash}"
HASH_SUFFIX "${hash_suffix}"
SHA "${sha}"
REPO "${repo}"
KEY "${key}"
PATCHES "${patches}"
OPTIONS "${options}"
FIND_PACKAGE_ARGUMENTS "${find_args}"
BUNDLED_PACKAGE "${bundled}"
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION ${git_version}
GIT_HOST ${git_host}
ARTIFACT ${artifact}
TAG ${tag})
return()
endif()
get_json_element("${object}" hash hash "")
get_json_element("${object}" hash_suffix hash_suffix "")
get_json_element("${object}" sha sha "")
get_json_element("${object}" url url "")
get_json_element("${object}" key key "")
get_json_element("${object}" tag tag "")
get_json_element("${object}" artifact artifact "")
get_json_element("${object}" git_version git_version "")
get_json_element("${object}" git_host git_host "")
get_json_element("${object}" source_subdir source_subdir "")
get_json_element("${object}" bundled bundled "unset")
get_json_element("${object}" find_args find_args "")
get_json_element("${object}" raw_patches patches "")
# okay here comes the fun part: REPLACEMENTS!
# first: tag gets %VERSION% replaced if applicable,
# with either git_version (preferred) or version
# second: artifact gets %VERSION% and %TAG% replaced
# accordingly (same rules for VERSION)
if(git_version)
set(version_replace ${git_version})
else()
set(version_replace ${version})
endif()
# TODO(crueter): fmt module for cmake
if(tag)
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
endif()
if(artifact)
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact})
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
endif()
# format patchdir
if(raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
foreach(IDX RANGE ${range})
string(JSON _patch GET "${raw_patches}" "${IDX}")
set(full_patch
"${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
if(NOT EXISTS ${full_patch})
cpm_utils_message(FATAL_ERROR ${JSON_NAME}
"specifies patch ${full_patch} which does not exist")
endif()
list(APPEND patches "${full_patch}")
endforeach()
endif()
# end format patchdir
# options
get_json_element("${object}" raw_options options "")
if(raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
set(options ${options} ${JSON_OPTIONS})
# end options
# system/bundled
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE})
endif()
AddPackage(
NAME "${package}"
VERSION "${version}"
URL "${url}"
HASH "${hash}"
HASH_SUFFIX "${hash_suffix}"
SHA "${sha}"
REPO "${repo}"
KEY "${key}"
PATCHES "${patches}"
OPTIONS "${options}"
FIND_PACKAGE_ARGUMENTS "${find_args}"
BUNDLED_PACKAGE "${bundled}"
SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION ${git_version}
GIT_HOST ${git_host}
ARTIFACT ${artifact}
TAG ${tag})
# pass stuff to parent scope
Propagate(${package}_ADDED)
Propagate(${package}_SOURCE_DIR)
Propagate(${package}_BINARY_DIR)
set(${package}_ADDED "${${package}_ADDED}"
PARENT_SCOPE)
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}"
PARENT_SCOPE)
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
PARENT_SCOPE)
endfunction()
function(AddPackage)
@ -413,7 +343,7 @@ function(AddPackage)
if(DEFINED PKG_ARGS_ARTIFACT)
set(pkg_url
"${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}")
${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT})
else()
set(pkg_url
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
@ -695,8 +625,7 @@ function(AddCIPackage)
endif()
if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
set(ARTIFACT
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
set(ARTIFACT "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
AddPackage(
NAME ${ARTIFACT_PACKAGE}

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
@ -15,43 +15,27 @@ endfunction()
get_timestamp(BUILD_DATE)
if (DEFINED GIT_RELEASE)
set(BUILD_VERSION "${GIT_TAG}")
set(GIT_REFSPEC "${GIT_RELEASE}")
set(IS_DEV_BUILD false)
set(BUILD_VERSION "${GIT_TAG}")
set(GIT_REFSPEC "${GIT_RELEASE}")
set(IS_DEV_BUILD false)
else()
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
set(IS_DEV_BUILD true)
endif()
if (NIGHTLY_BUILD)
set(IS_NIGHTLY_BUILD true)
else()
set(IS_NIGHTLY_BUILD false)
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
set(IS_DEV_BUILD true)
endif()
set(GIT_DESC ${BUILD_VERSION})
# Generate cpp with Git revision from template
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
if (NIGHTLY_BUILD)
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
set(BUILD_AUTO_UPDATE_API "api.github.com")
set(BUILD_AUTO_UPDATE_API_PATH "/repos/")
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
set(REPO_NAME "Eden Nightly")
else()
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
set(BUILD_AUTO_UPDATE_REPO "eden-emu/eden")
set(REPO_NAME "Eden")
endif()
set(REPO_NAME "Eden")
set(BUILD_ID ${GIT_REFSPEC})
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
set(CXX_COMPILER "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
set(BUILD_AUTO_UPDATE_API "http://api.github.com")
set(BUILD_AUTO_UPDATE_REPO "eden-emulator/Releases")
configure_file(scm_rev.cpp.in scm_rev.cpp @ONLY)

View File

@ -12,12 +12,14 @@
"repo": "boostorg/boost",
"tag": "boost-%VERSION%",
"artifact": "%TAG%-cmake.tar.xz",
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
"git_version": "1.90.0",
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9",
"git_version": "1.89.0",
"version": "1.57",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"patches": [
"0001-clang-cl.patch"
"0001-clang-cl.patch",
"0002-use-marmasm.patch",
"0003-armasm-options.patch"
]
},
"fmt": {
@ -46,9 +48,9 @@
"package": "ZLIB",
"repo": "madler/zlib",
"tag": "v%VERSION%",
"hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
"hash": "8c9642495bafd6fad4ab9fb67f09b268c69ff9af0f4f20cf15dfc18852ff1f312bd8ca41de761b3f8d8e90e77d79f2ccacd3d4c5b19e475ecf09d021fdfe9088",
"version": "1.2",
"git_version": "1.3.1.2",
"git_version": "1.3.1",
"options": [
"ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF"
@ -67,17 +69,13 @@
},
"opus": {
"package": "Opus",
"repo": "xiph/opus",
"sha": "a3f0ec02b3",
"hash": "9506147b0de35befda8633ff272981cc2575c860874791bd455b752f797fd7dbd1079f0ba42ccdd7bb1fe6773fa5e84b3d75667c2883dd1fb2d0e4a5fa4f8387",
"repo": "crueter/opus",
"sha": "ab19c44fad",
"hash": "d632e8f83c5d3245db404bcb637113f9860bf16331498ba2c8e77979d1febee6b52d8b1da448e7d54eeac373e912cd55e3e300fc6c242244923323280dc43fbe",
"version": "1.3",
"find_args": "MODULE",
"options": [
"OPUS_PRESUME_NEON ON"
],
"patches": [
"0001-disable-clang-runtime-neon.patch",
"0002-no-install.patch"
]
},
"boost_headers": {
@ -101,18 +99,5 @@
"git_version": "1.4.335.0",
"artifact": "android-binaries-%VERSION%.zip",
"hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
},
"quazip": {
"package": "QuaZip-Qt6",
"repo": "stachenov/quazip",
"sha": "2e95c9001b",
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
"version": "1.3",
"git_version": "1.5",
"options": [
"QUAZIP_QT_MAJOR_VERSION 6",
"QUAZIP_INSTALL OFF",
"QUAZIP_ENABLE_QTEXTCODEC OFF"
]
}
}

View File

@ -1,211 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="EdenLogoLoveWhiteV3.svg"
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
xml:space="preserve"
inkscape:export-filename="dev.eden_emu.eden.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata1">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:creator>
<cc:Agent>
<dc:title>Madeline_Dev</dc:title>
<dc:identifier>mailto:madelvidel@gmail.com</dc:identifier>
</cc:Agent>
</dc:creator>
<dc:date>2025</dc:date>
<dc:license
rdf:resource="https://www.gnu.org/licenses/gpl-3.0.html" />
<dc:rights>2025 Eden Emulator Project</dc:rights>
<dc:source>https://git.eden-emu.dev</dc:source>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs7"><linearGradient
id="swatch14"
inkscape:swatch="solid"><stop
style="stop-color:#66003b;stop-opacity:1;"
offset="0"
id="stop14" /></linearGradient><linearGradient
id="linearGradient1"
inkscape:collect="always"><stop
style="stop-color:#ffffff;stop-opacity:0.50980395;"
offset="0"
id="stop1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2" /></linearGradient><linearGradient
id="linearGradient11"
inkscape:collect="always"><stop
style="stop-color:#9523a7;stop-opacity:0.50980395;"
offset="0"
id="stop11" /><stop
style="stop-color:#ffabad;stop-opacity:1;"
offset="0.99898213"
id="stop20" /><stop
style="stop-color:#ffffff;stop-opacity:0;"
offset="0.99898213"
id="stop12" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient11"
id="linearGradient12"
x1="109.74531"
y1="106.54533"
x2="431.05463"
y2="427.85461"
gradientUnits="userSpaceOnUse"
spreadMethod="reflect"
gradientTransform="matrix(1.0945321,0,0,1.0945321,-39.661525,-35.159057)" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient2"
x1="125.40197"
y1="271.834"
x2="431.02424"
y2="271.834"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0964243,0,0,1.0253208,-40.187969,-20.060025)" /><filter
inkscape:label="Light Contour"
inkscape:menu="Image Paint and Draw"
inkscape:menu-tooltip="Uses vertical specular light to draw lines"
style="color-interpolation-filters:sRGB"
id="filter11"
x="-0.01907517"
y="-0.054959154"
width="1.0379885"
height="1.1092314"><feGaussianBlur
in="SourceGraphic"
stdDeviation="0.38250006"
result="result3"
id="feGaussianBlur9" /><feComponentTransfer
result="result1"
in="result3"
id="feComponentTransfer9"><feFuncR
type="discrete"
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
id="feFuncR9" /><feFuncG
type="discrete"
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
id="feFuncG9" /><feFuncB
type="discrete"
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
id="feFuncB9" /></feComponentTransfer><feGaussianBlur
result="result5"
stdDeviation="0.01"
id="feGaussianBlur10" /><feBlend
in2="result5"
result="result6"
mode="lighten"
in="result5"
id="feBlend10" /><feColorMatrix
in="result6"
type="luminanceToAlpha"
result="result2"
id="feColorMatrix10" /><feSpecularLighting
surfaceScale="5"
result="result9"
specularExponent="20"
in="result2"
specularConstant="1"
id="feSpecularLighting10"><feDistantLight
azimuth="180"
elevation="90"
id="feDistantLight10" /></feSpecularLighting><feComposite
in2="result6"
operator="arithmetic"
in="result9"
k1="0.4"
k3="0.7"
result="result3"
id="feComposite10"
k2="0"
k4="0" /><feBlend
in2="result1"
in="result3"
mode="normal"
result="result8"
id="feBlend11" /><feComposite
in2="SourceGraphic"
in="result8"
operator="in"
result="result7"
id="feComposite11" /></filter></defs><sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.35"
inkscape:cx="254.81481"
inkscape:cy="260.37037"
inkscape:window-width="1920"
inkscape:window-height="1001"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="svg7"
showguides="false" /><circle
style="fill:url(#linearGradient12);fill-opacity:1;stroke:#e4e4e4;stroke-width:14.0448;stroke-opacity:1;paint-order:stroke fill markers"
id="path8"
cx="256.2999"
cy="257.2999"
r="248.67769" /><path
id="path15"
style="fill:url(#linearGradient2);fill-opacity:1;stroke:#ffffff;stroke-width:16.9642;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.52156866;paint-order:stroke fill markers"
d="m 306.72111,24.233031 c 0,0 -11.98397,40.08696 -18.0546,60.431848 -12.96613,9.503601 -21.49377,18.397701 -21.49377,18.397701 0,0 -23.41313,-31.029398 -45.74145,-43.934598 -22.32833,-12.905201 -52.42065,-11.242483 -52.42065,-11.242483 0,0 -12.50052,4e-4 -27.63117,5.537132 -15.13066,5.536732 -21.27107,9.227888 -21.27107,9.227888 0,0 15.35165,-0.410529 37.93799,6.766716 22.58635,7.177243 32.45374,11.484796 32.45374,11.484796 l 31.02752,-6.562453 -8.44161,11.074275 c 0,0 14.80259,8.920284 22.80648,16.917787 8.0039,7.9975 11.73088,12.50812 11.73088,12.50812 0,0 -13.92373,-5.43341 -56.68427,-1.74226 -42.76055,3.69116 -84.86368,56.39265 -84.86368,56.39265 0,0 41.22428,-15.9958 65.5649,-21.32747 24.34062,-5.33166 47.58524,-7.9983 47.58524,-7.9983 0,0 -18.41865,7.3827 -38.15428,38.3474 -19.73564,30.96468 -14.0351,80.18128 -14.0351,80.18128 0,0 31.35774,-59.05848 61.39977,-78.94969 30.04203,-19.89124 36.182,-20.50642 36.182,-20.50642 0,0 -24.1209,48.39514 -33.55015,126.11445 -9.42924,77.71931 30.26089,207.72959 30.26089,207.72959 l 47.80367,-6.97298 c 0,0 -45.61041,-42.24281 -49.55753,-151.33695 -3.94713,-109.09413 16.66479,-174.30453 16.66479,-174.30453 0,0 17.10508,1.23039 42.54213,32.81027 25.43704,31.57987 40.34713,57.41795 40.34713,57.41795 0,0 9.65024,-51.26683 -11.4011,-74.64415 -21.05135,-23.3773 -53.50637,-33.62931 -53.50637,-33.62931 0,0 13.70573,-6.66419 42.10313,-4.71609 28.39738,1.94813 87.30278,27.12897 87.30278,27.12896 0,0 -15.8158,-25.5214 -52.87463,-43.21167 -37.05881,-17.69028 -81.35597,0.29238 -81.35597,0.29238 0,0 4.13589,-15.37277 29.10021,-32.628037 24.96432,-17.25527 56.907,-34.36427 56.907,-34.36427 0,0 -26.3595,1.160335 -55.20025,12.035504 -7.176,2.7059 -14.064,6.299542 -20.36309,10.173104 l 17.40574,-51.051762 z" /><path
id="path4-2-6"
style="fill:#ffffff;fill-opacity:0.509804;stroke:#ffffff;stroke-width:8.49614;stroke-dasharray:none;stroke-opacity:0.521569"
d="m 105.64857,363.95957 a 27.851731,25.309928 0 0 0 -7.115581,35.20471 27.851731,25.309928 0 0 0 5.656921,5.48617 c 7.16052,5.83605 21.58543,14.65841 45.88391,17.847 37.57172,4.93039 41.7105,10.38005 41.71122,10.381 -4.5e-4,-6e-4 -4.13529,-5.45309 4.14459,-39.11953 5.35491,-21.77281 1.40624,-37.16078 -2.05217,-45.31698 a 27.851731,25.309928 0 0 0 -3.63281,-6.75471 27.851731,25.309928 0 0 0 -38.74027,-6.4662 27.851731,25.309928 0 0 0 -7.11556,35.20472 27.851731,25.309928 0 0 0 -38.74025,-6.46618 z" /><path
id="path4-2-6-1"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:6.89965;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 351.29524,378.32915 a 20.862787,22.283365 87.929759 0 0 -28.94663,11.7882 20.862787,22.283365 87.929759 0 0 -1.50914,6.04125 c -1.09835,7.13663 -1.16134,20.16231 7.28339,36.72931 13.0577,25.61689 10.90177,30.69793 10.90138,30.69882 2.4e-4,-5.6e-4 2.16,-5.08045 30.49632,-15.43688 18.32576,-6.69765 27.84648,-16.23659 32.287,-22.14109 a 20.862787,22.283365 87.929759 0 0 3.33646,-5.36755 20.862787,22.283365 87.929759 0 0 -12.45108,-27.05014 20.862787,22.283365 87.929759 0 0 -28.94664,11.78822 20.862787,22.283365 87.929759 0 0 -12.45106,-27.05014 z" /><path
id="path4-2-6-1-5"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:6.89965;stroke-dasharray:none;stroke-opacity:0.52156866"
d="m 83.024352,249.4105 a 22.283365,20.862787 0 0 0 -29.353581,10.73482 22.283365,20.862787 0 0 0 -1.72639,5.98278 c -1.35544,7.0923 -1.88894,20.10721 5.951801,36.96846 12.12378,26.07187 9.7857,31.07171 9.78528,31.07259 2.6e-4,-5.5e-4 2.34212,-4.99911 31.03406,-14.32513 18.555748,-6.03127 28.414858,-15.22005 33.065768,-20.96029 a 22.283365,20.862787 0 0 0 3.52818,-5.24352 22.283365,20.862787 0 0 0 -11.46577,-27.48227 22.283365,20.862787 0 0 0 -29.353588,10.73483 22.283365,20.862787 0 0 0 -11.46576,-27.48227 z" /><path
id="path4-2-6-1-9"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:5.01078;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 320.97602,291.36362 a 16.625347,14.748181 0 0 0 -21.90036,7.58859 16.625347,14.748181 0 0 0 -1.28804,4.2293 c -1.01128,5.01364 -1.40932,14.21406 4.44057,26.1335 9.0454,18.43055 7.30098,21.965 7.30067,21.96562 2e-4,-3.9e-4 1.74743,-3.53394 23.15415,-10.12663 13.84423,-4.26358 21.19999,-10.75925 24.66998,-14.8171 a 16.625347,14.748181 0 0 0 2.63233,-3.70671 16.625347,14.748181 0 0 0 -8.55447,-19.42758 16.625347,14.748181 0 0 0 -21.90037,7.58859 16.625347,14.748181 0 0 0 -8.55446,-19.42758 z" /><path
id="path4-2-6-1-9-8"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:4.22313;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 416.90426,193.13811 a 12.322476,14.134158 82.30909 0 0 -17.56469,8.83612 12.322476,14.134158 82.30909 0 0 -0.59553,3.65007 c -0.27175,4.26676 0.45709,11.92642 6.76182,21.10665 9.74867,14.19503 8.68847,17.32338 8.68827,17.3239 1.2e-4,-3.3e-4 1.06282,-3.12827 18.32699,-11.08266 11.16516,-5.14424 16.60821,-11.37802 19.06101,-15.14093 a 12.322476,14.134158 82.30909 0 0 1.788,-3.37453 12.322476,14.134158 82.30909 0 0 -9.45059,-15.07737 12.322476,14.134158 82.30909 0 0 -17.5647,8.83612 12.322476,14.134158 82.30909 0 0 -9.45058,-15.07737 z" /><path
id="path4-2-6-1-9-8-4"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:3.48342;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 96.169666,99.754795 a 10.296362,11.508677 62.365085 0 0 -7.330189,13.841365 10.296362,11.508677 62.365085 0 0 1.473186,2.6805 c 2.008889,2.93989 6.437543,7.66269 15.415927,10.84633 13.88288,4.92276 14.77391,7.47086 14.77407,7.4713 -1e-4,-2.8e-4 -0.88925,-2.54921 6.69094,-15.67164 4.90232,-8.48655 5.37758,-15.08279 5.10162,-18.6847 a 10.296362,11.508677 62.365085 0 0 -0.5257,-3.042454 10.296362,11.508677 62.365085 0 0 -14.1348,-5.641051 10.296362,11.508677 62.365085 0 0 -7.33021,13.841385 10.296362,11.508677 62.365085 0 0 -14.134844,-5.641035 z" /><path
id="path4-2-6-1-9-8-4-9-2"
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:3.05139;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 296.64657,233.26347 a 8.8114666,10.319195 80.445391 0 0 -11.58972,8.06989 8.8114666,10.319195 80.445391 0 0 0.0366,2.62543 c 0.35019,3.02392 1.86017,8.32306 7.60386,14.09672 8.8812,8.92759 8.51432,11.23624 8.51426,11.23663 5e-5,-2.5e-4 0.36876,-2.30885 11.85385,-9.72788 7.4277,-4.79799 10.57103,-9.75243 11.86523,-12.65455 a 8.8114666,10.319195 80.445391 0 0 0.86244,-2.55882 8.8114666,10.319195 80.445391 0 0 -8.77837,-9.57865 8.8114666,10.319195 80.445391 0 0 -11.58975,8.06989 8.8114666,10.319195 80.445391 0 0 -8.77842,-9.57866 z" /><path
id="path4-2-6-1-9-8-4-9-2-6"
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:2.49738;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 403.95446,91.44744 a 7.0950375,8.5844443 82.142743 0 0 -9.64888,6.492875 7.0950375,8.5844443 82.142743 0 0 0.0305,2.112375 c 0.29155,2.43298 1.54866,6.69657 6.33049,11.34195 7.39392,7.18298 7.08848,9.04046 7.08843,9.04078 4e-5,-2.1e-4 0.307,-1.85765 9.86876,-7.82686 6.18383,-3.86038 8.80077,-7.84663 9.87824,-10.18161 a 7.0950375,8.5844443 82.142743 0 0 0.718,-2.05879 7.0950375,8.5844443 82.142743 0 0 -7.30831,-7.706798 7.0950375,8.5844443 82.142743 0 0 -9.64889,6.492875 7.0950375,8.5844443 82.142743 0 0 -7.30834,-7.706797 z" /><path
id="path4-2-6-1-9-8-4-9-5"
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:7.54176;stroke-dasharray:none;stroke-opacity:0.521569"
d="m 410.46352,265.86621 a 24.802737,22.39469 2.0523303 0 0 -30.17074,15.95063 24.802737,22.39469 2.0523303 0 0 -0.74427,6.61415 c -0.12337,7.7333 1.79614,21.62064 13.65297,38.28529 18.33368,25.76802 16.72835,31.4347 16.72811,31.43572 1.4e-4,-6.3e-4 1.60998,-5.66652 31.32518,-20.02007 19.21759,-9.28266 28.27643,-20.56192 32.27921,-27.37361 a 24.802737,22.39469 2.0523303 0 0 2.8654,-6.11021 24.802737,22.39469 2.0523303 0 0 -17.88248,-27.36631 24.802737,22.39469 2.0523303 0 0 -30.17081,15.95066 24.802737,22.39469 2.0523303 0 0 -17.88257,-27.36625 z" /><path
id="path4-2-6-1-9-8-5"
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:3.6405;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 114.43099,189.33754 a 10.333135,12.525309 79.004057 0 0 -14.00848,9.75499 10.333135,12.525309 79.004057 0 0 0.0547,3.08382 c 0.43602,3.54457 2.28588,9.73496 9.26636,16.38295 10.79359,10.27954 10.35819,13.00063 10.3581,13.00111 6e-5,-3e-4 0.4377,-2.72141 14.32197,-11.70923 8.9793,-5.8126 12.76786,-11.70785 14.32433,-15.14812 a 10.333135,12.525309 79.004057 0 0 1.03481,-3.02677 10.333135,12.525309 79.004057 0 0 -10.67161,-11.04688 10.333135,12.525309 79.004057 0 0 -14.00848,9.75501 10.333135,12.525309 79.004057 0 0 -10.6716,-11.04688 z" /><path
id="path4-2-6-1-9-8-4-9-2-2"
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:3.62674;stroke-dasharray:none;stroke-opacity:0.519962"
d="m 178.56055,273.32338 a 10.656883,12.053184 66.089768 0 0 -7.97821,14.16205 10.656883,12.053184 66.089768 0 0 1.49012,2.79504 c 2.04675,3.07265 6.5954,8.03049 15.94965,11.48124 14.46412,5.33577 15.34612,7.98353 15.34628,7.98398 -8e-5,-2.8e-4 -0.88012,-2.64887 7.3455,-16.0639 5.31973,-8.67576 5.95524,-15.47956 5.74045,-19.20452 a 10.656883,12.053184 66.089768 0 0 -0.48843,-3.15169 10.656883,12.053184 66.089768 0 0 -14.7135,-6.08217 10.656883,12.053184 66.089768 0 0 -7.97826,14.1621 10.656883,12.053184 66.089768 0 0 -14.71355,-6.08215 z" /></svg>

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1 +0,0 @@
#e48cc9ff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 42 KiB

1740
dist/languages/ar.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/ca.ts vendored

File diff suppressed because it is too large Load Diff

1715
dist/languages/cs.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/da.ts vendored

File diff suppressed because it is too large Load Diff

1725
dist/languages/de.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/el.ts vendored

File diff suppressed because it is too large Load Diff

1834
dist/languages/es.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/fi.ts vendored

File diff suppressed because it is too large Load Diff

1730
dist/languages/fr.ts vendored

File diff suppressed because it is too large Load Diff

1715
dist/languages/hu.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/id.ts vendored

File diff suppressed because it is too large Load Diff

1772
dist/languages/it.ts vendored

File diff suppressed because it is too large Load Diff

1715
dist/languages/ja_JP.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/ko_KR.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/nl.ts vendored

File diff suppressed because it is too large Load Diff

1818
dist/languages/pl.ts vendored

File diff suppressed because it is too large Load Diff

1715
dist/languages/pt_BR.ts vendored

File diff suppressed because it is too large Load Diff

1715
dist/languages/pt_PT.ts vendored

File diff suppressed because it is too large Load Diff

1784
dist/languages/ru_RU.ts vendored

File diff suppressed because it is too large Load Diff

1806
dist/languages/sv.ts vendored

File diff suppressed because it is too large Load Diff

2263
dist/languages/tr_TR.ts vendored

File diff suppressed because it is too large Load Diff

1831
dist/languages/uk.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/vi.ts vendored

File diff suppressed because it is too large Load Diff

1711
dist/languages/vi_VN.ts vendored

File diff suppressed because it is too large Load Diff

1812
dist/languages/zh_CN.ts vendored

File diff suppressed because it is too large Load Diff

1716
dist/languages/zh_TW.ts vendored

File diff suppressed because it is too large Load Diff

View File

@ -6,11 +6,9 @@
This is a full-fledged guide to build Eden on all supported platforms.
## Dependencies
First, you must [install some dependencies](Deps.md).
## Clone
Next, you will want to clone Eden via the terminal:
```sh
@ -55,35 +53,30 @@ Hit "Configure Project", then wait for CMake to finish configuring (may take a w
> [!WARNING]
>For all systems:
>
>- *CMake* **MUST** be in your PATH (and also *ninja*, if you are using it as `<GENERATOR>`)
>- You *MUST* be in the cloned *Eden* directory
>
>On Windows:
>
> - It's recommended to install **[Ninja](https://ninja-build.org/)**
> - You must load **Visual C++ development environment**, this can be done by running our convenience script:
> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+)
> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc)
> - It's recommended to install **[Ninja](https://ninja-build.org/)**
> - You must load **Visual C++ development environment**, this can be done by running our convenience script:
> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+)
> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc)
Available `<GENERATOR>`:
- MSYS2: `MSYS Makefiles`
- MSVC: `Ninja` (preferred) or `Visual Studio 17 2022`
- macOS: `Ninja` (preferred) or `Xcode`
- Others: `Ninja` (preferred) or `UNIX Makefiles`
Available `<BUILD_TYPE>`:
- `Release` (default)
- `RelWithDebInfo` (debug symbols--compiled executable will be large)
- `Debug` (if you are using a debugger and annoyed with stuff getting optimized out)
Caveat for Debug Builds:
- If you're building with CCache, you will need to add the environment variable `CL` with the `/FS` flag ([Reference](https://learn.microsoft.com/pt-br/cpp/build/reference/fs-force-synchronous-pdb-writes?view=msvc-170))
Also see the root CMakeLists.txt for more build options. Usually the default will provide the best experience, however.
Also see the [Options](Options.md) page for additional CMake options.
```sh
cmake -S . -B build -G "<GENERATOR>" -DCMAKE_BUILD_TYPE=<BUILD_TYPE> -DYUZU_TESTS=OFF
@ -102,7 +95,7 @@ cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COM
<details>
<summary>Click to Open</summary>
- Clone the Repository:
* Clone the Repository:
<img src="https://user-images.githubusercontent.com/42481638/216899046-0d41d7d6-8e4d-4ed2-9587-b57088af5214.png" width="500">
<img src="https://user-images.githubusercontent.com/42481638/216899061-b2ea274a-e88c-40ae-bf0b-4450b46e9fea.png" width="500">
@ -112,26 +105,26 @@ cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COM
### Building & Setup
- Once Cloned, You will be taken to a prompt like the image below:
* Once Cloned, You will be taken to a prompt like the image below:
<img src="https://user-images.githubusercontent.com/42481638/216899092-3fe4cec6-a540-44e3-9e1e-3de9c2fffc2f.png" width="500">
- Set the settings to the image below:
- Change `Build type: Release`
- Change `Name: Release`
- Change `Toolchain Visual Studio`
- Change `Generator: Let CMake decide`
- Change `Build directory: build`
* Set the settings to the image below:
* Change `Build type: Release`
* Change `Name: Release`
* Change `Toolchain Visual Studio`
* Change `Generator: Let CMake decide`
* Change `Build directory: build`
<img src="https://user-images.githubusercontent.com/42481638/216899164-6cee8482-3d59-428f-b1bc-e6dc793c9b20.png" width="500">
- Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient.
- Once this process has been completed (No loading bar bottom right), you can now build eden
- In the top right, click on the drop-down menu, select all configurations, then select eden
* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient.
* Once this process has been completed (No loading bar bottom right), you can now build eden
* In the top right, click on the drop-down menu, select all configurations, then select eden
<img src="https://user-images.githubusercontent.com/42481638/216899226-975048e9-bc6d-4ec1-bc2d-bd8a1e15ed04.png" height="500" >
- Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built.
* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built.
<img src="https://user-images.githubusercontent.com/42481638/216899275-d514ec6a-e563-470e-81e2-3e04f0429b68.png" width="500">
</details>
@ -139,7 +132,6 @@ cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COM
## Troubleshooting
If your initial configure failed:
- *Carefully* re-read the [dependencies guide](Deps.md)
- Clear the CPM cache (`.cache/cpm`) and CMake cache (`<build directory>/CMakeCache.txt`)
- Evaluate the error and find any related settings
@ -161,16 +153,20 @@ Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "pla
If you are using the `UNIX Makefiles` or `Visual Studio 17 2022` as `<GENERATOR>`, you should also add `--parallel` for faster build times.
```sh
```
cmake --build build
```
Your compiled executable will be in:
- `build/bin/eden.exe` for Windows,
- `build/bin/eden.app/Contents/MacOS/eden` for macOS,
- and `build/bin/eden` for others.
## Scripts
Take a look at our [CI scripts](https://github.com/Eden-CI/Workflow). You can use `.ci/common/configure.sh` on any POSIX-compliant shell, but you are heavily encouraged to instead write your own based. It's not really that hard, provided you can read CMake.
Some platforms have convenience scripts provided for building.
- **[Linux](scripts/Linux.md)**
- **[Windows](scripts/Windows.md)**
macOS scripts will come soon.

View File

@ -18,7 +18,7 @@
src/web_service @AleksandrPopovich
src/dynarmic @Lizzie
src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 @JPikachu
src/core/hle @Maufeat @PavelBARABANOV
src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan
src/core/arm @Lizzie @MrPurple666
src/*_room @AleksandrPopovich
src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson

View File

@ -1,41 +0,0 @@
# AddDependentPackage
Use `AddDependentPackage` when you have multiple packages that are required to all be from the system, OR bundled. This is useful in cases where e.g. versions must absolutely match.
## Versioning
Versioning must be handled by the package itself.
## Examples
### Vulkan
`cpmfile.json`
```json
{
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"tag": "v%VERSION%"
}
}
```
`CMakeLists.txt`:
```cmake
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
```
If Vulkan Headers are installed, but NOT Vulkan Utility Libraries, then CPMUtil will throw an error.

View File

@ -31,10 +31,6 @@ The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackag
[`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil.
## AddDependentPackage
[`AddDependentPackage`](./AddDependentPackage.md) allows you to add multiple packages such that all of them must be from the system OR bundled.
## AddQt
[`AddQt`](./AddQt.md) adds a specific version of Qt to your project.

View File

@ -85,8 +85,6 @@ If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMak
## OpenBSD
System boost doesn't have `context` (as of 7.8); so you may need to specify `-DYUZU_USE_CPM=ON -DBoost_FORCE_BUNDLED=ON`.
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up.

View File

@ -11,7 +11,6 @@ Simply put, types/classes are named as `PascalCase`, same for methods and functi
Except for Qt MOC where `functionName` is preferred.
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
- `X`: Defined by some utility headers, avoid.
@ -28,7 +27,6 @@ Try not using hungarian notation, if you're able.
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
Do not put if/while/etc braces after lines:
```c++
// no dont do this
// this is more lines of code for no good reason (why braces need their separate lines?)
@ -107,7 +105,6 @@ device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str()
```
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
```c++
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine

View File

@ -277,7 +277,7 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv
```sh
pkg_add -u
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake llvm-19.1.7p3 qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.27
```
[Caveats](./Caveats.md#openbsd).

View File

@ -82,15 +82,15 @@ You may additionally need the `Qt Extension Pack` extension if building Qt.
# Build speedup
If you have an HDD, use ramdisk (build in RAM), approximatedly you need 4GB for a full build with debug symbols:
If you have an HDD, use ramdisk (build in RAM):
```sh
mkdir /tmp/ramdisk
chmod 777 /tmp/ramdisk
sudo mkdir /tmp/ramdisk
sudo chmod 777 /tmp/ramdisk
# about 8GB needed
mount -t tmpfs -o size=4G myramdisk /tmp/ramdisk
sudo mount -t tmpfs -o size=8G myramdisk /tmp/ramdisk
cmake -B /tmp/ramdisk
cmake --build /tmp/ramdisk -- -j32
umount /tmp/ramdisk
sudo umount /tmp/ramdisk
```
# Assets and large files

View File

@ -1,114 +0,0 @@
# Driver bugs
Non-exhaustive list of known drivers bugs.
See also: [Dolphin emulator](hhttps://github.com/dolphin-emu/dolphin/blob/cdbea8867df3d0a6fc375e78726dae95612fb1fd/Source/Core/VideoCommon/DriverDetails.h#L84) own list of driver bugs (which are also included here).
## Vulkan
| Vendor/GPU | OS | Drivers | Version | Bug |
|---|---|---|---|---|
| AMD | All | Proprietary | ? | `VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT` on GCN4 and lower is broken. |
| AMD | All | Proprietary | ? | GCN4 and earlier have broken `VK_EXT_sampler_filter_minmax`. |
| AMD | All | Proprietary | ? | If offset + stride * count is greater than the size, then the last attribute will have the wrong value for vertex buffers. |
| AMD | All | Proprietary | ? | On GCN, 2D mipmapped texture arrays (with width == height) where there are more than 6 layers causes broken corrupt mipmaps. |
| AMD | All | RADV, MESA | ? | Using LLVM emitter causes corrupt FP16, proprietary drivers unaffected. |
| AMD | All | Proprietary | ? | Incorrect implementation of VK_EXT_depth_clamp_control causes incorrect depth values to be written to the depth buffer. |
| AMD | macOS | Proprietary | ? | `gl_HelperInvocation` is actually `!gl_HelperInvocation`. |
| NVIDIA | All | Proprietary | ? | Drivers for ampere and newer have broken float16 math. |
| NVIDIA | All | Proprietary | >=510.0.0 | Versions >= 510 do not support MSAA->MSAA image blits. |
| NVIDIA | All | Proprietary | ? | Shader stencil export not supported. |
| NVIDIA | All | Proprietary | ? | Doesn't properly support conditional barriers. |
| NVIDIA | All | Proprietary | ? | GPUs pre-turing doesn't properly work with push descriptors. |
| NVIDIA | All | All | ? | Calling `vkCmdClearAttachments` with a partial rect, or specifying a render area in a render pass with the load op set to clear can cause the GPU to lock up, or raise a bounds violation. This only occurs on MSAA framebuffers, and it seems when there are multiple clears in a single command buffer. Worked around by back to the slow path (drawing quads) when MSAA is enabled. |
| Intel | All | Proprietary | <27.20.100.0 | Intel Windows versions before 27.20.100.0 has broken `VK_EXT_vertex_input_dynamic_state`. |
| Intel | All | Proprietary | ? | Intel proprietary drivers do not support MSAA->MSAA image blits. |
| Intel | All | Proprietary | 0.405.0<br>until<br>0.405.286 | Intel proprietary drivers 0.405.0 until 0.405.286 have broken compute. |
| Intel | macOS | Proprietary | ? | Using dynamic sampler indexing locks up the GPU. |
| Intel | macOS | Proprietary | ? | Using subgroupMax in a shader that can discard results in garbage data. |
| Intel | All | Mesa | ? | Broken lines in geometry shaders when writing to `gl_ClipDistance` in the vertex shader. |
| Qualcomm | All | Proprietary | ? | Using too many samplers (on A8XX is `65536`) can cause heap exhaustion, recommended to use 75% of total available samplers. |
| Qualcomm | All | Proprietary | ? | Qualcomm Adreno GPUs doesn't handle scaled vertex attributes `VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME`. |
| Qualcomm | All | Proprietary | ? | 64-bit integer extensions (`VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME`) cause nondescriptive crashes and stability issues. |
| Qualcomm | All | All | ? | Driver requires higher-than-reported binding limits (32). |
| Qualcomm | All | All | ? | On Adreno, enabling rasterizer discard somehow corrupts the viewport state; a workaround is forcing it to be updated on next use. |
| Qualcomm | All | Proprietary | ? | Concurrent fence waits are unsupported. |
| Qualcomm | All | Proprietary | ? | `vkCmdCopyImageToBuffer` allocates a staging image when used to copy from an image with optimal tiling; which results in overall slower performance. |
| Qualcomm | All | Proprietary | ? | 32-bit depth clears are broken in the Adreno Vulkan driver, and have no effect. |
| Qualcomm | All | Proprietary | ? | It should be safe to release the resources before actually resetting the VkCommandPool. However, devices running R drivers there was a few months period where the driver had a bug which it incorrectly was accessing objects on the command buffer while it was being reset. If these objects were already destroyed (which is a valid thing to do) it would crash. |
| Qualcomm | All | Proprietary | ? | On Pixel and Pixel2XL's with Adreno 530 and 540s, setting width and height to 10s reliably triggers what appears to be a driver race condition. |
| Qualcomm | All | Proprietary | ? | Non-descriptive broken OpPhi SPIRV lowering, originally using OpPhi to choose the result is crashing on Adreno 4xx. Switched to storing the result in a temp variable as glslang does. |
| Qualcomm | All | Proprietary | ? | See crbug.com/1241134. The bug appears on Adreno 5xx devices with OS PQ3A. It does not repro on the earlier PPR1 version since the extend blend func extension was not present on the older driver. |
| Mali | Android | Proprietary | ? | Non-descriptive green screen on various locations. |
| Mali | Android | Proprietary | ? | Cached memory is significantly slower for readbacks than coherent memory in the Mali Vulkan driver, causing high CPU usage in the `__pi___inval_cache_range` kernel function. |
| Mali | Android | Proprietary | ? | On some old ARM driver versions, dynamic state for stencil write mask doesn't work correctly in the presence of discard or alpha to coverage, if the static state provided when creating the pipeline has a value of 0 (`alphaToCoverageEnable` and `rasterizerDiscardEnable`). |
| Mali | Android | Proprietary | ? | Failing to submit because of a device loss still needs to wait for the fence to signal before deleting. However, there is an ARM bug (b/359822580) where the driver early outs on the fence wait if in a device lost state and thus we can't wait on it. Instead, we just wait on the queue to finish. |
| Mali | Android | Proprietary | ? | With Galaxy S7 we see lots of rendering issues when we suballocate VkImages. |
| Mali | Android | Proprietary | ? | With Galaxy S7 and S9 we see lots of rendering issues with image filters dropping out when using only primary command buffers. We also see issues on the P30 running Android 28. |
| Mali | Android | Proprietary | ? | `RGBA_F32` mipmaps appear to be broken on some Mali devices. |
| Mali | Android | Proprietary | ? | Matrix IR lowering for matrix swizzle, scalar multiplication and unary `(+m)`/`(-m)` present extraneous unexplained bugs with more than 32 matrix temporals. |
| Apple | All | MoltenVK | ? | Driver breaks when using more than 16 vertex attributes/bindings. |
| Apple | macOS | Proprietary | >4 | Some driver and Apple Silicon GPU combinations have problems with fragment discard when early depth test is enabled. Discarded fragments may appear corrupted. |
| Apple | iOS | MoltenVK | ? | Null descriptors cause non-descriptive issues. |
| Apple | iOS | MoltenVK | ? | Push descriptors cause non-descriptive issues. |
| Imagination | Android | Proprietary | ? | Some vulkan implementations don't like the 'clear' loadop renderpass if you try to use a framebuffer with a different load/store op than that which it was created with, despite the spec saying they should be compatible. |
| Intel<br>Qualcomm<br>AMD<br>Apple | All | All<br>MoltenVK (Apple) | ? | Reversed viewport depth range does not work as intended on some Vulkan drivers. The Vulkan spec allows the `minDepth`/`maxDepth` fields in the viewport to be reversed, however the implementation is broken on some drivers. |
AMD: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_AMD_PROPRIETARY`
- OSS: `VK_DRIVER_ID_AMD_OPEN_SOURCE`
- MESA: `VK_DRIVER_ID_MESA_RADV`
NVIDIA: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_NVIDIA_PROPRIETARY`.
- MESA: `VK_DRIVER_ID_MESA_NVK`.
Intel: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS`.
- MESA: `VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA`.
Qualcomm: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_QUALCOMM_PROPRIETARY`.
- MESA: `VK_DRIVER_ID_MESA_TURNIP`.
Mali: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_ARM_PROPRIETARY`.
- MESA: `VK_DRIVER_ID_MESA_PANVK`.
Samsung: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_SAMSUNG_PROPRIETARY`.
Apple: List of driver IDs:
- MoltenVK/MVK: `VK_DRIVER_ID_MOLTENVK`.
- KosmicKrisp: `VK_DRIVER_ID_MESA_KOSMICKRISP`.
- HoneyKrisp: `VK_DRIVER_ID_MESA_HONEYKRISP`.
PowerVR: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_IMAGINATION_PROPRIETARY`.
- MESA: `VK_DRIVER_ID_IMAGINATION_OPEN_SOURCE_MESA`.
Software Rasterizers: List of driver IDs:
- SwiftShader: `VK_DRIVER_ID_GOOGLE_SWIFTSHADER`.
- LLVMPipe: `VK_DRIVER_ID_MESA_LLVMPIPE`.
Broadcom: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_BROADCOM_PROPRIETARY`.
- MESA: `VK_DRIVER_ID_MESA_V3DV`.
Verisilicon: List of driver IDs:
- Proprietary: `VK_DRIVER_ID_VERISILICON_PROPRIETARY`.
Other: List of driver IDs:
- SC: `VK_DRIVER_ID_VULKAN_SC_EMULATION_ON_VULKAN`.
- GGP (Stadia): `VK_DRIVER_ID_GGP_PROPRIETARY`.
- CoreAVI (Cars): `VK_DRIVER_ID_COREAVI_PROPRIETARY`.
- Juice (Remote GPU): `VK_DRIVER_ID_JUICE_PROPRIETARY`.
- Venus (Virtio GPU): `VK_DRIVER_ID_MESA_VENUS`.
- Dozen (Vulkan on DirectX): `VK_DRIVER_ID_MESA_DOZEN`.
## OpenGL
| Vendor/GPU | OS | Drivers | Version | Bug |
|---|---|---|---|---|
| All | Android | All | ? | In OpenGL, multi-threaded shader pre-compilation sometimes crashes. |
| All | All | Mesa | ? | https://github.com/KhronosGroup/glslang/pull/2646 |

68
docs/Options.md Normal file
View File

@ -0,0 +1,68 @@
# CMake Options
To change these options, add `-DOPTION_NAME=NEWVALUE` to the command line.
- On Qt Creator, go to Project -> Current Configuration
Notes:
- Defaults are marked per-platform.
- "Non-UNIX" just means Windows/MSVC and Android (yes, macOS is UNIX
- Android generally doesn't need to change anything; if you do, go to `src/android/app/build.gradle.kts`
- To set a boolean variable to on, use `ON` for the value; to turn it off, use `OFF`
- If a variable is mentioned as being e.g. "ON" for a specific platform(s), that means it is defaulted to OFF on others
- TYPE is always boolean unless otherwise specified
- Format:
* `OPTION_NAME` (TYPE DEFAULT) DESCRIPTION
## Options
- `YUZU_USE_CPM` (ON for non-UNIX) Use CPM to fetch system dependencies (fmt, boost, etc) if needed. Externals will still be fetched. See the [CPM](CPM.md) and [Deps](Deps.md) docs for more info.
- `ENABLE_WEB_SERVICE` (ON) Enable multiplayer service
- `ENABLE_WIFI_SCAN` (OFF) Enable WiFi scanning (requires iw on Linux) - experimental
- `YUZU_USE_BUNDLED_FFMPEG` (ON for non-UNIX) Download (Windows, Android) or build (UNIX) bundled FFmpeg
- `ENABLE_CUBEB` (ON) Enables the cubeb audio backend
- `YUZU_TESTS` (ON) Compile tests - requires Catch2
- `YUZU_DOWNLOAD_ANDROID_VVL` (ON) Download validation layer binary for Android
- `YUZU_ENABLE_LTO` (OFF) Enable link-time optimization
* Not recommended on Windows
* UNIX may be better off appending `-flto=thin` to compiler args
- `YUZU_DOWNLOAD_TIME_ZONE_DATA` (ON) Always download time zone binaries
* Currently, build fails without this
- `YUZU_USE_FASTER_LD` (ON) Check if a faster linker is available
* Only available on UNIX
- `YUZU_USE_BUNDLED_MOLTENVK` (ON, macOS only) Download bundled MoltenVK lib)
- `YUZU_TZDB_PATH` (string) Path to a pre-downloaded timezone database (useful for nixOS)
- `ENABLE_OPENSSL` (ON for Linux and *BSD) Enable OpenSSL backend for the ssl service
* Always enabled if the web service is enabled
- `YUZU_USE_BUNDLED_OPENSSL` (ON for MSVC) Download bundled OpenSSL build
* Always on for Android
* Unavailable on OpenBSD
- `ENABLE_UPDATE_CHECKER` (OFF) Enable update checker for the Qt an Android frontends
The following options are desktop only:
- `ENABLE_SDL2` (ON) Enable the SDL2 desktop, audio, and input frontend (HIGHLY RECOMMENDED!)
* Unavailable on Android
- `YUZU_USE_EXTERNAL_SDL2` (ON for non-UNIX) Compiles SDL2 from source
- `YUZU_USE_BUNDLED_SDL2` (ON for MSVC) Download a prebuilt SDL2
* Unavailable on OpenBSD
* Only enabled if YUZU_USE_CPM and ENABLE_SDL2 are both ON
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input frontend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics frontend
* Unavailable on Windows/ARM64 and Android
- `ENABLE_QT` (ON) Enable the Qt frontend (recommended)
- `ENABLE_QT_TRANSLATION` (OFF) Enable translations for the Qt frontend
- `YUZU_USE_BUNDLED_QT` (ON for MSVC) Download bundled Qt binaries
* Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g:
* `-DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
- `YUZU_QT_MIRROR` (string) What mirror to use for downloading the bundled Qt libraries
- `YUZU_USE_QT_MULTIMEDIA` (OFF) Use QtMultimedia for camera support
- `YUZU_USE_QT_WEB_ENGINE` (OFF) Use QtWebEngine for web applet implementation (requires the huge QtWebEngine dependency; not recommended)
- `USE_DISCORD_PRESENCE` (OFF) Enables Discord Rich Presence (Qt frontend only)
- `YUZU_ROOM` (ON) Enable dedicated room functionality
- `YUZU_ROOM_STANDALONE` (ON) Enable standalone room executable (eden-room)
* Requires `YUZU_ROOM`
- `YUZU_CMD` (ON) Compile the SDL2 frontend (eden-cli) - requires SDL2
- `YUZU_CRASH_DUMPS` Compile crash dump (Minidump) support"
* Currently only available on Windows and Linux
See `src/dynarmic/CMakeLists.txt` for additional options--usually, these don't need changed

View File

@ -5,20 +5,13 @@ This contains documentation created by developers. This contains build instructi
- **[General Build Instructions](Build.md)**
- **[Cross Compiling](CrossCompile.md)**
- **[Development Guidelines](Development.md)**
- **[Coding guidelines](Coding.md)**
- **[Dependencies](Deps.md)**
- **[Debug Guidelines](./Debug.md)**
- **[CPM - CMake Package Manager](./CPMUtil)**
- **[CPM - CMake Package Manager](CPMUtil.md)**
- **[Platform-Specific Caveats](Caveats.md)**
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
- **[User Handbook](./user)**
- **[Release Policy](./ReleasePolicy.md)**
- **[Dynarmic](./dynarmic)**
- **[Cross compilation](./CrossCompile.md)**
- **[Driver Bugs](./DriverBugs.md)**
## Policies
Policies and information on development.
- **[AI and LLM Usage](./policies/AI.md)**
- **[Release Policy](./policies/Release.md)**
- **[Coding guidelines](./policies/Coding.md)**

25
docs/ReleasePolicy.md Normal file
View File

@ -0,0 +1,25 @@
# Release Policy
While releases are usually made at the discretion of the team, we feel that establishing a clearer guideline on how those come to be will help expectations when it comes to features and fixes per version.
## Release candidates
Every full release is *preceded* by at least, 3 release candidates. The reasoning is that each week of the month, there will be a release candidate, with the "4th one" being the final full release.
The main expectation is that the release candidates bring both fixes and, sometimes, new features. But not guarantee a regression-free experience.
The criteria for choosing a date for a release candidate is at discretion, or "perceived necesity" at any given time.
## Full release
A full release means there are *no major* leftover regressions, importantly this means that a grand portion of regressions found between release candidates are swept out before declaring a full release. This doesn't mean a full release is regression-free; but we do a best-effort approach to reduce them for end-users.
The main expectation is that users can safely upgrade from a stable build to another, with no major regressions.
## Snapshot/rolling release
While we don't publish rolling releases, we are aware users may compile from source and/or provide binaries to master builds of the project.
This is mostly fine since we keep master very stable from major hiccups. However sometimes bugs do slip between tests or reviews - so users are advised to keep that in mind.
We advise that users also read git logs (`git log --oneline`) before recompiling to get a clearer picture of the changes given into the emulator.

View File

@ -1,107 +0,0 @@
# AI Policy
Use at your peril.
AI is a *tool*, not a replacement or catch-all solution. It is generally okay at a few *very specific* use cases:
- Automation of tedious changes where you have already made the pattern clear and done the necessary groundwork.
- Conversion of code from one paradigm to another.
For everything else, AI is subpar at best, and actively harmful at worst. In general, you are **heavily** encouraged to not use AI at all.
## Why?
AI is notorious for hallucinating facts out of thin air and sometimes outright lying to users. Additionally, code written by LLMs is often needlessly verbose and horrifically inefficient (not to mention the rather ridiculous level of over-commenting). The end result is often one of three things:
- Completely nonfunctional code
- Code that works, but is extraordinarily verbose or not nearly as efficient as it can be
- Code that works well and is written well, but solves a different problem than was intended, or solves the same problem but in a completely incorrect way that will break other things horribly.
Human-written code will, without exception, always be of infinitely higher quality when properly researched and implemented by someone both familiar with the surrounding code and the programming language in use. LLMs may produce a "good enough" result, but this result is often subpar. Keep in mind: all code is held under a standard of excellence. If your code sucks, it will be rejected. AI-generated code just so happens to be a particularly sucky genre of code, and will thus be held to this same standard.
On a lesser-known note, LLM outputs often contain unicode symbols such as emojis or the arrow symbol. Please don't put Unicode symbols in your code. It messes with many an IDE, and the three people viewing your code on Lynx will be very unhappy.
**Learn to code**. It's worth it, we promise!
## Acceptable Use
- As stated previously, AI is good in a few *very specific* cases. In these cases, it's usually fine to use AI, as long as you **explicitly provide notice that it was used**.
- Anything directly outside of the realm of the code written in your PR or patch is none of our business.
- This primarily covers research.
- However, we *still* strongly discourage this for the reasons mentioned above.
- Assistance with cleanups, and minor nitpicks/optimizations.
- This is still discouraged, but it's okay to occasionally use LLMs to catch any minor mistakes you made in your code.
- Debugging
- In general, LLMs are not good at debugging, but if you solve a bug you're facing with help from an AI, and said fix **works properly**, then that's fine.
## Unacceptable Use
Well, everything else. But here are a few examples:
- Commit messages
- LLMs are absolutely horrible at this. They are needlessly verbose, almost never catch the actual intent of the commit, and will almost always hallucinate false information about said changes.
- See the addendum for an example
- Solving problems
- Slapping a few files and a "please fix bug XYZ" into an LLM is a recipe for disaster that will pretty much never work.
- Fully AI-generated code
- This shouldn't need explaining. Do not do this under any circumstance, especially if you don't actually understand what's going on.
- Writing code based on pseudo-instructions
- If you don't know how to write code, don't. If you've figured out the root cause (preferably without feeding random files into an LLM) and actively know what's going on, provide information to other developers or friends of yours who have knowledge of the language and/or the codebase.
## Addendum: Commit Messages
The patchset for pull request [#3422](https://git.eden-emu.dev/eden-emu/eden/pulls/3422) was fed into several LLMs to generate a commit message. All of them sucked, and not a single one caught on to what the commit actually did. For example:
```txt
profile_manager: Refactor user deletion to use index instead of UUID
This commit improves the user profile deletion flow in the following ways:
1. **UI layer (ConfigureProfileManager)**:
- Replaces UUID-based deletion with index-based deletion
- Passes the selected row index from the tree view to the confirmation dialog
- Emits deleteUser(int index) signal from ConfigureProfileManagerDeleteDialog
- Removes lambda capture and std::function callback in favor of direct signal/slot connection
2. **Delete dialog**:
- Changes SetInfo() signature to accept index instead of accept callback
- Stores index internally and emits it via deleteUser signal on acceptance
3. **Core ProfileManager**:
- Refactors RemoveUser(UUID) → RemoveProfileAtIndex(index)
- Removes duplicated stable_partition + save logic from RemoveUser()
- Centralizes profile removal and save-file writing in RemoveProfileAtIndex()
4. **Minor follow-up**:
- Adds profile_manager.ResetUserSaveFile() call in PopulateUserList() to ensure consistent state when refreshing the user list
These changes eliminate redundant code, reduce the risk of UUID → index mismatch bugs, and make the deletion path more straightforward and maintainable.
Fixes: potential inconsistency when user order changes between UI and backend
```
This is:
- Needlessly verbose (nobody cares about most of these details)
- Doesn't address the actual purpose of the PR (fixing a double-deletion bug in the profile manager)
- Has unicode arrows
- Uses overly corporate and, well, robotic language
As another example:
```txt
profile(manager/ui): switch to index-based deletion and unify removal logic
- Make ConfigureProfileManagerDeleteDialog emit a deleteUser(int) signal instead of taking a callback. Store the target index in the dialog (m_index) and emit it on acceptance.
- Change ConfigureProfileManager to pass an index to SetInfo and to accept an index in DeleteUser. Wire the dialog signal to the new DeleteUser slot.
- Update headers: add the signal and m_index, add TODO to move dialog to a .ui file, and update slot/signature declarations.
- Add ProfileManager::RemoveProfileAtIndex(std::size_t) and refactor RemoveUser(UUID) to call RemoveProfileAtIndex to avoid duplicated removal logic. Ensure the removal path marks saves as needed and writes the user save file.
- Ensure the profile list updates immediately after deletes by calling profile_manager.ResetUserSaveFile() when populating the user list (qlaunch fix).
- Misc: update SPDX copyright year and fix build breakages caused by the API changes.
This consolidates profile removal behavior, fixes potential race conditions in the profile dialog, and removes duplicated removal code.
```
This has all of the same problems as the other one. Needlessly verbose, doesn't address *what* it actually fixes ("consolidates profile removal behavior"... okay, why? What does it fix?), etc. It even has the bonus of totally hallucinating the addition of a method!
**Don't use AI for commit messages**.

View File

@ -1,10 +0,0 @@
# Release Policy
Release when lots of new changes and fixes. Hotfix if more bugs. Release candidate if lot of things to test. Simple as.
## Checklist
- [ ] Update Transifex
- [ ] Test for regressions and bugs
- [ ] Write a changelog
- [ ] Ensure all platforms work

31
docs/scripts/Linux.md Normal file
View File

@ -0,0 +1,31 @@
# Linux Build Scripts
* Provided script: `.ci/linux/build.sh`
* Must specify arch target, e.g.: `.ci/linux/build.sh amd64`
* Valid targets:
* `native`: Optimize to your native host architecture
* `legacy`: x86\_64 generic, only needed for CPUs older than 2013 or so
* `amd64`: x86\_64-v3, for CPUs newer than 2013 or so
* `steamdeck` / `zen2`: For Steam Deck or Zen >= 2 AMD CPUs (untested on Intel)
* `rog-ally` / `allyx` / `zen4`: For ROG Ally X or Zen >= 4 AMD CPUs (untested on Intel)
* `aarch64`: For armv8-a CPUs, older than mid-2021 or so
* `armv9`: For armv9-a CPUs, newer than mid-2021 or so
* Extra CMake flags go after the arch target.
### Environment Variables
* `NPROC`: Number of compilation threads (default: all cores)
* `TARGET`: Set `appimage` to disable standalone `eden-cli` and `eden-room`
* `BUILD_TYPE`: Build type (default: `Release`)
Boolean flags (set `true` to enable, `false` to disable):
* `DEVEL` (default `FALSE`): Disable Qt update checker
* `USE_WEBENGINE` (default `FALSE`): Enable Qt WebEngine
* `USE_MULTIMEDIA` (default `FALSE`): Enable Qt Multimedia
* AppImage packaging script: `.ci/linux/package.sh`
* Accepts same arch targets as build script
* Use `DEVEL=true` to rename app to `Eden Nightly` and disable the update checker
* This should generally not be used unless in a tailor-made packaging environment (e.g. Actions/CI)

29
docs/scripts/Windows.md Normal file
View File

@ -0,0 +1,29 @@
# Windows Build Scripts
* A convenience script for building is provided in `.ci/windows/build.sh`.
* You must run this with Bash, e.g. Git Bash or the MinGW TTY.
* To use this script, you must have `windeployqt` installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location:
* `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`.
* You can use `aqtinstall`, more info on <https://github.com/miurahr/aqtinstall> and <https://ddalcino.github.io/aqt-list-server/>
* Extra CMake flags should be placed in the arguments of the script.
#### Additional environment variables can be used to control building:
* `BUILD_TYPE` (default `Release`): Sets the build type to use.
* The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
* `DEVEL` (default FALSE): Disable Qt update checker
* `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
* `USE_MULTIMEDIA` (default FALSE): Enable Qt Multimedia
* `BUNDLE_QT` (default FALSE): Use bundled Qt
* Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`
* `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
* After building, a zip can be packaged via `.ci/windows/package.sh`. You must have 7-zip installed and in your PATH.
* The resulting zip will be placed into `artifacts` in the source directory.

View File

@ -6,4 +6,7 @@ Debugging on physical hardware can get tedious and time consuming. Users are emp
**Standard key prefix**: Allows to redirect the key manager to a file other than `prod.keys` (for example `other` would redirect to `other.keys`). This is useful for testing multiple keysets. Default is `prod`.
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as only the first digits (excluding the last) will be accoutned for. Region settings will affect the generated serial. The serial corresponds to a non-OLED/Lite console.
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as it will be writen in-place (that is, it will be filled with the default serial and then overwrite the serial from the beginning).
- Battery serial: `YUZU0EMULATOR14022024`
- Board serial: `YUZ10000000001`
If the user were to set their board serial as `ABC`, then it will be written in-place and the resulting serial would be `ABC10000000001`. There are no underlying checks to ensure correctness of serials other than a hard limit of 16-characters for both.

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2016 Citra Emulator Project
@ -27,46 +27,11 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# Xbyak
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
AddJsonPackage(xbyak)
endif()
# enet
AddJsonPackage(enet)
if (enet_ADDED)
target_include_directories(enet INTERFACE ${enet_SOURCE_DIR}/include)
endif()
if (NOT TARGET enet::enet)
add_library(enet::enet ALIAS enet)
endif()
# mbedtls
AddJsonPackage(mbedtls)
# stb
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
# ItaniumDemangle
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
if (NOT MSVC)
target_compile_options(demangle PRIVATE -Wno-deprecated-declarations) # std::is_pod
if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_NETBSD OR PLATFORM_DRAGONFLY)
AddJsonPackage(xbyak_sun)
else()
AddJsonPackage(xbyak)
endif()
add_library(LLVM::Demangle ALIAS demangle)
endif()
# unordered_dense
AddJsonPackage(unordered-dense)
if (YUZU_STATIC_ROOM)
return()
endif()
# Oaknut
@ -82,11 +47,27 @@ endif()
# mcl
AddJsonPackage(mcl)
# Vulkan stuff
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
# enet
AddJsonPackage(enet)
# frozen
AddJsonPackage(frozen)
if (enet_ADDED)
target_include_directories(enet INTERFACE ${enet_SOURCE_DIR}/include)
endif()
if (NOT TARGET enet::enet)
add_library(enet::enet ALIAS enet)
endif()
# mbedtls
AddJsonPackage(mbedtls)
# VulkanUtilityHeaders - pulls in headers and utility libs
AddJsonPackage(vulkan-utility-headers)
# small hack
if (NOT VulkanUtilityLibraries_ADDED)
find_package(VulkanHeaders 1.3.274 REQUIRED)
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE)
@ -234,7 +215,7 @@ if (VulkanMemoryAllocator_ADDED)
endif()
# httplib
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER OR USE_DISCORD_PRESENCE OR ENABLE_OPENSSL)
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
AddJsonPackage(httplib)
endif()
@ -243,6 +224,9 @@ if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
AddJsonPackage(cpp-jwt)
endif()
# unordered_dense
AddJsonPackage(unordered-dense)
# FFMpeg
if (YUZU_USE_EXTERNAL_FFMPEG OR YUZU_USE_BUNDLED_FFMPEG)
add_subdirectory(ffmpeg)
@ -259,6 +243,22 @@ endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
if (NOT MSVC)
target_compile_options(demangle PRIVATE -Wno-deprecated-declarations) # std::is_pod
endif()
add_library(LLVM::Demangle ALIAS demangle)
endif()
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
add_library(tz tz/tz/tz.cpp)
target_include_directories(tz PUBLIC ./tz)

View File

@ -28,8 +28,8 @@
"httplib": {
"repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%",
"hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
"git_version": "0.30.1",
"hash": "e7a8877d489c97669a8ee536e1498575be921e558ed947253013fe6b67a49d4569eedd01f543caa70183b92d8ac0e8687d662a70d880954412e387317008a239",
"git_version": "0.28.0",
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch"
@ -37,23 +37,31 @@
},
"cpp-jwt": {
"version": "1.4",
"repo": "arun11299/cpp-jwt",
"sha": "7f24eb4c32",
"hash": "d11cbd5ddb3197b4c5ca15679bcd76a49963e7b530b7dd132db91e042925efa20dfb2c24ccfbe7ef82a7012af80deff0f72ee25851312ae80381a462df8534b8",
"repo": "crueter/cpp-jwt",
"sha": "9eaea6328f",
"hash": "35b0b2bfb143585c7b2bd6dc6ca7df5ae5c6e2681000b2ebca077b0ac4bc1e6b6afbe1ce8e47f6d2edad12fcc6404f677acc2ad205661d819b8821ce6f4823fd",
"find_args": "CONFIG",
"options": [
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
],
"patches": [
"0001-fix-missing-decl.patch"
]
},
"xbyak_sun": {
"package": "xbyak",
"repo": "herumi/xbyak",
"tag": "v%VERSION%",
"hash": "b40dade90fb0e46a2bd52934f7ce461e37be931b571e58cbe2203bc08ed5b54c7ff1a29026c74c7f9805e4e3f6c9636deca528e6b4a8093ce7eae145218599f1",
"git_version": "7.29",
"bundled": true,
"skip_updates": true
},
"xbyak": {
"package": "xbyak",
"repo": "herumi/xbyak",
"tag": "v%VERSION%",
"hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47",
"git_version": "7.33.2"
"hash": "1042090405c426e339506c179d53e91d4d545ce9c9f53d8f797caa092d589f913a9bcb9c8f31c4c60870acb954c556e305fb6732c66bc3c8f1cd924f9172def9",
"git_version": "7.22",
"bundled": true,
"skip_updates": true
},
"oaknut": {
"repo": "eden-emulator/oaknut",
@ -80,13 +88,10 @@
"unordered-dense": {
"package": "unordered_dense",
"repo": "martinus/unordered_dense",
"sha": "7b55cab841",
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
"tag": "v%VERSION%",
"hash": "b98b5d4d96f8e0081b184d6c4c1181fae4e41723b54bed4296717d7f417348b48fad0bbcc664cac142b8c8a47e95aa57c1eb1cf6caa855fd782fad3e3ab99e5e",
"find_args": "CONFIG",
"bundled": true,
"patches": [
"0001-avoid-memset-when-clearing-an-empty-table.patch"
]
"git_version": "4.8.1"
},
"mbedtls": {
"package": "MbedTLS",
@ -110,25 +115,33 @@
"git_version": "1.3.18",
"find_args": "MODULE"
},
"vulkan-utility-headers": {
"package": "VulkanUtilityLibraries",
"repo": "scripts/VulkanUtilityHeaders",
"tag": "%VERSION%",
"git_version": "1.4.335",
"artifact": "VulkanUtilityHeaders.tar.zst",
"git_host": "git.crueter.xyz",
"hash": "16dac0e6586702580c4279e4cd37ffe3cf909c93eb31b5069da7af36436d47b270a9cbaac953bb66c22ed12ed67ffa096688599267f307dfb62be1bc09f79833"
},
"spirv-tools": {
"package": "SPIRV-Tools",
"repo": "KhronosGroup/SPIRV-Tools",
"sha": "0a7e28689a",
"hash": "eadfcceb82f4b414528d99962335e4f806101168474028f3cf7691ac40c37f323decf2a42c525e2d5bfa6f14ff132d6c5cf9b87c151490efad01f5e13ade1520",
"repo": "crueter/SPIRV-Tools",
"sha": "2fa2d44485",
"hash": "3124bbddf7bd44f11445edeca6786b5bba9fb314f27dc087d0bbd9951b0936884ece2b9b40b75cfc8e31ab10ba55854e73aa63df835c40423b1c81dd47b1437d",
"git_version": "2025.4",
"options": [
"SPIRV_SKIP_EXECUTABLES ON"
],
"patches": [
"0001-netbsd-fix.patch",
"0002-allow-static-only.patch"
"0001-netbsd-fix.patch"
]
},
"spirv-headers": {
"package": "SPIRV-Headers",
"repo": "KhronosGroup/SPIRV-Headers",
"sha": "04f10f650d",
"hash": "cae8cd179c9013068876908fecc1d158168310ad6ac250398a41f0f5206ceff6469e2aaeab9c820bce9d1b08950c725c89c46e94b89a692be9805432cf749396",
"sha": "01e0577914",
"hash": "e2b90e95b6f492e640cd27c090d7072f0d03c8fc7382be67cbe176fc8f3fdd78b59f5f0b906198e09808fde645427f409cb9ab8fe4843de7f7dc5b510d454a0a",
"options": [
"SPIRV_WERROR OFF"
]
@ -150,16 +163,16 @@
"package": "SDL2",
"name": "SDL2",
"repo": "crueter-ci/SDL2",
"version": "2.32.10-cf5dabd6ea",
"version": "2.32.10-a65111bd2d",
"min_version": "2.26.4"
},
"catch2": {
"package": "Catch2",
"repo": "catchorg/Catch2",
"tag": "v%VERSION%",
"hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
"hash": "a95495142f915d6e9c2a23e80fe360343e9097680066a2f9d3037a070ba5f81ee5559a0407cc9e972dc2afae325873f1fc7ea07a64012c0f01aac6e549f03e3f",
"version": "3.0.1",
"git_version": "3.12.0",
"git_version": "3.11.0",
"patches": [
"0001-solaris-isnan-fix.patch"
]
@ -184,6 +197,7 @@
"repo": "libsdl-org/SDL",
"tag": "release-%VERSION%",
"hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383",
"key": "generic",
"bundled": true,
"git_version": "2.32.10",
"skip_updates": true
@ -193,6 +207,7 @@
"repo": "libsdl-org/SDL",
"sha": "cc016b0046",
"hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc",
"key": "steamdeck",
"bundled": true,
"skip_updates": "true"
},
@ -262,26 +277,5 @@
"tag": "%VERSION%",
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
"version": "121125"
},
"vulkan-headers": {
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"frozen": {
"package": "frozen",
"repo": "serge-sans-paille/frozen",
"sha": "61dce5ae18",
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c"
}
}

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@ -33,7 +33,7 @@ endif()
if(NOT YUZU_TZDB_PATH STREQUAL "")
set(NX_TZDB_BASE_DIR "${YUZU_TZDB_PATH}")
elseif (MSVC AND NOT CXX_CLANG AND ENABLE_LTO)
elseif (MSVC AND NOT CXX_CLANG AND YUZU_ENABLE_LTO)
# TODO(crueter): boot up the windows vm
set(NX_TZDB_VERSION "250725")
set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip")

View File

@ -20,4 +20,4 @@ pkgs.mkShellNoCC {
# optional components
discord-rpc gamemode
];
}
}

View File

@ -7,7 +7,7 @@
include_directories(.)
# Dynarmic
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64 AND NOT YUZU_STATIC_ROOM)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
@ -20,10 +20,6 @@ if (YUZU_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN)
endif()
if (NIGHTLY_BUILD)
add_compile_definitions(NIGHTLY_BUILD)
endif()
# Set compilation flags
if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
@ -205,29 +201,19 @@ else()
endif()
add_subdirectory(common)
add_subdirectory(network)
if (YUZU_ROOM)
add_subdirectory(dedicated_room)
endif()
if (YUZU_ROOM_STANDALONE)
add_subdirectory(yuzu_room_standalone)
set_target_properties(yuzu-room PROPERTIES OUTPUT_NAME "eden-room")
endif()
if (YUZU_STATIC_ROOM)
return()
endif()
add_subdirectory(core)
add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(hid_core)
add_subdirectory(network)
add_subdirectory(input_common)
add_subdirectory(frontend_common)
add_subdirectory(shader_recompiler)
if (YUZU_ROOM)
add_subdirectory(dedicated_room)
endif()
if (YUZU_TESTS)
add_subdirectory(tests)
endif()
@ -237,6 +223,11 @@ if (ENABLE_SDL2 AND YUZU_CMD)
set_target_properties(yuzu-cmd PROPERTIES OUTPUT_NAME "eden-cli")
endif()
if (YUZU_ROOM_STANDALONE)
add_subdirectory(yuzu_room_standalone)
set_target_properties(yuzu-room PROPERTIES OUTPUT_NAME "eden-room")
endif()
if (ENABLE_QT)
add_definitions(-DYUZU_QT_WIDGETS)
add_subdirectory(qt_common)

View File

@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// import android.annotation.SuppressLint
import com.android.build.gradle.api.ApplicationVariant
import kotlin.collections.setOf
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
@ -38,9 +37,6 @@ android {
compileSdkVersion = "android-36"
ndkVersion = "28.2.13676358"
val isNightly =
providers.gradleProperty("nightly").orNull?.toBooleanStrictOrNull() ?: false
buildFeatures {
viewBinding = true
}
@ -75,7 +71,6 @@ android {
val extraCMakeArgs =
(project.findProperty("YUZU_ANDROID_ARGS") as String?)?.split("\\s+".toRegex())
?: emptyList()
arguments.addAll(
listOf(
"-DENABLE_QT=0", // Don't use QT
@ -94,13 +89,6 @@ android {
)
)
if (isNightly) {
arguments.addAll(listOf(
"-DENABLE_UPDATE_CHECKER=ON",
"-DNIGHTLY_BUILD=ON",
))
}
abiFilters("arm64-v8a")
}
}
@ -137,12 +125,7 @@ android {
signingConfigs.getByName("default")
}
if (isNightly) {
applicationIdSuffix = ".nightly"
manifestPlaceholders += mapOf("appNameSuffix" to " Nightly")
} else {
manifestPlaceholders += mapOf("appNameSuffix" to "")
}
manifestPlaceholders += mapOf("appNameSuffix" to "")
isMinifyEnabled = true
isDebuggable = false
@ -256,15 +239,6 @@ android {
path = file("${edenDir}/CMakeLists.txt")
}
}
productFlavors.all {
val currentName = manifestPlaceholders["appNameBase"] as? String ?: "Eden"
val suffix = if (isNightly) " Nightly" else ""
// apply nightly suffix I/A
resValue("string", "app_name_suffixed", "$currentName$suffix")
resValue("string", "app_name", "Eden$suffix")
}
}
idea {
@ -284,7 +258,7 @@ tasks.register<Delete>("ktlintReset", fun Delete.() {
val showFormatHelp = {
logger.lifecycle(
"If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
"codestyle fixes"
"codestyle fixes"
)
}
tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@ -203,24 +203,6 @@ object NativeLibrary {
external fun getDebugKnobAt(index: Int): Boolean
/**
* Set the current speed limit to the configured turbo speed.
*/
external fun setTurboSpeedLimit(enabled: Boolean)
/**
* Set the current speed limit to the configured slow speed.
*/
external fun setSlowSpeedLimit(enabled: Boolean)
/**
* Set the current speed limit to the configured standard speed.
*/
external fun setStandardSpeedLimit(enabled: Boolean)
external fun isTurboMode(): Boolean
external fun isSlowMode(): Boolean
/**
* Returns Vulkan driver version / API version / GPU model
*/
@ -236,7 +218,7 @@ object NativeLibrary {
/**
* Checks for available updates.
*/
external fun checkForUpdate(): Array<String>?
external fun checkForUpdate(): String?
/**
* Return the URL to the release page
@ -246,18 +228,13 @@ object NativeLibrary {
/**
* Return the URL to download the APK for the given version
*/
external fun getUpdateApkUrl(tag: String, artifact: String, packageId: String): String
external fun getUpdateApkUrl(version: String, packageId: String): String
/**
* Returns whether the update checker is enabled through CMAKE options.
*/
external fun isUpdateCheckerEnabled(): Boolean
/**
* Returns whether or not this is a nightly build.
*/
external fun isNightlyBuild(): Boolean
/**
* Returns the build version generated by CMake (BUILD_VERSION).
*/
@ -632,23 +609,4 @@ object NativeLibrary {
* Updates the device power state to global variables
*/
external fun updatePowerState(percentage: Int, isCharging: Boolean, hasBattery: Boolean)
/**
* Profile manager native calls
*/
external fun getAllUsers(): Array<String>?
external fun getUserUsername(uuid: String): String?
external fun getUserCount(): Long
external fun canCreateUser(): Boolean
external fun createUser(uuid: String, username: String): Boolean
external fun updateUserUsername(uuid: String, username: String): Boolean
external fun removeUser(uuid: String): Boolean
external fun getCurrentUser(): String?
external fun setCurrentUser(uuid: String): Boolean
external fun getUserImagePath(uuid: String): String?
external fun saveUserImage(uuid: String, imagePath: String): Boolean
external fun reloadProfiles()
external fun getFirmwareAvatarCount(): Int
external fun getFirmwareAvatarImage(index: Int): ByteArray?
external fun getDefaultAccountBackupJpeg(): ByteArray
}

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@ -10,7 +10,6 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.PatchType
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
@ -32,12 +31,7 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
binding.addonSwitch.isChecked = model.enabled
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
if (PatchType.from(model.type) == PatchType.Update && checked) {
addonViewModel.enableOnlyThisUpdate(model)
notifyDataSetChanged()
} else {
model.enabled = checked
}
model.enabled = checked
}
val deleteAction = {

View File

@ -1,55 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.graphics.Bitmap
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ItemFirmwareAvatarBinding
class FirmwareAvatarAdapter(
private val avatars: List<Bitmap>,
private val onAvatarSelected: (Bitmap) -> Unit
) : RecyclerView.Adapter<FirmwareAvatarAdapter.AvatarViewHolder>() {
private var selectedPosition = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AvatarViewHolder {
val binding = ItemFirmwareAvatarBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return AvatarViewHolder(binding)
}
override fun onBindViewHolder(holder: AvatarViewHolder, position: Int) {
holder.bind(avatars[position], position == selectedPosition)
}
override fun getItemCount(): Int = avatars.size
inner class AvatarViewHolder(
private val binding: ItemFirmwareAvatarBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(avatar: Bitmap, isSelected: Boolean) {
binding.imageAvatar.setImageBitmap(avatar)
binding.root.isChecked = isSelected
binding.root.setOnClickListener {
val previousSelected = selectedPosition
selectedPosition = bindingAdapterPosition
if (previousSelected != -1) {
notifyItemChanged(previousSelected)
}
notifyItemChanged(selectedPosition)
onAvatarSelected(avatar)
}
}
}
}

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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,10 +7,8 @@ import android.net.Uri
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.DirectoryType
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
@ -36,12 +31,6 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
path.text = Uri.parse(model.uriString).path
path.marquee()
// Set type indicator, shows below folder name, to see if DLC or Games
typeIndicator.text = when (model.type) {
DirectoryType.GAME -> activity.getString(R.string.games)
DirectoryType.EXTERNAL_CONTENT -> activity.getString(R.string.external_content)
}
buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(model)
.show(

View File

@ -1,112 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.graphics.BitmapFactory
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemProfileBinding
import org.yuzu.yuzu_emu.model.UserProfile
import java.io.File
import org.yuzu.yuzu_emu.NativeLibrary
class ProfileAdapter(
private val onProfileClick: (UserProfile) -> Unit,
private val onEditClick: (UserProfile) -> Unit,
private val onDeleteClick: (UserProfile) -> Unit
) : RecyclerView.Adapter<ProfileAdapter.ProfileViewHolder>() {
private var currentUserUUID: String = ""
private val differ = AsyncListDiffer(this, object : DiffUtil.ItemCallback<UserProfile>() {
override fun areItemsTheSame(oldItem: UserProfile, newItem: UserProfile): Boolean {
return oldItem.uuid == newItem.uuid
}
override fun areContentsTheSame(oldItem: UserProfile, newItem: UserProfile): Boolean {
return oldItem == newItem
}
})
fun submitList(list: List<UserProfile>) {
differ.submitList(list)
}
fun setCurrentUser(uuid: String) {
currentUserUUID = uuid
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder {
val binding = ListItemProfileBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ProfileViewHolder(binding)
}
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
holder.bind(differ.currentList[position])
}
override fun getItemCount(): Int = differ.currentList.size
inner class ProfileViewHolder(private val binding: ListItemProfileBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(profile: UserProfile) {
binding.textUsername.text = profile.username
binding.textUuid.text = formatUUID(profile.uuid)
val imageFile = File(profile.imagePath)
if (imageFile.exists()) {
val bitmap = BitmapFactory.decodeFile(profile.imagePath)
binding.imageAvatar.setImageBitmap(bitmap)
} else {
val jpegData = NativeLibrary.getDefaultAccountBackupJpeg()
val bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.size)
binding.imageAvatar.setImageBitmap(bitmap)
}
if (profile.uuid == currentUserUUID) {
binding.checkContainer.visibility = View.VISIBLE
} else {
binding.checkContainer.visibility = View.GONE
}
binding.root.setOnClickListener {
onProfileClick(profile)
}
binding.buttonEdit.setOnClickListener {
onEditClick(profile)
}
binding.buttonDelete.setOnClickListener {
onDeleteClick(profile)
}
}
private fun formatUUID(uuid: String): String {
if (uuid.length != 32) return uuid
return buildString {
append(uuid.substring(0, 8))
append("-")
append(uuid.substring(8, 12))
append("-")
append(uuid.substring(12, 16))
append("-")
append(uuid.substring(16, 20))
append("-")
append(uuid.substring(20, 32))
}
}
}
}

View File

@ -11,7 +11,6 @@ import android.widget.RadioGroup
import android.widget.TextView
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.color.MaterialColors
import com.google.android.material.materialswitch.MaterialSwitch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@ -23,6 +22,14 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
class QuickSettings(val emulationFragment: EmulationFragment) {
// Kinda a crappy workaround to get a title from setting keys
// Idk how to do this witthout hardcoding every single one
private fun getSettingTitle(settingKey: String): String {
return settingKey.replace("_", " ").split(" ")
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
}
private fun saveSettings() {
if (emulationFragment.shouldUseCustom) {
NativeConfig.savePerGameConfig()
@ -53,7 +60,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
// settings
fun addIntSetting(
name: Int,
container: ViewGroup,
setting: IntSetting,
namesArrayId: Int,
@ -67,7 +73,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon)
val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group)
titleView.text = YuzuApplication.appContext.getString(name)
titleView.text = getSettingTitle(setting.key)
val names = emulationFragment.resources.getStringArray(namesArrayId)
val values = emulationFragment.resources.getIntArray(valuesArrayId)
@ -109,8 +115,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
}
fun addBooleanSetting(
name: Int,
container: ViewGroup,
setting: BooleanSetting
) {
@ -121,7 +125,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val titleView = itemView.findViewById<TextView>(R.id.switch_title)
val switchView = itemView.findViewById<com.google.android.material.materialswitch.MaterialSwitch>(R.id.setting_switch)
titleView.text = YuzuApplication.appContext.getString(name)
titleView.text = getSettingTitle(setting.key)
switchContainer.visibility = View.VISIBLE
switchView.isChecked = setting.getBoolean()
@ -136,41 +140,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
container.addView(itemView)
}
fun addCustomToggle(
name: Int,
isChecked: Boolean,
isEnabled: Boolean,
container: ViewGroup,
callback: (Boolean) -> Unit
): MaterialSwitch? {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
val switchContainer = itemView.findViewById<ViewGroup>(R.id.switch_container)
val titleView = itemView.findViewById<TextView>(R.id.switch_title)
val switchView = itemView.findViewById<MaterialSwitch>(R.id.setting_switch)
titleView.text = YuzuApplication.appContext.getString(name)
switchContainer.visibility = View.VISIBLE
switchView.isChecked = isChecked
switchView.setOnCheckedChangeListener { _, checked ->
callback(checked)
saveSettings()
}
switchContainer.setOnClickListener {
switchView.toggle()
}
container.addView(itemView)
return switchView
}
fun addSliderSetting(
name: Int,
container: ViewGroup,
setting: AbstractSetting,
minValue: Int = 0,
@ -186,7 +156,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
val slider = itemView.findViewById<com.google.android.material.slider.Slider>(R.id.setting_slider)
titleView.text = YuzuApplication.appContext.getString(name)
titleView.text = getSettingTitle(setting.key)
sliderContainer.visibility = View.VISIBLE
slider.valueFrom = minValue.toFloat()

View File

@ -24,7 +24,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"),
@ -33,14 +32,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
RENDERER_SAMPLE_SHADING("sample_shading"),
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"),
BLACK_BACKGROUNDS("black_backgrounds"),
ENABLE_FOLDER_BUTTON("enable_folder_button"),
ENABLE_QLAUNCH_BUTTON("enable_qlaunch_button"),
ENABLE_UPDATE_CHECKS("enable_update_checks"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"),
@ -74,17 +69,11 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_SHADERS_BUILDING("show_shaders_building"),
DEBUG_FLUSH_BY_LINE("flush_line"),
USE_LRU_CACHE("use_lru_cache"),
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
ENABLE_OVERLAY("enable_overlay"),
ENABLE_OVERLAY("enable_overlay");
// GPU Logging
GPU_LOGGING_ENABLED("gpu_logging_enabled"),
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
ENABLE_QUICK_SETTINGS("enable_quick_settings");
// external fun isFrameSkippingEnabled(): Boolean
external fun isFrameInterpolationEnabled(): Boolean

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
@ -9,8 +9,7 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class ByteSetting(override val key: String) : AbstractByteSetting {
AUDIO_VOLUME("volume"),
GPU_LOG_LEVEL("gpu_log_level");
AUDIO_VOLUME("volume"),;
override fun getByte(needsGlobal: Boolean): Byte = NativeConfig.getByte(key, needsGlobal)

View File

@ -29,7 +29,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_DYNA_STATE("dyna_state"),
DMA_ACCURACY("dma_accuracy"),
FRAME_PACING_MODE("frame_pacing_mode"),
AUDIO_OUTPUT_ENGINE("output_engine"),
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),
@ -68,8 +67,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
MY_PAGE_APPLET("my_page_applet_mode"),
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
OVERLAY_GRID_SIZE("overlay_grid_size"),
DEBUG_KNOBS("debug_knobs"),
GPU_LOG_RING_BUFFER_SIZE("gpu_log_ring_buffer_size")
DEBUG_KNOBS("debug_knobs")
;
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@ -9,10 +6,7 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class ShortSetting(override val key: String) : AbstractShortSetting {
RENDERER_SPEED_LIMIT("speed_limit"),
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
;
RENDERER_SPEED_LIMIT("speed_limit");
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)

View File

@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.ArrayRes
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
class GpuUnswizzleSetting(
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = "",
@ArrayRes val textureSizeChoicesId: Int,
@ArrayRes val textureSizeValuesId: Int,
@ArrayRes val streamSizeChoicesId: Int,
@ArrayRes val streamSizeValuesId: Int,
@ArrayRes val chunkSizeChoicesId: Int,
@ArrayRes val chunkSizeValuesId: Int
) : SettingsItem(
object : AbstractSetting {
override val key: String = SettingsItem.GPU_UNSWIZZLE_COMBINED
override val defaultValue: Any = false
override val isSaveable = true
override val isRuntimeModifiable = true
override val isSwitchable = true
override fun getValueAsString(needsGlobal: Boolean): String = "combined"
override fun reset() {
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset()
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset()
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset()
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset()
}
},
titleId,
titleString,
descriptionId,
descriptionString
) {
override val type = SettingsItem.TYPE_GPU_UNSWIZZLE
// Check if GPU unswizzle is enabled via the dedicated boolean setting
fun isEnabled(needsGlobal: Boolean = false): Boolean =
BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal)
fun setEnabled(value: Boolean) =
BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value)
fun enable() = setEnabled(true)
fun disable() = setEnabled(false)
fun getTextureSize(needsGlobal: Boolean = false): Int =
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal)
fun setTextureSize(value: Int) =
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value)
fun getStreamSize(needsGlobal: Boolean = false): Int =
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal)
fun setStreamSize(value: Int) =
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value)
fun getChunkSize(needsGlobal: Boolean = false): Int =
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal)
fun setChunkSize(value: Int) =
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value)
fun reset() = setting.reset()
}

View File

@ -60,11 +60,6 @@ abstract class SettingsItem(
return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld
}
// Can't edit enable_qlaunch_button if firmware is not available
if (setting.key == BooleanSetting.ENABLE_QLAUNCH_BUTTON.key) {
return NativeLibrary.isFirmwareAvailable()
}
// Can't edit settings that aren't saveable in per-game config even if they are switchable
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
return false
@ -104,10 +99,8 @@ abstract class SettingsItem(
const val TYPE_SPINBOX = 12
const val TYPE_LAUNCHABLE = 13
const val TYPE_PATH = 14
const val TYPE_GPU_UNSWIZZLE = 15
const val FASTMEM_COMBINED = "fastmem_combined"
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
val emptySetting = object : AbstractSetting {
override val key: String = ""
@ -125,6 +118,13 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
SwitchSetting(
BooleanSetting.USE_LRU_CACHE,
titleId = R.string.use_lru_cache,
descriptionId = R.string.use_lru_cache_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
@ -180,26 +180,6 @@ abstract class SettingsItem(
units = "%"
)
)
put(
SliderSetting(
ShortSetting.RENDERER_TURBO_SPEED_LIMIT,
titleId = R.string.turbo_speed_limit,
descriptionId = R.string.turbo_speed_limit_description,
min = 1,
max = 400,
units = "%"
)
)
put(
SliderSetting(
ShortSetting.RENDERER_SLOW_SPEED_LIMIT,
titleId = R.string.slow_speed_limit,
descriptionId = R.string.slow_speed_limit_description,
min = 1,
max = 400,
units = "%"
)
)
put(
SingleChoiceSetting(
IntSetting.CPU_BACKEND,
@ -663,15 +643,6 @@ abstract class SettingsItem(
valuesId = R.array.dmaAccuracyValues
)
)
put(
SingleChoiceSetting(
IntSetting.FRAME_PACING_MODE,
titleId = R.string.frame_pacing_mode,
descriptionId = R.string.frame_pacing_mode_description,
choicesId = R.array.framePacingModeNames,
valuesId = R.array.framePacingModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
@ -715,18 +686,6 @@ abstract class SettingsItem(
valuesId = R.array.gpuSwizzleChunkValues
)
)
put(
GpuUnswizzleSetting(
titleId = R.string.gpu_unswizzle_settings,
descriptionId = R.string.gpu_unswizzle_settings_description,
textureSizeChoicesId = R.array.gpuTextureSizeSwizzleEntries,
textureSizeValuesId = R.array.gpuTextureSizeSwizzleValues,
streamSizeChoicesId = R.array.gpuSwizzleEntries,
streamSizeValuesId = R.array.gpuSwizzleValues,
chunkSizeChoicesId = R.array.gpuSwizzleChunkEntries,
chunkSizeValuesId = R.array.gpuSwizzleChunkValues
)
)
put(
SingleChoiceSetting(
IntSetting.FAST_CPU_TIME,
@ -781,13 +740,6 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_reactive_flushing_description
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_BUFFER_HISTORY,
titleId = R.string.enable_buffer_history,
descriptionId = R.string.enable_buffer_history_description
)
)
put(
SwitchSetting(
BooleanSetting.SYNC_MEMORY_OPERATIONS,
@ -842,27 +794,6 @@ abstract class SettingsItem(
descriptionId = R.string.enable_update_checks_description,
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_QUICK_SETTINGS,
titleId = R.string.enable_quick_settings,
descriptionId = R.string.enable_quick_settings_description,
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_FOLDER_BUTTON,
titleId = R.string.enable_folder_button,
descriptionId = R.string.enable_folder_button_description,
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_QLAUNCH_BUTTON,
titleId = R.string.enable_qlaunch_button,
descriptionId = R.string.enable_qlaunch_button_description,
)
)
put(
SingleChoiceSetting(
IntSetting.APP_LANGUAGE,
@ -905,62 +836,6 @@ abstract class SettingsItem(
)
)
// GPU Logging settings
put(
SwitchSetting(
BooleanSetting.GPU_LOGGING_ENABLED,
titleId = R.string.gpu_logging_enabled,
descriptionId = R.string.gpu_logging_enabled_description
)
)
put(
SingleChoiceSetting(
ByteSetting.GPU_LOG_LEVEL,
titleId = R.string.gpu_log_level,
descriptionId = R.string.gpu_log_level_description,
choicesId = R.array.gpuLogLevelEntries,
valuesId = R.array.gpuLogLevelValues
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_VULKAN_CALLS,
titleId = R.string.gpu_log_vulkan_calls,
descriptionId = R.string.gpu_log_vulkan_calls_description
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_SHADER_DUMPS,
titleId = R.string.gpu_log_shader_dumps,
descriptionId = R.string.gpu_log_shader_dumps_description
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_MEMORY_TRACKING,
titleId = R.string.gpu_log_memory_tracking,
descriptionId = R.string.gpu_log_memory_tracking_description
)
)
put(
SwitchSetting(
BooleanSetting.GPU_LOG_DRIVER_DEBUG,
titleId = R.string.gpu_log_driver_debug,
descriptionId = R.string.gpu_log_driver_debug_description
)
)
put(
SpinBoxSetting(
IntSetting.GPU_LOG_RING_BUFFER_SIZE,
titleId = R.string.gpu_log_ring_buffer_size,
descriptionId = R.string.gpu_log_ring_buffer_size_description,
valueHint = R.string.gpu_log_ring_buffer_size_hint,
min = 64,
max = 4096
)
)
val fastmem = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
BooleanSetting.FASTMEM.getBoolean() &&

View File

@ -1,206 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.ArrayAdapter
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
class GpuUnswizzleDialogFragment : DialogFragment() {
private var position = 0
private val settingsViewModel: SettingsViewModel by activityViewModels()
private lateinit var binding: DialogGpuUnswizzleBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
position = requireArguments().getInt(POSITION)
if (settingsViewModel.clickedItem == null) dismiss()
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext()))
val item = settingsViewModel.clickedItem as GpuUnswizzleSetting
// Setup texture size dropdown
val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId)
val textureSizeValues = resources.getIntArray(item.textureSizeValuesId)
val textureSizeAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
textureSizeEntries.toMutableList()
)
binding.dropdownTextureSize.setAdapter(textureSizeAdapter)
// Setup stream size dropdown
val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId)
val streamSizeValues = resources.getIntArray(item.streamSizeValuesId)
val streamSizeAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
streamSizeEntries.toMutableList()
)
binding.dropdownStreamSize.setAdapter(streamSizeAdapter)
// Setup chunk size dropdown
val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId)
val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId)
val chunkSizeAdapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
chunkSizeEntries.toMutableList()
)
binding.dropdownChunkSize.setAdapter(chunkSizeAdapter)
// Load current values
val isEnabled = item.isEnabled()
binding.switchEnable.isChecked = isEnabled
if (isEnabled) {
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
if (textureSizeIndex >= 0) {
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
}
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
if (streamSizeIndex >= 0) {
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
}
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
if (chunkSizeIndex >= 0) {
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
}
} else {
// Set default/recommended values when disabling
binding.dropdownTextureSize.setText(textureSizeEntries[3], false)
binding.dropdownStreamSize.setText(streamSizeEntries[3], false)
binding.dropdownChunkSize.setText(chunkSizeEntries[3], false)
}
// Clear adapter filters after setText to fix rotation bug
textureSizeAdapter.filter.filter(null)
streamSizeAdapter.filter.filter(null)
chunkSizeAdapter.filter.filter(null)
// Enable/disable dropdowns based on switch state
updateDropdownsState(isEnabled)
binding.switchEnable.setOnCheckedChangeListener { _, checked ->
updateDropdownsState(checked)
}
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(item.title)
.setView(binding.root)
.create()
// Setup button listeners
binding.btnDefault.setOnClickListener {
// Reset to defaults
item.reset()
// Refresh values with adapters reset
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
if (textureSizeIndex >= 0) {
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
}
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
if (streamSizeIndex >= 0) {
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
}
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
if (chunkSizeIndex >= 0) {
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
}
// Clear filters
textureSizeAdapter.filter.filter(null)
streamSizeAdapter.filter.filter(null)
chunkSizeAdapter.filter.filter(null)
settingsViewModel.setAdapterItemChanged(position)
settingsViewModel.setShouldReloadSettingsList(true)
}
binding.btnCancel.setOnClickListener {
dialog.dismiss()
}
binding.btnOk.setOnClickListener {
if (binding.switchEnable.isChecked) {
item.enable()
// Save the selected values
val selectedTextureIndex = textureSizeEntries.indexOf(
binding.dropdownTextureSize.text.toString()
)
if (selectedTextureIndex >= 0) {
item.setTextureSize(textureSizeValues[selectedTextureIndex])
}
val selectedStreamIndex = streamSizeEntries.indexOf(
binding.dropdownStreamSize.text.toString()
)
if (selectedStreamIndex >= 0) {
item.setStreamSize(streamSizeValues[selectedStreamIndex])
}
val selectedChunkIndex = chunkSizeEntries.indexOf(
binding.dropdownChunkSize.text.toString()
)
if (selectedChunkIndex >= 0) {
item.setChunkSize(chunkSizeValues[selectedChunkIndex])
}
} else {
// Disable GPU unswizzle
item.disable()
}
settingsViewModel.setAdapterItemChanged(position)
settingsViewModel.setShouldReloadSettingsList(true)
dialog.dismiss()
}
// Ensure filters are cleared after dialog is shown
binding.root.post {
textureSizeAdapter.filter.filter(null)
streamSizeAdapter.filter.filter(null)
chunkSizeAdapter.filter.filter(null)
}
return dialog
}
private fun updateDropdownsState(enabled: Boolean) {
binding.layoutTextureSize.isEnabled = enabled
binding.dropdownTextureSize.isEnabled = enabled
binding.layoutStreamSize.isEnabled = enabled
binding.dropdownStreamSize.isEnabled = enabled
binding.layoutChunkSize.isEnabled = enabled
binding.dropdownChunkSize.isEnabled = enabled
}
companion object {
const val TAG = "GpuUnswizzleDialogFragment"
const val POSITION = "Position"
fun newInstance(
settingsViewModel: SettingsViewModel,
item: GpuUnswizzleSetting,
position: Int
): GpuUnswizzleDialogFragment {
val dialog = GpuUnswizzleDialogFragment()
val args = Bundle()
args.putInt(POSITION, position)
dialog.arguments = args
settingsViewModel.clickedItem = item
return dialog
}
}
}

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
@ -101,11 +101,6 @@ class SettingsAdapter(
SettingsItem.TYPE_PATH -> {
PathViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_GPU_UNSWIZZLE -> {
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@ -479,14 +474,6 @@ class SettingsAdapter(
settingsViewModel.setShouldShowPathResetDialog(true)
}
fun onGpuUnswizzleClick(item: GpuUnswizzleSetting, position: Int) {
GpuUnswizzleDialogFragment.newInstance(
settingsViewModel,
item,
position
).show(fragment.childFragmentManager, GpuUnswizzleDialogFragment.TAG)
}
private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
return oldItem.setting.key == newItem.setting.key

View File

@ -227,8 +227,6 @@ class SettingsFragmentPresenter(
add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_TURBO_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SLOW_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
add(IntSetting.REGION_INDEX.key)
add(IntSetting.LANGUAGE_INDEX.key)
@ -237,6 +235,7 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.cpu))
add(IntSetting.FAST_CPU_TIME.key)
add(BooleanSetting.USE_LRU_CACHE.key)
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
add(IntSetting.MEMORY_LAYOUT.key)
@ -267,7 +266,6 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_ACCURACY.key)
add(IntSetting.DMA_ACCURACY.key)
add(IntSetting.FRAME_PACING_MODE.key)
add(IntSetting.MAX_ANISOTROPY.key)
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
@ -277,7 +275,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(HeaderSetting(R.string.hacks))
@ -285,7 +282,9 @@ class SettingsFragmentPresenter(
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
add(IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.key)
add(IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.key)
add(IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.key)
add(HeaderSetting(R.string.extensions))
@ -1075,8 +1074,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
}
add(BooleanSetting.ENABLE_QUICK_SETTINGS.key)
add(HeaderSetting(R.string.theme_and_color))
@ -1194,13 +1191,6 @@ class SettingsFragmentPresenter(
descriptionId = R.string.use_black_backgrounds_description
)
)
add(HeaderSetting(R.string.buttons))
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)
if (!NativeLibrary.isFirmwareAvailable()) {
BooleanSetting.ENABLE_QLAUNCH_BUTTON.setBoolean(false)
}
}
}
@ -1230,15 +1220,6 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.general))
add(IntSetting.DEBUG_KNOBS.key)
add(HeaderSetting(R.string.gpu_logging_header))
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
add(ByteSetting.GPU_LOG_LEVEL.key)
add(BooleanSetting.GPU_LOG_VULKAN_CALLS.key)
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
}
}

View File

@ -1,71 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: GpuUnswizzleSetting
override fun bind(item: SettingsItem) {
setting = item as GpuUnswizzleSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
binding.textSettingDescription.text = item.description
binding.textSettingValue.setVisible(true)
val resMgr = binding.root.context.resources
if (setting.isEnabled()) {
// Show a summary of current settings
val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId)
val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId)
val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize())
val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?"
val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId)
val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId)
val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize())
val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?"
val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId)
val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId)
val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize())
val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?"
binding.textSettingValue.text = "$textureSizeLabel$streamSizeLabel$chunkSizeLabel"
} else {
binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled)
}
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
setStyle(setting.isEditable, binding)
}
override fun onClick(clicked: View) {
if (!setting.isEditable) {
return
}
adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition)
}
override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) {
return adapter.onLongClick(setting, bindingAdapterPosition)
}
return false
}
}

View File

@ -1,459 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.FirmwareAvatarAdapter
import org.yuzu.yuzu_emu.databinding.FragmentEditUserDialogBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.ProfileUtils
import org.yuzu.yuzu_emu.model.UserProfile
import java.io.File
import java.io.FileOutputStream
import androidx.core.graphics.scale
import androidx.core.graphics.createBitmap
class EditUserDialogFragment : Fragment() {
private var _binding: FragmentEditUserDialogBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private var currentUUID: String = ""
private var isEditMode = false
private var selectedImageUri: Uri? = null
private var selectedFirmwareAvatar: Bitmap? = null
private var hasCustomImage = false
private var revertedToDefault = false
companion object {
private const val ARG_UUID = "uuid"
private const val ARG_USERNAME = "username"
fun newInstance(profile: UserProfile?): EditUserDialogFragment {
val fragment = EditUserDialogFragment()
profile?.let {
val args = Bundle()
args.putString(ARG_UUID, it.uuid)
args.putString(ARG_USERNAME, it.username)
fragment.arguments = args
}
return fragment
}
}
private val imagePickerLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
selectedImageUri = uri
loadImage(uri)
hasCustomImage = true
binding.buttonRevertImage.visibility = View.VISIBLE
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentEditUserDialogBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
val existingUUID = arguments?.getString(ARG_UUID)
val existingUsername = arguments?.getString(ARG_USERNAME)
if (existingUUID != null && existingUsername != null) {
isEditMode = true
currentUUID = existingUUID
binding.toolbarNewUser.title = getString(R.string.profile_edit_user)
binding.editUsername.setText(existingUsername)
binding.textUuid.text = formatUUID(existingUUID)
binding.buttonGenerateUuid.visibility = View.GONE
val imagePath = NativeLibrary.getUserImagePath(existingUUID)
val imageFile = File(imagePath)
if (imageFile.exists()) {
val bitmap = BitmapFactory.decodeFile(imagePath)
binding.imageUserAvatar.setImageBitmap(bitmap)
hasCustomImage = true
binding.buttonRevertImage.visibility = View.VISIBLE
} else {
loadDefaultAvatar()
}
} else {
isEditMode = false
currentUUID = ProfileUtils.generateRandomUUID()
binding.toolbarNewUser.title = getString(R.string.profile_new_user)
binding.textUuid.text = formatUUID(currentUUID)
loadDefaultAvatar()
}
binding.toolbarNewUser.setNavigationOnClickListener {
findNavController().popBackStack()
}
binding.editUsername.doAfterTextChanged {
validateInput()
}
binding.buttonGenerateUuid.setOnClickListener {
currentUUID = ProfileUtils.generateRandomUUID()
binding.textUuid.text = formatUUID(currentUUID)
}
binding.buttonSelectImage.setOnClickListener {
selectImage()
}
binding.buttonRevertImage.setOnClickListener {
revertToDefaultImage()
}
if (NativeLibrary.isFirmwareAvailable()) {
binding.buttonFirmwareAvatars.visibility = View.VISIBLE
binding.buttonFirmwareAvatars.setOnClickListener {
showFirmwareAvatarPicker()
}
}
binding.buttonSave.setOnClickListener {
saveUser()
}
binding.buttonCancel.setOnClickListener {
findNavController().popBackStack()
}
validateInput()
setInsets()
}
private fun showFirmwareAvatarPicker() {
val dialogView = LayoutInflater.from(requireContext())
.inflate(R.layout.dialog_firmware_avatar_picker, null)
val gridAvatars = dialogView.findViewById<RecyclerView>(R.id.grid_avatars)
val progressLoading = dialogView.findViewById<View>(R.id.progress_loading)
val textEmpty = dialogView.findViewById<View>(R.id.text_empty)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.profile_firmware_avatars)
.setView(dialogView)
.setNegativeButton(android.R.string.cancel, null)
.create()
dialog.show()
viewLifecycleOwner.lifecycleScope.launch {
val avatars = withContext(Dispatchers.IO) {
loadFirmwareAvatars()
}
if (avatars.isEmpty()) {
progressLoading.visibility = View.GONE
textEmpty.visibility = View.VISIBLE
} else {
progressLoading.visibility = View.GONE
gridAvatars.visibility = View.VISIBLE
val adapter = FirmwareAvatarAdapter(avatars) { selectedAvatar ->
val scaledBitmap = selectedAvatar.scale(256, 256)
binding.imageUserAvatar.setImageBitmap(scaledBitmap)
selectedFirmwareAvatar = scaledBitmap
hasCustomImage = true
binding.buttonRevertImage.visibility = View.VISIBLE
dialog.dismiss()
}
gridAvatars.apply {
layoutManager = GridLayoutManager(requireContext(), 4)
this.adapter = adapter
}
}
}
}
private fun loadFirmwareAvatars(): List<Bitmap> {
val avatars = mutableListOf<Bitmap>()
val count = NativeLibrary.getFirmwareAvatarCount()
for (i in 0 until count) {
try {
val imageData = NativeLibrary.getFirmwareAvatarImage(i) ?: continue
val argbData = IntArray(256 * 256)
for (pixel in 0 until 256 * 256) {
val offset = pixel * 4
val r = imageData[offset].toInt() and 0xFF
val g = imageData[offset + 1].toInt() and 0xFF
val b = imageData[offset + 2].toInt() and 0xFF
val a = imageData[offset + 3].toInt() and 0xFF
argbData[pixel] = (a shl 24) or (r shl 16) or (g shl 8) or b
}
val bitmap = Bitmap.createBitmap(argbData, 256, 256, Bitmap.Config.ARGB_8888)
avatars.add(bitmap)
} catch (e: Exception) {
continue
}
}
return avatars
}
private fun formatUUID(uuid: String): String {
if (uuid.length != 32) return uuid
return buildString {
append(uuid.substring(0, 8))
append("-")
append(uuid.substring(8, 12))
append("-")
append(uuid.substring(12, 16))
append("-")
append(uuid.substring(16, 20))
append("-")
append(uuid.substring(20, 32))
}
}
private fun validateInput() {
val username = binding.editUsername.text.toString()
val isValid = username.isNotEmpty() && username.length <= 32
binding.buttonSave.isEnabled = isValid
}
private fun selectImage() {
val intent = Intent(Intent.ACTION_PICK).apply {
type = "image/*"
}
imagePickerLauncher.launch(intent)
}
private fun loadImage(uri: Uri) {
try {
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val source = ImageDecoder.createSource(requireContext().contentResolver, uri)
ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
decoder.setTargetSampleSize(1)
}
} else {
@Suppress("DEPRECATION")
MediaStore.Images.Media.getBitmap(requireContext().contentResolver, uri)
}
val croppedBitmap = centerCropBitmap(bitmap, 256, 256)
binding.imageUserAvatar.setImageBitmap(croppedBitmap)
} catch (e: Exception) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.error)
.setMessage(getString(R.string.profile_image_load_error, e.message))
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
private fun loadDefaultAvatar() {
val jpegData = NativeLibrary.getDefaultAccountBackupJpeg()
val bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.size)
binding.imageUserAvatar.setImageBitmap(bitmap)
hasCustomImage = false
binding.buttonRevertImage.visibility = View.GONE
}
private fun revertToDefaultImage() {
selectedImageUri = null
selectedFirmwareAvatar = null
revertedToDefault = true
loadDefaultAvatar()
}
private fun saveUser() {
val username = binding.editUsername.text.toString()
if (isEditMode) {
if (NativeLibrary.updateUserUsername(currentUUID, username)) {
saveImageIfNeeded()
findNavController().popBackStack()
} else {
showError(getString(R.string.profile_update_failed))
}
} else {
if (NativeLibrary.createUser(currentUUID, username)) {
saveImageIfNeeded()
findNavController().popBackStack()
} else {
showError(getString(R.string.profile_create_failed))
}
}
}
private fun saveImageIfNeeded() {
if (revertedToDefault && isEditMode) {
val imagePath = NativeLibrary.getUserImagePath(currentUUID)
if (imagePath != null) {
val imageFile = File(imagePath)
if (imageFile.exists()) {
imageFile.delete()
}
}
return
}
if (!hasCustomImage) {
return
}
try {
val bitmapToSave: Bitmap? = when {
selectedFirmwareAvatar != null -> selectedFirmwareAvatar
selectedImageUri != null -> {
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val source = ImageDecoder.createSource(
requireContext().contentResolver,
selectedImageUri!!
)
ImageDecoder.decodeBitmap(source)
} else {
@Suppress("DEPRECATION")
MediaStore.Images.Media.getBitmap(
requireContext().contentResolver,
selectedImageUri
)
}
centerCropBitmap(bitmap, 256, 256)
}
else -> null
}
if (bitmapToSave == null) {
return
}
val tempFile = File(requireContext().cacheDir, "temp_avatar_${currentUUID}.jpg")
FileOutputStream(tempFile).use { out ->
bitmapToSave.compress(Bitmap.CompressFormat.JPEG, 100, out)
}
NativeLibrary.saveUserImage(currentUUID, tempFile.absolutePath)
tempFile.delete()
} catch (e: Exception) {
showError(getString(R.string.profile_image_save_error, e.message))
}
}
private fun centerCropBitmap(source: Bitmap, targetWidth: Int, targetHeight: Int): Bitmap {
val sourceWidth = source.width
val sourceHeight = source.height
val scale = maxOf(
targetWidth.toFloat() / sourceWidth,
targetHeight.toFloat() / sourceHeight
)
val scaledWidth = (sourceWidth * scale).toInt()
val scaledHeight = (sourceHeight * scale).toInt()
val scaledBitmap = source.scale(scaledWidth, scaledHeight)
val x = (scaledWidth - targetWidth) / 2
val y = (scaledHeight - targetHeight) / 2
return Bitmap.createBitmap(scaledBitmap, x, y, targetWidth, targetHeight)
}
private fun showError(message: String) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.error)
.setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInset = barInsets.left + cutoutInsets.left
val topInset = cutoutInsets.top
val rightInset = barInsets.right + cutoutInsets.right
val bottomInset = barInsets.bottom + cutoutInsets.bottom
binding.appbar.updatePadding(
left = leftInset,
top = topInset,
right = rightInset
)
binding.scrollContent.updatePadding(
left = leftInset,
right = rightInset
)
binding.buttonContainer.updatePadding(
left = leftInset,
right = rightInset,
bottom = bottomInset
)
windowInsets
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -55,7 +55,6 @@ import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
@ -691,18 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
})
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
if (!BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()) {
binding.drawerLayout.setDrawerLockMode(
DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
binding.quickSettingsSheet
)
}
updateGameTitle()
binding.inGameMenu.menu.findItem(R.id.menu_quick_settings)?.isVisible =
BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt()
val titleId = if (lockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
@ -760,11 +749,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
if (BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean())
R.id.menu_quick_settings else 0 -> {
openQuickSettingsMenu()
true
}
R.id.menu_quick_settings -> {
openQuickSettingsMenu()
true
}
R.id.menu_settings_per_game -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
@ -1056,50 +1044,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addPerGameConfigStatusIndicator(container)
}
lateinit var slowSpeed: MaterialSwitch
lateinit var turboSpeed: MaterialSwitch
turboSpeed = quickSettings.addCustomToggle(
R.string.turbo_speed_limit,
NativeLibrary.isTurboMode(),
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
container
) { enabled ->
if (enabled)
slowSpeed.isChecked = false
NativeLibrary.setTurboSpeedLimit(enabled)
}!!
slowSpeed = quickSettings.addCustomToggle(
R.string.slow_speed_limit,
NativeLibrary.isSlowMode(),
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
container
) { enabled ->
if (enabled)
turboSpeed.isChecked = false
NativeLibrary.setSlowSpeedLimit(enabled)
}!!
quickSettings.addCustomToggle(
R.string.frame_limit_enable,
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
true,
container
) { enabled ->
if (!enabled) {
turboSpeed.isChecked = false
slowSpeed.isChecked = false
}
turboSpeed.isEnabled = enabled
slowSpeed.isEnabled = enabled
NativeLibrary.setStandardSpeedLimit(enabled)
}!!
quickSettings.addBooleanSetting(
container,
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
)
quickSettings.addSliderSetting(
R.string.frame_limit_slider,
container,
ShortSetting.RENDERER_SPEED_LIMIT,
minValue = 0,
@ -1108,7 +1058,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
)
quickSettings.addBooleanSetting(
R.string.use_docked_mode,
container,
BooleanSetting.USE_DOCKED_MODE,
)
@ -1116,7 +1065,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container)
quickSettings.addIntSetting(
R.string.renderer_accuracy,
container,
IntSetting.RENDERER_ACCURACY,
R.array.rendererAccuracyNames,
@ -1125,7 +1073,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addIntSetting(
R.string.renderer_scaling_filter,
container,
IntSetting.RENDERER_SCALING_FILTER,
R.array.rendererScalingFilterNames,
@ -1133,7 +1080,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
)
quickSettings.addSliderSetting(
R.string.fsr_sharpness,
container,
IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0,
@ -1142,7 +1088,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
)
quickSettings.addIntSetting(
R.string.renderer_anti_aliasing,
container,
IntSetting.RENDERER_ANTI_ALIASING,
R.array.rendererAntiAliasingNames,

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@ -110,7 +110,7 @@ class FreedrenoSettingsFragment : Fragment() {
val commonVars = listOf(
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
"TU_BREADCRUMBS", "FD_DEV_FEATURES"
"TU_BREADCRUMBS"
)
for (varName in commonVars) {

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@ -9,13 +6,11 @@ package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding
import org.yuzu.yuzu_emu.model.DirectoryType
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.NativeConfig
@ -30,18 +25,14 @@ class GameFolderPropertiesDialogFragment : DialogFragment() {
val binding = DialogFolderPropertiesBinding.inflate(layoutInflater)
val gameDir = requireArguments().parcelable<GameDir>(GAME_DIR)!!
// Hide deepScan for external content, do automatically
if (gameDir.type == DirectoryType.EXTERNAL_CONTENT) {
binding.deepScanSwitch.visibility = View.GONE
} else {
// Restore checkbox state for game dirs
binding.deepScanSwitch.isChecked =
savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
// Restore checkbox state
binding.deepScanSwitch.isChecked =
savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
// Ensure that we can get the checkbox state even if the view is destroyed
deepScan = binding.deepScanSwitch.isChecked
binding.deepScanSwitch.setOnClickListener {
deepScan = binding.deepScanSwitch.isChecked
binding.deepScanSwitch.setOnClickListener {
deepScan = binding.deepScanSwitch.isChecked
}
}
return MaterialAlertDialogBuilder(requireContext())
@ -50,10 +41,8 @@ class GameFolderPropertiesDialogFragment : DialogFragment() {
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val folderIndex = gamesViewModel.folders.value.indexOf(gameDir)
if (folderIndex != -1) {
if (gameDir.type == DirectoryType.GAME) {
gamesViewModel.folders.value[folderIndex].deepScan =
binding.deepScanSwitch.isChecked
}
gamesViewModel.folders.value[folderIndex].deepScan =
binding.deepScanSwitch.isChecked
gamesViewModel.updateGameDirs()
}
}

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@ -15,14 +15,11 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.FolderAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
import org.yuzu.yuzu_emu.model.DirectoryType
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
@ -76,25 +73,7 @@ class GameFoldersFragment : Fragment() {
val mainActivity = requireActivity() as MainActivity
binding.buttonAdd.setOnClickListener {
// Show a model to choose between Game and External Content
val options = arrayOf(
getString(R.string.games),
getString(R.string.external_content)
)
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.add_folders)
.setItems(options) { _, which ->
when (which) {
0 -> { // Game Folder
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
}
1 -> { // External Content Folder
mainActivity.getExternalContentDirectory.launch(null)
}
}
}
.show()
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
}
setInsets()

View File

@ -1,7 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@ -117,17 +114,6 @@ class HomeSettingsFragment : Fragment() {
}
)
)
add(
HomeSetting(
R.string.profile_manager,
R.string.profile_manager_description,
R.drawable.ic_account_circle,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_profileManagerFragment)
}
)
)
add(
HomeSetting(
R.string.gpu_driver_manager,
@ -236,14 +222,6 @@ class HomeSettingsFragment : Fragment() {
{ shareLog() }
)
)
add(
HomeSetting(
R.string.share_gpu_log,
R.string.share_gpu_log_description,
R.drawable.ic_log,
{ shareGpuLog() }
)
)
add(
HomeSetting(
R.string.open_user_folder,
@ -430,40 +408,6 @@ class HomeSettingsFragment : Fragment() {
}
}
private fun shareGpuLog() {
val currentLog = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/eden_gpu.log"
)
)!!
val oldLog = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/eden_gpu.log.old.txt"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (!Log.gameLaunched && oldLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_gpu_log)))
} else if (currentLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_gpu_log)))
} else {
Toast.makeText(
requireContext(),
getText(R.string.share_gpu_log_missing),
Toast.LENGTH_SHORT
).show()
}
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view, windowInsets ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())

View File

@ -1,190 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.ProfileAdapter
import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.UserProfile
import org.yuzu.yuzu_emu.utils.NativeConfig
class ProfileManagerFragment : Fragment() {
private var _binding: FragmentProfileManagerBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private lateinit var profileAdapter: ProfileAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentProfileManagerBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarProfiles.setNavigationOnClickListener {
findNavController().popBackStack()
}
setupRecyclerView()
loadProfiles()
binding.buttonAddUser.setOnClickListener {
if (NativeLibrary.canCreateUser()) {
findNavController().navigate(R.id.action_profileManagerFragment_to_newUserDialog)
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.profile_max_users_title)
.setMessage(R.string.profile_max_users_message)
.setPositiveButton(android.R.string.ok, null)
.show()
}
}
setInsets()
}
override fun onResume() {
super.onResume()
loadProfiles()
}
private fun setupRecyclerView() {
profileAdapter = ProfileAdapter(
onProfileClick = { profile -> selectProfile(profile) },
onEditClick = { profile -> editProfile(profile) },
onDeleteClick = { profile -> confirmDeleteProfile(profile) }
)
binding.listProfiles.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = profileAdapter
}
}
private fun loadProfiles() {
val profiles = mutableListOf<UserProfile>()
val userUUIDs = NativeLibrary.getAllUsers() ?: emptyArray()
val currentUserUUID = NativeLibrary.getCurrentUser()
for (uuid in userUUIDs) {
if (uuid.isNotEmpty()) {
val username = NativeLibrary.getUserUsername(uuid)
if (!username.isNullOrEmpty()) {
val imagePath = NativeLibrary.getUserImagePath(uuid) ?: ""
profiles.add(UserProfile(uuid, username, imagePath))
}
}
}
profileAdapter.submitList(profiles)
profileAdapter.setCurrentUser(currentUserUUID ?: "")
binding.buttonAddUser.isEnabled = NativeLibrary.canCreateUser()
}
private fun selectProfile(profile: UserProfile) {
if (NativeLibrary.setCurrentUser(profile.uuid)) {
loadProfiles()
}
}
private fun editProfile(profile: UserProfile) {
val bundle = Bundle().apply {
putString("uuid", profile.uuid)
putString("username", profile.username)
}
findNavController().navigate(R.id.action_profileManagerFragment_to_newUserDialog, bundle)
}
private fun confirmDeleteProfile(profile: UserProfile) {
val currentUser = NativeLibrary.getCurrentUser()
val isCurrentUser = profile.uuid == currentUser
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.profile_delete_confirm_title)
.setMessage(
if (isCurrentUser) {
getString(R.string.profile_delete_current_user_message, profile.username)
} else {
getString(R.string.profile_delete_confirm_message, profile.username)
}
)
.setPositiveButton(R.string.profile_delete) { _, _ ->
deleteProfile(profile)
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
private fun deleteProfile(profile: UserProfile) {
val currentUser = NativeLibrary.getCurrentUser()
if (!currentUser.isNullOrEmpty() && profile.uuid == currentUser) {
val users = NativeLibrary.getAllUsers() ?: emptyArray()
for (uuid in users) {
if (uuid.isNotEmpty() && uuid != profile.uuid) {
NativeLibrary.setCurrentUser(uuid)
break
}
}
}
if (NativeLibrary.removeUser(profile.uuid)) {
loadProfiles()
}
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val fabLayoutParams = binding.buttonAddUser.layoutParams as ViewGroup.MarginLayoutParams
fabLayoutParams.leftMargin = leftInsets + 24
fabLayoutParams.rightMargin = rightInsets + 24
fabLayoutParams.bottomMargin = barInsets.bottom + 24
binding.buttonAddUser.layoutParams = fabLayoutParams
windowInsets
}
}
override fun onDestroyView() {
super.onDestroyView()
NativeConfig.saveGlobalConfig()
_binding = null
}
}

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@ -51,68 +48,16 @@ class AddonViewModel : ViewModel() {
?: emptyArray()
).toMutableList()
patchList.sortBy { it.name }
// Ensure only one update is enabled
ensureSingleUpdateEnabled(patchList)
removeDuplicates(patchList)
_patchList.value = patchList
isRefreshing.set(false)
}
}
}
private fun ensureSingleUpdateEnabled(patchList: MutableList<Patch>) {
val updates = patchList.filter { PatchType.from(it.type) == PatchType.Update }
if (updates.size <= 1) {
return
}
val enabledUpdates = updates.filter { it.enabled }
if (enabledUpdates.size > 1) {
val nandOrSdmcEnabled = enabledUpdates.find {
it.name.contains("(NAND)") || it.name.contains("(SDMC)")
}
val updateToKeep = nandOrSdmcEnabled ?: enabledUpdates.first()
for (patch in patchList) {
if (PatchType.from(patch.type) == PatchType.Update) {
patch.enabled = (patch === updateToKeep)
}
}
}
}
private fun removeDuplicates(patchList: MutableList<Patch>) {
val seen = mutableSetOf<String>()
val iterator = patchList.iterator()
while (iterator.hasNext()) {
val patch = iterator.next()
val key = "${patch.name}|${patch.version}|${patch.type}"
if (seen.contains(key)) {
iterator.remove()
} else {
seen.add(key)
}
}
}
fun setAddonToDelete(patch: Patch?) {
_addonToDelete.value = patch
}
fun enableOnlyThisUpdate(selectedPatch: Patch) {
val currentList = _patchList.value
for (patch in currentList) {
if (PatchType.from(patch.type) == PatchType.Update) {
patch.enabled = (patch === selectedPatch)
}
}
}
fun onDeleteAddon(patch: Patch) {
when (PatchType.from(patch.type)) {
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
@ -127,27 +72,13 @@ class AddonViewModel : ViewModel() {
return
}
// Check if there are multiple update versions
val updates = _patchList.value.filter { PatchType.from(it.type) == PatchType.Update }
val hasMultipleUpdates = updates.size > 1
NativeConfig.setDisabledAddons(
game!!.programId,
_patchList.value.mapNotNull {
if (it.enabled) {
null
} else {
if (PatchType.from(it.type) == PatchType.Update) {
if (it.name.contains("(NAND)") || it.name.contains("(SDMC)")) {
it.name
} else if (hasMultipleUpdates) {
"Update@${it.numericVersion}"
} else {
it.name
}
} else {
it.name
}
it.name
}
}.toTypedArray()
)

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@ -12,14 +9,5 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class GameDir(
val uriString: String,
var deepScan: Boolean,
val type: DirectoryType = DirectoryType.GAME
) : Parcelable {
// Needed for JNI backward compatability
constructor(uriString: String, deepScan: Boolean) : this(uriString, deepScan, DirectoryType.GAME)
}
enum class DirectoryType {
GAME,
EXTERNAL_CONTENT
}
var deepScan: Boolean
) : Parcelable

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
@ -56,7 +56,7 @@ class GamesViewModel : ViewModel() {
// Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys()
getGameDirsAndExternalContent()
getGameDirs()
reloadGames(directoriesChanged = false, firstStartup = true)
}
@ -144,19 +144,11 @@ class GamesViewModel : ViewModel() {
fun addFolder(gameDir: GameDir, savedFromGameFragment: Boolean) =
viewModelScope.launch {
withContext(Dispatchers.IO) {
when (gameDir.type) {
DirectoryType.GAME -> {
NativeConfig.addGameDir(gameDir)
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
getGameDirsAndExternalContent(!isFirstTimeSetup)
}
DirectoryType.EXTERNAL_CONTENT -> {
addExternalContentDir(gameDir.uriString)
NativeConfig.saveGlobalConfig()
getGameDirsAndExternalContent()
}
}
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)
}
if (savedFromGameFragment) {
@ -176,15 +168,8 @@ class GamesViewModel : ViewModel() {
val removedDirIndex = gameDirs.indexOf(gameDir)
if (removedDirIndex != -1) {
gameDirs.removeAt(removedDirIndex)
when (gameDir.type) {
DirectoryType.GAME -> {
NativeConfig.setGameDirs(gameDirs.filter { it.type == DirectoryType.GAME }.toTypedArray())
}
DirectoryType.EXTERNAL_CONTENT -> {
removeExternalContentDir(gameDir.uriString)
}
}
getGameDirsAndExternalContent()
NativeConfig.setGameDirs(gameDirs.toTypedArray())
getGameDirs()
}
}
}
@ -192,16 +177,15 @@ class GamesViewModel : ViewModel() {
fun updateGameDirs() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
val gameDirs = _folders.value.filter { it.type == DirectoryType.GAME }
NativeConfig.setGameDirs(gameDirs.toTypedArray())
getGameDirsAndExternalContent()
NativeConfig.setGameDirs(_folders.value.toTypedArray())
getGameDirs()
}
}
fun onOpenGameFoldersFragment() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
getGameDirsAndExternalContent()
getGameDirs()
}
}
@ -209,36 +193,16 @@ class GamesViewModel : ViewModel() {
NativeConfig.saveGlobalConfig()
viewModelScope.launch {
withContext(Dispatchers.IO) {
getGameDirsAndExternalContent(true)
getGameDirs(true)
}
}
}
private fun getGameDirsAndExternalContent(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs().toMutableList()
val externalContentDirs = NativeConfig.getExternalContentDirs().map {
GameDir(it, false, DirectoryType.EXTERNAL_CONTENT)
}
gameDirs.addAll(externalContentDirs)
_folders.value = gameDirs
private fun getGameDirs(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs()
_folders.value = gameDirs.toMutableList()
if (reloadList) {
reloadGames(true)
}
}
private fun addExternalContentDir(path: String) {
val currentDirs = NativeConfig.getExternalContentDirs().toMutableList()
if (!currentDirs.contains(path)) {
currentDirs.add(path)
NativeConfig.setExternalContentDirs(currentDirs.toTypedArray())
NativeConfig.saveGlobalConfig()
}
}
private fun removeExternalContentDir(path: String) {
val currentDirs = NativeConfig.getExternalContentDirs().toMutableList()
currentDirs.remove(path)
NativeConfig.setExternalContentDirs(currentDirs.toTypedArray())
NativeConfig.saveGlobalConfig()
}
}

View File

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 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
@ -15,6 +12,5 @@ data class Patch(
val version: String,
val type: Int,
val programId: String,
val titleId: String,
val numericVersion: Long = 0
val titleId: String
)

View File

@ -1,21 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class UserProfile(
val uuid: String,
val username: String,
val imagePath: String = ""
) : Parcelable
object ProfileUtils {
fun generateRandomUUID(): String {
val uuid = java.util.UUID.randomUUID()
return uuid.toString().replace("-", "")
}
}

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui
@ -13,7 +13,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.PopupMenu
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
@ -28,14 +27,10 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
@ -178,16 +173,10 @@ class GamesFragment : Fragment() {
setupTopView()
updateButtonsVisibility()
binding.addDirectory.setOnClickListener {
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
}
binding.launchQlaunch?.setOnClickListener {
launchQLaunch()
}
setInsets()
}
@ -456,47 +445,6 @@ class GamesFragment : Fragment() {
}
}
private fun launchQLaunch() {
try {
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.QLaunch.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
requireContext(),
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
NativeLibrary.setCurrentAppletId(AppletInfo.QLaunch.appletId)
val qlaunchGame = Game(
title = getString(R.string.qlaunch_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(qlaunchGame)
findNavController().navigate(action)
} catch (e: Exception) {
Toast.makeText(
requireContext(),
"Failed to launch QLaunch: ${e.message}",
Toast.LENGTH_SHORT
).show()
}
}
private fun updateButtonsVisibility() {
val showQLaunch = BooleanSetting.ENABLE_QLAUNCH_BUTTON.getBoolean()
val showFolder = BooleanSetting.ENABLE_FOLDER_BUTTON.getBoolean()
val isFirmwareAvailable = NativeLibrary.isFirmwareAvailable()
val shouldShowQLaunch = showQLaunch && isFirmwareAvailable
binding.launchQlaunch.visibility = if (shouldShowQLaunch) View.VISIBLE else View.GONE
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@ -550,13 +498,6 @@ class GamesFragment : Fragment() {
mlpFab.rightMargin = rightInset + fabPadding
binding.addDirectory.layoutParams = mlpFab
binding.launchQlaunch?.let { qlaunchButton ->
val mlpQLaunch = qlaunchButton.layoutParams as ViewGroup.MarginLayoutParams
mlpQLaunch.leftMargin = leftInset + fabPadding
mlpQLaunch.bottomMargin = barInsets.bottom + fabPadding
qlaunchButton.layoutParams = mlpQLaunch
}
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.ui.main
@ -63,7 +63,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.documentfile.provider.DocumentFile
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@ -184,25 +183,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val latestVersion = NativeLibrary.checkForUpdate()
if (latestVersion != null) {
runOnUiThread {
val tag: String = latestVersion[0]
val name: String = latestVersion[1]
showUpdateDialog(tag, name)
showUpdateDialog(latestVersion)
}
}
}.start()
}
private fun showUpdateDialog(tag: String, name: String) {
private fun showUpdateDialog(version: String) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.update_available)
.setMessage(getString(R.string.update_available_description, name))
.setMessage(getString(R.string.update_available_description, version))
.setPositiveButton(android.R.string.ok) { _, _ ->
var artifact = tag
// Nightly builds have a slightly different format
if (NativeLibrary.isNightlyBuild()) {
artifact = tag.substringAfter('.', tag)
}
downloadAndInstallUpdate(tag, artifact)
downloadAndInstallUpdate(version)
}
.setNeutralButton(R.string.cancel) { dialog, _ ->
dialog.dismiss()
@ -215,11 +207,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
.show()
}
private fun downloadAndInstallUpdate(version: String, artifact: String) {
private fun downloadAndInstallUpdate(version: String) {
CoroutineScope(Dispatchers.IO).launch {
val packageId = applicationContext.packageName
val apkUrl = NativeLibrary.getUpdateApkUrl(version, artifact, packageId)
val apkFile = File(cacheDir, "update-$artifact.apk")
val apkUrl = NativeLibrary.getUpdateApkUrl(version, packageId)
val apkFile = File(cacheDir, "update-$version.apk")
withContext(Dispatchers.Main) {
showDownloadProgressDialog()
@ -389,13 +381,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
val getExternalContentDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
processExternalContentDir(result)
}
}
fun processGamesDir(result: Uri, calledFromGameFragment: Boolean = false) {
contentResolver.takePersistableUriPermission(
result,
@ -417,27 +402,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
.show(supportFragmentManager, AddGameFolderDialogFragment.TAG)
}
fun processExternalContentDir(result: Uri) {
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val uriString = result.toString()
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
if (folder != null) {
Toast.makeText(
applicationContext,
R.string.folder_already_added,
Toast.LENGTH_SHORT
).show()
return
}
val externalContentDir = org.yuzu.yuzu_emu.model.GameDir(uriString, false, org.yuzu.yuzu_emu.model.DirectoryType.EXTERNAL_CONTENT)
gamesViewModel.addFolder(externalContentDir, savedFromGameFragment = false)
}
val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
processKey(result, "keys")

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
@ -25,7 +25,6 @@ object DirectoryInitialization {
initializeInternalStorage()
NativeLibrary.initializeSystem(false)
NativeConfig.initializeGlobalConfig()
NativeLibrary.reloadProfiles()
migrateSettings()
areDirectoriesReady = true
}

Some files were not shown because too many files have changed in this diff Show More