mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-30 10:26:14 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0878b636 | |||
| c0a85d0e53 | |||
| 54cd5fb8eb |
@@ -1,66 +0,0 @@
|
|||||||
#!/usr/local/bin/bash -ex
|
|
||||||
|
|
||||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
[ -z ${OO_PS4_TOOLCHAIN+x} ] && exit
|
|
||||||
|
|
||||||
# cmake
|
|
||||||
[ -f "ps4-toolchain.cmake" ] || cat << EOF >"ps4-toolchain.cmake"
|
|
||||||
set(CMAKE_SYSROOT "$OO_PS4_TOOLCHAIN")
|
|
||||||
set(CMAKE_STAGING_PREFIX "$OO_PS4_TOOLCHAIN")
|
|
||||||
set(CMAKE_SYSTEM_NAME "OpenOrbis")
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "-D__OPENORBIS__ -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 --target=x86_64-pc-freebsd12-elf -mtune=btver2 -march=btver2 -fPIC -funwind-tables -femulated-tls")
|
|
||||||
set(CMAKE_CXX_FLAGS "-D__OPENORBIS__ -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 --target=x86_64-pc-freebsd12-elf -mtune=btver2 -march=btver2 -fPIC -funwind-tables -femulated-tls")
|
|
||||||
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
|
||||||
set(CMAKE_C_LINK_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
|
||||||
set(CMAKE_CXX_LINK_FLAGS "-m elf_x86_64 -pie -T $OO_PS4_TOOLCHAIN/link.x --eh-frame-hdr -L$OO_PS4_TOOLCHAIN/lib")
|
|
||||||
|
|
||||||
set(CMAKE_C_COMPILER clang)
|
|
||||||
set(CMAKE_CXX_COMPILER clang++)
|
|
||||||
set(CMAKE_LINKER ld.lld)
|
|
||||||
set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_C_LINK_FLAGS> <OBJECTS> -o <TARGET> -lc -lkernel -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o <LINK_LIBRARIES>")
|
|
||||||
set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> <OBJECTS> -o <TARGET> -lc -lkernel -lc++ -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o <LINK_LIBRARIES>")
|
|
||||||
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|
||||||
|
|
||||||
# TODO: Why does cmake not set this?
|
|
||||||
set(CMAKE_SIZEOF_VOID_P 8)
|
|
||||||
EOF
|
|
||||||
|
|
||||||
[ -z ${NPROC+x} ] && NPROC=$(nproc || 1)
|
|
||||||
|
|
||||||
# Normally a platform has a package manager
|
|
||||||
# PS4 does not, atleast not in the normal sense
|
|
||||||
export EXTRA_CMAKE_FLAGS=("${EXTRA_CMAKE_FLAGS[@]}" $@)
|
|
||||||
[ -f "build/Makefile" ] || cmake -S . -B build -G "Unix Makefiles" \
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="ps4-toolchain.cmake" \
|
|
||||||
-DENABLE_QT_TRANSLATION=OFF \
|
|
||||||
-DENABLE_CUBEB=OFF \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_CXX_FLAGS="$ARCH_FLAGS" \
|
|
||||||
-DCMAKE_C_FLAGS="$ARCH_FLAGS" \
|
|
||||||
-DENABLE_SDL2=ON \
|
|
||||||
-DENABLE_LIBUSB=OFF \
|
|
||||||
-DENABLE_UPDATE_CHECKER=OFF \
|
|
||||||
-DENABLE_QT=OFF \
|
|
||||||
-DENABLE_OPENGL=ON \
|
|
||||||
-DENABLE_WEB_SERVICE=OFF \
|
|
||||||
-DUSE_DISCORD_PRESENCE=OFF \
|
|
||||||
-DCPMUTIL_FORCE_BUNDLED=ON \
|
|
||||||
-DYUZU_USE_EXTERNAL_FFMPEG=ON \
|
|
||||||
-DYUZU_USE_CPM=ON \
|
|
||||||
-DDYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=OFF \
|
|
||||||
-DDYNARMIC_TESTS=ON \
|
|
||||||
-DYUZU_TESTS=ON \
|
|
||||||
-DYUZU_USE_EXTERNAL_SDL2=ON \
|
|
||||||
"${EXTRA_CMAKE_FLAGS[@]}" || exit
|
|
||||||
|
|
||||||
cmake --build build -t yuzu-cmd_pkg -- -j$NPROC
|
|
||||||
#cmake --build build -t dynarmic_tests_pkg -- -j$NPROC
|
|
||||||
#cmake --build build -t testps4_pkg -- -j$NPROC
|
|
||||||
@@ -63,6 +63,3 @@ artifacts
|
|||||||
/install*
|
/install*
|
||||||
vulkansdk*.exe
|
vulkansdk*.exe
|
||||||
*.tar.zst
|
*.tar.zst
|
||||||
|
|
||||||
# PS4 toolchain stuff
|
|
||||||
ps4-toolchain.cmake
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
diff --git a/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp b/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
|
||||||
index 0129511c..10fc9b04 100644
|
|
||||||
--- a/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
|
||||||
+++ b/libs/asio/include/boost/asio/detail/impl/socket_ops.ipp
|
|
||||||
@@ -15,6 +15,12 @@
|
|
||||||
# pragma once
|
|
||||||
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
||||||
|
|
||||||
+// hacky fix for ps4
|
|
||||||
+#if defined(__OPENORBIS__)
|
|
||||||
+# define FIONBIO 0
|
|
||||||
+# define FIONREAD 1
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#include <boost/asio/detail/config.hpp>
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/unix.c b/unix.c
|
|
||||||
index 6669216..86a2faa 100644
|
|
||||||
--- a/unix.c
|
|
||||||
+++ b/unix.c
|
|
||||||
@@ -53,7 +53,7 @@
|
|
||||||
#include <poll.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-#if !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
|
|
||||||
+#if !defined(__OPENORBIS__) && !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
|
|
||||||
typedef int socklen_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
diff --git a/libavutil/cpu.c b/libavutil/cpu.c
|
|
||||||
index 03e2720..874258f 100644
|
|
||||||
--- a/libavutil/cpu.c
|
|
||||||
+++ b/libavutil/cpu.c
|
|
||||||
@@ -43,8 +43,10 @@
|
|
||||||
#include <sys/param.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/types.h>
|
|
||||||
+#ifndef __OPENORBIS__
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#endif
|
|
||||||
+#endif
|
|
||||||
#if HAVE_UNISTD_H
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
|
|
||||||
index 3012e05..2ae23ff 100644
|
|
||||||
--- a/cmake/ConfigureOpenSSL.cmake
|
|
||||||
+++ b/cmake/ConfigureOpenSSL.cmake
|
|
||||||
@@ -108,7 +108,8 @@ function(configure_openssl)
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
|
|
||||||
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
|
|
||||||
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
|
|
||||||
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
|
|
||||||
message(STATUS "Found previous configure results. Don't perform configuration")
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
@@ -134,10 +135,24 @@ function(configure_openssl)
|
|
||||||
set(VERBOSE_OPTION OUTPUT_QUIET)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
|
||||||
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
|
|
||||||
+ find_program(EMCC emcc REQUIRED)
|
|
||||||
+ set(EMSCRIPTEN_LINKER ${EMCC})
|
|
||||||
+ list(APPEND CONFIGURE_COMMAND wasm64)
|
|
||||||
+ else()
|
|
||||||
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
|
|
||||||
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
|
|
||||||
+ endif ()
|
|
||||||
+
|
|
||||||
execute_process(
|
|
||||||
- COMMAND ${CMAKE_COMMAND} -E env
|
|
||||||
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
|
|
||||||
"CFLAGS=${CMAKE_C_FLAGS}"
|
|
||||||
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
|
|
||||||
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
|
|
||||||
+ "CC=${CMAKE_C_COMPILER}"
|
|
||||||
+ "CXX=${CMAKE_CXX_COMPILER}"
|
|
||||||
+ "LD=${EMSCRIPTEN_LINKER}"
|
|
||||||
${CONFIGURE_COMMAND}
|
|
||||||
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
|
|
||||||
${VERBOSE_OPTION}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
||||||
index 1ca57e1..593fd62 100644
|
|
||||||
--- a/CMakeLists.txt
|
|
||||||
+++ b/CMakeLists.txt
|
|
||||||
@@ -148,6 +148,16 @@ if(NOT BUILD_SHARED_LIBS)
|
|
||||||
list(APPEND OPENSSL_CONFIGURE_OPTIONS no-shared)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
+list(APPEND OPENSSL_CONFIGURE_OPTIONS
|
|
||||||
+ no-threads no-thread-pool no-shared no-afalgeng no-async no-capieng no-cmp no-cms no-comp no-ct
|
|
||||||
+ no-docs no-dgram no-dso no-dynamic-engine no-engine no-filenames no-gost
|
|
||||||
+ no-http no-legacy no-module no-nextprotoneg no-ocsp no-padlockeng no-quic
|
|
||||||
+ no-srp no-srtp no-ssl-trace no-static-engine no-tests no-thread-pool no-ts
|
|
||||||
+ no-ui-console no-uplink no-ssl3-method no-tls1-method no-tls1_1-method
|
|
||||||
+ no-dtls1-method no-dtls1_2-method no-argon2 no-bf no-blake2 no-cast
|
|
||||||
+ no-dsa no-idea no-md4 no-mdc2 no-ocb no-rc2 no-rc4 no-rmd160 no-scrypt
|
|
||||||
+ no-siphash no-siv no-sm2 no-sm3 no-sm4 no-whirlpool no-apps)
|
|
||||||
+
|
|
||||||
list(SORT OPENSSL_CONFIGURE_OPTIONS)
|
|
||||||
list(REMOVE_DUPLICATES OPENSSL_CONFIGURE_OPTIONS)
|
|
||||||
list(PREPEND OPENSSL_CONFIGURE_OPTIONS ${OPENSSL_TARGET_PLATFORM})
|
|
||||||
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
|
|
||||||
index 2ae23ff..22a4a27 100644
|
|
||||||
--- a/cmake/ConfigureOpenSSL.cmake
|
|
||||||
+++ b/cmake/ConfigureOpenSSL.cmake
|
|
||||||
@@ -154,6 +154,7 @@ function(configure_openssl)
|
|
||||||
"CXX=${CMAKE_CXX_COMPILER}"
|
|
||||||
"LD=${EMSCRIPTEN_LINKER}"
|
|
||||||
${CONFIGURE_COMMAND}
|
|
||||||
+ "--prefix=${CMAKE_SYSROOT}"
|
|
||||||
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
|
|
||||||
${VERBOSE_OPTION}
|
|
||||||
COMMAND_ERROR_IS_FATAL ANY
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
diff --git a/crypto/o_str.c b/crypto/o_str.c
|
|
||||||
index 8aadff5..ba427c9 100644
|
|
||||||
--- a/crypto/o_str.c
|
|
||||||
+++ b/crypto/o_str.c
|
|
||||||
@@ -385,7 +385,7 @@ int openssl_strerror_r(int errnum, char *buf, size_t buflen)
|
|
||||||
* It can return a pointer to some (immutable) static string in which case
|
|
||||||
* buf is left unused.
|
|
||||||
*/
|
|
||||||
- err = strerror_r(errnum, buf, buflen);
|
|
||||||
+ err = (char *)strerror_r(errnum, buf, buflen);
|
|
||||||
if (err == NULL || buflen == 0)
|
|
||||||
return 0;
|
|
||||||
/*
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
||||||
index 8872895..b0eeca8 100644
|
|
||||||
--- a/CMakeLists.txt
|
|
||||||
+++ b/CMakeLists.txt
|
|
||||||
@@ -1770,7 +1770,6 @@ elseif(EMSCRIPTEN)
|
|
||||||
|
|
||||||
CheckPTHREAD()
|
|
||||||
CheckLibUnwind()
|
|
||||||
-
|
|
||||||
elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
|
|
||||||
|
|
||||||
set(SDL_DISABLE_DLOPEN_NOTES TRUE)
|
|
||||||
@@ -3647,10 +3646,30 @@ if(NOT HAVE_CAMERA)
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
+# Always on?
|
|
||||||
+if (OPENORBIS)
|
|
||||||
+ sdl_sources(
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_systhread.c"
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_sysmutex.c" # Can be faked, if necessary
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_syscond.c" # Can be faked, if necessary
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_sysrwlock.c" # Can be faked, if necessary
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/pthread/SDL_systls.c"
|
|
||||||
+ "${SDL3_SOURCE_DIR}/src/thread/generic/SDL_syssem.c"
|
|
||||||
+ )
|
|
||||||
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c")
|
|
||||||
+ sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c")
|
|
||||||
+ set(HAVE_SDL_THREADS TRUE)
|
|
||||||
+ set(HAVE_SDL_TIMERS TRUE)
|
|
||||||
+ set(HAVE_SDL_TIME TRUE)
|
|
||||||
+ set(SDL_TIME_UNIX 1)
|
|
||||||
+ set(SDL_TIMER_UNIX 1)
|
|
||||||
+ set(SDL_THREAD_PTHREAD 1)
|
|
||||||
+endif ()
|
|
||||||
+
|
|
||||||
# We always need to have threads and timers around
|
|
||||||
if(NOT HAVE_SDL_THREADS)
|
|
||||||
# The Emscripten and N-Gage platform has been carefully vetted to work without threads
|
|
||||||
- if(EMSCRIPTEN OR NGAGE)
|
|
||||||
+ if(EMSCRIPTEN OR NGAGE OR OPENORBIS)
|
|
||||||
set(SDL_THREADS_DISABLED 1)
|
|
||||||
sdl_glob_sources(
|
|
||||||
"${SDL3_SOURCE_DIR}/src/thread/generic/*.c"
|
|
||||||
diff --git a/build-scripts/rename_macros.py b/build-scripts/rename_macros.py
|
|
||||||
index 978120c..b6063dd 100755
|
|
||||||
--- a/build-scripts/rename_macros.py
|
|
||||||
+++ b/build-scripts/rename_macros.py
|
|
||||||
@@ -124,6 +124,7 @@ RENAMED_MACROS = {
|
|
||||||
"__RISCOS__": "SDL_PLATFORM_RISCOS",
|
|
||||||
"__SOLARIS__": "SDL_PLATFORM_SOLARIS",
|
|
||||||
"__PSP__": "SDL_PLATFORM_PSP",
|
|
||||||
+ "__PS4__": "SDL_PLATFORM_PS4",
|
|
||||||
"__PS2__": "SDL_PLATFORM_PS2",
|
|
||||||
"__VITA__": "SDL_PLATFORM_VITA",
|
|
||||||
"__3DS__": "SDL_PLATFORM_3DS",
|
|
||||||
diff --git a/include/SDL3/SDL_platform_defines.h b/include/SDL3/SDL_platform_defines.h
|
|
||||||
index 7963149..6c0dd5d 100644
|
|
||||||
--- a/include/SDL3/SDL_platform_defines.h
|
|
||||||
+++ b/include/SDL3/SDL_platform_defines.h
|
|
||||||
@@ -442,6 +442,16 @@
|
|
||||||
#define SDL_PLATFORM_PSP 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+#if defined(__PS4__) || defined(__OPENORBIS__)
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * A preprocessor macro that is only defined if compiling for Sony PSP.
|
|
||||||
+ *
|
|
||||||
+ * \since This macro is available since SDL 3.2.0.
|
|
||||||
+ */
|
|
||||||
+#define SDL_PLATFORM_PS4 1
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#if defined(__PS2__) || defined(PS2)
|
|
||||||
|
|
||||||
/**
|
|
||||||
diff --git a/src/SDL.c b/src/SDL.c
|
|
||||||
index b1a1a56..0a3fec6 100644
|
|
||||||
--- a/src/SDL.c
|
|
||||||
+++ b/src/SDL.c
|
|
||||||
@@ -714,7 +714,7 @@ void SDL_Quit(void)
|
|
||||||
SDL_DBus_Quit();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) && !defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(SDL_PLATFORM_PRIVATE)
|
|
||||||
+#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) && !defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(SDL_PLATFORM_PRIVATE) && !defined(SDL_PLATFORM_PS4)
|
|
||||||
SDL_Gtk_Quit();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -813,6 +813,8 @@ const char *SDL_GetPlatform(void)
|
|
||||||
return "tvOS";
|
|
||||||
#elif defined(SDL_PLATFORM_PS2)
|
|
||||||
return "PlayStation 2";
|
|
||||||
+#elif defined(SDL_PLATFORM_PS4)
|
|
||||||
+ return "PlayStation 4";
|
|
||||||
#elif defined(SDL_PLATFORM_PSP)
|
|
||||||
return "PlayStation Portable";
|
|
||||||
#elif defined(SDL_PLATFORM_VITA)
|
|
||||||
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
|
|
||||||
index c1e4241..65b1e6d 100644
|
|
||||||
--- a/src/audio/SDL_audio.c
|
|
||||||
+++ b/src/audio/SDL_audio.c
|
|
||||||
@@ -80,6 +80,9 @@ static const AudioBootStrap *const bootstrap[] = {
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_NGAGE
|
|
||||||
&NGAGEAUDIO_bootstrap,
|
|
||||||
#endif
|
|
||||||
+#ifdef SDL_AUDIO_DRIVER_PS4
|
|
||||||
+ &PS4AUDIO_bootstrap,
|
|
||||||
+#endif
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN
|
|
||||||
&EMSCRIPTENAUDIO_bootstrap,
|
|
||||||
#endif
|
|
||||||
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
|
|
||||||
index 60c9743..2d12c3a 100644
|
|
||||||
--- a/src/joystick/SDL_joystick.c
|
|
||||||
+++ b/src/joystick/SDL_joystick.c
|
|
||||||
@@ -106,6 +106,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
|
|
||||||
#ifdef SDL_JOYSTICK_N3DS
|
|
||||||
&SDL_N3DS_JoystickDriver,
|
|
||||||
#endif
|
|
||||||
+#ifdef SDL_JOYSTICK_PS4
|
|
||||||
+ &SDL_PS4_JoystickDriver,
|
|
||||||
+#endif
|
|
||||||
#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
|
|
||||||
&SDL_DUMMY_JoystickDriver
|
|
||||||
#endif
|
|
||||||
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
|
|
||||||
index 47ce42c..42ab9de 100644
|
|
||||||
--- a/src/joystick/SDL_sysjoystick.h
|
|
||||||
+++ b/src/joystick/SDL_sysjoystick.h
|
|
||||||
@@ -253,6 +253,7 @@ extern SDL_JoystickDriver SDL_HAIKU_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_HIDAPI_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_IOS_JoystickDriver;
|
|
||||||
+extern SDL_JoystickDriver SDL_PS4_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_LINUX_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
|
|
||||||
extern SDL_JoystickDriver SDL_WGI_JoystickDriver;
|
|
||||||
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
|
|
||||||
index a1bb44a..51843c4 100644
|
|
||||||
--- a/src/render/SDL_sysrender.h
|
|
||||||
+++ b/src/render/SDL_sysrender.h
|
|
||||||
@@ -381,6 +381,7 @@ extern SDL_RenderDriver NGAGE_RenderDriver;
|
|
||||||
extern SDL_RenderDriver VULKAN_RenderDriver;
|
|
||||||
extern SDL_RenderDriver PS2_RenderDriver;
|
|
||||||
extern SDL_RenderDriver PSP_RenderDriver;
|
|
||||||
+extern SDL_RenderDriver PS4_RenderDriver;
|
|
||||||
extern SDL_RenderDriver SW_RenderDriver;
|
|
||||||
extern SDL_RenderDriver VITA_GXM_RenderDriver;
|
|
||||||
extern SDL_RenderDriver GPU_RenderDriver;
|
|
||||||
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
|
|
||||||
index 4a870f6..69ca1db 100644
|
|
||||||
--- a/src/stdlib/SDL_string.c
|
|
||||||
+++ b/src/stdlib/SDL_string.c
|
|
||||||
@@ -32,7 +32,9 @@
|
|
||||||
|
|
||||||
#include "SDL_casefolding.h"
|
|
||||||
|
|
||||||
-#if defined(__SIZEOF_WCHAR_T__)
|
|
||||||
+#if defined(SDL_PLATFORM_PS4)
|
|
||||||
+#define SDL_SIZEOF_WCHAR_T 2
|
|
||||||
+#elif defined(__SIZEOF_WCHAR_T__)
|
|
||||||
#define SDL_SIZEOF_WCHAR_T __SIZEOF_WCHAR_T__
|
|
||||||
#elif defined(SDL_PLATFORM_NGAGE)
|
|
||||||
#define SDL_SIZEOF_WCHAR_T 2
|
|
||||||
@@ -43,7 +45,6 @@
|
|
||||||
#endif
|
|
||||||
SDL_COMPILE_TIME_ASSERT(sizeof_wchar_t, sizeof(wchar_t) == SDL_SIZEOF_WCHAR_T);
|
|
||||||
|
|
||||||
-
|
|
||||||
char *SDL_UCS4ToUTF8(Uint32 codepoint, char *dst)
|
|
||||||
{
|
|
||||||
if (!dst) {
|
|
||||||
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
|
|
||||||
index f650f27..e23424f 100644
|
|
||||||
--- a/src/video/SDL_video.c
|
|
||||||
+++ b/src/video/SDL_video.c
|
|
||||||
@@ -131,6 +131,9 @@ static VideoBootStrap *bootstrap[] = {
|
|
||||||
#ifdef SDL_VIDEO_DRIVER_NGAGE
|
|
||||||
&NGAGE_bootstrap,
|
|
||||||
#endif
|
|
||||||
+#ifdef SDL_VIDEO_DRIVER_PS4
|
|
||||||
+ &PS4_bootstrap,
|
|
||||||
+#endif
|
|
||||||
#ifdef SDL_VIDEO_DRIVER_KMSDRM
|
|
||||||
&KMSDRM_bootstrap,
|
|
||||||
#endif
|
|
||||||
@@ -314,6 +317,7 @@ static bool SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window,
|
|
||||||
SDL_GetWindowSizeInPixels(window, &w, &h);
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
+
|
|
||||||
SDL_Renderer *renderer = NULL;
|
|
||||||
const char *render_driver = NULL;
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
|||||||
diff --git a/source/opt/loop_dependence.cpp b/source/opt/loop_dependence.cpp
|
|
||||||
index e41c044..a51b53b 100644
|
|
||||||
--- a/source/opt/loop_dependence.cpp
|
|
||||||
+++ b/source/opt/loop_dependence.cpp
|
|
||||||
@@ -12,6 +12,12 @@
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
+// PS4: issue?
|
|
||||||
+#ifdef __PS4__
|
|
||||||
+#pragma clang diagnostic ignored "-Wabsolute-value"
|
|
||||||
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#include "source/opt/loop_dependence.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
@@ -19,6 +25,7 @@
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
+#include <cstdlib>
|
|
||||||
|
|
||||||
#include "source/opt/instruction.h"
|
|
||||||
#include "source/opt/scalar_analysis_nodes.h"
|
|
||||||
@@ -22,7 +22,6 @@ include(UseCcache)
|
|||||||
include(CMakeDependentOption)
|
include(CMakeDependentOption)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(CPMUtil)
|
include(CPMUtil)
|
||||||
include(OpenOrbis)
|
|
||||||
|
|
||||||
if (NOT DEFINED ARCHITECTURE)
|
if (NOT DEFINED ARCHITECTURE)
|
||||||
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?")
|
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?")
|
||||||
|
|||||||
@@ -18,13 +18,10 @@ if (DEFINED GIT_RELEASE)
|
|||||||
set(BUILD_VERSION "${GIT_TAG}")
|
set(BUILD_VERSION "${GIT_TAG}")
|
||||||
set(GIT_REFSPEC "${GIT_RELEASE}")
|
set(GIT_REFSPEC "${GIT_RELEASE}")
|
||||||
set(IS_DEV_BUILD false)
|
set(IS_DEV_BUILD false)
|
||||||
elseif(DEFINED GIT_COMMIT)
|
else()
|
||||||
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
|
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
|
||||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
|
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
|
||||||
set(IS_DEV_BUILD true)
|
set(IS_DEV_BUILD true)
|
||||||
else()
|
|
||||||
set(BUILD_VERSION "NoGitInfo")
|
|
||||||
set(IS_DEV_BUILD true)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NIGHTLY_BUILD)
|
if (NIGHTLY_BUILD)
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
function(create_ps4_eboot project target content_id)
|
|
||||||
set(sce_sys_dir sce_sys)
|
|
||||||
set(sce_sys_param ${sce_sys_dir}/param.sfo)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT "${target}.pkg"
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/create-fself -in=bin/${target} -out=${target}.oelf --eboot ${target}_eboot.bin
|
|
||||||
VERBATIM
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
DEPENDS ${project}
|
|
||||||
)
|
|
||||||
add_custom_target(${project}_pkg ALL DEPENDS "${target}.pkg")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(create_ps4_pkg project target content_id)
|
|
||||||
set(sce_sys_dir sce_sys)
|
|
||||||
set(sce_sys_param ${sce_sys_dir}/param.sfo)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT "${target}.pkg"
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/create-fself -in=bin/${target} -out=${target}.oelf --eboot ${target}_eboot.bin
|
|
||||||
COMMAND mkdir -p ${sce_sys_dir}
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_new ${sce_sys_param}
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} APP_TYPE --type Integer --maxsize 4 --value 1
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} APP_VER --type Utf8 --maxsize 8 --value 1.03
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} ATTRIBUTE --type Integer --maxsize 4 --value 0
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} CATEGORY --type Utf8 --maxsize 4 --value gd
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} CONTENT_ID --type Utf8 --maxsize 48 --value ${content_id}
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} DOWNLOAD_DATA_SIZE --type Integer --maxsize 4 --value 0
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} SYSTEM_VER --type Integer --maxsize 4 --value 0
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} TITLE --type Utf8 --maxsize 128 --value ${target}
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} TITLE_ID --type Utf8 --maxsize 12 --value BREW00090
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core sfo_setentry ${sce_sys_param} VERSION --type Utf8 --maxsize 8 --value 1.03
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/create-gp4 -out ${target}.gp4 --content-id=${content_id} --files "${target}_eboot.bin ${sce_sys_param} sce_module/libc.prx sce_module/libSceFios2.prx"
|
|
||||||
COMMAND ${CMAKE_SYSROOT}/bin/PkgTool.Core pkg_build ${target}.gp4 .
|
|
||||||
VERBATIM
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
DEPENDS ${project}
|
|
||||||
)
|
|
||||||
add_custom_target(${project}_pkg ALL DEPENDS "${target}.pkg")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
if (NOT DEFINED ENV{OO_PS4_TOOLCHAIN})
|
|
||||||
set(ENV{OO_PS4_TOOLCHAIN} ${CMAKE_SYSROOT})
|
|
||||||
endif ()
|
|
||||||
+6
-20
@@ -12,8 +12,7 @@
|
|||||||
"min_version": "1.57",
|
"min_version": "1.57",
|
||||||
"package": "Boost",
|
"package": "Boost",
|
||||||
"patches": [
|
"patches": [
|
||||||
"0001-clang-cl.patch",
|
"0001-clang-cl.patch"
|
||||||
"0004-openorbis.patch"
|
|
||||||
],
|
],
|
||||||
"repo": "boostorg/boost",
|
"repo": "boostorg/boost",
|
||||||
"version": "boost-1.90.0"
|
"version": "boost-1.90.0"
|
||||||
@@ -71,18 +70,12 @@
|
|||||||
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
"hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9",
|
||||||
"min_version": "1.3",
|
"min_version": "1.3",
|
||||||
"repo": "lsalzman/enet",
|
"repo": "lsalzman/enet",
|
||||||
"version": "v1.3.18",
|
"version": "v1.3.18"
|
||||||
"patches": [
|
|
||||||
"0001-openorbis.patch"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
"hash": "ed177621176b3961bdcaa339187d3a7688c1c8b060b79c4bb0257cbc67ad7021ae5d5adca5303b45625abbbe3d9aafdd87ce777b8690ac295290d744c875489a",
|
||||||
"repo": "FFmpeg/FFmpeg",
|
"repo": "FFmpeg/FFmpeg",
|
||||||
"version": "c7b5f1537d",
|
"version": "c7b5f1537d"
|
||||||
"patches": [
|
|
||||||
"0001-sysctl-openorbis.patch"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"ffmpeg-ci": {
|
"ffmpeg-ci": {
|
||||||
"ci": true,
|
"ci": true,
|
||||||
@@ -197,8 +190,7 @@
|
|||||||
"min_version": "3.0.0",
|
"min_version": "3.0.0",
|
||||||
"package": "OpenSSL",
|
"package": "OpenSSL",
|
||||||
"patches": [
|
"patches": [
|
||||||
"0001-add-bundled-cert.patch",
|
"0001-add-bundled-cert.patch"
|
||||||
"0002-fix-gnu-openorbis.patch"
|
|
||||||
],
|
],
|
||||||
"repo": "openssl/openssl",
|
"repo": "openssl/openssl",
|
||||||
"version": "openssl-3.6.2"
|
"version": "openssl-3.6.2"
|
||||||
@@ -221,9 +213,7 @@
|
|||||||
"0001-cpmutil-compat.patch",
|
"0001-cpmutil-compat.patch",
|
||||||
"0002-use-ccache.patch",
|
"0002-use-ccache.patch",
|
||||||
"0003-use-cmake-compiler-flags.patch",
|
"0003-use-cmake-compiler-flags.patch",
|
||||||
"0004-use-shell-wrapper.patch",
|
"0004-use-shell-wrapper.patch"
|
||||||
"0005-wasm-support.patch",
|
|
||||||
"0006-openorbis-prefix.patch"
|
|
||||||
],
|
],
|
||||||
"repo": "jimmy-park/openssl-cmake",
|
"repo": "jimmy-park/openssl-cmake",
|
||||||
"version": "3.6.2"
|
"version": "3.6.2"
|
||||||
@@ -261,11 +251,7 @@
|
|||||||
"min_version": "3.2.10",
|
"min_version": "3.2.10",
|
||||||
"package": "SDL3",
|
"package": "SDL3",
|
||||||
"repo": "libsdl-org/SDL",
|
"repo": "libsdl-org/SDL",
|
||||||
"version": "release-3.4.8",
|
"version": "release-3.4.8"
|
||||||
"patches": [
|
|
||||||
"0001-ps4.patch",
|
|
||||||
"0002-ps4-drivers.patch"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"sdl3-ci": {
|
"sdl3-ci": {
|
||||||
"ci": true,
|
"ci": true,
|
||||||
|
|||||||
Vendored
-215
@@ -1,215 +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="ps4.svg"
|
|
||||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
|
||||||
inkscape:export-filename="base.svg.2026_01_12_14_43_47.0.svg"
|
|
||||||
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">
|
|
||||||
<defs
|
|
||||||
id="defs7">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient5"
|
|
||||||
inkscape:collect="always">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#003e74;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop4" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#2ea8ff;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop5" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient1"
|
|
||||||
inkscape:collect="always">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#3579ff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop2" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#0b00ff;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop3" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch37"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop37" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch28"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#252525;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop28" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch27"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#000000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop27" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch15"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop16" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient14"
|
|
||||||
inkscape:swatch="gradient">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop14" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop15" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch9"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop10" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="swatch8"
|
|
||||||
inkscape:swatch="solid">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop9" />
|
|
||||||
</linearGradient>
|
|
||||||
<rect
|
|
||||||
x="22.627417"
|
|
||||||
y="402.76802"
|
|
||||||
width="521.34025"
|
|
||||||
height="248.94868"
|
|
||||||
id="rect24" />
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath18">
|
|
||||||
<circle
|
|
||||||
style="opacity:1;mix-blend-mode:normal;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.8382;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
|
||||||
id="circle18"
|
|
||||||
cx="-246.8315"
|
|
||||||
cy="246.8338"
|
|
||||||
inkscape:label="Circle"
|
|
||||||
r="191.89999" />
|
|
||||||
</clipPath>
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath22">
|
|
||||||
<circle
|
|
||||||
style="opacity:1;mix-blend-mode:normal;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.8382;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
|
||||||
id="circle22"
|
|
||||||
cx="256"
|
|
||||||
cy="256"
|
|
||||||
inkscape:label="Circle"
|
|
||||||
r="191.89999" />
|
|
||||||
</clipPath>
|
|
||||||
<clipPath
|
|
||||||
clipPathUnits="userSpaceOnUse"
|
|
||||||
id="clipPath128">
|
|
||||||
<circle
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#03ffff;stroke-width:0;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="circle128"
|
|
||||||
cx="256"
|
|
||||||
cy="256"
|
|
||||||
r="192" />
|
|
||||||
</clipPath>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient1"
|
|
||||||
id="linearGradient3"
|
|
||||||
x1="256"
|
|
||||||
y1="64"
|
|
||||||
x2="256"
|
|
||||||
y2="448"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(1.1874952,0,0,-1.1874952,-47.370662,560.66391)" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5"
|
|
||||||
id="linearGradient4"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(-1.1874952,0,0,1.1874952,560.61528,-47.345282)"
|
|
||||||
x1="256"
|
|
||||||
y1="64"
|
|
||||||
x2="256"
|
|
||||||
y2="448" />
|
|
||||||
</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"
|
|
||||||
inkscape:cx="286.49999"
|
|
||||||
inkscape:cy="236.99999"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="849"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg7" />
|
|
||||||
<path
|
|
||||||
id="path8-7"
|
|
||||||
style="display:inline;mix-blend-mode:color;fill:url(#linearGradient3);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
|
||||||
inkscape:label="Circle"
|
|
||||||
d="m 256.62812,484.66422 a 227.99908,228.00372 0 0 1 -94.5288,-20.52182 c 17.54241,-0.96226 34.77843,-4.71825 46.2868,-10.46733 12.69502,-6.3416 25.70492,-17.24174 35.38366,-26.3736 h 36.45053 c 0.93211,0.61234 1.88758,1.23061 2.86668,1.85086 4.89722,14.18571 13.27668,38.80747 17.49933,51.23264 a 227.99908,228.00372 0 0 1 -43.9582,4.27925 z m 55.26491,-6.80041 -16.39068,-41.47736 c 6.99845,3.71973 14.6813,7.07649 22.62503,9.76921 20.75463,7.03513 39.46594,9.14022 49.31353,9.76688 a 227.99908,228.00372 0 0 1 -55.54788,21.94127 z m 58.6952,-23.72487 c -8.69086,-4.31956 -29.66977,-14.9556 -49.97917,-26.83747 h 87.23219 a 227.99908,228.00372 0 0 1 -37.25302,26.83747 z m -241.49988,-8.48659 a 227.99908,228.00372 0 0 1 -23.67337,-18.35088 h 95.80906 c -1.32304,0.72101 -2.11058,1.13186 -2.11058,1.13186 l 9.37935,10.63433 -34.47214,-6.30174 c 0,-2e-5 -10.96427,4.13624 -36.05858,11.02861 -3.09966,0.85131 -6.06162,1.45425 -8.87374,1.85782 z M 96.054184,418.52496 A 227.99908,228.00372 0 0 1 64.743277,379.80069 h 53.833883 c 16.2593,10.82121 36.03109,20.56336 55.93056,22.04797 47.50893,3.54468 62.979,-1.67459 62.979,-1.67459 0,0 -4.13964,4.3321 -13.0323,12.01204 -2.4447,2.11135 -5.46123,4.28998 -8.54904,6.33885 z m 156.603246,0 c 4.10483,-4.24043 6.54978,-7.04162 6.54978,-7.04162 0,0 3.17296,2.8621 8.63949,7.04162 z m 54.10293,0 c -19.35695,-13.88741 -22.86161,-25.14433 -22.86161,-25.14433 0,0 49.2172,17.2685 90.39112,0.28064 10.4477,-4.31055 19.37408,-9.1049 26.88099,-13.86058 h 47.3421 a 227.99908,228.00372 0 0 1 -31.31091,38.72427 z M 59.385631,371.02418 A 227.99908,228.00372 0 0 1 41.543041,332.29992 H 159.20017 c 1.30773,2.3199 2.75142,4.57103 4.34409,6.73083 12.77878,17.32895 25.06121,26.96783 33.0852,31.99343 h -27.15468 c -5.2719,-0.84 -10.81534,-1.78919 -16.40924,-2.84819 -27.04349,-5.11994 -72.845405,-20.48007 -72.845405,-20.48007 0,0 10.296355,11.13669 26.131855,23.32826 z m 238.424449,0 c 30.05703,-10.10404 47.05829,-24.07021 56.34572,-38.72426 h 117.55739 a 227.99908,228.00372 0 0 1 -17.84258,38.72426 h -40.22176 c 13.33114,-10.41219 19.38725,-18.85882 19.38725,-18.85882 0,0 -30.08919,11.11726 -59.29823,18.85882 z m -41.2585,-14.62133 c -0.90134,-0.01 -9.08983,-0.64727 -40.38179,-18.55498 -2.80164,-1.60327 -5.61377,-3.47048 -8.41452,-5.54795 h 37.53597 c 6.43727,15.58979 11.34615,24.10293 11.34615,24.10293 0,0 -0.0258,6.4e-4 -0.0858,0 z m 12.72847,-0.0418 c 0,0 -2.97304,-8.3824 -6.55675,-24.06118 h 46.48163 c -24.16151,23.08045 -39.92488,24.06123 -39.92488,24.06123 z M 38.653164,323.52341 a 227.99908,228.00372 0 0 1 -8.28,-38.72426 H 147.25331 c 0.50409,11.4347 2.40394,25.61361 7.73264,38.72426 z m 158.567726,0 c -12.80955,-11.77756 -24.77977,-26.58932 -33.71374,-38.72426 h 65.85729 c 4.01234,15.08912 8.39298,28.09557 12.44549,38.72426 z m 63.60057,0 c -2.16662,-10.57203 -4.40101,-23.55135 -6.23667,-38.72426 h 95.8253 c -7.46906,9.7857 -18.55287,23.51811 -32.62829,38.72426 z m 97.99155,0 c 5.95644,-13.69644 6.2152,-27.44278 5.00743,-38.72426 h 119.06262 a 227.99908,228.00372 0 0 1 -8.28,38.72426 z M 29.452392,276.02264 A 227.99908,228.00372 0 0 1 28.62903,256.6605 227.99908,228.00372 0 0 1 29.452392,237.29836 H 219.64878 c 2.02639,13.95226 4.61647,26.90106 7.48446,38.72428 H 157.2241 c -5.80191,-8.33616 -9.27267,-13.98815 -9.27267,-13.98815 0,0 -0.7416,5.5263 -0.8396,13.98815 z m 224.144368,0 c -1.22897,-11.76966 -2.19512,-24.70616 -2.7113,-38.72428 h 232.91837 a 227.99908,228.00372 0 0 1 0.82337,19.36214 227.99908,228.00372 0 0 1 -0.82337,19.36214 H 362.56568 c -0.39943,-2.2888 -0.8157,-4.40137 -1.19214,-6.30638 0,0 -1.52342,2.281 -4.43454,6.30638 z M 30.373164,228.52186 a 227.99908,228.00372 0 0 1 8.28,-38.72427 H 218.96921 c -1.16001,13.8461 -1.41371,26.99937 -0.37573,38.72427 z m 220.252536,0 c -0.27246,-12.17105 -0.19776,-25.09608 0.33166,-38.72427 h 223.6457 a 227.99908,228.00372 0 0 1 8.28,38.72427 z M 41.540725,181.02109 a 227.99908,228.00372 0 0 1 17.84259,-38.72427 H 225.63264 c -2.42151,12.98777 -4.45297,26.07281 -5.81919,38.72427 z m 209.892105,0 c 1.03997,-13.90922 3.42234,-26.81596 6.74692,-38.72427 h 195.69317 a 227.99908,228.00372 0 0 1 17.84258,38.72427 z M 64.740961,133.52032 A 227.99908,228.00372 0 0 1 96.051856,94.796041 H 236.06961 c -3.05987,12.287259 -6.07477,25.390049 -8.74155,38.724279 z m 196.112969,0 c 4.80192,-14.52175 10.96848,-27.42708 17.67095,-38.724279 h 138.6795 a 227.99908,228.00372 0 0 1 31.3109,38.724279 z M 105.41267,86.019537 A 227.99908,228.00372 0 0 1 254.61726,28.666053 c -3.30134,10.858112 -9.7429,31.901037 -16.31878,57.353484 z m 178.62756,0 C 299.82749,62.260898 317.24402,46.83239 326.52798,39.636692 a 227.99908,228.00372 0 0 1 81.31559,46.382845 z" />
|
|
||||||
<path
|
|
||||||
id="path8-7-2"
|
|
||||||
style="display:inline;mix-blend-mode:color;fill:url(#linearGradient4);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-dasharray:none;stroke-opacity:0.566238;paint-order:stroke fill markers"
|
|
||||||
inkscape:label="Circle"
|
|
||||||
d="m 256.61651,28.654412 a 227.99908,228.00372 0 0 1 94.5288,20.521817 C 333.6029,50.138492 316.36688,53.89448 304.85851,59.64356 292.1635,65.985164 279.15359,76.8853 269.47486,86.017162 h -36.45054 c -0.93211,-0.612343 -1.88758,-1.230613 -2.86668,-1.850865 -4.89722,-14.18571 -13.27667,-38.807461 -17.49932,-51.232639 a 227.99908,228.00372 0 0 1 43.95819,-4.279246 z m -55.26491,6.800405 16.39068,41.477366 c -6.99844,-3.719734 -14.6813,-7.076497 -22.62503,-9.769213 -20.75463,-7.035126 -39.46594,-9.140222 -49.31352,-9.766875 A 227.99908,228.00372 0 0 1 201.3516,35.454817 Z m -58.6952,23.724872 c 8.69086,4.319561 29.66977,14.955598 49.97917,26.837473 H 105.40338 A 227.99908,228.00372 0 0 1 142.6564,59.179689 Z m 241.49988,8.486589 a 227.99908,228.00372 0 0 1 23.67337,18.350884 h -95.80906 c 1.32305,-0.721011 2.11058,-1.131861 2.11058,-1.131861 l -9.37935,-10.634328 34.47215,6.30174 c 0,1.2e-5 10.96426,-4.136248 36.05857,-11.028611 3.09966,-0.851315 6.06162,-1.454254 8.87374,-1.857824 z m 33.03417,27.127388 a 227.99908,228.00372 0 0 1 31.31091,38.724274 h -53.83388 c -16.25931,-10.82121 -36.0311,-20.56336 -55.93057,-22.04797 -47.50892,-3.54468 -62.97899,1.67459 -62.97899,1.67459 0,0 4.13963,-4.3321 13.03229,-12.01204 2.4447,-2.111347 5.46123,-4.289985 8.54904,-6.338854 z m -156.60325,0 c -4.10482,4.240439 -6.54978,7.041624 -6.54978,7.041624 0,0 -3.17295,-2.862104 -8.63949,-7.041624 z m -54.10293,0 c 19.35695,13.887414 22.86161,25.144334 22.86161,25.144334 0,0 -49.2172,-17.2685 -90.39112,-0.28064 -10.4477,4.31055 -19.37408,9.1049 -26.88099,13.86058 H 64.73166 A 227.99908,228.00372 0 0 1 96.042582,94.793666 Z m 247.37474,47.500784 a 227.99908,228.00372 0 0 1 17.84257,38.72426 H 354.04446 c -1.30773,-2.3199 -2.75142,-4.57103 -4.34409,-6.73083 -12.77878,-17.32895 -25.06121,-26.96783 -33.0852,-31.99343 h 27.15468 c 5.2719,0.84 10.81534,1.78919 16.40924,2.84819 27.04349,5.11994 72.84541,20.48007 72.84541,20.48007 0,0 -10.29636,-11.13669 -26.13186,-23.32826 z m -238.42446,0 c -30.05702,10.10404 -47.05829,24.07021 -56.34572,38.72426 H 41.531435 a 227.99908,228.00372 0 0 1 17.842579,-38.72426 h 40.221767 c -13.331141,10.41219 -19.387248,18.85882 -19.387248,18.85882 0,0 30.089197,-11.11726 59.298227,-18.85882 z m 41.2585,14.62133 c 0.90134,0.009 9.08984,0.64727 40.38179,18.55498 2.80164,1.60327 5.61377,3.47048 8.41452,5.54795 h -37.53597 c -6.43727,-15.58979 -11.34615,-24.10293 -11.34615,-24.10293 0,0 0.0258,-6.4e-4 0.0859,0 z m -12.72847,0.0418 c 0,0 2.97304,8.3824 6.55675,24.06118 H 204.0397 c 24.16152,-23.08045 39.92488,-24.06123 39.92488,-24.06123 z m 230.62688,32.83764 a 227.99908,228.00372 0 0 1 8.28,38.72426 H 365.99132 c -0.50409,-11.4347 -2.40394,-25.61361 -7.73264,-38.72426 z m -158.56772,0 c 12.80955,11.77756 24.77977,26.58932 33.71374,38.72426 h -65.85729 c -4.01234,-15.08912 -8.39298,-28.09557 -12.44549,-38.72426 z m -63.60057,0 c 2.16662,10.57203 4.40101,23.55135 6.23667,38.72426 h -95.8253 c 7.46906,-9.7857 18.55287,-23.51811 32.62829,-38.72426 z m -97.99154,0 c -5.95644,13.69644 -6.21521,27.44278 -5.00743,38.72426 H 30.361558 a 227.99908,228.00372 0 0 1 8.280001,-38.72426 z m 329.3606,47.50077 a 227.99908,228.00372 0 0 1 0.82336,19.36214 227.99908,228.00372 0 0 1 -0.82336,19.36214 H 293.59585 c -2.02639,-13.95226 -4.61647,-26.90106 -7.48446,-38.72428 h 69.90914 c 5.80191,8.33616 9.27267,13.98815 9.27267,13.98815 0,0 0.74161,-5.5263 0.8396,-13.98815 z m -224.14436,0 c 1.22897,11.76966 2.19512,24.70616 2.7113,38.72428 H 29.440786 a 227.99908,228.00372 0 0 1 -0.823361,-19.36214 227.99908,228.00372 0 0 1 0.823361,-19.36214 H 150.67895 c 0.39943,2.2888 0.8157,4.40137 1.19214,6.30638 0,0 1.52343,-2.281 4.43455,-6.30638 z m 223.22359,47.50078 a 227.99908,228.00372 0 0 1 -8.28,38.72427 H 294.27542 c 1.16002,-13.8461 1.41371,-26.99937 0.37574,-38.72427 z m -220.25252,0 c 0.27245,12.17105 0.19775,25.09608 -0.33167,38.72427 H 38.641559 a 227.99908,228.00372 0 0 1 -8.280001,-38.72427 z m 209.08496,47.50077 a 227.99908,228.00372 0 0 1 -17.84258,38.72427 H 287.61199 c 2.42151,-12.98777 4.45297,-26.07281 5.81919,-38.72427 z m -209.89209,0 c -1.03998,13.90921 -3.42235,26.81596 -6.74692,38.72427 H 59.371698 A 227.99908,228.00372 0 0 1 41.52912,332.29754 Z m 186.69187,47.50077 a 227.99908,228.00372 0 0 1 -31.3109,38.72428 H 277.17503 c 3.05986,-12.28726 6.07476,-25.39005 8.74154,-38.72428 z m -196.11297,0 c -4.80193,14.52175 -10.96849,27.42708 -17.67095,38.72428 H 96.040255 A 227.99908,228.00372 0 0 1 64.729344,379.79831 Z m 155.44125,47.50078 a 227.99908,228.00372 0 0 1 -149.20459,57.35348 c 3.30135,-10.85811 9.7429,-31.90104 16.31879,-57.35348 z m -178.62756,0 c -15.78726,23.75863 -33.20379,39.18714 -42.48775,46.38284 a 227.99908,228.00372 0 0 1 -81.31558,-46.38284 z" />
|
|
||||||
<ellipse
|
|
||||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="path192"
|
|
||||||
cx="88.20919"
|
|
||||||
cy="216.80666"
|
|
||||||
rx="43.306744"
|
|
||||||
ry="42.75507" />
|
|
||||||
<path
|
|
||||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="M 211.35225,346.71976 151.38374,303.96994 137.1338,384.12586 Z"
|
|
||||||
id="path195" />
|
|
||||||
<path
|
|
||||||
id="path196-7"
|
|
||||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:17.5749;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
d="m 309.50171,362.30735 86.0678,57.93833 m -75.48269,11.93227 57.93833,-86.0678" />
|
|
||||||
<rect
|
|
||||||
style="fill:none;fill-opacity:0.679924;stroke:#97f0e5;stroke-width:13.9781;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="rect197"
|
|
||||||
width="66.112526"
|
|
||||||
height="59.501274"
|
|
||||||
x="93.831078"
|
|
||||||
y="-503.61469"
|
|
||||||
transform="rotate(108)" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -12,7 +12,6 @@
|
|||||||
- [NetBSD](#netbsd)
|
- [NetBSD](#netbsd)
|
||||||
- [MSYS2](#msys2)
|
- [MSYS2](#msys2)
|
||||||
- [RedoxOS](#redoxos)
|
- [RedoxOS](#redoxos)
|
||||||
- [PlayStation 4](#playstation-4)
|
|
||||||
- [Windows](#windows)
|
- [Windows](#windows)
|
||||||
- [Windows 7, Windows 8 and Windows 8.1](#windows-7-windows-8-and-windows-81)
|
- [Windows 7, Windows 8 and Windows 8.1](#windows-7-windows-8-and-windows-81)
|
||||||
- [Windows Vista and below](#windows-vista-and-below)
|
- [Windows Vista and below](#windows-vista-and-below)
|
||||||
@@ -246,17 +245,6 @@ The package install may randomly hang at times, in which case it has to be resta
|
|||||||
|
|
||||||
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
|
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
|
||||||
|
|
||||||
## PlayStation 4
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export OO_PS4_TOOLCHAIN="$HOME/OpenOrbis/PS4Toolchain/prefix"
|
|
||||||
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cp $OO_PS4_TOOLCHAIN/include/endian.h $OO_PS4_TOOLCHAIN/include/sys/endian.h
|
|
||||||
```
|
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
### Windows 7, Windows 8 and Windows 8.1
|
### Windows 7, Windows 8 and Windows 8.1
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ Eden will store configuration files in the following directories:
|
|||||||
- **Android**: Data is stored internally.
|
- **Android**: Data is stored internally.
|
||||||
- **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`.
|
- **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`.
|
||||||
- **HaikuOS**: `/boot/home/config/settings/eden`
|
- **HaikuOS**: `/boot/home/config/settings/eden`
|
||||||
- **PlayStation 4**: `/data/eden`
|
|
||||||
|
|
||||||
If a `user` directory is present in the current working directory, that will override all global configuration directories and the emulator will use that instead.
|
If a `user` directory is present in the current working directory, that will override all global configuration directories and the emulator will use that instead.
|
||||||
|
|
||||||
|
|||||||
Vendored
-5
@@ -260,11 +260,6 @@ target_include_directories(tz PUBLIC ./tz)
|
|||||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||||
|
|
||||||
if (OPENORBIS)
|
|
||||||
add_library(ps4sup ps4sup/emutls.c ps4sup/stub.cpp)
|
|
||||||
target_include_directories(ps4sup PUBLIC ./ps4sup)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT TARGET RenderDoc::API)
|
if (NOT TARGET RenderDoc::API)
|
||||||
add_library(renderdoc INTERFACE)
|
add_library(renderdoc INTERFACE)
|
||||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||||
|
|||||||
Vendored
-184
@@ -1,184 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
/* ===---------- emutls.c - Implements __emutls_get_address ---------------===
|
|
||||||
*
|
|
||||||
* The LLVM Compiler Infrastructure
|
|
||||||
*
|
|
||||||
* This file is dual licensed under the MIT and the University of Illinois Open
|
|
||||||
* Source Licenses. See LICENSE.TXT for details.
|
|
||||||
*
|
|
||||||
* ===----------------------------------------------------------------------===
|
|
||||||
*/
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//#include "int_util.h"
|
|
||||||
|
|
||||||
/* Default is not to use posix_memalign, so systems like Android
|
|
||||||
* can use thread local data without heavier POSIX memory allocators.
|
|
||||||
*/
|
|
||||||
#ifndef EMUTLS_USE_POSIX_MEMALIGN
|
|
||||||
#define EMUTLS_USE_POSIX_MEMALIGN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* For every TLS variable xyz,
|
|
||||||
* there is one __emutls_control variable named __emutls_v.xyz.
|
|
||||||
* If xyz has non-zero initial value, __emutls_v.xyz's "value"
|
|
||||||
* will point to __emutls_t.xyz, which has the initial value.
|
|
||||||
*/
|
|
||||||
typedef struct __emutls_control {
|
|
||||||
size_t size; /* size of the object in bytes */
|
|
||||||
size_t align; /* alignment of the object in bytes */
|
|
||||||
union {
|
|
||||||
uintptr_t index; /* data[index-1] is the object address */
|
|
||||||
void* address; /* object address, when in single thread env */
|
|
||||||
} object;
|
|
||||||
void* value; /* null or non-zero initial value for the object */
|
|
||||||
} __emutls_control;
|
|
||||||
|
|
||||||
static inline void* emutls_memalign_alloc(size_t align, size_t size) {
|
|
||||||
void *base;
|
|
||||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
|
||||||
if (posix_memalign(&base, align, size) != 0)
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
#define EXTRA_ALIGN_PTR_BYTES (align - 1 + sizeof(void*))
|
|
||||||
char* object;
|
|
||||||
if ((object = malloc(EXTRA_ALIGN_PTR_BYTES + size)) == NULL)
|
|
||||||
abort();
|
|
||||||
base = (void*)(((uintptr_t)(object + EXTRA_ALIGN_PTR_BYTES))
|
|
||||||
& ~(uintptr_t)(align - 1));
|
|
||||||
|
|
||||||
((void**)base)[-1] = object;
|
|
||||||
#endif
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void emutls_memalign_free(void* base) {
|
|
||||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
|
||||||
free(base);
|
|
||||||
#else
|
|
||||||
/* The mallocated address is in ((void**)base)[-1] */
|
|
||||||
free(((void**)base)[-1]);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Emulated TLS objects are always allocated at run-time. */
|
|
||||||
static inline void* emutls_allocate_object(__emutls_control* control) {
|
|
||||||
/* Use standard C types, check with gcc's emutls.o. */
|
|
||||||
//typedef unsigned int gcc_word __attribute__((mode(word)));
|
|
||||||
//typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
|
|
||||||
//COMPILE_TIME_ASSERT(sizeof(size_t) == sizeof(gcc_word));
|
|
||||||
//COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer));
|
|
||||||
//COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*));
|
|
||||||
|
|
||||||
size_t size = control->size;
|
|
||||||
size_t align = control->align;
|
|
||||||
if (align < sizeof(void*))
|
|
||||||
align = sizeof(void*);
|
|
||||||
/* Make sure that align is power of 2. */
|
|
||||||
if ((align & (align - 1)) != 0)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
void* base = emutls_memalign_alloc(align, size);
|
|
||||||
if (control->value)
|
|
||||||
memcpy(base, control->value, size);
|
|
||||||
else
|
|
||||||
memset(base, 0, size);
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
static size_t emutls_num_object = 0; /* number of allocated TLS objects */
|
|
||||||
|
|
||||||
typedef struct emutls_address_array {
|
|
||||||
uintptr_t size; /* number of elements in the 'data' array */
|
|
||||||
void* data[];
|
|
||||||
} emutls_address_array;
|
|
||||||
|
|
||||||
static pthread_key_t emutls_pthread_key;
|
|
||||||
|
|
||||||
static void emutls_key_destructor(void* ptr) {
|
|
||||||
emutls_address_array* array = (emutls_address_array*)ptr;
|
|
||||||
uintptr_t i;
|
|
||||||
for (i = 0; i < array->size; ++i) {
|
|
||||||
if (array->data[i])
|
|
||||||
emutls_memalign_free(array->data[i]);
|
|
||||||
}
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void emutls_init(void) {
|
|
||||||
if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns control->object.index; set index if not allocated yet. */
|
|
||||||
static inline uintptr_t emutls_get_index(__emutls_control* control) {
|
|
||||||
uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE);
|
|
||||||
if (!index) {
|
|
||||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
|
||||||
pthread_once(&once, emutls_init);
|
|
||||||
pthread_mutex_lock(&emutls_mutex);
|
|
||||||
index = control->object.index;
|
|
||||||
if (!index) {
|
|
||||||
index = ++emutls_num_object;
|
|
||||||
__atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE);
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&emutls_mutex);
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates newly allocated thread local emutls_address_array. */
|
|
||||||
static inline void emutls_check_array_set_size(emutls_address_array* array,
|
|
||||||
uintptr_t size) {
|
|
||||||
if (array == NULL)
|
|
||||||
abort();
|
|
||||||
array->size = size;
|
|
||||||
pthread_setspecific(emutls_pthread_key, (void*)array);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the new 'data' array size, number of elements,
|
|
||||||
* which must be no smaller than the given index.
|
|
||||||
*/
|
|
||||||
static inline uintptr_t emutls_new_data_array_size(uintptr_t index) {
|
|
||||||
/* Need to allocate emutls_address_array with one extra slot
|
|
||||||
* to store the data array size.
|
|
||||||
* Round up the emutls_address_array size to multiple of 16.
|
|
||||||
*/
|
|
||||||
return ((index + 1 + 15) & ~((uintptr_t)15)) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the thread local emutls_address_array.
|
|
||||||
* Extends its size if necessary to hold address at index.
|
|
||||||
*/
|
|
||||||
static inline emutls_address_array* emutls_get_address_array(uintptr_t index) {
|
|
||||||
emutls_address_array* array = pthread_getspecific(emutls_pthread_key);
|
|
||||||
if (array == NULL) {
|
|
||||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
|
||||||
array = calloc(new_size + 1, sizeof(void*));
|
|
||||||
emutls_check_array_set_size(array, new_size);
|
|
||||||
} else if (index > array->size) {
|
|
||||||
uintptr_t orig_size = array->size;
|
|
||||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
|
||||||
array = realloc(array, (new_size + 1) * sizeof(void*));
|
|
||||||
if (array)
|
|
||||||
memset(array->data + orig_size, 0,
|
|
||||||
(new_size - orig_size) * sizeof(void*));
|
|
||||||
emutls_check_array_set_size(array, new_size);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* __emutls_get_address(__emutls_control* control) {
|
|
||||||
uintptr_t index = emutls_get_index(control);
|
|
||||||
emutls_address_array* array = emutls_get_address_array(index);
|
|
||||||
if (array->data[index - 1] == NULL)
|
|
||||||
array->data[index - 1] = emutls_allocate_object(control);
|
|
||||||
return array->data[index - 1];
|
|
||||||
}
|
|
||||||
Vendored
-38
@@ -1,38 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#define STUB_WEAK(name) \
|
|
||||||
extern "C" void name() { \
|
|
||||||
printf("called " #name); \
|
|
||||||
asm volatile("ud2"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int __pthread_cxa_finalize();
|
|
||||||
extern "C" void __cxa_thread_atexit_impl() {
|
|
||||||
//printf("__cxa_thread_atexit_impl called!\n");
|
|
||||||
//__pthread_cxa_finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
STUB_WEAK(__assert)
|
|
||||||
STUB_WEAK(ZSTD_trace_compress_begin)
|
|
||||||
STUB_WEAK(ZSTD_trace_compress_end)
|
|
||||||
STUB_WEAK(ZSTD_trace_decompress_begin)
|
|
||||||
STUB_WEAK(ZSTD_trace_decompress_end)
|
|
||||||
|
|
||||||
extern "C" thread_local struct {
|
|
||||||
char pad[512];
|
|
||||||
} _ThreadRuneLocale = {};
|
|
||||||
extern "C" struct {
|
|
||||||
char pad[512];
|
|
||||||
} __h_errno = {};
|
|
||||||
|
|
||||||
FILE* __stderrp = stdout;
|
|
||||||
FILE* __stdinp = stdin;
|
|
||||||
|
|
||||||
#undef STUB_WEAK
|
|
||||||
|
|
||||||
// THIS MAKES STD::COUT AND SUCH WORK :)
|
|
||||||
#include <iostream>
|
|
||||||
std::ios_base::Init init;
|
|
||||||
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -24,6 +27,6 @@ object SettingsFile {
|
|||||||
|
|
||||||
fun loadCustomConfig(game: Game) {
|
fun loadCustomConfig(game: Game) {
|
||||||
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
||||||
NativeConfig.initializePerGameConfig(game.programId, fileName)
|
NativeConfig.initializePerGameConfig(game.applicationId, fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ class AddonsFragment : Fragment() {
|
|||||||
fragmentManager = parentFragmentManager,
|
fragmentManager = parentFragmentManager,
|
||||||
addonViewModel = addonViewModel,
|
addonViewModel = addonViewModel,
|
||||||
documents = documents,
|
documents = documents,
|
||||||
programId = args.game.programId
|
programId = args.game.applicationId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (GpuDriverHelper.isAdrenoGpu()) {
|
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||||
val programIdHex = game!!.programIdHex
|
val programIdHex = game!!.applicationIdHex
|
||||||
if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) {
|
if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) {
|
||||||
Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex")
|
Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+2
-2
@@ -59,7 +59,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
|||||||
NativeFreedrenoConfig.initializeFreedrenoConfig()
|
NativeFreedrenoConfig.initializeFreedrenoConfig()
|
||||||
|
|
||||||
if (isPerGameConfig) {
|
if (isPerGameConfig) {
|
||||||
NativeFreedrenoConfig.loadPerGameConfig(game!!.programIdHex)
|
NativeFreedrenoConfig.loadPerGameConfig(game!!.applicationIdHex)
|
||||||
} else {
|
} else {
|
||||||
NativeFreedrenoConfig.reloadFreedrenoConfig()
|
NativeFreedrenoConfig.reloadFreedrenoConfig()
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
|||||||
|
|
||||||
binding.buttonSave.setOnClickListener {
|
binding.buttonSave.setOnClickListener {
|
||||||
if (isPerGameConfig) {
|
if (isPerGameConfig) {
|
||||||
NativeFreedrenoConfig.savePerGameConfig(game!!.programIdHex)
|
NativeFreedrenoConfig.savePerGameConfig(game!!.applicationIdHex)
|
||||||
showSnackbar(getString(R.string.freedreno_per_game_saved))
|
showSnackbar(getString(R.string.freedreno_per_game_saved))
|
||||||
} else {
|
} else {
|
||||||
NativeFreedrenoConfig.saveFreedrenoConfig()
|
NativeFreedrenoConfig.saveFreedrenoConfig()
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
|
|
||||||
val shaderCacheDir = File(
|
val shaderCacheDir = File(
|
||||||
DirectoryInitialization.userDirectory +
|
DirectoryInitialization.userDirectory +
|
||||||
"/cache/shader/" + args.game.settingsName.lowercase()
|
"/cache/shader/" + args.game.shaderCacheName.lowercase()
|
||||||
)
|
)
|
||||||
if (shaderCacheDir.exists()) {
|
if (shaderCacheDir.exists()) {
|
||||||
add(
|
add(
|
||||||
@@ -600,7 +600,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
val files = cacheSaveDir.listFiles()
|
val files = cacheSaveDir.listFiles()
|
||||||
var savesFolderFile: File? = null
|
var savesFolderFile: File? = null
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
val savesFolderName = args.game.programIdHex
|
val savesFolderName = args.game.applicationIdHex
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
if (file.isDirectory && file.name == savesFolderName) {
|
if (file.isDirectory && file.name == savesFolderName) {
|
||||||
savesFolderFile = file
|
savesFolderFile = file
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ class InstallableFragment : Fragment() {
|
|||||||
fragmentManager = parentFragmentManager,
|
fragmentManager = parentFragmentManager,
|
||||||
addonViewModel = addonViewModel,
|
addonViewModel = addonViewModel,
|
||||||
documents = documents,
|
documents = documents,
|
||||||
programId = addonViewModel.game?.programId
|
programId = addonViewModel.game?.applicationId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,10 +142,11 @@ class AddonViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onDeleteAddon(patch: Patch) {
|
fun onDeleteAddon(patch: Patch) {
|
||||||
|
val currentGame = game ?: return
|
||||||
when (PatchType.from(patch.type)) {
|
when (PatchType.from(patch.type)) {
|
||||||
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
|
PatchType.Update -> NativeLibrary.removeUpdate(currentGame.programId)
|
||||||
PatchType.DLC -> NativeLibrary.removeDLC(patch.programId)
|
PatchType.DLC -> NativeLibrary.removeDLC(currentGame.applicationId)
|
||||||
PatchType.Mod -> NativeLibrary.removeMod(patch.programId, patch.name)
|
PatchType.Mod -> NativeLibrary.removeMod(currentGame.programId, patch.name)
|
||||||
}
|
}
|
||||||
refreshAddons(force = true)
|
refreshAddons(force = true)
|
||||||
}
|
}
|
||||||
@@ -165,7 +166,7 @@ class AddonViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NativeConfig.setDisabledAddons(
|
NativeConfig.setDisabledAddons(
|
||||||
currentGame.programId,
|
currentGame.applicationId,
|
||||||
currentList.mapNotNull {
|
currentList.mapNotNull {
|
||||||
if (it.enabled) {
|
if (it.enabled) {
|
||||||
null
|
null
|
||||||
@@ -199,6 +200,6 @@ class AddonViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun gameKey(game: Game): String {
|
private fun gameKey(game: Game): String {
|
||||||
return "${game.programId}|${game.path}"
|
return "${game.applicationId}|${game.path}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class DriverViewModel : ViewModel() {
|
|||||||
?: return@withContext
|
?: return@withContext
|
||||||
val shaderDir = File(
|
val shaderDir = File(
|
||||||
externalFilesDir.absolutePath +
|
externalFilesDir.absolutePath +
|
||||||
"/shader/" + game.settingsName.lowercase()
|
"/shader/" + game.shaderCacheName.lowercase()
|
||||||
)
|
)
|
||||||
if (shaderDir.exists()) {
|
if (shaderDir.exists()) {
|
||||||
shaderDir.deleteRecursively()
|
shaderDir.deleteRecursively()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
@@ -35,19 +35,26 @@ class Game(
|
|||||||
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
|
val keyAddedToLibraryTime get() = "${path}_AddedToLibraryTime"
|
||||||
val keyLastPlayedTime get() = "${path}_LastPlayed"
|
val keyLastPlayedTime get() = "${path}_LastPlayed"
|
||||||
|
|
||||||
|
private val programIdLong: Long
|
||||||
|
get() = programId.toLongOrNull() ?: 0L
|
||||||
|
|
||||||
|
private val applicationIdLong: Long
|
||||||
|
get() = programIdLong and -8192L
|
||||||
|
|
||||||
|
val applicationId: String
|
||||||
|
get() = applicationIdLong.toString()
|
||||||
|
|
||||||
val settingsName: String
|
val settingsName: String
|
||||||
get() {
|
get() {
|
||||||
val programIdLong = programId.toLong()
|
return if (applicationIdLong == 0L) {
|
||||||
return if (programIdLong == 0L) {
|
|
||||||
FileUtil.getFilename(Uri.parse(path))
|
FileUtil.getFilename(Uri.parse(path))
|
||||||
} else {
|
} else {
|
||||||
"0" + programIdLong.toString(16).uppercase()
|
"0" + applicationIdLong.toString(16).uppercase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val programIdHex: String
|
val programIdHex: String
|
||||||
get() {
|
get() {
|
||||||
val programIdLong = programId.toLong()
|
|
||||||
return if (programIdLong == 0L) {
|
return if (programIdLong == 0L) {
|
||||||
"0"
|
"0"
|
||||||
} else {
|
} else {
|
||||||
@@ -55,16 +62,32 @@ class Game(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val shaderCacheName: String
|
||||||
|
get() = if (programIdLong == 0L) {
|
||||||
|
FileUtil.getFilename(Uri.parse(path))
|
||||||
|
} else {
|
||||||
|
"0" + programIdLong.toString(16).uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
val applicationIdHex: String
|
||||||
|
get() {
|
||||||
|
return if (applicationIdLong == 0L) {
|
||||||
|
"0"
|
||||||
|
} else {
|
||||||
|
"0" + applicationIdLong.toString(16).uppercase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val saveZipName: String
|
val saveZipName: String
|
||||||
get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${
|
get() = "$title ${YuzuApplication.appContext.getString(R.string.save_data).lowercase()} - ${
|
||||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||||
}.zip"
|
}.zip"
|
||||||
|
|
||||||
val saveDir: String
|
val saveDir: String
|
||||||
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(programId)
|
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(applicationId)
|
||||||
|
|
||||||
val addonDir: String
|
val addonDir: String
|
||||||
get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/"
|
get() = DirectoryInitialization.userDirectory + "/load/" + applicationIdHex + "/"
|
||||||
|
|
||||||
val launchIntent: Intent
|
val launchIntent: Intent
|
||||||
get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
|
get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
@@ -65,7 +65,7 @@ object CustomSettingsHandler {
|
|||||||
// Initialize per-game config
|
// Initialize per-game config
|
||||||
try {
|
try {
|
||||||
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
val fileName = FileUtil.getFilename(Uri.parse(game.path))
|
||||||
NativeConfig.initializePerGameConfig(game.programId, fileName)
|
NativeConfig.initializePerGameConfig(game.applicationId, fileName)
|
||||||
Log.info("[CustomSettingsHandler] Successfully applied custom settings")
|
Log.info("[CustomSettingsHandler] Successfully applied custom settings")
|
||||||
return game
|
return game
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -333,20 +333,20 @@ object CustomSettingsHandler {
|
|||||||
*/
|
*/
|
||||||
fun findGameByTitleId(titleId: String, context: Context): Game? {
|
fun findGameByTitleId(titleId: String, context: Context): Game? {
|
||||||
Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId")
|
Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId")
|
||||||
// Convert hex title ID to decimal for comparison with programId
|
// Convert the program ID to the application ID used by per-game settings.
|
||||||
val programIdDecimal = try {
|
val applicationIdLong = try {
|
||||||
titleId.toLong(16).toString()
|
titleId.toLong(16) and -8192L
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId")
|
Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
val applicationIdDecimal = applicationIdLong.toString()
|
||||||
|
|
||||||
// Expected hex format with "0" prefix
|
// Expected hex format with "0" prefix
|
||||||
val expectedHex = "0${titleId.uppercase()}"
|
val expectedHex = "0${applicationIdLong.toString(16).uppercase()}"
|
||||||
// First check cached games for fast lookup
|
// First check cached games for fast lookup
|
||||||
GameHelper.cachedGameList.find { game ->
|
GameHelper.cachedGameList.find { game ->
|
||||||
game.programId == programIdDecimal ||
|
game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
|
||||||
game.programIdHex.equals(expectedHex, ignoreCase = true)
|
|
||||||
}?.let { foundGame ->
|
}?.let { foundGame ->
|
||||||
Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}")
|
Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}")
|
||||||
return foundGame
|
return foundGame
|
||||||
@@ -355,8 +355,7 @@ object CustomSettingsHandler {
|
|||||||
Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...")
|
Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...")
|
||||||
val allGames = GameHelper.getGames()
|
val allGames = GameHelper.getGames()
|
||||||
val foundGame = allGames.find { game ->
|
val foundGame = allGames.find { game ->
|
||||||
game.programId == programIdDecimal ||
|
game.applicationId == applicationIdDecimal || game.applicationIdHex.equals(expectedHex, ignoreCase = true)
|
||||||
game.programIdHex.equals(expectedHex, ignoreCase = true)
|
|
||||||
}
|
}
|
||||||
if (foundGame != null) {
|
if (foundGame != null) {
|
||||||
Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}")
|
Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}")
|
||||||
|
|||||||
@@ -170,12 +170,12 @@ object GameHelper {
|
|||||||
val game = getGame(it.uri, true, false)
|
val game = getGame(it.uri, true, false)
|
||||||
if (game != null) {
|
if (game != null) {
|
||||||
games.add(game)
|
games.add(game)
|
||||||
if (game.programId != "0") {
|
if (game.applicationId != "0") {
|
||||||
gamesByProgramId[game.programId] = game
|
gamesByProgramId[game.applicationId] = game
|
||||||
}
|
}
|
||||||
} else if (mountedContainer) {
|
} else if (mountedContainer) {
|
||||||
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
||||||
gamesByProgramId[(programId and 0x800L.inv()).toString()]
|
gamesByProgramId[(programId and -8192L).toString()]
|
||||||
}?.let { existingGame ->
|
}?.let { existingGame ->
|
||||||
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
||||||
existingGame.version = GameMetadata.getVersion(
|
existingGame.version = GameMetadata.getVersion(
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ extern "C" {
|
|||||||
#include "core/frontend/applets/software_keyboard.h"
|
#include "core/frontend/applets/software_keyboard.h"
|
||||||
#include "core/frontend/applets/web_browser.h"
|
#include "core/frontend/applets/web_browser.h"
|
||||||
#include "common/android/applets/web_browser.h"
|
#include "common/android/applets/web_browser.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
@@ -311,11 +312,23 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
ConfigureFilesystemProvider(filepath);
|
ConfigureFilesystemProvider(filepath);
|
||||||
|
|
||||||
// Load the ROM.
|
// Load the ROM.
|
||||||
|
const u64 previous_program_id =
|
||||||
|
program_index != 0 && m_next_program_id.load() >
|
||||||
|
static_cast<u64>(Service::AM::AppletProgramId::MaxProgramId)
|
||||||
|
? m_next_program_id.load()
|
||||||
|
: 0;
|
||||||
|
|
||||||
Service::AM::FrontendAppletParameters params{
|
Service::AM::FrontendAppletParameters params{
|
||||||
|
.program_id = previous_program_id,
|
||||||
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
.applet_id = static_cast<Service::AM::AppletId>(m_applet_id),
|
||||||
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
.launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated
|
||||||
: Service::AM::LaunchType::ApplicationInitiated,
|
: Service::AM::LaunchType::ApplicationInitiated,
|
||||||
.program_index = static_cast<s32>(program_index),
|
.program_index = static_cast<s32>(program_index),
|
||||||
|
.previous_program_index =
|
||||||
|
previous_program_id != 0
|
||||||
|
? static_cast<s32>(previous_program_id -
|
||||||
|
FileSys::GetBaseTitleID(previous_program_id))
|
||||||
|
: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
|
||||||
@@ -328,8 +341,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
m_system.GetCpuManager().OnGpuReady();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
|
m_system.RegisterApplicationChangedCallback(
|
||||||
|
[&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); });
|
||||||
|
|
||||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||||
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||||
|
m_next_program_id = m_system.GetApplicationProcessProgramID();
|
||||||
m_next_program_index = program_index_;
|
m_next_program_index = program_index_;
|
||||||
EmulationSession::GetInstance().HaltEmulation();
|
EmulationSession::GetInstance().HaltEmulation();
|
||||||
});
|
});
|
||||||
@@ -407,20 +424,58 @@ void EmulationSession::RunEmulation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
std::optional<u64> reload_title;
|
||||||
{
|
{
|
||||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
|
||||||
[&]() { return !m_is_running; })) {
|
return !m_is_running || m_pending_shader_cache_title.has_value();
|
||||||
// Emulation halted.
|
})) {
|
||||||
break;
|
if (!m_is_running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reload_title.has_value())
|
||||||
|
ReloadDiskShaderCache(*reload_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset current applet ID.
|
// Reset current applet ID.
|
||||||
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
m_pending_shader_cache_title = program_id;
|
||||||
|
}
|
||||||
|
m_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmulationSession::ReloadDiskShaderCache(u64 program_id) {
|
||||||
|
if (!Settings::values.use_disk_shader_cache.GetValue())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
|
||||||
|
|
||||||
|
const bool was_paused = m_is_paused;
|
||||||
|
|
||||||
|
m_system.Pause();
|
||||||
|
m_system.GPU().WaitForIdle();
|
||||||
|
m_system.GPU().ObtainContext();
|
||||||
|
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||||
|
m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{},
|
||||||
|
LoadDiskCacheProgress);
|
||||||
|
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||||
|
|
||||||
|
m_system.GPU().ReleaseContext();
|
||||||
|
|
||||||
|
if (!was_paused)
|
||||||
|
m_system.Run();
|
||||||
|
}
|
||||||
|
|
||||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||||
return m_software_keyboard;
|
return m_software_keyboard;
|
||||||
}
|
}
|
||||||
@@ -733,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
|||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jupdatePath) {
|
jstring jupdatePath) {
|
||||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||||
@@ -741,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
|||||||
for (const auto& item : nsp->GetNCAs()) {
|
for (const auto& item : nsp->GetNCAs()) {
|
||||||
for (const auto& nca_details : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||||
if (update_id == program_id) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1436,7 +1491,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_firmwareVersion(JNIEnv* env, jclas
|
|||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_gameRequiresFirmware(JNIEnv* env, jclass clazz, jstring jprogramId) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_gameRequiresFirmware(JNIEnv* env, jclass clazz, jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
|
|
||||||
return FirmwareManager::GameRequiresFirmware(program_id);
|
return FirmwareManager::GameRequiresFirmware(program_id);
|
||||||
}
|
}
|
||||||
@@ -1520,20 +1575,21 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
|
|||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
|
ContentManager::RemoveUpdate(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
program_id);
|
program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(
|
||||||
|
EmulationSession::GetProgramId(env, jprogramId));
|
||||||
ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
|
ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
||||||
jstring jname) {
|
jstring jname) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
|
ContentManager::RemoveMod(EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||||
program_id, Common::Android::GetJString(env, jname));
|
program_id, Common::Android::GetJString(env, jname));
|
||||||
}
|
}
|
||||||
@@ -1580,7 +1636,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
|
|||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
if (program_id == 0) {
|
if (program_id == 0) {
|
||||||
return Common::Android::ToJString(env, "");
|
return Common::Android::ToJString(env, "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include "common/android/applets/software_keyboard.h"
|
#include "common/android/applets/software_keyboard.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -44,6 +46,7 @@ public:
|
|||||||
void HaltEmulation();
|
void HaltEmulation();
|
||||||
void RunEmulation();
|
void RunEmulation();
|
||||||
void ShutdownEmulation();
|
void ShutdownEmulation();
|
||||||
|
void RequestDiskShaderCacheReload(u64 program_id);
|
||||||
|
|
||||||
const Core::PerfStatsResults& PerfStats();
|
const Core::PerfStatsResults& PerfStats();
|
||||||
int ShadersBuilding();
|
int ShadersBuilding();
|
||||||
@@ -65,6 +68,7 @@ private:
|
|||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||||
static void ChangeProgram(std::size_t program_index);
|
static void ChangeProgram(std::size_t program_index);
|
||||||
|
void ReloadDiskShaderCache(u64 program_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Window management
|
// Window management
|
||||||
@@ -83,6 +87,7 @@ private:
|
|||||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
int m_applet_id{1};
|
int m_applet_id{1};
|
||||||
|
std::optional<u64> m_pending_shader_cache_title;
|
||||||
|
|
||||||
// GPU driver parameters
|
// GPU driver parameters
|
||||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||||
@@ -93,4 +98,5 @@ private:
|
|||||||
|
|
||||||
// Program index for next boot
|
// Program index for next boot
|
||||||
std::atomic<s32> m_next_program_index = -1;
|
std::atomic<s32> m_next_program_index = -1;
|
||||||
|
std::atomic<u64> m_next_program_id = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "frontend_common/settings_generator.h"
|
#include "frontend_common/settings_generator.h"
|
||||||
#include "native.h"
|
#include "native.h"
|
||||||
@@ -56,7 +57,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jo
|
|||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jfileName) {
|
jstring jfileName) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
auto file_name = Common::Android::GetJString(env, jfileName);
|
auto file_name = Common::Android::GetJString(env, jfileName);
|
||||||
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
||||||
per_game_config =
|
per_game_config =
|
||||||
@@ -322,7 +323,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject
|
|||||||
|
|
||||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
||||||
jobjectArray jdisabledAddonsArray =
|
jobjectArray jdisabledAddonsArray =
|
||||||
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
env->NewObjectArray(disabledAddons.size(), Common::Android::GetStringClass(),
|
||||||
@@ -337,7 +338,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
|
|||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jobjectArray jdisabledAddons) {
|
jobjectArray jdisabledAddons) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
const auto program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||||
Settings::values.disabled_addons[program_id].clear();
|
Settings::values.disabled_addons[program_id].clear();
|
||||||
std::vector<std::string> disabled_addons;
|
std::vector<std::string> disabled_addons;
|
||||||
const int size = env->GetArrayLength(jdisabledAddons);
|
const int size = env->GetArrayLength(jdisabledAddons);
|
||||||
|
|||||||
@@ -254,10 +254,4 @@ else()
|
|||||||
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
target_compile_definitions(audio_core PRIVATE HAVE_SDL3)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OPENORBIS)
|
|
||||||
target_sources(audio_core PRIVATE
|
|
||||||
sink/ps4_sink.cpp
|
|
||||||
sink/ps4_sink.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
create_target_directory_groups(audio_core)
|
create_target_directory_groups(audio_core)
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -7,13 +10,12 @@
|
|||||||
namespace AudioCore::ADSP::OpusDecoder {
|
namespace AudioCore::ADSP::OpusDecoder {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool IsValidChannelCount(u32 channel_count) {
|
constexpr u32 OpusStreamCountMax = 255;
|
||||||
return channel_count == 1 || channel_count == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
|
||||||
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
|
return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax &&
|
||||||
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
|
static_cast<s32>(stereo_stream_count) >= 0 &&
|
||||||
|
stereo_stream_count <= total_stream_count;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -105,7 +108,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan
|
|||||||
shared_memory.host_send_data[4] = total_stream_count;
|
shared_memory.host_send_data[4] = total_stream_count;
|
||||||
shared_memory.host_send_data[5] = stereo_stream_count;
|
shared_memory.host_send_data[5] = stereo_stream_count;
|
||||||
|
|
||||||
ASSERT(channel_count <= MaxChannels);
|
ASSERT(channel_count <= shared_memory.channel_mapping.size());
|
||||||
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8));
|
||||||
|
|
||||||
opus_decoder.Send(ADSP::Direction::DSP,
|
opus_decoder.Send(ADSP::Direction::DSP,
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <span>
|
|
||||||
#include <stop_token>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <orbis/AudioOut.h>
|
|
||||||
|
|
||||||
#include "audio_core/common/common.h"
|
|
||||||
#include "audio_core/sink/ps4_sink.h"
|
|
||||||
#include "audio_core/sink/sink_stream.h"
|
|
||||||
#include "common/alignment.h"
|
|
||||||
#include "common/logging.h"
|
|
||||||
#include "common/scope_exit.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
|
|
||||||
namespace AudioCore::Sink {
|
|
||||||
|
|
||||||
/// @brief PS4 sink stream, responsible for sinking samples to hardware.
|
|
||||||
struct PS4SinkStream final : public SinkStream {
|
|
||||||
/// @brief Create a new sink stream.
|
|
||||||
/// @param device_channels_ - Number of channels supported by the hardware.
|
|
||||||
/// @param system_channels_ - Number of channels the audio systems expect.
|
|
||||||
/// @param output_device - Name of the output device to use for this stream.
|
|
||||||
/// @param input_device - Name of the input device to use for this stream.
|
|
||||||
/// @param type_ - Type of this stream.
|
|
||||||
/// @param system_ - Core system.
|
|
||||||
/// @param event - Event used only for audio renderer, signalled on buffer consume.
|
|
||||||
PS4SinkStream(u32 device_channels_, u32 system_channels_, const std::string& output_device, const std::string& input_device, StreamType type_, Core::System& system_)
|
|
||||||
: SinkStream{system_, type_}
|
|
||||||
{
|
|
||||||
system_channels = system_channels_;
|
|
||||||
device_channels = device_channels_;
|
|
||||||
|
|
||||||
constexpr size_t PREFERRED_SAMPLE_COUNT = 1024;
|
|
||||||
auto const param = (device_channels > 1 ? ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO : ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO);
|
|
||||||
audio_dev = sceAudioOutOpen(ORBIS_USER_SERVICE_USER_ID_SYSTEM, ORBIS_AUDIO_OUT_PORT_TYPE_MAIN, 0, PREFERRED_SAMPLE_COUNT, TargetSampleRate, param);
|
|
||||||
if (audio_dev > 0) {
|
|
||||||
audio_thread = std::jthread([=, this](std::stop_token stop_token) {
|
|
||||||
std::array<s16, PREFERRED_SAMPLE_COUNT * MaxChannels> buffer;
|
|
||||||
while (!stop_token.stop_requested()) {
|
|
||||||
if (this->type == StreamType::In) {
|
|
||||||
this->ProcessAudioIn(buffer, PREFERRED_SAMPLE_COUNT);
|
|
||||||
(void)buffer; // TODO: microphone support
|
|
||||||
} else {
|
|
||||||
int err = 0;
|
|
||||||
std::fill(buffer.begin(), buffer.end(), 0);
|
|
||||||
this->ProcessAudioOutAndRender(buffer, PREFERRED_SAMPLE_COUNT);
|
|
||||||
sceAudioOutOutput(audio_dev, nullptr);
|
|
||||||
if ((err = sceAudioOutOutput(audio_dev, buffer.data())) < 0)
|
|
||||||
LOG_ERROR(Service_Audio, "{}", err);
|
|
||||||
}
|
|
||||||
// Wait for pause, we don't really have a native pause
|
|
||||||
// so this is the best i can do
|
|
||||||
while (paused && !stop_token.stop_requested())
|
|
||||||
asm volatile ("pause"); //dont shootup cpu usage to 100%
|
|
||||||
}
|
|
||||||
sceAudioOutClose(audio_dev);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Service_Audio, "Failed to create audio device! {:#x}", uint32_t(audio_dev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~PS4SinkStream() override {
|
|
||||||
LOG_DEBUG(Service_Audio, "Destroying PS4 stream {}", name);
|
|
||||||
if (audio_thread.joinable()) {
|
|
||||||
audio_thread.request_stop();
|
|
||||||
audio_thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Finalize() override {
|
|
||||||
if (audio_dev > 0) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Start(bool resume = false) override {
|
|
||||||
if (audio_dev > 0 && paused) {
|
|
||||||
paused = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stop() override {
|
|
||||||
if (audio_dev > 0 && !paused) {
|
|
||||||
SignalPause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::jthread audio_thread;
|
|
||||||
int32_t audio_dev{};
|
|
||||||
};
|
|
||||||
|
|
||||||
PS4Sink::PS4Sink(std::string_view target_device_name) {
|
|
||||||
int32_t rc = sceAudioOutInit();
|
|
||||||
if (rc == 0 || unsigned(rc) == ORBIS_AUDIO_OUT_ERROR_ALREADY_INIT) {
|
|
||||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
|
||||||
output_device = target_device_name;
|
|
||||||
} else {
|
|
||||||
output_device.clear();
|
|
||||||
}
|
|
||||||
device_channels = 2;
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Service_Audio, "Unable to open audio out! {:#x}", uint32_t(rc));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PS4Sink::~PS4Sink() = default;
|
|
||||||
|
|
||||||
/// @brief Create a new sink stream.
|
|
||||||
/// @param system - Core system.
|
|
||||||
/// @param system_channels - Number of channels the audio system expects. May differ from the device's channel count.
|
|
||||||
/// @param name - Name of this stream.
|
|
||||||
/// @param type - Type of this stream, render/in/out.
|
|
||||||
/// @return A pointer to the created SinkStream
|
|
||||||
SinkStream* PS4Sink::AcquireSinkStream(Core::System& system, u32 system_channels_, const std::string&, StreamType type) {
|
|
||||||
system_channels = system_channels_;
|
|
||||||
SinkStreamPtr& stream = sink_streams.emplace_back(std::make_unique<PS4SinkStream>(device_channels, system_channels, output_device, input_device, type, system));
|
|
||||||
return stream.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PS4Sink::CloseStream(SinkStream* stream) {
|
|
||||||
for (size_t i = 0; i < sink_streams.size(); i++) {
|
|
||||||
if (sink_streams[i].get() == stream) {
|
|
||||||
sink_streams[i].reset();
|
|
||||||
sink_streams.erase(sink_streams.begin() + i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PS4Sink::CloseStreams() {
|
|
||||||
sink_streams.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
f32 PS4Sink::GetDeviceVolume() const {
|
|
||||||
return sink_streams.size() > 0 ? sink_streams[0]->GetDeviceVolume() : 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PS4Sink::SetDeviceVolume(f32 volume) {
|
|
||||||
for (auto& stream : sink_streams)
|
|
||||||
stream->SetDeviceVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PS4Sink::SetSystemVolume(f32 volume) {
|
|
||||||
for (auto& stream : sink_streams)
|
|
||||||
stream->SetSystemVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> ListPS4SinkDevices(bool capture) {
|
|
||||||
return {{"Default"}};
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetPS4Latency() {
|
|
||||||
return TargetSampleCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "audio_core/sink/sink.h"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace AudioCore::Sink {
|
|
||||||
class SinkStream;
|
|
||||||
|
|
||||||
/// @brief PS4 backend sink, holds multiple output streams and is responsible for sinking samples to
|
|
||||||
/// hardware. Used by Audio Render, Audio In and Audio Out.
|
|
||||||
struct PS4Sink final : public Sink {
|
|
||||||
explicit PS4Sink(std::string_view device_id);
|
|
||||||
~PS4Sink() override;
|
|
||||||
SinkStream* AcquireSinkStream(Core::System& system, u32 system_channels, const std::string& name, StreamType type) override;
|
|
||||||
void CloseStream(SinkStream* stream) override;
|
|
||||||
void CloseStreams() override;
|
|
||||||
f32 GetDeviceVolume() const override;
|
|
||||||
void SetDeviceVolume(f32 volume) override;
|
|
||||||
void SetSystemVolume(f32 volume) override;
|
|
||||||
/// Name of the output device used by streams
|
|
||||||
std::string output_device;
|
|
||||||
/// Name of the input device used by streams
|
|
||||||
std::string input_device;
|
|
||||||
/// Vector of streams managed by this sink
|
|
||||||
std::vector<SinkStreamPtr> sink_streams;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::string> ListPS4SinkDevices(bool capture);
|
|
||||||
u32 GetPS4Latency();
|
|
||||||
|
|
||||||
} // namespace AudioCore::Sink
|
|
||||||
@@ -19,9 +19,6 @@
|
|||||||
#ifdef HAVE_SDL3
|
#ifdef HAVE_SDL3
|
||||||
#include "audio_core/sink/sdl3_sink.h"
|
#include "audio_core/sink/sdl3_sink.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
#include "audio_core/sink/ps4_sink.h"
|
|
||||||
#endif
|
|
||||||
#include "audio_core/sink/null_sink.h"
|
#include "audio_core/sink/null_sink.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
@@ -54,16 +51,6 @@ struct SinkDetails {
|
|||||||
|
|
||||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
// sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||||
constexpr SinkDetails sink_details[] = {
|
constexpr SinkDetails sink_details[] = {
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
SinkDetails{
|
|
||||||
Settings::AudioEngine::Ps4,
|
|
||||||
[](std::string_view device_id) -> std::unique_ptr<Sink> {
|
|
||||||
return std::make_unique<PS4Sink>(device_id);
|
|
||||||
},
|
|
||||||
&ListPS4SinkDevices,
|
|
||||||
&GetPS4Latency,
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_OBOE
|
#ifdef HAVE_OBOE
|
||||||
SinkDetails{
|
SinkDetails{
|
||||||
Settings::AudioEngine::Oboe,
|
Settings::AudioEngine::Oboe,
|
||||||
@@ -128,9 +115,7 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
|||||||
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
// BEGIN REINTRODUCED FROM 3833 - REPLACED CODE BLOCK ABOVE - DIABLO 3 FIX
|
||||||
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
// Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
|
||||||
// causes audio issues, in that case go with SDL.
|
// causes audio issues, in that case go with SDL.
|
||||||
#if defined(__OPENORBIS__)
|
#if defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
||||||
iter = find_backend(Settings::AudioEngine::Ps4);
|
|
||||||
#elif defined(HAVE_CUBEB) && defined(HAVE_SDL3)
|
|
||||||
iter = find_backend(Settings::AudioEngine::Cubeb);
|
iter = find_backend(Settings::AudioEngine::Cubeb);
|
||||||
if (iter->latency() > TargetSampleCount * 3) {
|
if (iter->latency() > TargetSampleCount * 3) {
|
||||||
iter = find_backend(Settings::AudioEngine::Sdl3);
|
iter = find_backend(Settings::AudioEngine::Sdl3);
|
||||||
|
|||||||
@@ -229,7 +229,6 @@ if(CXX_CLANG)
|
|||||||
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>)
|
$<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,15>:CANNOT_EXPLICITLY_INSTANTIATE>)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Has to be BEFORE httplib
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool)
|
target_link_libraries(common PUBLIC Boost::algorithm Boost::heap Boost::icl Boost::pool)
|
||||||
else()
|
else()
|
||||||
|
|||||||
+10
-12
@@ -20,9 +20,6 @@
|
|||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define fileno _fileno
|
#define fileno _fileno
|
||||||
@@ -405,27 +402,28 @@ u64 IOFile::GetSize() const {
|
|||||||
file_size = Android::GetSize(file_path);
|
file_size = Android::GetSize(file_path);
|
||||||
} else {
|
} else {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
file_size = fs::file_size(file_path, ec);
|
file_size = fs::file_size(file_path, ec);
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", PathToUTF8String(file_path), ec.message());
|
LOG_ERROR(Common_Filesystem,
|
||||||
|
"Failed to retrieve the file size of path={}, ec_message={}",
|
||||||
|
PathToUTF8String(file_path), ec.message());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif defined(__OPENORBIS__)
|
|
||||||
// TODO: implementation of fs::file_size() is buggy on PS4, why?
|
|
||||||
// probably toolchain issue... ugh
|
|
||||||
OrbisKernelStat st{};
|
|
||||||
stat(file_path.c_str(), reinterpret_cast<struct stat *>(std::addressof(st)));
|
|
||||||
auto const file_size = st.st_size;
|
|
||||||
LOG_DEBUG(Common_Filesystem, "size for {} = {}", file_path.c_str(), file_size);
|
|
||||||
#else
|
#else
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
|
||||||
const auto file_size = fs::file_size(file_path, ec);
|
const auto file_size = fs::file_size(file_path, ec);
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", PathToUTF8String(file_path), ec.message());
|
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
|
||||||
|
PathToUTF8String(file_path), ec.message());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return file_size;
|
return file_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,14 +174,11 @@ bool CreateDir(const fs::path& path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe this is what causes death?
|
|
||||||
#ifndef __OPENORBIS__
|
|
||||||
if (!Exists(path.parent_path())) {
|
if (!Exists(path.parent_path())) {
|
||||||
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
|
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
|
||||||
PathToUTF8String(path));
|
PathToUTF8String(path));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (IsDir(path)) {
|
if (IsDir(path)) {
|
||||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
|
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
|
||||||
|
|||||||
+12
-11
@@ -130,10 +130,6 @@ public:
|
|||||||
ASSERT(!eden_path.empty());
|
ASSERT(!eden_path.empty());
|
||||||
eden_path_cache = eden_path / CACHE_DIR;
|
eden_path_cache = eden_path / CACHE_DIR;
|
||||||
eden_path_config = eden_path / CONFIG_DIR;
|
eden_path_config = eden_path / CONFIG_DIR;
|
||||||
#elif defined(__OPENORBIS__)
|
|
||||||
eden_path = "/data/eden";
|
|
||||||
eden_path_cache = eden_path / CACHE_DIR;
|
|
||||||
eden_path_config = eden_path / CONFIG_DIR;
|
|
||||||
#else
|
#else
|
||||||
eden_path = GetCurrentDir() / PORTABLE_DIR;
|
eden_path = GetCurrentDir() / PORTABLE_DIR;
|
||||||
if (!Exists(eden_path) || !IsDir(eden_path)) {
|
if (!Exists(eden_path) || !IsDir(eden_path)) {
|
||||||
@@ -176,11 +172,12 @@ public:
|
|||||||
GenerateEdenPath(EdenPath::ShaderDir, eden_path_cache / SHADER_DIR);
|
GenerateEdenPath(EdenPath::ShaderDir, eden_path_cache / SHADER_DIR);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
|
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
|
||||||
#elif !defined(__OPENORBIS__)
|
#else
|
||||||
// In Ryujinx's infinite wisdom, it places EVERYTHING in the config directory on UNIX
|
// In Ryujinx's infinite wisdom, it places EVERYTHING in the config directory on UNIX
|
||||||
// This is incredibly stupid and violates a million XDG standards, but whatever
|
// This is incredibly stupid and violates a million XDG standards, but whatever
|
||||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetDataDirectory("XDG_CONFIG_HOME") / RYUJINX_DIR);
|
GenerateLegacyPath(EmuPath::RyujinxDir, GetDataDirectory("XDG_CONFIG_HOME") / RYUJINX_DIR);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -342,33 +339,36 @@ fs::path GetAppDataRoamingDirectory() {
|
|||||||
return fs_appdata_roaming_path;
|
return fs_appdata_roaming_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__OPENORBIS__)
|
|
||||||
|
|
||||||
// Not required/defined
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
fs::path GetHomeDirectory() {
|
fs::path GetHomeDirectory() {
|
||||||
const char* home_env_var = getenv("HOME");
|
const char* home_env_var = getenv("HOME");
|
||||||
|
|
||||||
if (home_env_var) {
|
if (home_env_var) {
|
||||||
return fs::path{home_env_var};
|
return fs::path{home_env_var};
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Common_Filesystem,
|
LOG_INFO(Common_Filesystem,
|
||||||
"$HOME is not defined in the environment variables, "
|
"$HOME is not defined in the environment variables, "
|
||||||
"attempting to query passwd to get the home path of the current user");
|
"attempting to query passwd to get the home path of the current user");
|
||||||
|
|
||||||
const auto* pw = getpwuid(getuid());
|
const auto* pw = getpwuid(getuid());
|
||||||
|
|
||||||
if (!pw) {
|
if (!pw) {
|
||||||
LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user");
|
LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return fs::path{pw->pw_dir};
|
return fs::path{pw->pw_dir};
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path GetDataDirectory(const std::string& env_name) {
|
fs::path GetDataDirectory(const std::string& env_name) {
|
||||||
const char* data_env_var = getenv(env_name.c_str());
|
const char* data_env_var = getenv(env_name.c_str());
|
||||||
|
|
||||||
if (data_env_var) {
|
if (data_env_var) {
|
||||||
return fs::path{data_env_var};
|
return fs::path{data_env_var};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env_name == "XDG_DATA_HOME") {
|
if (env_name == "XDG_DATA_HOME") {
|
||||||
return GetHomeDirectory() / ".local/share";
|
return GetHomeDirectory() / ".local/share";
|
||||||
} else if (env_name == "XDG_CACHE_HOME") {
|
} else if (env_name == "XDG_CACHE_HOME") {
|
||||||
@@ -376,6 +376,7 @@ fs::path GetDataDirectory(const std::string& env_name) {
|
|||||||
} else if (env_name == "XDG_CONFIG_HOME") {
|
} else if (env_name == "XDG_CONFIG_HOME") {
|
||||||
return GetHomeDirectory() / ".config";
|
return GetHomeDirectory() / ".config";
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -38,15 +38,15 @@
|
|||||||
|
|
||||||
// FreeBSD
|
// FreeBSD
|
||||||
#ifndef MAP_NORESERVE
|
#ifndef MAP_NORESERVE
|
||||||
# define MAP_NORESERVE 0
|
#define MAP_NORESERVE 0
|
||||||
#endif
|
#endif
|
||||||
// Solaris 11 and illumos
|
// Solaris 11 and illumos
|
||||||
#ifndef MAP_ALIGNED_SUPER
|
#ifndef MAP_ALIGNED_SUPER
|
||||||
# define MAP_ALIGNED_SUPER 0
|
#define MAP_ALIGNED_SUPER 0
|
||||||
#endif
|
#endif
|
||||||
// macOS
|
// macOS
|
||||||
#ifndef MAP_ANONYMOUS
|
#ifndef MAP_ANONYMOUS
|
||||||
# define MAP_ANONYMOUS MAP_ANON
|
#define MAP_ANONYMOUS MAP_ANON
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
@@ -441,15 +441,13 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static void* ChooseVirtualBase(size_t size) {
|
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||||
void* virtual_base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||||
if (virtual_base != MAP_FAILED)
|
if (virtual_base != MAP_FAILED)
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
|
||||||
#else
|
|
||||||
return mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
|
||||||
#endif
|
#endif
|
||||||
|
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -731,8 +729,9 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, M
|
|||||||
ASSERT(length % PageAlignment == 0);
|
ASSERT(length % PageAlignment == 0);
|
||||||
ASSERT(virtual_offset + length <= virtual_size);
|
ASSERT(virtual_offset + length <= virtual_size);
|
||||||
ASSERT(host_offset + length <= backing_size);
|
ASSERT(host_offset + length <= backing_size);
|
||||||
if (length == 0 || !virtual_base || !impl)
|
if (length == 0 || !virtual_base || !impl) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
|
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -742,8 +741,9 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
|
|||||||
ASSERT(virtual_offset % PageAlignment == 0);
|
ASSERT(virtual_offset % PageAlignment == 0);
|
||||||
ASSERT(length % PageAlignment == 0);
|
ASSERT(length % PageAlignment == 0);
|
||||||
ASSERT(virtual_offset + length <= virtual_size);
|
ASSERT(virtual_offset + length <= virtual_size);
|
||||||
if (length == 0 || !virtual_base || !impl)
|
if (length == 0 || !virtual_base || !impl) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
impl->Unmap(virtual_offset + virtual_base_offset, length);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,13 +74,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
size_t backing_size{};
|
||||||
|
size_t virtual_size{};
|
||||||
|
|
||||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||||
// Low level handler for the platform dependent memory routines
|
// Low level handler for the platform dependent memory routines
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
#endif
|
#endif
|
||||||
size_t backing_size{};
|
|
||||||
size_t virtual_size{};
|
|
||||||
u8* backing_base{};
|
u8* backing_base{};
|
||||||
u8* virtual_base{};
|
u8* virtual_base{};
|
||||||
size_t virtual_base_offset{};
|
size_t virtual_base_offset{};
|
||||||
|
|||||||
@@ -3,14 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// TODO: httplib port can't use OpenSSL because of package misconfigs on OO toolchain
|
|
||||||
// right now the solution is to tell httplib that OpenSSL doesn't exist, but that's bad
|
|
||||||
// and the issue is a bit more of how OpenSSL lacks some essential functionality
|
|
||||||
// which is a bit annoying to reconfigure atm.
|
|
||||||
#ifndef __OPENORBIS__
|
|
||||||
#define CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES 1
|
#define CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES 1
|
||||||
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
#define CPPHTTPLIB_OPENSSL_SUPPORT 1
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef __OPENORBIS__
|
||||||
/// @brief Backend that writes to a file passed into the constructor
|
/// @brief Backend that writes to a file passed into the constructor
|
||||||
struct FileBackend final : public Backend {
|
struct FileBackend final : public Backend {
|
||||||
explicit FileBackend(const std::filesystem::path filename) noexcept {
|
explicit FileBackend(const std::filesystem::path filename) noexcept {
|
||||||
@@ -298,7 +299,7 @@ private:
|
|||||||
std::size_t bytes_written = 0;
|
std::size_t bytes_written = 0;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/// @brief Backend that writes to Visual Studio's output window
|
/// @brief Backend that writes to Visual Studio's output window
|
||||||
struct DebuggerBackend final : public Backend {
|
struct DebuggerBackend final : public Backend {
|
||||||
@@ -342,8 +343,10 @@ struct Impl {
|
|||||||
template<typename F>
|
template<typename F>
|
||||||
void ForEachBackend(F&& lambda) noexcept {
|
void ForEachBackend(F&& lambda) noexcept {
|
||||||
lambda(static_cast<Backend&>(color_console_backend));
|
lambda(static_cast<Backend&>(color_console_backend));
|
||||||
|
#ifndef __OPENORBIS__
|
||||||
if (file_backend)
|
if (file_backend)
|
||||||
lambda(static_cast<Backend&>(*file_backend));
|
lambda(static_cast<Backend&>(*file_backend));
|
||||||
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
lambda(static_cast<Backend&>(debugger_backend));
|
lambda(static_cast<Backend&>(debugger_backend));
|
||||||
#endif
|
#endif
|
||||||
@@ -354,7 +357,9 @@ struct Impl {
|
|||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
ColorConsoleBackend color_console_backend{};
|
ColorConsoleBackend color_console_backend{};
|
||||||
|
#ifndef __OPENORBIS__
|
||||||
std::optional<FileBackend> file_backend;
|
std::optional<FileBackend> file_backend;
|
||||||
|
#endif
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DebuggerBackend debugger_backend{};
|
DebuggerBackend debugger_backend{};
|
||||||
#endif
|
#endif
|
||||||
@@ -391,10 +396,12 @@ void Initialize() {
|
|||||||
} else {
|
} else {
|
||||||
logging_instance.emplace();
|
logging_instance.emplace();
|
||||||
logging_instance->filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
logging_instance->filter.ParseFilterString(Settings::values.log_filter.GetValue());
|
||||||
|
#ifndef __OPENORBIS__
|
||||||
using namespace Common::FS;
|
using namespace Common::FS;
|
||||||
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
const auto& log_dir = GetEdenPath(EdenPath::LogDir);
|
||||||
void(CreateDir(log_dir));
|
void(CreateDir(log_dir));
|
||||||
logging_instance->file_backend.emplace(log_dir / LOG_FILE);
|
logging_instance->file_backend.emplace(log_dir / LOG_FILE);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -11,7 +8,7 @@
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
#else
|
#else
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#if defined(__APPLE__) || (defined(__FreeBSD__) && !defined(__OPENORBIS__))
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ std::optional<std::string> MakeRequest(const std::string& url, const std::string
|
|||||||
client->set_read_timeout(timeout_seconds);
|
client->set_read_timeout(timeout_seconds);
|
||||||
client->set_write_timeout(timeout_seconds);
|
client->set_write_timeout(timeout_seconds);
|
||||||
|
|
||||||
#if defined(YUZU_BUNDLED_OPENSSL) && !defined(__OPENORBIS__)
|
#ifdef YUZU_BUNDLED_OPENSSL
|
||||||
client->load_ca_cert_store(kCert, sizeof(kCert));
|
client->load_ca_cert_store(kCert, sizeof(kCert));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/time_zone.h"
|
#include "common/time_zone.h"
|
||||||
|
|
||||||
#ifdef __unix__
|
#if defined(__linux__ ) && defined(ARCHITECTURE_arm64)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -187,9 +187,12 @@ bool IsFastmemEnabled() {
|
|||||||
return bool(values.cpuopt_fastmem);
|
return bool(values.cpuopt_fastmem);
|
||||||
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
|
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
|
||||||
return bool(values.cpuopt_unsafe_host_mmu);
|
return bool(values.cpuopt_unsafe_host_mmu);
|
||||||
#if defined(__unix__)
|
#if defined(__linux__) && defined(ARCHITECTURE_arm64)
|
||||||
// TODO: Support this since only 4kb systems support host MMU right now
|
// Only 4kb systems support host MMU right now
|
||||||
|
// TODO: Support this
|
||||||
return getpagesize() == 4096;
|
return getpagesize() == 4096;
|
||||||
|
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__) && !defined(__FreeBSD__)
|
||||||
|
return false;
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -196,14 +196,7 @@ struct Values {
|
|||||||
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
SwitchableSetting<bool> use_multi_core{linkage,
|
SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
// Re-enable once proper TLS support is added
|
|
||||||
false,
|
|
||||||
#else
|
|
||||||
true,
|
|
||||||
#endif
|
|
||||||
"use_multi_core", Category::Core};
|
|
||||||
SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
|
SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
|
||||||
MemoryLayout::Memory_4Gb,
|
MemoryLayout::Memory_4Gb,
|
||||||
"memory_layout_mode",
|
"memory_layout_mode",
|
||||||
@@ -337,7 +330,7 @@ struct Values {
|
|||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
SwitchableSetting<RendererBackend, true> renderer_backend{linkage,
|
SwitchableSetting<RendererBackend, true> renderer_backend{linkage,
|
||||||
#if defined(__sun__) || defined(__managarm__) || defined(__OPENORBIS__)
|
#if defined(__sun__) || defined(__managarm__)
|
||||||
RendererBackend::OpenGL_GLSL,
|
RendererBackend::OpenGL_GLSL,
|
||||||
#else
|
#else
|
||||||
RendererBackend::Vulkan,
|
RendererBackend::Vulkan,
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ struct EnumMetadata {
|
|||||||
// AudioEngine must be specified discretely due to having existing but slightly different
|
// AudioEngine must be specified discretely due to having existing but slightly different
|
||||||
// canonicalizations
|
// canonicalizations
|
||||||
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
||||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, Ps4, };
|
enum class AudioEngine : u32 { Auto, Cubeb, Sdl3, Null, Oboe, };
|
||||||
template<>
|
template<>
|
||||||
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
|
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||||
return {
|
return {
|
||||||
@@ -100,7 +100,6 @@ inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioE
|
|||||||
{"cubeb", AudioEngine::Cubeb},
|
{"cubeb", AudioEngine::Cubeb},
|
||||||
{"sdl3", AudioEngine::Sdl3},
|
{"sdl3", AudioEngine::Sdl3},
|
||||||
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
||||||
{"ps4", AudioEngine::Ps4},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here
|
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here
|
||||||
|
|||||||
@@ -10,135 +10,11 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
#include <ranges>
|
|
||||||
#include <csignal>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <boost/container/static_vector.hpp>
|
|
||||||
#include <orbis/SystemService.h>
|
|
||||||
#include <orbis/libkernel.h>
|
|
||||||
typedef void (*SceKernelExceptionHandler)(int32_t, void*);
|
|
||||||
extern "C" int32_t sceKernelInstallExceptionHandler(int32_t signum, SceKernelExceptionHandler handler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
#include "common/logging.h"
|
|
||||||
|
|
||||||
// PlayStation 4
|
|
||||||
// Flag needs to be undef-ed on non PS4 since it has different semantics
|
|
||||||
// on some platforms.
|
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
# ifndef MAP_SYSTEM
|
|
||||||
# define MAP_SYSTEM 0x2000
|
|
||||||
# endif
|
|
||||||
# ifndef MAP_VOID
|
|
||||||
# define MAP_VOID 0x100
|
|
||||||
# endif
|
|
||||||
// sigaction(2) has a motherfucking bug on musl where the thing isnt even properly prefixed
|
|
||||||
# undef sa_sigaction
|
|
||||||
# define sa_sigaction __sa_handler.__sa_sigaction
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
|
|
||||||
namespace Orbis {
|
|
||||||
constexpr size_t ORBIS_PAGE_SIZE = 16384;
|
|
||||||
|
|
||||||
struct Ucontext {
|
|
||||||
struct Sigset {
|
|
||||||
u64 bits[2];
|
|
||||||
} uc_sigmask;
|
|
||||||
int field1_0x10[12];
|
|
||||||
struct Mcontext {
|
|
||||||
u64 mc_onstack;
|
|
||||||
u64 mc_rdi;
|
|
||||||
u64 mc_rsi;
|
|
||||||
u64 mc_rdx;
|
|
||||||
u64 mc_rcx;
|
|
||||||
u64 mc_r8;
|
|
||||||
u64 mc_r9;
|
|
||||||
u64 mc_rax;
|
|
||||||
u64 mc_rbx;
|
|
||||||
u64 mc_rbp;
|
|
||||||
u64 mc_r10;
|
|
||||||
u64 mc_r11;
|
|
||||||
u64 mc_r12;
|
|
||||||
u64 mc_r13;
|
|
||||||
u64 mc_r14;
|
|
||||||
u64 mc_r15;
|
|
||||||
int mc_trapno;
|
|
||||||
u16 mc_fs;
|
|
||||||
u16 mc_gs;
|
|
||||||
u64 mc_addr;
|
|
||||||
int mc_flags;
|
|
||||||
u16 mc_es;
|
|
||||||
u16 mc_ds;
|
|
||||||
u64 mc_err;
|
|
||||||
u64 mc_rip;
|
|
||||||
u64 mc_cs;
|
|
||||||
u64 mc_rflags;
|
|
||||||
u64 mc_rsp;
|
|
||||||
u64 mc_ss;
|
|
||||||
u64 mc_len;
|
|
||||||
u64 mc_fpformat;
|
|
||||||
u64 mc_ownedfp;
|
|
||||||
u64 mc_lbrfrom;
|
|
||||||
u64 mc_lbrto;
|
|
||||||
u64 mc_aux1;
|
|
||||||
u64 mc_aux2;
|
|
||||||
u64 mc_fpstate[104];
|
|
||||||
u64 mc_fsbase;
|
|
||||||
u64 mc_gsbase;
|
|
||||||
u64 mc_spare[6];
|
|
||||||
} uc_mcontext;
|
|
||||||
struct Ucontext* uc_link;
|
|
||||||
struct ExStack {
|
|
||||||
void* ss_sp;
|
|
||||||
std::size_t ss_size;
|
|
||||||
int ss_flags;
|
|
||||||
int _align;
|
|
||||||
} uc_stack;
|
|
||||||
int uc_flags;
|
|
||||||
int __spare[4];
|
|
||||||
int field7_0x4f4[3];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static boost::container::static_vector<std::pair<void*, size_t>, 16> swap_regions;
|
|
||||||
static std::mutex evil_swap_mutex;
|
|
||||||
extern "C" int sceKernelRemoveExceptionHandler(s32 sig_num);
|
|
||||||
static void SwapHandler(int sig, void* raw_context) {
|
|
||||||
std::unique_lock lk{evil_swap_mutex};
|
|
||||||
auto& mctx = ((Orbis::Ucontext*)raw_context)->uc_mcontext;
|
|
||||||
if (auto const it = std::ranges::find_if(swap_regions, [addr = mctx.mc_addr](auto const& e) {
|
|
||||||
return uintptr_t(addr) >= uintptr_t(e.first) && uintptr_t(addr) < uintptr_t(e.first) + e.second;
|
|
||||||
}); it != swap_regions.end()) {
|
|
||||||
size_t const page_size = Orbis::ORBIS_PAGE_SIZE * 128; //128K
|
|
||||||
size_t const page_mask = ~(page_size - 1);
|
|
||||||
// should replace the existing mapping... ugh
|
|
||||||
void* aligned_addr = reinterpret_cast<void*>(uintptr_t(mctx.mc_addr) & page_mask);
|
|
||||||
void* res = mmap(aligned_addr, page_size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
||||||
if (res == MAP_FAILED) {
|
|
||||||
LOG_ERROR(HW_Memory, "{:#x},{} @ {}:{:#x}", mctx.mc_addr, aligned_addr, it->first, it->second);
|
|
||||||
sceKernelRemoveExceptionHandler(SIGSEGV); // to not catch the next signal
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG(HW_Memory, "{:#x},{} @ {}:{:#x}", mctx.mc_addr, aligned_addr, it->first, it->second);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(HW_Memory, "fault in addr {:#x} at {:#x}", mctx.mc_addr, mctx.mc_rip); // print caller address
|
|
||||||
sceKernelRemoveExceptionHandler(SIGSEGV); // to not catch the next signal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void InitSwap() noexcept {
|
|
||||||
sceKernelInstallExceptionHandler(SIGSEGV, &SwapHandler);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void InitSwap() noexcept {}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||||
@@ -146,61 +22,22 @@ void* AllocateMemoryPages(std::size_t size) noexcept {
|
|||||||
// Probably failing to reserve is less likely than failing to commit
|
// Probably failing to reserve is less likely than failing to commit
|
||||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
}
|
}
|
||||||
#elif defined(__OPENORBIS__)
|
|
||||||
bool use_void_mem = true;
|
|
||||||
void* addr = nullptr;
|
|
||||||
if (size < 4294967296ull) {
|
|
||||||
size_t align = Orbis::ORBIS_PAGE_SIZE;
|
|
||||||
off_t offset;
|
|
||||||
int32_t res;
|
|
||||||
size = (size + align - 1) / align * align;
|
|
||||||
if ((res = sceKernelAllocateDirectMemory(0, ORBIS_KERNEL_MAIN_DMEM_SIZE, size, align, ORBIS_KERNEL_WB_ONION, &offset)) == 0) {
|
|
||||||
if ((res = sceKernelMapDirectMemory(&addr, size, ORBIS_KERNEL_PROT_CPU_READ | ORBIS_KERNEL_PROT_CPU_WRITE, 0, offset, size)) == 0) {
|
|
||||||
if ((res = sceKernelMprotect(addr, size, VM_PROT_ALL)) == 0 && addr != nullptr) {
|
|
||||||
LOG_WARNING(HW_Memory, "Using DMem for {} bytes area @ {}", size, addr);
|
|
||||||
use_void_mem = false; //Memory properly mapped
|
|
||||||
} else {
|
|
||||||
sceKernelReleaseDirectMemory(offset, size);
|
|
||||||
LOG_ERROR(HW_Memory, "{} = sceKernelMprotect({}, {})", res, offset, size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sceKernelReleaseDirectMemory(offset, size);
|
|
||||||
LOG_ERROR(HW_Memory, "{} = sceKernelMapDirectMemory({}, {})", res, offset, size);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(HW_Memory, "{} = sceKernelAllocateDirectMemory({}, {}, {})", res, size, align, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (use_void_mem) {
|
|
||||||
addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_VOID | MAP_PRIVATE, -1, 0);
|
|
||||||
LOG_WARNING(HW_Memory, "Using VoidMem for {} bytes area @ {}", size, addr);
|
|
||||||
ASSERT(addr != MAP_FAILED);
|
|
||||||
swap_regions.emplace_back(addr, size);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
void* addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||||
ASSERT(addr != MAP_FAILED);
|
if (base == MAP_FAILED)
|
||||||
|
base = nullptr;
|
||||||
#endif
|
#endif
|
||||||
return addr;
|
ASSERT(base);
|
||||||
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeMemoryPages(void* addr, [[maybe_unused]] std::size_t size) noexcept {
|
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||||
if (!addr)
|
if (!base)
|
||||||
return;
|
return;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
VirtualFree(addr, 0, MEM_RELEASE);
|
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||||
#elif defined(__OPENORBIS__)
|
|
||||||
if (auto const it = std::ranges::find_if(swap_regions, [=](auto const& e) {
|
|
||||||
return uintptr_t(addr) >= uintptr_t(e.first) && uintptr_t(addr) < uintptr_t(e.first) + e.second;
|
|
||||||
}); it != swap_regions.end()) {
|
|
||||||
int rc = munmap(addr, size);
|
|
||||||
ASSERT(rc == 0);
|
|
||||||
} else {
|
|
||||||
sceKernelCheckedReleaseDirectMemory(off_t(addr), size_t(size));
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
int rc = munmap(addr, size);
|
ASSERT(munmap(base, size) == 0);
|
||||||
ASSERT(rc == 0);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ namespace Common {
|
|||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
void InitSwap() noexcept;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class VirtualBuffer final {
|
class VirtualBuffer final {
|
||||||
|
|||||||
@@ -199,9 +199,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||||
|
|
||||||
// Code cache size
|
// Code cache size
|
||||||
#if defined(__OPENORBIS__)
|
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||||
config.code_cache_size = std::uint32_t(8_MiB);
|
|
||||||
#elif defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
|
||||||
config.code_cache_size = std::uint32_t(128_MiB);
|
config.code_cache_size = std::uint32_t(128_MiB);
|
||||||
#else
|
#else
|
||||||
config.code_cache_size = std::uint32_t(512_MiB);
|
config.code_cache_size = std::uint32_t(512_MiB);
|
||||||
|
|||||||
@@ -252,9 +252,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||||
|
|
||||||
// Code cache size
|
// Code cache size
|
||||||
#if defined(__OPENORBIS__)
|
#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
||||||
config.code_cache_size = std::uint32_t(8_MiB);
|
|
||||||
#elif defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
|
||||||
config.code_cache_size = std::uint32_t(128_MiB);
|
config.code_cache_size = std::uint32_t(128_MiB);
|
||||||
#else
|
#else
|
||||||
config.code_cache_size = std::uint32_t(512_MiB);
|
config.code_cache_size = std::uint32_t(512_MiB);
|
||||||
|
|||||||
+79
-9
@@ -4,6 +4,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/arm/exclusive_monitor.h"
|
#include "core/arm/exclusive_monitor.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
|
|
||||||
#include "launch_timestamp_cache.h"
|
#include "launch_timestamp_cache.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
@@ -248,12 +250,30 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active) {
|
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
nvdec_active = is_nvdec_active;
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
++nvdec_active_channels[process_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotifyNVDECChannelClose(u64 process_id) {
|
||||||
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
const auto it = nvdec_active_channels.find(process_id);
|
||||||
|
if (it == nvdec_active_channels.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (--it->second == 0) {
|
||||||
|
nvdec_active_channels.erase(it);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
return nvdec_active;
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
return !nvdec_active_channels.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||||
|
std::scoped_lock lock{nvdec_active_mutex};
|
||||||
|
return nvdec_active_channels.contains(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -295,6 +315,10 @@ struct System::Impl {
|
|||||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
||||||
InitializeKernel(system);
|
InitializeKernel(system);
|
||||||
|
|
||||||
|
if (params.applet_type == Service::AM::AppletType::Application) {
|
||||||
|
current_application_filepath = filepath;
|
||||||
|
}
|
||||||
|
|
||||||
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
|
||||||
|
|
||||||
// Create the application process
|
// Create the application process
|
||||||
@@ -330,7 +354,7 @@ struct System::Impl {
|
|||||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||||
|
|
||||||
// Make the process created be the application
|
// Make the process created be the application
|
||||||
kernel.MakeApplicationProcess(process->GetHandle());
|
kernel.SetApplicationProcess(process->GetHandle());
|
||||||
|
|
||||||
// Set up the rest of the system.
|
// Set up the rest of the system.
|
||||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||||
@@ -373,7 +397,7 @@ struct System::Impl {
|
|||||||
LOG_ERROR(Core, "Failed to find program id for ROM");
|
LOG_ERROR(Core, "Failed to find program id for ROM");
|
||||||
}
|
}
|
||||||
|
|
||||||
GameSettings::LoadOverrides(program_id, gpu_core->Renderer());
|
GameSettings::LoadOverrides(FileSys::GetBaseTitleID(program_id), gpu_core->Renderer());
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
Network::GameInfo game_info;
|
Network::GameInfo game_info;
|
||||||
game_info.name = name;
|
game_info.name = name;
|
||||||
@@ -467,6 +491,7 @@ struct System::Impl {
|
|||||||
Core::SpeedLimiter speed_limiter;
|
Core::SpeedLimiter speed_limiter;
|
||||||
ExecuteProgramCallback execute_program_callback;
|
ExecuteProgramCallback execute_program_callback;
|
||||||
ExitCallback exit_callback;
|
ExitCallback exit_callback;
|
||||||
|
ApplicationChangedCallback application_changed_callback;
|
||||||
|
|
||||||
std::optional<Service::Services> services;
|
std::optional<Service::Services> services;
|
||||||
std::optional<Core::Debugger> debugger;
|
std::optional<Core::Debugger> debugger;
|
||||||
@@ -488,6 +513,8 @@ struct System::Impl {
|
|||||||
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
|
||||||
std::array<u8, 0x20> build_id{};
|
std::array<u8, 0x20> build_id{};
|
||||||
|
|
||||||
|
std::string current_application_filepath;
|
||||||
|
|
||||||
/// Service manager
|
/// Service manager
|
||||||
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
||||||
/// ContentProviderUnion instance
|
/// ContentProviderUnion instance
|
||||||
@@ -498,6 +525,8 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
std::mutex general_channel_mutex;
|
||||||
|
std::mutex nvdec_active_mutex;
|
||||||
|
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||||
std::atomic_bool is_paused{};
|
std::atomic_bool is_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -505,7 +534,6 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
bool nvdec_active : 1 = false;
|
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -569,14 +597,22 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||||
impl->SetNVDECActive(is_nvdec_active);
|
impl->NotifyNVDECChannelOpen(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||||
|
impl->NotifyNVDECChannelClose(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||||
|
return impl->IsNVDECActiveForProcess(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
@@ -727,7 +763,25 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 System::GetApplicationProcessProgramID() const {
|
u64 System::GetApplicationProcessProgramID() const {
|
||||||
return impl->kernel.ApplicationProcess()->GetProgramId();
|
const auto* const process = impl->kernel.ApplicationProcess();
|
||||||
|
return process != nullptr ? process->GetProgramId() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 System::GetProgramIdForProcessId(u64 process_id) const {
|
||||||
|
auto process = impl->kernel.GetProcessByProcessId(process_id);
|
||||||
|
return process.IsNull() ? 0 : process->GetProgramId();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 System::ResolveCallerProgramId(u64 process_id) const {
|
||||||
|
if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) {
|
||||||
|
return program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fallback = this->GetApplicationProcessProgramID();
|
||||||
|
LOG_WARNING(Core,
|
||||||
|
"Could not resolve caller process_id={}, falling back to application {:016X}",
|
||||||
|
process_id, fallback);
|
||||||
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||||
@@ -910,6 +964,10 @@ void System::ExecuteProgram(std::size_t program_index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& System::GetCurrentApplicationFilePath() const {
|
||||||
|
return impl->current_application_filepath;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Gets a reference to the user channel stack.
|
/// @brief Gets a reference to the user channel stack.
|
||||||
/// It is used to transfer data between programs.
|
/// It is used to transfer data between programs.
|
||||||
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
std::vector<std::vector<u8>>& System::GetUserChannel() {
|
||||||
@@ -961,6 +1019,18 @@ void System::Exit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) {
|
||||||
|
impl->application_changed_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::NotifyApplicationChanged(u64 program_id) {
|
||||||
|
//LOG_DEBUG(Core, "Running application changed to {:016X}", program_id);
|
||||||
|
|
||||||
|
if (impl->application_changed_callback) {
|
||||||
|
impl->application_changed_callback(program_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void System::ApplySettings() {
|
void System::ApplySettings() {
|
||||||
impl->RefreshTime(*this);
|
impl->RefreshTime(*this);
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -191,8 +191,10 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void SetNVDECActive(bool is_nvdec_active);
|
void NotifyNVDECChannelOpen(u64 process_id);
|
||||||
|
void NotifyNVDECChannelClose(u64 process_id);
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
|
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
@@ -322,6 +324,10 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
||||||
|
|
||||||
|
[[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const;
|
||||||
|
|
||||||
|
[[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const;
|
||||||
|
|
||||||
/// Gets the name of the current game
|
/// Gets the name of the current game
|
||||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||||
|
|
||||||
@@ -422,6 +428,7 @@ public:
|
|||||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||||
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
||||||
|
[[nodiscard]] const std::string& GetCurrentApplicationFilePath() const;
|
||||||
|
|
||||||
/// Type used for the frontend to designate a callback for System to exit the application.
|
/// Type used for the frontend to designate a callback for System to exit the application.
|
||||||
using ExitCallback = std::function<void()>;
|
using ExitCallback = std::function<void()>;
|
||||||
@@ -435,6 +442,10 @@ public:
|
|||||||
/// Instructs the frontend to exit the application.
|
/// Instructs the frontend to exit the application.
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
|
using ApplicationChangedCallback = std::function<void(u64 program_id)>;
|
||||||
|
void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback);
|
||||||
|
void NotifyApplicationChanged(u64 program_id);
|
||||||
|
|
||||||
/// Applies any changes to settings to this core instance.
|
/// Applies any changes to settings to this core instance.
|
||||||
void ApplySettings();
|
void ApplySettings();
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) {
|
|||||||
return std::max<u8>(key_generation, 1) - 1;
|
return std::max<u8>(key_generation, 1) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
||||||
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
: file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
status = Loader::ResultStatus::ErrorNullFile;
|
status = Loader::ResultStatus::ErrorNullFile;
|
||||||
@@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_update && base_nca == nullptr) {
|
if (is_update && base_nca == nullptr && !allow_missing_base) {
|
||||||
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
|
||||||
} else {
|
} else {
|
||||||
status = Loader::ResultStatus::Success;
|
status = Loader::ResultStatus::Success;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) {
|
|||||||
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
// After construction, use GetStatus to determine if the file is valid and ready to be used.
|
||||||
class NCA : public ReadOnlyVfsDirectory {
|
class NCA : public ReadOnlyVfsDirectory {
|
||||||
public:
|
public:
|
||||||
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr);
|
explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false);
|
||||||
~NCA() override;
|
~NCA() override;
|
||||||
|
|
||||||
Loader::ResultStatus GetStatus() const;
|
Loader::ResultStatus GetStatus() const;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/logging.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
#include "core/file_sys/fssystem/fssystem_indirect_storage.h"
|
||||||
|
|
||||||
@@ -99,6 +100,18 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IndirectStorage::ReportMissingOriginal(s64 offset, s64 size) {
|
||||||
|
if (m_reported_missing_original) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_reported_missing_original = true;
|
||||||
|
|
||||||
|
LOG_ERROR(Common_Filesystem,
|
||||||
|
"Patch storage requests {:#x} bytes at {:#x} from a base storage that is not "
|
||||||
|
"present; this data will read as garbage",
|
||||||
|
size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const {
|
size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const {
|
||||||
// Validate pre-conditions.
|
// Validate pre-conditions.
|
||||||
ASSERT(this->IsInitialized());
|
ASSERT(this->IsInitialized());
|
||||||
|
|||||||
@@ -85,6 +85,9 @@ public:
|
|||||||
void SetStorage(s32 idx, VirtualFile storage) {
|
void SetStorage(s32 idx, VirtualFile storage) {
|
||||||
ASSERT(0 <= idx && idx < StorageCount);
|
ASSERT(0 <= idx && idx < StorageCount);
|
||||||
m_data_storage[idx] = storage;
|
m_data_storage[idx] = storage;
|
||||||
|
if (idx == 0) {
|
||||||
|
m_original_missing = storage == nullptr || storage->GetSize() == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -130,6 +133,9 @@ protected:
|
|||||||
|
|
||||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||||
Result OperatePerEntry(s64 offset, s64 size, F func);
|
Result OperatePerEntry(s64 offset, s64 size, F func);
|
||||||
|
// Launching another game makes the original storage inaccessable.
|
||||||
|
// This is a helper for multi-nca games.
|
||||||
|
void ReportMissingOriginal(s64 offset, s64 size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ContinuousReadingEntry {
|
struct ContinuousReadingEntry {
|
||||||
@@ -154,6 +160,8 @@ private:
|
|||||||
private:
|
private:
|
||||||
mutable BucketTree m_table;
|
mutable BucketTree m_table;
|
||||||
std::array<VirtualFile, StorageCount> m_data_storage;
|
std::array<VirtualFile, StorageCount> m_data_storage;
|
||||||
|
bool m_original_missing{false};
|
||||||
|
bool m_reported_missing_original{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
template <bool ContinuousCheck, bool RangeCheck, typename F>
|
||||||
@@ -272,6 +280,10 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
|
|||||||
if (needs_operate) {
|
if (needs_operate) {
|
||||||
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
||||||
|
|
||||||
|
if (cur_entry.storage_index == 0 && m_original_missing) {
|
||||||
|
this->ReportMissingOriginal(cur_offset, cur_size);
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr (RangeCheck) {
|
if constexpr (RangeCheck) {
|
||||||
// Get the current data storage's size.
|
// Get the current data storage's size.
|
||||||
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize();
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ struct ContentRecord {
|
|||||||
std::array<u8, 0x10> nca_id;
|
std::array<u8, 0x10> nca_id;
|
||||||
std::array<u8, 0x6> size;
|
std::array<u8, 0x6> size;
|
||||||
ContentRecordType type;
|
ContentRecordType type;
|
||||||
INSERT_PADDING_BYTES(1);
|
u8 id_offset;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size.");
|
||||||
|
|
||||||
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}};
|
constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0};
|
||||||
|
|
||||||
struct MetaRecord {
|
struct MetaRecord {
|
||||||
u64_le title_id;
|
u64_le title_id;
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
|||||||
PatchManager::PatchManager(u64 title_id_,
|
PatchManager::PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
const ContentProvider& content_provider_)
|
const ContentProvider& content_provider_)
|
||||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
: title_id{title_id_}, application_id{GetBaseTitleID(title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||||
|
|
||||||
PatchManager::~PatchManager() = default;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -169,13 +169,41 @@ u64 PatchManager::GetTitleID() const {
|
|||||||
return title_id;
|
return title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u64 PatchManager::GetUpdateTitleIDForContent() const {
|
||||||
|
const auto program_update_id = GetUpdateTitleID(title_id);
|
||||||
|
if (program_update_id == GetUpdateTitleID(application_id) || content_provider.HasEntry(program_update_id, ContentRecordType::Program)) {
|
||||||
|
return program_update_id;
|
||||||
|
}
|
||||||
|
return GetUpdateTitleID(application_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> PatchManager::GetModificationLoadRoots() const {
|
||||||
|
std::vector<VirtualDir> roots;
|
||||||
|
roots.push_back(fs_controller.GetModificationLoadRoot(title_id));
|
||||||
|
if (application_id != title_id) {
|
||||||
|
roots.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(roots, nullptr);
|
||||||
|
return roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> PatchManager::GetSDMCModificationLoadRoots() const {
|
||||||
|
std::vector<VirtualDir> roots;
|
||||||
|
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(title_id));
|
||||||
|
if (application_id != title_id) {
|
||||||
|
roots.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(roots, nullptr);
|
||||||
|
return roots;
|
||||||
|
}
|
||||||
|
|
||||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||||
|
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
return exefs;
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -183,7 +211,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
bool checked_manual = false;
|
bool checked_manual = false;
|
||||||
|
|
||||||
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
|
const auto* content_union = static_cast<const ContentProviderUnion*>(&content_provider);
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
|
|
||||||
if (content_union) {
|
if (content_union) {
|
||||||
// First, check ExternalContentProvider
|
// First, check ExternalContentProvider
|
||||||
@@ -303,17 +331,21 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
const auto sdmc_load_dirs = GetSDMCModificationLoadRoots();
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs;
|
||||||
if (load_dir != nullptr) {
|
for (const auto& sdmc_load_dir : sdmc_load_dirs) {
|
||||||
|
patch_dirs.push_back(sdmc_load_dir);
|
||||||
|
}
|
||||||
|
for (const auto& load_dir : load_dirs) {
|
||||||
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
std::vector<VirtualDir> layers;
|
std::vector<VirtualDir> layers;
|
||||||
layers.reserve(patch_dirs.size() + 1);
|
layers.reserve(patch_dirs.size() + 1);
|
||||||
@@ -347,7 +379,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const {
|
const std::string& build_id) const {
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
@@ -412,15 +444,20 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return nso;
|
return nso;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
for (const auto& load_dir : load_dirs) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
const auto patches = CollectPatches(patch_dirs, build_id);
|
const auto patches = CollectPatches(patch_dirs, build_id);
|
||||||
|
|
||||||
auto out = nso;
|
auto out = nso;
|
||||||
@@ -455,29 +492,39 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||||
|
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
for (const auto& load_dir : load_dirs) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
return !CollectPatches(patch_dirs, build_id).empty();
|
return !CollectPatches(patch_dirs, build_id).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dirs = GetModificationLoadRoots();
|
||||||
if (load_dir == nullptr) {
|
if (load_dirs.empty()) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
for (const auto& load_dir : load_dirs) {
|
||||||
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(),
|
||||||
|
[](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||||
|
|
||||||
// <mod dir> / <folder> / cheats / <build id>.txt
|
// <mod dir> / <folder> / cheats / <build id>.txt
|
||||||
std::vector<Core::Memory::CheatEntry> out;
|
std::vector<Core::Memory::CheatEntry> out;
|
||||||
@@ -493,39 +540,52 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
|||||||
}
|
}
|
||||||
// Uncareless user-friendly loading of patches (must start with 'cheat_')
|
// Uncareless user-friendly loading of patches (must start with 'cheat_')
|
||||||
// <mod dir> / <cheat file>.txt
|
// <mod dir> / <cheat file>.txt
|
||||||
for (auto const& f : load_dir->GetFiles()) {
|
for (const auto& load_dir : load_dirs) {
|
||||||
auto const name = f->GetName();
|
for (auto const& f : load_dir->GetFiles()) {
|
||||||
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
auto const name = f->GetName();
|
||||||
std::vector<u8> data(f->GetSize());
|
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
|
||||||
if (f->Read(data.data(), data.size()) == data.size()) {
|
std::vector<u8> data(f->GetSize());
|
||||||
const Core::Memory::TextCheatParser parser;
|
if (f->Read(data.data(), data.size()) == data.size()) {
|
||||||
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
const Core::Memory::TextCheatParser parser;
|
||||||
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
|
||||||
} else {
|
std::copy(res.begin(), res.end(), std::back_inserter(out));
|
||||||
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
} else {
|
||||||
|
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, u64 application_id, ContentRecordType type,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
std::vector<VirtualDir> load_dirs{fs_controller.GetModificationLoadRoot(title_id)};
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
std::vector<VirtualDir> sdmc_load_dirs{fs_controller.GetSDMCModificationLoadRoot(title_id)};
|
||||||
|
if (application_id != title_id) {
|
||||||
|
load_dirs.push_back(fs_controller.GetModificationLoadRoot(application_id));
|
||||||
|
sdmc_load_dirs.push_back(fs_controller.GetSDMCModificationLoadRoot(application_id));
|
||||||
|
}
|
||||||
|
std::erase(load_dirs, nullptr);
|
||||||
|
std::erase(sdmc_load_dirs, nullptr);
|
||||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||||
type != ContentRecordType::HtmlDocument) ||
|
type != ContentRecordType::HtmlDocument) ||
|
||||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
(load_dirs.empty() && sdmc_load_dirs.empty())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs;
|
||||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
for (const auto& load_dir : load_dirs) {
|
||||||
patch_dirs.push_back(sdmc_load_dir);
|
const auto load_patch_dirs = load_dir->GetSubdirectories();
|
||||||
|
patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
|
||||||
}
|
}
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||||
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
patch_dirs.insert(patch_dirs.end(), sdmc_load_dirs.begin(), sdmc_load_dirs.end());
|
||||||
|
}
|
||||||
|
std::stable_sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) {
|
||||||
|
return l->GetName() < r->GetName();
|
||||||
|
});
|
||||||
|
|
||||||
std::vector<VirtualDir> layers;
|
std::vector<VirtualDir> layers;
|
||||||
std::vector<VirtualDir> layers_ext;
|
std::vector<VirtualDir> layers_ext;
|
||||||
@@ -597,8 +657,8 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
auto romfs = base_romfs;
|
auto romfs = base_romfs;
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -705,7 +765,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
ApplyLayeredFS(romfs, title_id, application_id, type, fs_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -717,10 +777,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Patch> out;
|
std::vector<Patch> out;
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto& disabled = Settings::values.disabled_addons[application_id];
|
||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
|
|
||||||
std::vector<Patch> external_update_patches;
|
std::vector<Patch> external_update_patches;
|
||||||
|
|
||||||
@@ -869,7 +929,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
.version = "",
|
.version = "",
|
||||||
.type = PatchType::Update,
|
.type = PatchType::Update,
|
||||||
.program_id = title_id,
|
.program_id = title_id,
|
||||||
.title_id = title_id,
|
.title_id = update_tid,
|
||||||
.source = PatchSource::Unknown,
|
.source = PatchSource::Unknown,
|
||||||
.numeric_version = 0};
|
.numeric_version = 0};
|
||||||
|
|
||||||
@@ -895,8 +955,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// General Mods (LayeredFS and IPS)
|
// General Mods (LayeredFS and IPS)
|
||||||
const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
|
for (const auto& mod_dir : GetModificationLoadRoots()) {
|
||||||
if (mod_dir != nullptr) {
|
|
||||||
for (auto const& f : mod_dir->GetFiles())
|
for (auto const& f : mod_dir->GetFiles())
|
||||||
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
|
if (auto const name = f->GetName(); name.starts_with("cheat_")) {
|
||||||
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
|
auto const mod_disabled = std::find(disabled.begin(), disabled.end(), name) != disabled.end();
|
||||||
@@ -963,8 +1022,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SDMC mod directory (RomFS LayeredFS)
|
// SDMC mod directory (RomFS LayeredFS)
|
||||||
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
for (const auto& sdmc_mod_dir : GetSDMCModificationLoadRoots()) {
|
||||||
if (sdmc_mod_dir != nullptr) {
|
|
||||||
std::string types;
|
std::string types;
|
||||||
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs")))
|
||||||
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
AppendCommaIfNotEmpty(types, "LayeredExeFS");
|
||||||
@@ -999,10 +1057,10 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
dlc_match.reserve(dlc_entries_with_origin.size());
|
dlc_match.reserve(dlc_entries_with_origin.size());
|
||||||
for (const auto& [slot, entry] : dlc_entries_with_origin) {
|
for (const auto& [slot, entry] : dlc_entries_with_origin) {
|
||||||
const auto base_tid = GetBaseTitleID(entry.title_id);
|
const auto base_tid = GetBaseTitleID(entry.title_id);
|
||||||
const bool matches_base = base_tid == title_id;
|
const bool matches_base = base_tid == application_id;
|
||||||
if (!matches_base) {
|
if (!matches_base) {
|
||||||
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
|
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
|
||||||
entry.title_id, base_tid, title_id);
|
entry.title_id, base_tid, application_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,16 +1135,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleIDForContent();
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||||
return content_provider.GetEntryVersion(update_tid);
|
return content_provider.GetEntryVersion(update_tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return content_provider.GetEntryVersion(title_id);
|
if (const auto version = content_provider.GetEntryVersion(title_id); version.has_value()) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
return content_provider.GetEntryVersion(application_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||||
|
if (base_control_nca == nullptr && application_id != title_id) {
|
||||||
|
base_control_nca = content_provider.GetEntry(application_id, ContentRecordType::Control);
|
||||||
|
}
|
||||||
if (base_control_nca == nullptr) {
|
if (base_control_nca == nullptr) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -1162,8 +1226,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
|
|||||||
auto metadata = pm.GetControlMetadata();
|
auto metadata = pm.GetControlMetadata();
|
||||||
if (metadata.first != nullptr)
|
if (metadata.first != nullptr)
|
||||||
return metadata;
|
return metadata;
|
||||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()};
|
const auto update_id = FileSys::GetUpdateTitleID(application_id);
|
||||||
return pm_update.GetControlMetadata();
|
const auto application_update_id = FileSys::GetUpdateTitleID(GetBaseTitleID(application_id));
|
||||||
|
const FileSys::PatchManager pm_update{update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
metadata = pm_update.GetControlMetadata();
|
||||||
|
if (metadata.first != nullptr || update_id == application_update_id)
|
||||||
|
return metadata;
|
||||||
|
const FileSys::PatchManager pm_application_update{application_update_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
|
return pm_application_update.GetControlMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/vfs/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
@@ -109,10 +110,14 @@ public:
|
|||||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
[[nodiscard]] u64 GetUpdateTitleIDForContent() const;
|
||||||
|
[[nodiscard]] std::vector<VirtualDir> GetModificationLoadRoots() const;
|
||||||
|
[[nodiscard]] std::vector<VirtualDir> GetSDMCModificationLoadRoots() const;
|
||||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const;
|
const std::string& build_id) const;
|
||||||
|
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
|
u64 application_id;
|
||||||
const Service::FileSystem::FileSystemController& fs_controller;
|
const Service::FileSystem::FileSystemController& fs_controller;
|
||||||
const ContentProvider& content_provider;
|
const ContentProvider& content_provider;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -567,20 +567,45 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
static std::optional<NcaID> CheckMapForContentRecord(const ankerl::unordered_dense::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
|
||||||
const auto cmnt_iter = map.find(title_id);
|
auto cmnt_iter = map.find(title_id);
|
||||||
|
u8 id_offset = 0;
|
||||||
|
|
||||||
if (cmnt_iter == map.cend()) {
|
if (cmnt_iter == map.cend()) {
|
||||||
return std::nullopt;
|
const auto program_index = title_id & AOC_TITLE_ID_MASK;
|
||||||
|
if (program_index == 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmnt_iter = map.find(title_id & ~AOC_TITLE_ID_MASK);
|
||||||
|
if (cmnt_iter == map.cend()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_offset = static_cast<u8>(program_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& cnmt = cmnt_iter->second;
|
const auto& cnmt = cmnt_iter->second;
|
||||||
const auto& content_records = cnmt.GetContentRecords();
|
const auto& content_records = cnmt.GetContentRecords();
|
||||||
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
const auto iter = std::find_if(content_records.cbegin(), content_records.cend(),
|
||||||
[type](const ContentRecord& rec) { return rec.type == type; });
|
[type, id_offset](const ContentRecord& rec) {
|
||||||
if (iter == content_records.cend()) {
|
return rec.type == type && rec.id_offset == id_offset;
|
||||||
|
});
|
||||||
|
if (iter != content_records.cend()) {
|
||||||
|
return std::make_optional(iter->nca_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id_offset != 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_optional(iter->nca_id);
|
const auto fallback_iter =
|
||||||
|
std::find_if(content_records.cbegin(), content_records.cend(),
|
||||||
|
[type](const ContentRecord& rec) { return rec.type == type; });
|
||||||
|
if (fallback_iter == content_records.cend()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_optional(fallback_iter->nca_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/partition_filesystem.h"
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
@@ -65,10 +66,12 @@ u64 NSP::GetProgramTitleID() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto program_id = expected_program_id;
|
auto program_id = expected_program_id;
|
||||||
if (program_id == 0) {
|
if (program_id == 0 && !program_status.empty()) {
|
||||||
if (!program_status.empty()) {
|
program_id = std::min_element(program_status.cbegin(), program_status.cend(),
|
||||||
program_id = program_status.begin()->first;
|
[](const auto& lhs, const auto& rhs) {
|
||||||
}
|
return lhs.first < rhs.first;
|
||||||
|
})
|
||||||
|
->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
program_id = program_id + program_index;
|
program_id = program_id + program_index;
|
||||||
@@ -273,8 +276,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
|||||||
// If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
|
// If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
|
||||||
// 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
|
// 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
|
||||||
// update NCA.
|
// update NCA.
|
||||||
if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
|
if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) {
|
||||||
(next_nca->GetTitleId() & 0x800) == 0) {
|
|
||||||
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
|
||||||
std::move(next_nca);
|
std::move(next_nca);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -461,19 +461,8 @@ std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
|||||||
|
|
||||||
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
||||||
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
||||||
#ifdef __OPENORBIS__
|
|
||||||
// DO NOT USE NORMAL STAT, it's bugged on OO toolchain
|
|
||||||
OrbisKernelStat st{};
|
|
||||||
const auto stat_result = stat(full_path.c_str(), reinterpret_cast<struct stat *>(std::addressof(st)));
|
|
||||||
if (stat_result != 0)
|
|
||||||
return {};
|
|
||||||
return {
|
|
||||||
.created{u64(st.st_ctime)},
|
|
||||||
.accessed{u64(st.st_atime)},
|
|
||||||
.modified{u64(st.st_mtime)},
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
struct _stat64 file_status;
|
struct _stat64 file_status;
|
||||||
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
||||||
@@ -481,14 +470,16 @@ FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) cons
|
|||||||
struct stat file_status;
|
struct stat file_status;
|
||||||
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
||||||
#endif
|
#endif
|
||||||
if (stat_result != 0)
|
|
||||||
|
if (stat_result != 0) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.created{u64(file_status.st_ctime)},
|
.created{static_cast<u64>(file_status.st_ctime)},
|
||||||
.accessed{u64(file_status.st_atime)},
|
.accessed{static_cast<u64>(file_status.st_atime)},
|
||||||
.modified{u64(file_status.st_mtime)},
|
.modified{static_cast<u64>(file_status.st_mtime)},
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
||||||
|
|||||||
@@ -349,9 +349,18 @@ struct KernelCore::Impl {
|
|||||||
object_name_global_data.emplace(kernel);
|
object_name_global_data.emplace(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
|
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||||
|
if (application_process == process)
|
||||||
|
return;
|
||||||
|
|
||||||
|
KProcess* const previous = application_process;
|
||||||
application_process = process;
|
application_process = process;
|
||||||
application_process->Open(kernel);
|
|
||||||
|
if (application_process != nullptr)
|
||||||
|
application_process->Open(kernel);
|
||||||
|
|
||||||
|
if (previous != nullptr)
|
||||||
|
previous->Close(kernel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the host thread ID for the caller.
|
/// Sets the host thread ID for the caller.
|
||||||
@@ -879,8 +888,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::MakeApplicationProcess(KProcess* process) {
|
void KernelCore::SetApplicationProcess(KProcess* process) {
|
||||||
impl->MakeApplicationProcess(*this, process);
|
impl->SetApplicationProcess(*this, process);
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcess* KernelCore::ApplicationProcess() {
|
KProcess* KernelCore::ApplicationProcess() {
|
||||||
@@ -891,6 +900,14 @@ const KProcess* KernelCore::ApplicationProcess() const {
|
|||||||
return impl->application_process;
|
return impl->application_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KScopedAutoObject<KProcess> KernelCore::GetProcessByProcessId(u64 process_id) {
|
||||||
|
std::scoped_lock lk{impl->process_list_lock};
|
||||||
|
for (auto* const process : impl->process_list)
|
||||||
|
if (process != nullptr && process->GetProcessId() == process_id)
|
||||||
|
return {*this, process};
|
||||||
|
return {*this, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
||||||
std::list<KScopedAutoObject<KProcess>> processes;
|
std::list<KScopedAutoObject<KProcess>> processes;
|
||||||
std::scoped_lock lk{impl->process_list_lock};
|
std::scoped_lock lk{impl->process_list_lock};
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ public:
|
|||||||
void AppendNewProcess(KProcess* process);
|
void AppendNewProcess(KProcess* process);
|
||||||
void RemoveProcess(KProcess* process);
|
void RemoveProcess(KProcess* process);
|
||||||
|
|
||||||
/// Makes the given process the new application process.
|
/// Makes the given process the current application process.
|
||||||
void MakeApplicationProcess(KProcess* process);
|
void SetApplicationProcess(KProcess* process);
|
||||||
|
|
||||||
/// Retrieves a pointer to the application process.
|
/// Retrieves a pointer to the application process.
|
||||||
KProcess* ApplicationProcess();
|
KProcess* ApplicationProcess();
|
||||||
@@ -133,6 +133,9 @@ public:
|
|||||||
/// Retrieves a const pointer to the application process.
|
/// Retrieves a const pointer to the application process.
|
||||||
const KProcess* ApplicationProcess() const;
|
const KProcess* ApplicationProcess() const;
|
||||||
|
|
||||||
|
/// Retrieves the process with the given process ID, or a null object.
|
||||||
|
KScopedAutoObject<KProcess> GetProcessByProcessId(u64 process_id);
|
||||||
|
|
||||||
/// Retrieves the list of processes.
|
/// Retrieves the list of processes.
|
||||||
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -10,6 +13,7 @@ namespace Service::AM {
|
|||||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||||
|
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
|
||||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -201,6 +201,14 @@ enum class ProgramSpecifyKind : u32 {
|
|||||||
RestartProgram = 2,
|
RestartProgram = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Maufeat: Use enums for zindex instead of using random zindex numbers
|
||||||
|
enum AppletZIndex : s32 {
|
||||||
|
Background = 0,
|
||||||
|
Foreground = 1,
|
||||||
|
ForegroundVisible = 2,
|
||||||
|
Overlay = 3,
|
||||||
|
};
|
||||||
|
|
||||||
struct CommonArguments {
|
struct CommonArguments {
|
||||||
CommonArgumentVersion arguments_version;
|
CommonArgumentVersion arguments_version;
|
||||||
CommonArgumentSize size;
|
CommonArgumentSize size;
|
||||||
|
|||||||
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::SetInteractibleLocked(bool interactible) {
|
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
|
||||||
if (is_interactible == interactible) {
|
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_interactible = interactible;
|
is_pad_interactible = pad_interactible;
|
||||||
|
is_touch_interactible = touch_interactible;
|
||||||
|
|
||||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||||
const bool input_enabled = interactible && !exit_requested;
|
const bool pad_enabled = pad_interactible && !exit_requested;
|
||||||
|
const bool touch_enabled = touch_interactible && !exit_requested;
|
||||||
|
|
||||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
|
||||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
|
||||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Applet::OnProcessTerminatedLocked() {
|
void Applet::OnProcessTerminatedLocked() {
|
||||||
|
|||||||
@@ -125,9 +125,11 @@ struct Applet {
|
|||||||
bool album_image_taken_notification_enabled{};
|
bool album_image_taken_notification_enabled{};
|
||||||
bool record_volume_muted{};
|
bool record_volume_muted{};
|
||||||
bool is_activity_runnable{};
|
bool is_activity_runnable{};
|
||||||
bool is_interactible{true};
|
bool is_pad_interactible{true};
|
||||||
|
bool is_touch_interactible{true};
|
||||||
bool window_visible{true};
|
bool window_visible{true};
|
||||||
bool overlay_in_foreground{false};
|
bool overlay_watching_short_home_button{false};
|
||||||
|
bool overlay_handling_touch_input{false};
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
Event overlay_event;
|
Event overlay_event;
|
||||||
@@ -148,7 +150,7 @@ struct Applet {
|
|||||||
|
|
||||||
// Process state management
|
// Process state management
|
||||||
void UpdateSuspensionStateLocked(bool force_message);
|
void UpdateSuspensionStateLocked(bool force_message);
|
||||||
void SetInteractibleLocked(bool interactible);
|
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
|
||||||
void OnProcessTerminatedLocked();
|
void OnProcessTerminatedLocked();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/am/display_layer_manager.h"
|
#include "core/hle/service/am/display_layer_manager.h"
|
||||||
|
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/hle/service/vi/application_display_service.h"
|
#include "core/hle/service/vi/application_display_service.h"
|
||||||
#include "core/hle/service/vi/container.h"
|
#include "core/hle/service/vi/container.h"
|
||||||
@@ -33,6 +34,7 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
|||||||
m_system_shared_buffer_id = 0;
|
m_system_shared_buffer_id = 0;
|
||||||
m_system_shared_layer_id = 0;
|
m_system_shared_layer_id = 0;
|
||||||
m_applet_id = applet_id;
|
m_applet_id = applet_id;
|
||||||
|
m_library_applet_mode = mode;
|
||||||
m_buffer_sharing_enabled = false;
|
m_buffer_sharing_enabled = false;
|
||||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||||
@@ -72,14 +74,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
|||||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||||
|
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
|
|
||||||
if (m_applet_id != AppletId::Application) {
|
if (m_applet_id != AppletId::Application) {
|
||||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||||
} else {
|
} else {
|
||||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,10 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
|||||||
|
|
||||||
// Ensure the overlay layer is visible
|
// Ensure the overlay layer is visible
|
||||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||||
|
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||||
|
this->GetLayerStackMask());
|
||||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||||
s32 initial_z = 1;
|
s32 initial_z = Foreground;
|
||||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||||
initial_z = -1;
|
initial_z = Overlay;
|
||||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||||
}
|
}
|
||||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||||
@@ -142,6 +148,36 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 DisplayLayerManager::GetLayerStackMask() const {
|
||||||
|
using Nvnflinger::LayerStackBit;
|
||||||
|
using Nvnflinger::LayerStackId;
|
||||||
|
|
||||||
|
constexpr u32 Displayed = LayerStackBit(LayerStackId::Default);
|
||||||
|
constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot);
|
||||||
|
constexpr u32 Recording = LayerStackBit(LayerStackId::Recording);
|
||||||
|
constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame);
|
||||||
|
constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug);
|
||||||
|
|
||||||
|
switch (m_applet_id) {
|
||||||
|
case AppletId::Application:
|
||||||
|
return Displayed | Screenshot | Recording | LastFrame | Debug;
|
||||||
|
case AppletId::OverlayDisplay:
|
||||||
|
return Displayed;
|
||||||
|
case AppletId::QLaunch:
|
||||||
|
return Displayed;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_library_applet_mode) {
|
||||||
|
case LibraryAppletMode::AllForeground:
|
||||||
|
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||||
|
return Displayed | Screenshot | LastFrame;
|
||||||
|
default:
|
||||||
|
return Displayed | Screenshot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||||
if (m_visible == visible) {
|
if (m_visible == visible) {
|
||||||
return;
|
return;
|
||||||
@@ -185,10 +221,17 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||||
s32* out_fbshare_layer_index) {
|
s32* out_fbshare_layer_index,
|
||||||
|
VI::CaptureKind kind) {
|
||||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||||
out_was_written, out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) {
|
||||||
|
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||||
|
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer(
|
||||||
|
fbshare_layer_index, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -23,6 +23,7 @@ class KProcess;
|
|||||||
namespace Service::VI {
|
namespace Service::VI {
|
||||||
class IApplicationDisplayService;
|
class IApplicationDisplayService;
|
||||||
class IManagerDisplayService;
|
class IManagerDisplayService;
|
||||||
|
enum class CaptureKind : u32;
|
||||||
} // namespace Service::VI
|
} // namespace Service::VI
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
@@ -48,9 +49,13 @@ public:
|
|||||||
|
|
||||||
void SetOverlayZIndex(s32 z_index);
|
void SetOverlayZIndex(s32 z_index);
|
||||||
|
|
||||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
|
||||||
|
VI::CaptureKind kind);
|
||||||
|
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
u32 GetLayerStackMask() const;
|
||||||
|
|
||||||
Kernel::KProcess* m_process{};
|
Kernel::KProcess* m_process{};
|
||||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||||
@@ -59,6 +64,7 @@ private:
|
|||||||
u64 m_system_shared_buffer_id{};
|
u64 m_system_shared_buffer_id{};
|
||||||
u64 m_system_shared_layer_id{};
|
u64 m_system_shared_layer_id{};
|
||||||
AppletId m_applet_id{};
|
AppletId m_applet_id{};
|
||||||
|
LibraryAppletMode m_library_applet_mode{};
|
||||||
bool m_buffer_sharing_enabled{};
|
bool m_buffer_sharing_enabled{};
|
||||||
bool m_blending_enabled{};
|
bool m_blending_enabled{};
|
||||||
bool m_visible{true};
|
bool m_visible{true};
|
||||||
|
|||||||
@@ -36,12 +36,17 @@ HidRegistration::~HidRegistration() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
|
||||||
if (m_process.IsInitialized()) {
|
if (!m_process.IsInitialized())
|
||||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
return;
|
||||||
enable);
|
|
||||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
const auto resource_manager = m_hid_server->GetResourceManager();
|
||||||
}
|
const u64 aruid = m_process.GetProcessId();
|
||||||
|
|
||||||
|
resource_manager->EnablePadInput(aruid, enable_pad);
|
||||||
|
resource_manager->EnableTouchScreen(aruid, enable_touch);
|
||||||
|
|
||||||
|
resource_manager->SetAruidValidForVibration(aruid, enable_pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public:
|
|||||||
~HidRegistration();
|
~HidRegistration();
|
||||||
|
|
||||||
void RegisterCurrentProcess();
|
void RegisterCurrentProcess();
|
||||||
void EnableAppletToGetInput(bool enable);
|
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Process& m_process;
|
Process& m_process;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
@@ -104,8 +105,16 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
|
|||||||
|
|
||||||
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
||||||
// current_process_game_card use correct StorageId
|
// current_process_game_card use correct StorageId
|
||||||
launch.base_game_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program));
|
auto base_slot = storage.GetSlotForEntry(launch.title_id, FileSys::ContentRecordType::Program);
|
||||||
launch.update_storage_id = GetStorageIdForFrontendSlot(storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
if (!base_slot) {
|
||||||
|
base_slot = storage.GetSlotForEntry(FileSys::GetBaseTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||||
|
}
|
||||||
|
launch.base_game_storage_id = GetStorageIdForFrontendSlot(base_slot);
|
||||||
|
auto update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program);
|
||||||
|
if (!update_slot) {
|
||||||
|
update_slot = storage.GetSlotForEntry(FileSys::GetUpdateTitleID(FileSys::GetBaseTitleID(launch.title_id)), FileSys::ContentRecordType::Program);
|
||||||
|
}
|
||||||
|
launch.update_storage_id = GetStorageIdForFrontendSlot(update_slot);
|
||||||
|
|
||||||
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
system.GetARPManager().Register(launch.title_id, launch, out_control);
|
||||||
return process;
|
return process;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
@@ -78,7 +80,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
|||||||
|
|
||||||
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
|
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "common/hex_util.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/uuid.h"
|
#include "common/uuid.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
@@ -28,6 +30,27 @@
|
|||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
FileSys::PatchManager::Metadata GetApplicationMetadata(Core::System& system, u64 program_id) {
|
||||||
|
auto metadata = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, program_id);
|
||||||
|
if (metadata.first != nullptr) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto application_id = FileSys::GetBaseTitleID(program_id);
|
||||||
|
if (application_id == program_id) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_AM,
|
||||||
|
"no metadata for program_id={:016X}, falling back to application_id={:016X}",
|
||||||
|
program_id, application_id);
|
||||||
|
return FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, application_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
|
||||||
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
: ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -135,7 +158,7 @@ Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID use
|
|||||||
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
|
||||||
|
|
||||||
FileSys::SaveDataAttribute attribute{};
|
FileSys::SaveDataAttribute attribute{};
|
||||||
attribute.program_id = m_applet->program_id;
|
attribute.program_id = FileSys::GetBaseTitleID(m_applet->program_id);
|
||||||
attribute.user_id = user_id.AsU128();
|
attribute.user_id = user_id.AsU128();
|
||||||
attribute.type = FileSys::SaveDataType::Account;
|
attribute.type = FileSys::SaveDataType::Account;
|
||||||
|
|
||||||
@@ -156,7 +179,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
|||||||
// Default to 0 (all languages supported)
|
// Default to 0 (all languages supported)
|
||||||
u32 supported_languages = 0;
|
u32 supported_languages = 0;
|
||||||
|
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||||
if (res.first != nullptr) {
|
if (res.first != nullptr) {
|
||||||
supported_languages = res.first->GetSupportedLanguages();
|
supported_languages = res.first->GetSupportedLanguages();
|
||||||
}
|
}
|
||||||
@@ -194,7 +217,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
|||||||
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
const auto res = GetApplicationMetadata(system, m_applet->program_id);
|
||||||
if (res.first != nullptr) {
|
if (res.first != nullptr) {
|
||||||
const auto& version = res.first->GetVersionString();
|
const auto& version = res.first->GetVersionString();
|
||||||
std::memcpy(out_display_version->string.data(), version.data(),
|
std::memcpy(out_display_version->string.data(), version.data(),
|
||||||
@@ -215,7 +238,7 @@ Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys
|
|||||||
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
|
static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
|
||||||
|
|
||||||
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
|
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
|
||||||
type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size});
|
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128(), {normal_size, journal_size});
|
||||||
|
|
||||||
// The following value is used to indicate the amount of space remaining on failure
|
// The following value is used to indicate the amount of space remaining on failure
|
||||||
// due to running out of space. Since we always succeed, this should be 0.
|
// due to running out of space. Since we always succeed, this should be 0.
|
||||||
@@ -229,7 +252,7 @@ Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64>
|
|||||||
LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
|
LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
|
||||||
|
|
||||||
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
|
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
|
||||||
type, m_applet->program_id, user_id.AsU128());
|
type, FileSys::GetBaseTitleID(m_applet->program_id), user_id.AsU128());
|
||||||
|
|
||||||
*out_normal_size = size.normal;
|
*out_normal_size = size.normal;
|
||||||
*out_journal_size = size.journal;
|
*out_journal_size = size.journal;
|
||||||
@@ -431,15 +454,21 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value)
|
|||||||
|
|
||||||
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
// https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart
|
||||||
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) {
|
||||||
LOG_INFO(Service_AM, "called, application_id={:016X}", application_id);
|
LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id);
|
||||||
|
|
||||||
// If application_id is 0, relaunch the current application
|
if (application_id == 0 ||
|
||||||
const u64 target_application_id =
|
FileSys::GetBaseTitleID(application_id) == FileSys::GetBaseTitleID(m_applet->program_id)) {
|
||||||
(application_id == 0) ? m_applet->program_id : application_id;
|
const auto program_index = application_id == 0
|
||||||
|
? 0
|
||||||
|
: application_id - FileSys::GetBaseTitleID(application_id);
|
||||||
|
|
||||||
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
system.GetUserChannel() = m_applet->user_channel_launch_parameter;
|
||||||
system.ExecuteProgram(target_application_id);
|
system.ExecuteProgram(program_index);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR(Service_AM, "Launching a different application ({:016X}) is not implemented!", application_id);
|
||||||
|
R_THROW(ResultUnknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IApplicationFunctions::ClearUserChannel() {
|
Result IApplicationFunctions::ClearUserChannel() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/service/display_controller.h"
|
#include "core/hle/service/am/service/display_controller.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
@@ -71,16 +72,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
|
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
|
||||||
unknown0, fbshare_layer_index, color);
|
fbshare_layer_index, color);
|
||||||
R_SUCCEED();
|
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||||
@@ -90,9 +91,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
|||||||
|
|
||||||
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||||
@@ -102,9 +103,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
|||||||
|
|
||||||
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
||||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||||
out_fbshare_layer_index));
|
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
@@ -22,7 +22,7 @@ namespace Service::AM {
|
|||||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
|
||||||
{30, nullptr, "SetHealthWarningShowingState"},
|
{30, nullptr, "SetHealthWarningShowingState"},
|
||||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||||
@@ -43,10 +43,12 @@ namespace Service::AM {
|
|||||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
m_applet->overlay_in_foreground = true;
|
{
|
||||||
m_applet->home_button_short_pressed_blocked = false;
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_watching_short_home_button = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||||
window_system->RequestUpdate();
|
window_system->RequestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,16 +58,26 @@ namespace Service::AM {
|
|||||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
m_applet->overlay_in_foreground = false;
|
{
|
||||||
m_applet->home_button_short_pressed_blocked = false;
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_watching_short_home_button = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||||
window_system->RequestUpdate();
|
window_system->RequestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) {
|
||||||
|
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||||
|
|
||||||
|
std::scoped_lock lk{m_applet->lock};
|
||||||
|
m_applet->overlay_handling_touch_input = enabled;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -20,6 +20,7 @@ namespace Service::AM {
|
|||||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||||
|
Result SetHandlingTouchScreenInputEnabled(bool enabled);
|
||||||
Result Unknown70();
|
Result Unknown70();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/am/am_results.h"
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/hle/service/am/applet.h"
|
#include "core/hle/service/am/applet.h"
|
||||||
#include "core/hle/service/am/applet_manager.h"
|
#include "core/hle/service/am/applet_manager.h"
|
||||||
@@ -32,46 +36,82 @@ void WindowSystem::RequestUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::Update() {
|
void WindowSystem::Update() {
|
||||||
std::scoped_lock lk{m_lock};
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||||
|
|
||||||
// Loop through all applets and remove terminated applets.
|
// Loop through all applets and remove terminated applets.
|
||||||
this->PruneTerminatedAppletsLocked();
|
this->PruneTerminatedAppletsLocked();
|
||||||
|
|
||||||
// If the home menu is being locked into the foreground, handle that.
|
// If the home menu is being locked into the foreground, handle that.
|
||||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
if (!this->LockHomeMenuIntoForegroundLocked()) {
|
||||||
return;
|
const bool overlay_takes_input = this->DoesOverlayTakeInputLocked();
|
||||||
|
|
||||||
|
this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input);
|
||||||
|
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input);
|
||||||
|
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool overlay_blocks_input = false;
|
this->NotifyApplicationChangedIfNeeded();
|
||||||
if (m_overlay_display) {
|
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
|
||||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively update each applet root.
|
|
||||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
|
||||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
|
||||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||||
std::scoped_lock lk{m_lock};
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::QLaunch) {
|
if (applet->applet_id == AppletId::QLaunch) {
|
||||||
ASSERT(m_home_menu == nullptr);
|
ASSERT(m_home_menu == nullptr);
|
||||||
m_home_menu = applet.get();
|
m_home_menu = applet.get();
|
||||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||||
m_overlay_display = applet.get();
|
m_overlay_display = applet.get();
|
||||||
} else if (is_application) {
|
} else if (is_application) {
|
||||||
ASSERT(m_application == nullptr);
|
ASSERT(m_application == nullptr);
|
||||||
m_application = applet.get();
|
m_application = applet.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateCurrentApplicationLocked();
|
||||||
|
|
||||||
|
m_event_observer->TrackAppletProcess(*applet);
|
||||||
|
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_event_observer->TrackAppletProcess(*applet);
|
this->NotifyApplicationChangedIfNeeded();
|
||||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
}
|
||||||
|
|
||||||
|
void WindowSystem::UpdateCurrentApplicationLocked() {
|
||||||
|
const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu;
|
||||||
|
if (candidate == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* const process = candidate->process->GetHandle();
|
||||||
|
if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id);
|
||||||
|
|
||||||
|
m_system.Kernel().SetApplicationProcess(process);
|
||||||
|
Settings::SetCurrentProgramID(candidate->program_id);
|
||||||
|
|
||||||
|
m_pending_application_notification = candidate->program_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowSystem::NotifyApplicationChangedIfNeeded() {
|
||||||
|
std::optional<u64> program_id;
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{m_lock};
|
||||||
|
program_id = std::exchange(m_pending_application_notification, std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!program_id.has_value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_system.NotifyApplicationChanged(*program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||||
@@ -169,18 +209,40 @@ void WindowSystem::OnExitRequested() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||||
if (m_home_menu) {
|
const auto is_blocked = [message](const Applet& applet) {
|
||||||
std::scoped_lock lk_home{m_home_menu->lock};
|
if (message == AppletMessage::DetectShortPressingHomeButton &&
|
||||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
applet.applet_id == AppletId::OverlayDisplay &&
|
||||||
}
|
!applet.overlay_watching_short_home_button) {
|
||||||
if (m_overlay_display) {
|
return true;
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
}
|
||||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
|
||||||
}
|
switch (message) {
|
||||||
if (m_application) {
|
case AppletMessage::DetectShortPressingHomeButton:
|
||||||
std::scoped_lock lk_application{m_application->lock};
|
return applet.home_button_short_pressed_blocked;
|
||||||
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
case AppletMessage::DetectLongPressingHomeButton:
|
||||||
}
|
return applet.home_button_long_pressed_blocked;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto send_to = [&](Applet* applet) {
|
||||||
|
if (!applet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::scoped_lock lk{applet->lock};
|
||||||
|
if (is_blocked(*applet)) {
|
||||||
|
LOG_DEBUG(Service_AM, "Applet {} is blocking message {}",
|
||||||
|
static_cast<u32>(applet->applet_id), static_cast<u32>(message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||||
|
};
|
||||||
|
|
||||||
|
send_to(m_home_menu);
|
||||||
|
send_to(m_overlay_display);
|
||||||
|
send_to(m_application);
|
||||||
|
|
||||||
if (m_event_observer) {
|
if (m_event_observer) {
|
||||||
m_event_observer->RequestUpdate();
|
m_event_observer->RequestUpdate();
|
||||||
}
|
}
|
||||||
@@ -192,19 +254,9 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
|||||||
case SystemButtonType::HomeButtonShortPressing:
|
case SystemButtonType::HomeButtonShortPressing:
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||||
break;
|
break;
|
||||||
case SystemButtonType::HomeButtonLongPressing: {
|
case SystemButtonType::HomeButtonLongPressing:
|
||||||
// Toggle overlay foreground visibility on long home press
|
|
||||||
if (m_overlay_display) {
|
|
||||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
|
||||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
|
||||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
|
||||||
}
|
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||||
// Force a state update after toggling overlay
|
break;
|
||||||
if (m_event_observer) {
|
|
||||||
m_event_observer->RequestUpdate();
|
|
||||||
}
|
|
||||||
break; }
|
|
||||||
case SystemButtonType::CaptureButtonShortPressing:
|
case SystemButtonType::CaptureButtonShortPressing:
|
||||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||||
break;
|
break;
|
||||||
@@ -317,6 +369,8 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
|||||||
m_overlay_display = nullptr;
|
m_overlay_display = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->UpdateCurrentApplicationLocked();
|
||||||
|
|
||||||
// Finalize applet.
|
// Finalize applet.
|
||||||
applet->OnProcessTerminatedLocked();
|
applet->OnProcessTerminatedLocked();
|
||||||
|
|
||||||
@@ -389,7 +443,20 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
|||||||
applet->lock.lock();
|
applet->lock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const {
|
||||||
|
return overlay.window_visible && overlay.overlay_watching_short_home_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowSystem::DoesOverlayTakeInputLocked() const {
|
||||||
|
if (m_overlay_display == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::scoped_lock lk{m_overlay_display->lock};
|
||||||
|
return this->IsOverlayOpenLocked(*m_overlay_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) {
|
||||||
// With no applet, we don't have anything to do.
|
// With no applet, we don't have anything to do.
|
||||||
if (!applet) {
|
if (!applet) {
|
||||||
return;
|
return;
|
||||||
@@ -420,24 +487,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
|
||||||
|
|
||||||
// Update visibility state.
|
// Update visibility state.
|
||||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
const bool should_be_visible =
|
||||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
|
||||||
? applet->window_visible
|
|
||||||
: (is_foreground && applet->window_visible);
|
|
||||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||||
|
|
||||||
|
const bool needs_hid_input =
|
||||||
|
is_overlay ? this->IsOverlayOpenLocked(*applet)
|
||||||
|
: (is_foreground && applet->window_visible && !overlay_takes_input);
|
||||||
|
|
||||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
|
||||||
? applet->overlay_in_foreground
|
|
||||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
|
||||||
|
|
||||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
|
||||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
|
||||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
|
||||||
}
|
|
||||||
|
|
||||||
applet->SetInteractibleLocked(should_be_interactible);
|
|
||||||
|
|
||||||
// Update focus state and suspension.
|
// Update focus state and suspension.
|
||||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||||
@@ -453,23 +514,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
applet->UpdateSuspensionStateLocked(true);
|
applet->UpdateSuspensionStateLocked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||||
s32 z_index = 0;
|
s32 z_index = Background;
|
||||||
const bool now_foreground = inherited_foreground;
|
if (is_overlay) {
|
||||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
z_index = Overlay;
|
||||||
z_index = applet->overlay_in_foreground ? 100000 : -1;
|
} else if (inherited_foreground) {
|
||||||
} else if (now_foreground && !is_obscured) {
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
z_index = 2;
|
|
||||||
} else if (now_foreground) {
|
|
||||||
z_index = 1;
|
|
||||||
} else {
|
|
||||||
z_index = 0;
|
|
||||||
}
|
}
|
||||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||||
|
|
||||||
// Recurse into child applets.
|
// Recurse into child applets.
|
||||||
for (const auto& child_applet : applet->child_applets) {
|
for (const auto& child_applet : applet->child_applets) {
|
||||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/service/am/am_types.h"
|
#include "core/hle/service/am/am_types.h"
|
||||||
@@ -60,11 +61,16 @@ public:
|
|||||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void UpdateCurrentApplicationLocked();
|
||||||
|
void NotifyApplicationChangedIfNeeded();
|
||||||
void PruneTerminatedAppletsLocked();
|
void PruneTerminatedAppletsLocked();
|
||||||
bool RestartAppletProcessLocked(Applet* applet);
|
bool RestartAppletProcessLocked(Applet* applet);
|
||||||
bool LockHomeMenuIntoForegroundLocked();
|
bool LockHomeMenuIntoForegroundLocked();
|
||||||
void TerminateChildAppletsLocked(Applet* applet);
|
void TerminateChildAppletsLocked(Applet* applet);
|
||||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
bool IsOverlayOpenLocked(const Applet& overlay) const;
|
||||||
|
bool DoesOverlayTakeInputLocked() const;
|
||||||
|
|
||||||
|
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
|
||||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -88,6 +94,7 @@ private:
|
|||||||
|
|
||||||
// Applet map by aruid.
|
// Applet map by aruid.
|
||||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||||
|
std::optional<u64> m_pending_application_notification{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
|
#include "core/hle/service/am/am_results.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace Service::AOC {
|
namespace Service::AOC {
|
||||||
@@ -31,6 +32,10 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
|||||||
return FileSys::GetBaseTitleID(title_id) == base;
|
return FileSys::GetBaseTitleID(title_id) == base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) {
|
||||||
|
return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id));
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||||
std::vector<u64> add_on_content;
|
std::vector<u64> add_on_content;
|
||||||
const auto& rcu = system.GetContentProvider();
|
const auto& rcu = system.GetContentProvider();
|
||||||
@@ -91,7 +96,7 @@ IAddOnContentManager::~IAddOnContentManager() {
|
|||||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto current = system.GetApplicationProcessProgramID();
|
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[current];
|
const auto& disabled = Settings::values.disabled_addons[current];
|
||||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||||
@@ -112,7 +117,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
|||||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||||
process_id.pid);
|
process_id.pid);
|
||||||
|
|
||||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||||
|
|
||||||
std::vector<u32> out;
|
std::vector<u32> out;
|
||||||
const auto& disabled = Settings::values.disabled_addons[current];
|
const auto& disabled = Settings::values.disabled_addons[current];
|
||||||
@@ -126,8 +131,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(DarkLordZach): Find the correct error code.
|
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
|
||||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
|
||||||
|
|
||||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||||
@@ -141,7 +145,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
|||||||
ClientProcessId process_id) {
|
ClientProcessId process_id) {
|
||||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto title_id = system.GetApplicationProcessProgramID();
|
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
|
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
|
||||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
|
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
|
||||||
progress{{
|
progress{{
|
||||||
ProgressServiceBackend{system_, "Normal"},
|
ProgressServiceBackend{system_, "Normal"},
|
||||||
ProgressServiceBackend{system_, "Directory"},
|
ProgressServiceBackend{system_, "Directory"},
|
||||||
@@ -70,8 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache(
|
|||||||
LOG_DEBUG(Service_BCAT, "called");
|
LOG_DEBUG(Service_BCAT, "called");
|
||||||
|
|
||||||
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
||||||
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
|
||||||
GetProgressBackend(SyncType::Normal));
|
GetProgressBackend(SyncType::Normal));
|
||||||
|
|
||||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||||
@@ -86,9 +85,8 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
|
|||||||
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
||||||
|
|
||||||
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
||||||
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
name, progress_backend);
|
||||||
name, progress_backend);
|
|
||||||
|
|
||||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||||
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
@@ -19,7 +22,7 @@ class IDeliveryCacheProgressService;
|
|||||||
|
|
||||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||||
public:
|
public:
|
||||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
|
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
|
||||||
~IBcatService() override;
|
~IBcatService() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -39,6 +42,7 @@ private:
|
|||||||
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
||||||
|
|
||||||
BcatBackend& backend;
|
BcatBackend& backend;
|
||||||
|
u64 program_id;
|
||||||
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ std::vector<u8> DownloadImage(const std::string& url_path, const std::filesystem
|
|||||||
cli.set_connection_timeout(std::chrono::seconds(2));
|
cli.set_connection_timeout(std::chrono::seconds(2));
|
||||||
cli.set_read_timeout(std::chrono::seconds(2));
|
cli.set_read_timeout(std::chrono::seconds(2));
|
||||||
|
|
||||||
#if defined(YUZU_BUNDLED_OPENSSL) && !defined(__OPENORBIS__)
|
#ifdef YUZU_BUNDLED_OPENSSL
|
||||||
cli.load_ca_cert_store(kCert, sizeof(kCert));
|
cli.load_ca_cert_store(kCert, sizeof(kCert));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
#include "core/hle/service/bcat/bcat_service.h"
|
#include "core/hle/service/bcat/bcat_service.h"
|
||||||
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
||||||
#include "core/hle/service/bcat/service_creator.h"
|
#include "core/hle/service/bcat/service_creator.h"
|
||||||
@@ -37,7 +41,8 @@ IServiceCreator::~IServiceCreator() = default;
|
|||||||
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
||||||
OutInterface<IBcatService> out_interface) {
|
OutInterface<IBcatService> out_interface) {
|
||||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||||
*out_interface = std::make_shared<IBcatService>(system, *backend);
|
*out_interface =
|
||||||
|
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +50,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
|
|||||||
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
||||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||||
|
|
||||||
const auto title_id = system.GetApplicationProcessProgramID();
|
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||||
*out_interface =
|
*out_interface =
|
||||||
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|||||||
@@ -243,10 +243,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
|||||||
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
|
return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, AlbumReportOption report_option, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) {
|
Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||||
|
const ScreenShotAttribute& attribute,
|
||||||
|
AlbumReportOption report_option,
|
||||||
|
const ApplicationData& app_data, std::span<const u8> image_data,
|
||||||
|
u64 aruid) {
|
||||||
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
|
R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ???
|
||||||
|
|
||||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
const u64 title_id = system.ResolveCallerProgramId(aruid);
|
||||||
|
|
||||||
auto static_service =
|
auto static_service =
|
||||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,8 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
|
|||||||
manager->FlipVerticallyOnWrite(invert_y);
|
manager->FlipVerticallyOnWrite(invert_y);
|
||||||
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
||||||
},
|
},
|
||||||
layout);
|
layout,
|
||||||
|
Nvnflinger::LayerStackId::Screenshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::Capture
|
} // namespace Service::Capture
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/bis_factory.h"
|
#include "core/file_sys/bis_factory.h"
|
||||||
#include "core/file_sys/card_image.h"
|
#include "core/file_sys/card_image.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
@@ -339,7 +340,7 @@ Result FileSystemController::RegisterProcess(
|
|||||||
registrations.emplace(process_id, Registration{
|
registrations.emplace(process_id, Registration{
|
||||||
.program_id = program_id,
|
.program_id = program_id,
|
||||||
.romfs_factory = std::move(romfs_factory),
|
.romfs_factory = std::move(romfs_factory),
|
||||||
.save_data_factory = CreateSaveDataFactory(program_id),
|
.save_data_factory = CreateSaveDataFactory(FileSys::GetBaseTitleID(program_id)),
|
||||||
});
|
});
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
|
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fs_directory.h"
|
#include "core/file_sys/fs_directory.h"
|
||||||
@@ -313,7 +314,7 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSyste
|
|||||||
FileSys::ResultInvalidArgument);
|
FileSys::ResultInvalidArgument);
|
||||||
|
|
||||||
if (attribute.program_id == 0) {
|
if (attribute.program_id == 0) {
|
||||||
attribute.program_id = program_id;
|
attribute.program_id = FileSys::GetBaseTitleID(program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSys::VirtualDir dir{};
|
FileSys::VirtualDir dir{};
|
||||||
|
|||||||
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
|
|||||||
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
||||||
GetResourceManager()->GetSevenSixAxis()->Activate();
|
GetResourceManager()->GetSevenSixAxis()->Activate();
|
||||||
|
|
||||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
|
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
|
|||||||
|
|
||||||
auto& device = devices[device_index.value()].device;
|
auto& device = devices[device_index.value()].device;
|
||||||
device->SetPollingMode(polling_mode);
|
device->SetPollingMode(polling_mode);
|
||||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -147,7 +150,7 @@ Result IRS::RunImageTransferProcessor(
|
|||||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||||
image_transfer_processor.SetConfig(processor_config);
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||||
Common::Input::PollingMode::IR);
|
Common::Input::PollingMode::IR);
|
||||||
|
|
||||||
@@ -295,7 +298,8 @@ Result IRS::RunImageTransferExProcessor(
|
|||||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||||
image_transfer_processor.SetConfig(processor_config);
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
|
||||||
|
t_mem->GetOwner());
|
||||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||||
Common::Input::PollingMode::IR);
|
Common::Input::PollingMode::IR);
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user