mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-01 10:52:26 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa0878b636 |
@@ -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"
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
From b3622608433c183ba868a1dc8dd9cf285eb3b916 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Dario Petrillo <dario.pk1@gmail.com>
|
||||||
|
Date: Thu, 27 Nov 2025 23:12:38 +0100
|
||||||
|
Subject: [PATCH] avoid extra memset when clearing an empty table
|
||||||
|
|
||||||
|
---
|
||||||
|
include/ankerl/unordered_dense.h | 6 ++++--
|
||||||
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h
|
||||||
|
index 0835342..4938212 100644
|
||||||
|
--- a/include/ankerl/unordered_dense.h
|
||||||
|
+++ b/include/ankerl/unordered_dense.h
|
||||||
|
@@ -1490,8 +1490,10 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
|
||||||
|
// modifiers //////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
- m_values.clear();
|
||||||
|
- clear_buckets();
|
||||||
|
+ if (!empty()) {
|
||||||
|
+ m_values.clear();
|
||||||
|
+ clear_buckets();
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
auto insert(value_type const& value) -> std::pair<iterator, bool> {
|
||||||
+1
-1
@@ -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?")
|
||||||
@@ -541,6 +540,7 @@ add_subdirectory(externals)
|
|||||||
# pass targets from externals
|
# pass targets from externals
|
||||||
# TODO(crueter): CPMUtil Propagate func?
|
# TODO(crueter): CPMUtil Propagate func?
|
||||||
find_package(enet)
|
find_package(enet)
|
||||||
|
find_package(unordered_dense REQUIRED)
|
||||||
|
|
||||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||||
find_package(xbyak)
|
find_package(xbyak)
|
||||||
|
|||||||
@@ -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 ()
|
|
||||||
+19
-22
@@ -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,
|
||||||
@@ -315,6 +301,17 @@
|
|||||||
"repo": "eden-emu/tzdb_to_nx",
|
"repo": "eden-emu/tzdb_to_nx",
|
||||||
"version": "230326"
|
"version": "230326"
|
||||||
},
|
},
|
||||||
|
"unordered-dense": {
|
||||||
|
"bundled": true,
|
||||||
|
"find_args": "CONFIG",
|
||||||
|
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||||
|
"package": "unordered_dense",
|
||||||
|
"patches": [
|
||||||
|
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||||
|
],
|
||||||
|
"repo": "martinus/unordered_dense",
|
||||||
|
"version": "7b55cab841"
|
||||||
|
},
|
||||||
"vulkan-headers": {
|
"vulkan-headers": {
|
||||||
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
|
||||||
"min_version": "1.4.317",
|
"min_version": "1.4.317",
|
||||||
@@ -343,10 +340,10 @@
|
|||||||
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
"version": "vulkan-sdk-%NUMERIC_VERSION%"
|
||||||
},
|
},
|
||||||
"xbyak": {
|
"xbyak": {
|
||||||
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
|
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
|
||||||
"package": "xbyak",
|
"package": "xbyak",
|
||||||
"repo": "herumi/xbyak",
|
"repo": "herumi/xbyak",
|
||||||
"version": "v7.40.1"
|
"version": "v7.35.2"
|
||||||
},
|
},
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||||
|
|||||||
Vendored
-223
@@ -1,223 +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.3 (0d15f75042, 2025-12-25)"
|
|
||||||
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.4142136"
|
|
||||||
inkscape:cx="262.69017"
|
|
||||||
inkscape:cy="76.367532"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="849"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="27"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg7" />
|
|
||||||
<circle
|
|
||||||
style="opacity:1;mix-blend-mode:screen;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:15.907;stroke-dasharray:none;stroke-opacity:1"
|
|
||||||
id="path1"
|
|
||||||
cx="256.61652"
|
|
||||||
cy="257.31778"
|
|
||||||
r="227.33479" />
|
|
||||||
<path
|
|
||||||
id="path8-7"
|
|
||||||
style="display:inline;mix-blend-mode:multiply;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"
|
|
||||||
sodipodi:insensitive="true" />
|
|
||||||
<path
|
|
||||||
id="path8-7-2"
|
|
||||||
style="display:inline;mix-blend-mode:multiply;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"
|
|
||||||
sodipodi:insensitive="true" />
|
|
||||||
<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 |
Vendored
-2
@@ -509,8 +509,6 @@ QWidget#contentRichDialog QLabel#label_title_rich {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QWidget#contentDialog QLabel#label_dialog {
|
QWidget#contentDialog QLabel#label_dialog {
|
||||||
background: #2E2E2E;
|
|
||||||
|
|
||||||
padding: 20px 65px;
|
padding: 20px 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+5
-4
@@ -80,6 +80,7 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
|
|||||||
* [httplib](https://github.com/yhirose/cpp-httplib) - if `ENABLE_UPDATE_CHECKER` or `ENABLE_WEB_SERVICE` are on
|
* [httplib](https://github.com/yhirose/cpp-httplib) - if `ENABLE_UPDATE_CHECKER` or `ENABLE_WEB_SERVICE` are on
|
||||||
* This package is known to be broken on the AUR.
|
* This package is known to be broken on the AUR.
|
||||||
* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on
|
* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on
|
||||||
|
* [unordered-dense](https://github.com/martinus/unordered_dense)
|
||||||
|
|
||||||
On amd64:
|
On amd64:
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ Now, install all deps:
|
|||||||
sudo emerge -a \
|
sudo emerge -a \
|
||||||
app-arch/lz4 app-arch/zstd app-arch/unzip \
|
app-arch/lz4 app-arch/zstd app-arch/unzip \
|
||||||
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit \
|
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit \
|
||||||
dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
|
||||||
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
|
||||||
dev-util/vulkan-utility-libraries dev-util/glslang \
|
dev-util/vulkan-utility-libraries dev-util/glslang \
|
||||||
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
|
||||||
@@ -259,7 +260,7 @@ brew install molten-vk
|
|||||||
<details>
|
<details>
|
||||||
<summary>FreeBSD</summary>
|
<summary>FreeBSD</summary>
|
||||||
|
|
||||||
As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib vulkan-headers quazip-qt6`
|
As root run: `pkg install devel/cmake sdl3 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/qt6-charts devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/unordered-dense vulkan-headers quazip-qt6`
|
||||||
|
|
||||||
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
|
|||||||
<summary>DragonFlyBSD</summary>
|
<summary>DragonFlyBSD</summary>
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 libva-vdpau-driver libva-utils libva-intel-driver
|
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl3 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base qt6-charts quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver
|
||||||
```
|
```
|
||||||
|
|
||||||
[Caveats](./Caveats.md#dragonflybsd).
|
[Caveats](./Caveats.md#dragonflybsd).
|
||||||
@@ -327,7 +328,7 @@ sudo pkgin install git cmake autoconf build-essential libusb-1 nasm gcc13
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
BASE="git make autoconf libtool automake-wrapper jq patch"
|
BASE="git make autoconf libtool automake-wrapper jq patch"
|
||||||
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb openssl SDL3"
|
MINGW="qt6-base qt6-charts qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus libusb unordered_dense openssl SDL3"
|
||||||
# Either x86_64 or clang-aarch64 (Windows on ARM)
|
# Either x86_64 or clang-aarch64 (Windows on ARM)
|
||||||
packages="$BASE"
|
packages="$BASE"
|
||||||
for pkg in $MINGW; do
|
for pkg in $MINGW; do
|
||||||
|
|||||||
+8
-24
@@ -43,7 +43,6 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
|||||||
Firstly add your desired toggle:
|
Firstly add your desired toggle:
|
||||||
|
|
||||||
Example: `src/common/setting.h`
|
Example: `src/common/setting.h`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||||
```
|
```
|
||||||
@@ -68,7 +67,6 @@ Common Categories:
|
|||||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||||
|
|
||||||
Example: `src/qt_common/config/shared_translation.cpp`
|
Example: `src/qt_common/config/shared_translation.cpp`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
your_setting_name,
|
your_setting_name,
|
||||||
@@ -93,7 +91,6 @@ INSERT(Settings,
|
|||||||
Add where it should be in the settings.
|
Add where it should be in the settings.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||||
```
|
```
|
||||||
@@ -109,7 +106,6 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
|||||||
Add the toggle to the Kotlin (Android) UI
|
Add the toggle to the Kotlin (Android) UI
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
@@ -127,7 +123,6 @@ put(
|
|||||||
Add your setting within the right category.
|
Add your setting within the right category.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||||
```
|
```
|
||||||
@@ -142,7 +137,6 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
|||||||
Add your setting and description in the appropriate place.
|
Add your setting and description in the appropriate place.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<string name="your_setting_name">Your Setting Display Name</string>
|
<string name="your_setting_name">Your Setting Display Name</string>
|
||||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||||
@@ -156,7 +150,6 @@ Now the UI part is done find a place in the code for the toggle,
|
|||||||
And use it to your heart's desire!
|
And use it to your heart's desire!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||||
|
|
||||||
@@ -203,31 +196,25 @@ Common advantages recap:
|
|||||||
|
|
||||||
#### Accessing Debug Knobs (dev side)
|
#### Accessing Debug Knobs (dev side)
|
||||||
|
|
||||||
Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
//cpp side
|
//cpp side
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
//To use it as a general purpose uint var:
|
|
||||||
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
|
|
||||||
|
|
||||||
// Check if bit 0 is set
|
// Check if bit 0 is set
|
||||||
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
bool feature_enabled = Settings::getDebugKnobAt(0);
|
||||||
|
|
||||||
// Check if bit 15 is set
|
// Check if bit 15 is set
|
||||||
bool another_feature = Settings::GetDebugKnobAt(15);
|
bool another_feature = Settings::getDebugKnobAt(15);
|
||||||
```
|
```
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
//kotlin side
|
//kotlin side
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
|
||||||
//To use it as a general purpose uint var
|
|
||||||
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
|
|
||||||
|
|
||||||
// Check if bit x is set
|
// Check if bit x is set
|
||||||
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
|
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
||||||
```
|
```
|
||||||
|
|
||||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||||
@@ -260,7 +247,6 @@ There are two main confusions when talking about knobs:
|
|||||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||||
|
|
||||||
Debug knobs are **zero-based**, which means:
|
Debug knobs are **zero-based**, which means:
|
||||||
|
|
||||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||||
|
|
||||||
@@ -273,7 +259,6 @@ Whenever you're instructing tests or reporting results, be precise about whether
|
|||||||
|
|
||||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knobs=0**: no knobs enabled
|
- **knobs=0**: no knobs enabled
|
||||||
- **knobs=1**: knob0 enabled, others disabled
|
- **knobs=1**: knob0 enabled, others disabled
|
||||||
- **knobs=2**: knob1 enabled, others disabled
|
- **knobs=2**: knob1 enabled, others disabled
|
||||||
@@ -285,7 +270,6 @@ Examples:
|
|||||||
|
|
||||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knob0**: knob 0 enabled, others disabled
|
- **knob0**: knob 0 enabled, others disabled
|
||||||
- **knob1**: knob 1 enabled, others disabled
|
- **knob1**: knob 1 enabled, others disabled
|
||||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||||
@@ -298,12 +282,12 @@ Examples:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void SomeFunction() {
|
void SomeFunction() {
|
||||||
if (Settings::GetDebugKnobAt(0)) {
|
if (Settings::getDebugKnobAt(0)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||||
// Additional debug code here
|
// Additional debug code here
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::GetDebugKnobAt(1)) {
|
if (Settings::getDebugKnobAt(1)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||||
// Different debug behavior
|
// Different debug behavior
|
||||||
}
|
}
|
||||||
@@ -315,7 +299,7 @@ void SomeFunction() {
|
|||||||
```cpp
|
```cpp
|
||||||
bool UseOptimizedPath() {
|
bool UseOptimizedPath() {
|
||||||
// Skip optimization if debug bit 2 is set for testing
|
// Skip optimization if debug bit 2 is set for testing
|
||||||
return !Settings::GetDebugKnobAt(2);
|
return !Settings::getDebugKnobAt(2);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -325,7 +309,7 @@ bool UseOptimizedPath() {
|
|||||||
void ExperimentalFeature() {
|
void ExperimentalFeature() {
|
||||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||||
|
|
||||||
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||||
// Fallback to stable implementation
|
// Fallback to stable implementation
|
||||||
StableImplementation();
|
StableImplementation();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -308,6 +308,32 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### unordered_dense
|
||||||
|
|
||||||
|
```
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Martin Leitner-Ankerl
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
```
|
||||||
|
|
||||||
### xbyak
|
### xbyak
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
+3
-5
@@ -58,6 +58,9 @@ if (WIN32 AND NOT TARGET LLVM::Demangle)
|
|||||||
add_library(LLVM::Demangle ALIAS demangle)
|
add_library(LLVM::Demangle ALIAS demangle)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# unordered_dense
|
||||||
|
AddJsonPackage(unordered-dense)
|
||||||
|
|
||||||
# httplib
|
# httplib
|
||||||
if (IOS)
|
if (IOS)
|
||||||
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
||||||
@@ -257,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
-44
@@ -1,44 +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"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STUB_ZERO(name) \
|
|
||||||
extern "C" int name() { \
|
|
||||||
printf("called " #name " = ret zero"); \
|
|
||||||
return 0; \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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(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;
|
|
||||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun refreshThreadPolicies()
|
external fun refreshThreadPolicies()
|
||||||
|
|
||||||
external fun GetDebugKnobAt(index: Int): Boolean
|
external fun getDebugKnobAt(index: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current speed limit to the configured turbo speed.
|
* Set the current speed limit to the configured turbo speed.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ object Settings {
|
|||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||||
|
|
||||||
fun GetDebugKnobAt(index: Int): Boolean {
|
fun getDebugKnobAt(index: Int): Boolean {
|
||||||
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
|
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
||||||
|
DEBUG_KNOBS("debug_knobs")
|
||||||
;
|
;
|
||||||
|
|
||||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||||
|
|||||||
-1
@@ -11,7 +11,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
enum class StringSetting(override val key: String) : AbstractStringSetting {
|
||||||
DRIVER_PATH("driver_path"),
|
DRIVER_PATH("driver_path"),
|
||||||
DEVICE_NAME("device_name"),
|
DEVICE_NAME("device_name"),
|
||||||
LOG_FILTER("log_filter"),
|
|
||||||
PROGRAM_ARGS("program_args"),
|
PROGRAM_ARGS("program_args"),
|
||||||
|
|
||||||
WEB_TOKEN("eden_token"),
|
WEB_TOKEN("eden_token"),
|
||||||
|
|||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.model
|
|
||||||
|
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
|
||||||
|
|
||||||
enum class UShortSetting(override val key: String) : AbstractIntSetting {
|
|
||||||
DEBUG_KNOBS("debug_knobs")
|
|
||||||
;
|
|
||||||
|
|
||||||
override fun getInt(needsGlobal: Boolean): Int =
|
|
||||||
NativeConfig.getUnsignedShort(key, needsGlobal)
|
|
||||||
|
|
||||||
override fun setInt(value: Int) {
|
|
||||||
if (NativeConfig.isPerGameConfigLoaded()) {
|
|
||||||
global = false
|
|
||||||
}
|
|
||||||
NativeConfig.setUnsignedShort(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
|
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
|
|
||||||
|
|
||||||
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
|
|
||||||
}
|
|
||||||
+1
-9
@@ -20,7 +20,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
|
||||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
@@ -1033,16 +1032,9 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.use_auto_stub_description
|
descriptionId = R.string.use_auto_stub_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
StringInputSetting(
|
|
||||||
StringSetting.LOG_FILTER,
|
|
||||||
titleId = R.string.log_filter,
|
|
||||||
descriptionId = R.string.log_filter_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SpinBoxSetting(
|
SpinBoxSetting(
|
||||||
UShortSetting.DEBUG_KNOBS,
|
ShortSetting.DEBUG_KNOBS,
|
||||||
titleId = R.string.debug_knobs,
|
titleId = R.string.debug_knobs,
|
||||||
descriptionId = R.string.debug_knobs_description,
|
descriptionId = R.string.debug_knobs_description,
|
||||||
valueHint = R.string.debug_knobs_hint,
|
valueHint = R.string.debug_knobs_hint,
|
||||||
|
|||||||
+1
-3
@@ -25,7 +25,6 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
@@ -1323,12 +1322,11 @@ class SettingsFragmentPresenter(
|
|||||||
add(HeaderSetting(R.string.log))
|
add(HeaderSetting(R.string.log))
|
||||||
|
|
||||||
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
||||||
add(StringSetting.LOG_FILTER.key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add(HeaderSetting(R.string.general))
|
add(HeaderSetting(R.string.general))
|
||||||
|
|
||||||
add(UShortSetting.DEBUG_KNOBS.key)
|
add(ShortSetting.DEBUG_KNOBS.key)
|
||||||
add(StringSetting.PROGRAM_ARGS.key)
|
add(StringSetting.PROGRAM_ARGS.key)
|
||||||
|
|
||||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
|||||||
+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 {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import info.debatty.java.stringsimilarity.Jaccard
|
|||||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.view.doOnNextLayout
|
||||||
|
|
||||||
class GamesFragment : Fragment() {
|
class GamesFragment : Fragment() {
|
||||||
private var _binding: FragmentGamesBinding? = null
|
private var _binding: FragmentGamesBinding? = null
|
||||||
@@ -58,6 +59,7 @@ class GamesFragment : Fragment() {
|
|||||||
private var originalHeaderLeftMargin: Int? = null
|
private var originalHeaderLeftMargin: Int? = null
|
||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
|
private var fallbackBottomInset: Int = 0
|
||||||
private var pendingPostReloadListSettle = false
|
private var pendingPostReloadListSettle = false
|
||||||
private var pendingPostReloadListSettleGeneration = 0
|
private var pendingPostReloadListSettleGeneration = 0
|
||||||
private var gameListSubmitGeneration = 0
|
private var gameListSubmitGeneration = 0
|
||||||
@@ -225,7 +227,12 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||||
}
|
}
|
||||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||||
|
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||||
|
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||||
|
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||||
}
|
}
|
||||||
adapter = gameAdapter
|
adapter = gameAdapter
|
||||||
@@ -583,6 +590,11 @@ class GamesFragment : Fragment() {
|
|||||||
qlaunchButton.layoutParams = mlpQLaunch
|
qlaunchButton.layoutParams = mlpQLaunch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||||
|
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||||
|
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||||
|
fallbackBottomInset = bottomInset
|
||||||
|
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -80,12 +80,6 @@ object NativeConfig {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
external fun setShort(key: String, value: Short)
|
external fun setShort(key: String, value: Short)
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
external fun setUnsignedShort(key: String, value: Int)
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,13 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
|
||||||
/**
|
/**
|
||||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||||
* It manages overlapping cards, center snapping, custom drawing order,
|
* It manages overlapping cards, center snapping, custom drawing order,
|
||||||
@@ -35,9 +32,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var overlapFactor: Float = 0f
|
private var overlapFactor: Float = 0f
|
||||||
private var overlapPx: Int = 0
|
private var overlapPx: Int = 0
|
||||||
private var bottomInset: Int = 0
|
private var bottomInset: Int = -1
|
||||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
|
||||||
private var cardGeometryInitialized: Boolean = false
|
|
||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
@@ -96,38 +91,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
setChildrenDrawingOrderEnabled(true)
|
setChildrenDrawingOrderEnabled(true)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
|
||||||
latestWindowInsets = insets
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
insets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
if (w != oldw || h != oldh) {
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
||||||
super.onLayout(changed, left, top, right, bottom)
|
|
||||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
|
||||||
if (hasFocus) {
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
post { updateCardGeometry() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setAdapter(adapter: Adapter<*>?) {
|
override fun setAdapter(adapter: Adapter<*>?) {
|
||||||
@@ -140,8 +103,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
super.setAdapter(adapter)
|
super.setAdapter(adapter)
|
||||||
|
|
||||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||||
@@ -292,71 +253,40 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
fun notifyInsetsReady(newBottomInset: Int) {
|
||||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
if (bottomInset != newBottomInset) {
|
||||||
0
|
bottomInset = newBottomInset
|
||||||
} else {
|
}
|
||||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
|
||||||
|
if (isCarouselMode) {
|
||||||
|
setupCarousel(true)
|
||||||
|
} else {
|
||||||
|
setupCarousel(false)
|
||||||
}
|
}
|
||||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.systemGestures()
|
|
||||||
)
|
|
||||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.displayCutout()
|
|
||||||
)
|
|
||||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCardGeometry() {
|
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||||
if (!isCarouselMode || height <= 0) return
|
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||||
|
var gameAdapter = adapter as? GameAdapter ?: return
|
||||||
|
var newCardSize = cardSize(bottomInset)
|
||||||
|
if (gameAdapter.cardSize != newCardSize) {
|
||||||
|
gameAdapter.setCardSize(newCardSize)
|
||||||
|
}
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
if (isCarouselMode) {
|
||||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
setupCarousel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
fun cardSize(bottomInset: Int): Int {
|
||||||
|
|
||||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
|
||||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
val scaledHeight = height * userFactor
|
||||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
val availableHeight = height - bottomInset
|
||||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||||
.pow(0.75f)
|
|
||||||
.coerceIn(0.5f, 1f)
|
|
||||||
val newCardSize = minOf(
|
|
||||||
(height * userFactor).toInt(),
|
|
||||||
height - newBottomInset,
|
|
||||||
(height * aspectFactor).toInt()
|
|
||||||
)
|
|
||||||
if (newCardSize <= 0) return
|
|
||||||
val insetChanged = bottomInset != newBottomInset
|
|
||||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
|
||||||
|
|
||||||
bottomInset = newBottomInset
|
|
||||||
cardGeometryInitialized = true
|
|
||||||
|
|
||||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
|
||||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyCarouselPadding() {
|
|
||||||
if (!isCarouselMode) return
|
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
|
||||||
val cardSize = gameAdapter.cardSize
|
|
||||||
if (cardSize <= 0 || bottomInset < 0) return
|
|
||||||
|
|
||||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
|
||||||
val sidePadding = (width - cardSize) / 2
|
|
||||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
|
||||||
paddingRight != sidePadding || paddingBottom != 0
|
|
||||||
) {
|
|
||||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
|
||||||
}
|
|
||||||
clipToPadding = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupCarousel(enabled: Boolean) {
|
fun setupCarousel(enabled: Boolean) {
|
||||||
@@ -385,6 +315,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
internalFlingMultiplier
|
internalFlingMultiplier
|
||||||
).coerceIn(1f, 5f)
|
).coerceIn(1f, 5f)
|
||||||
|
|
||||||
|
// Detach SnapHelper during setup
|
||||||
|
pagerSnapHelper?.attachToRecyclerView(null)
|
||||||
|
|
||||||
// Add overlap decoration if not present
|
// Add overlap decoration if not present
|
||||||
if (overlapDecoration == null) {
|
if (overlapDecoration == null) {
|
||||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||||
@@ -402,7 +335,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
addOnScrollListener(scalingScrollListener!!)
|
addOnScrollListener(scalingScrollListener!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCarouselPadding()
|
if (cardSize > 0) {
|
||||||
|
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||||
|
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||||
|
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||||
|
clipToPadding = false
|
||||||
|
}
|
||||||
|
|
||||||
if (pagerSnapHelper == null) {
|
if (pagerSnapHelper == null) {
|
||||||
pagerSnapHelper = CenterPagerSnapHelper()
|
pagerSnapHelper = CenterPagerSnapHelper()
|
||||||
@@ -424,7 +362,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
savedItemAnimator = null
|
savedItemAnimator = null
|
||||||
}
|
}
|
||||||
cardGeometryInitialized = false
|
|
||||||
useCustomDrawingOrder = false
|
useCustomDrawingOrder = false
|
||||||
// Reset padding and fling
|
// Reset padding and fling
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ struct RomMetadata {
|
|||||||
std::vector<u8> icon;
|
std::vector<u8> icon;
|
||||||
bool isHomebrew;
|
bool isHomebrew;
|
||||||
};
|
};
|
||||||
static ::Common::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
static ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||||
|
|
||||||
static RomMetadata CacheRomMetadata(const std::string& path) {
|
static RomMetadata CacheRomMetadata(const std::string& path) {
|
||||||
auto& instance = EmulationSession::GetInstance();
|
auto& instance = EmulationSession::GetInstance();
|
||||||
|
|||||||
@@ -788,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(
|
||||||
@@ -796,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;
|
||||||
}
|
}
|
||||||
@@ -1300,8 +1300,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
|||||||
Common::RefreshThreadPolicies();
|
Common::RefreshThreadPolicies();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||||
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||||
@@ -1491,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);
|
||||||
}
|
}
|
||||||
@@ -1575,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));
|
||||||
}
|
}
|
||||||
@@ -1635,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, "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =
|
||||||
@@ -130,25 +131,6 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
|||||||
setting->SetValue(value);
|
setting->SetValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
|
|
||||||
jstring jkey,
|
|
||||||
jboolean needGlobal) {
|
|
||||||
auto setting = getSetting<u16>(env, jkey);
|
|
||||||
if (setting == nullptr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
|
|
||||||
jstring jkey, jint value) {
|
|
||||||
auto setting = getSetting<u16>(env, jkey);
|
|
||||||
if (setting == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setting->SetValue(static_cast<u16>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||||
jboolean needGlobal) {
|
jboolean needGlobal) {
|
||||||
auto setting = getSetting<int>(env, jkey);
|
auto setting = getSetting<int>(env, jkey);
|
||||||
@@ -341,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(),
|
||||||
@@ -356,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);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#include "input_common/drivers/virtual_gamepad.h"
|
#include "input_common/drivers/virtual_gamepad.h"
|
||||||
#include "native.h"
|
#include "native.h"
|
||||||
|
|
||||||
::Common::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
ankerl::unordered_dense::map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||||
|
|
||||||
bool IsHandheldOnly() {
|
bool IsHandheldOnly() {
|
||||||
const auto npad_style_set =
|
const auto npad_style_set =
|
||||||
|
|||||||
@@ -636,8 +636,6 @@
|
|||||||
<string name="log">Logging</string>
|
<string name="log">Logging</string>
|
||||||
<string name="flush_by_line">Flush debug logs by line</string>
|
<string name="flush_by_line">Flush debug logs by line</string>
|
||||||
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
|
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
|
||||||
<string name="log_filter">Log filter</string>
|
|
||||||
<string name="log_filter_description">Controls Eden\'s log categories. Example: *:Info Service.LM:Debug</string>
|
|
||||||
|
|
||||||
<!-- GPU Logging strings -->
|
<!-- GPU Logging strings -->
|
||||||
<string name="gpu_logging_header">GPU Logging</string>
|
<string name="gpu_logging_header">GPU Logging</string>
|
||||||
|
|||||||
@@ -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,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);
|
||||||
|
|||||||
@@ -147,8 +147,7 @@ add_library(
|
|||||||
cpu_features.cpp
|
cpu_features.cpp
|
||||||
cpu_features.h
|
cpu_features.h
|
||||||
httplib.h
|
httplib.h
|
||||||
net/net.h net/net.cpp
|
net/net.h net/net.cpp)
|
||||||
container/unordered_map.h container/unordered_set.h)
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_sources(common PRIVATE windows/timer_resolution.cpp
|
target_sources(common PRIVATE windows/timer_resolution.cpp
|
||||||
@@ -230,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()
|
||||||
@@ -243,7 +241,7 @@ if (lz4_ADDED)
|
|||||||
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads)
|
target_link_libraries(common PUBLIC fmt::fmt stb::headers Threads::Threads unordered_dense::unordered_dense)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
|
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd)
|
||||||
|
|
||||||
# Please refer to src/common/demangle.cpp
|
# Please refer to src/common/demangle.cpp
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/container_hash.h"
|
|
||||||
|
|
||||||
#include <boost/unordered/unordered_flat_map.hpp>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
template <class Key, class T, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>,
|
|
||||||
class Allocator = std::allocator<std::pair<const Key, T>>>
|
|
||||||
using unordered_map = boost::unordered::unordered_flat_map<Key, T, Hash, Pred, Allocator>;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/container_hash.h"
|
|
||||||
|
|
||||||
#include <boost/unordered/unordered_flat_set.hpp>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
template <class Key, class Hash = std::hash<Key>, class Pred = std::equal_to<Key>,
|
|
||||||
class Allocator = std::allocator<Key>>
|
|
||||||
using unordered_set = boost::unordered::unordered_flat_set<Key, Hash, Pred, Allocator>;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -10,11 +10,8 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@@ -72,17 +69,10 @@ struct HashCombineImpl<64> {
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
requires std::is_unsigned_v<T>
|
|
||||||
inline void HashCombine(std::size_t& seed, const T& v) {
|
inline void HashCombine(std::size_t& seed, const T& v) {
|
||||||
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v));
|
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, detail::HashValue(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires (!std::is_unsigned_v<T>)
|
|
||||||
inline void HashCombine(std::size_t& seed, const T& v) {
|
|
||||||
seed = detail::HashCombineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, std::hash<T>{}(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
inline std::size_t HashRange(It first, It last) {
|
inline std::size_t HashRange(It first, It last) {
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
@@ -105,26 +95,3 @@ std::size_t HashValue(const std::vector<T, Allocator>& v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
struct hash<std::tuple<Args...>> {
|
|
||||||
std::size_t operator()(const std::tuple<Args...>& t) const noexcept {
|
|
||||||
std::size_t seed = 0;
|
|
||||||
std::apply([&seed](const Args&... args) { (Common::HashCombine(seed, args), ...); }, t);
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class A, class B>
|
|
||||||
struct hash<std::pair<A, B>> {
|
|
||||||
std::size_t operator()(const std::pair<A, B>& p) const noexcept {
|
|
||||||
std::size_t seed = 0;
|
|
||||||
Common::HashCombine(seed, p.first);
|
|
||||||
Common::HashCombine(seed, p.second);
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace std
|
|
||||||
|
|||||||
+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",
|
||||||
|
|||||||
+15
-14
@@ -7,7 +7,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fs/fs.h"
|
#include "common/fs/fs.h"
|
||||||
@@ -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:
|
||||||
@@ -199,8 +196,8 @@ private:
|
|||||||
SetLegacyPathImpl(legacy_path, new_path);
|
SetLegacyPathImpl(legacy_path, new_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
::Common::unordered_map<EdenPath, fs::path> eden_paths;
|
ankerl::unordered_dense::map<EdenPath, fs::path> eden_paths;
|
||||||
::Common::unordered_map<EmuPath, fs::path> legacy_paths;
|
ankerl::unordered_dense::map<EmuPath, fs::path> legacy_paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ValidatePath(const fs::path& path) {
|
bool ValidatePath(const fs::path& path) {
|
||||||
@@ -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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -7,7 +7,7 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <boost/icl/separate_interval_set.hpp>
|
#include <boost/icl/separate_interval_set.hpp>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include "common/dynamic_library.h"
|
#include "common/dynamic_library.h"
|
||||||
@@ -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 ^^^
|
||||||
@@ -391,7 +391,7 @@ private:
|
|||||||
|
|
||||||
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
std::mutex placeholder_mutex; ///< Mutex for placeholders
|
||||||
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
boost::icl::separate_interval_set<size_t> placeholders; ///< Mapped placeholders
|
||||||
::Common::unordered_map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
|
||||||
};
|
};
|
||||||
|
|
||||||
#elif defined(__OPENORBIS__) || defined(__managarm__)
|
#elif defined(__OPENORBIS__) || defined(__managarm__)
|
||||||
@@ -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
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -412,7 +412,7 @@ public:
|
|||||||
namespace Impl {
|
namespace Impl {
|
||||||
|
|
||||||
template <typename InputDeviceType>
|
template <typename InputDeviceType>
|
||||||
using FactoryListType = ::Common::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
using FactoryListType = ankerl::unordered_dense::map<std::string, std::shared_ptr<Factory<InputDeviceType>>>;
|
||||||
|
|
||||||
template <typename InputDeviceType>
|
template <typename InputDeviceType>
|
||||||
struct FactoryList {
|
struct FactoryList {
|
||||||
|
|||||||
+23
-28
@@ -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 {
|
||||||
@@ -328,7 +329,7 @@ struct LogcatBackend : public Backend {
|
|||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
__android_log_print(android_log_priority, "YuzuNative", "%s %s:%u:%s: %s", df.class_name, entry.filename, entry.line_num, entry.function, entry.message);
|
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||||
}
|
}
|
||||||
void Flush() noexcept override {}
|
void Flush() noexcept override {}
|
||||||
};
|
};
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,33 +428,21 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u
|
|||||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||||
Entry e{
|
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||||
.message = nullptr,
|
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||||
.message_len = 0,
|
backend.Write(Entry{
|
||||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
.message = buffer,
|
||||||
.log_class = log_class,
|
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||||
.log_level = log_level,
|
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||||
.filename = TrimSourcePath(filename),
|
.log_class = log_class,
|
||||||
.function = function,
|
.log_level = log_level,
|
||||||
.line_num = line_num,
|
.filename = TrimSourcePath(filename),
|
||||||
};
|
.function = function,
|
||||||
if (result.size <= sizeof(buffer) - 1) {
|
.line_num = line_num,
|
||||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
|
||||||
e.message = buffer;
|
|
||||||
e.message_len = (std::min)(result.size, sizeof(buffer) - 1);
|
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
|
||||||
backend.Write(e);
|
|
||||||
if (flush) backend.Flush();
|
|
||||||
});
|
});
|
||||||
} else {
|
if (flush)
|
||||||
std::string s = fmt::vformat(format, args);
|
backend.Flush();
|
||||||
e.message = s.c_str();
|
});
|
||||||
e.message_len = s.size();
|
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
|
||||||
backend.Write(e);
|
|
||||||
if (flush) backend.Flush();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace Common::Log
|
} // namespace Common::Log
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Common::Net {
|
namespace Common::Net {
|
||||||
|
|
||||||
struct Asset {
|
typedef struct {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
};
|
} Asset;
|
||||||
|
|
||||||
struct Release {
|
typedef struct Release {
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
std::string tag;
|
std::string tag;
|
||||||
@@ -39,7 +39,7 @@ struct Release {
|
|||||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
||||||
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
||||||
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
||||||
};
|
} Release;
|
||||||
|
|
||||||
// Make a request via httplib, and return the response body if applicable.
|
// Make a request via httplib, and return the response body if applicable.
|
||||||
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// A string-based key-value container supporting serializing to and deserializing from a string
|
/// A string-based key-value container supporting serializing to and deserializing from a string
|
||||||
class ParamPackage {
|
class ParamPackage {
|
||||||
public:
|
public:
|
||||||
using DataType = ::Common::unordered_map<std::string, std::string>;
|
using DataType = ankerl::unordered_dense::map<std::string, std::string>;
|
||||||
|
|
||||||
ParamPackage() = default;
|
ParamPackage() = default;
|
||||||
explicit ParamPackage(const std::string& serialized);
|
explicit ParamPackage(const std::string& serialized);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -126,9 +126,9 @@ void LogSettings() {
|
|||||||
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
setting->UsingGlobal() ? '-' : 'C', TranslateCategory(category),
|
||||||
setting->GetLabel());
|
setting->GetLabel());
|
||||||
if (is_default)
|
if (is_default)
|
||||||
settings_list.push_back(fmt::format("{}: {}", name, setting->Canonicalize()));
|
settings_list.push_back(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||||
else
|
else
|
||||||
settings_list.push_front(fmt::format("{}: {}", name, setting->Canonicalize()));
|
settings_list.push_front(fmt::format("{}: {}\n", name, setting->Canonicalize()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ void LogSettings() {
|
|||||||
#undef LOG_PATH
|
#undef LOG_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetDebugKnobAt(u8 i) {
|
bool getDebugKnobAt(u8 i) {
|
||||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
+4
-11
@@ -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,
|
||||||
@@ -911,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::System,
|
Category::Debugging,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
@@ -954,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
|||||||
|
|
||||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||||
|
|
||||||
bool GetDebugKnobAt(u8 i);
|
bool getDebugKnobAt(u8 i);
|
||||||
|
|
||||||
void UpdateGPUAccuracy();
|
void UpdateGPUAccuracy();
|
||||||
bool IsGPULevelHigh();
|
bool IsGPULevelHigh();
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -22,11 +22,10 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include "common/container/unordered_set.h"
|
|
||||||
#include <boost/unordered_map.hpp>
|
#include <boost/unordered_map.hpp>
|
||||||
#define XBYAK_STD_UNORDERED_SET ::Common::unordered_set
|
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||||
#define XBYAK_STD_UNORDERED_MAP ::Common::unordered_map
|
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||||
#include <xbyak/xbyak.h>
|
#include <xbyak/xbyak.h>
|
||||||
#include <xbyak/xbyak_util.h>
|
#include <xbyak/xbyak_util.h>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
|
|
||||||
#include <dynarmic/interface/A64/a64.h>
|
#include <dynarmic/interface/A64/a64.h>
|
||||||
#include <dynarmic/interface/code_page.h>
|
#include <dynarmic/interface/code_page.h>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <oaknut/code_block.hpp>
|
#include <oaknut/code_block.hpp>
|
||||||
#include <oaknut/oaknut.hpp>
|
#include <oaknut/oaknut.hpp>
|
||||||
@@ -46,7 +46,7 @@ enum class PatchMode : u32 {
|
|||||||
|
|
||||||
using ModuleTextAddress = u64;
|
using ModuleTextAddress = u64;
|
||||||
using PatchTextAddress = u64;
|
using PatchTextAddress = u64;
|
||||||
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
using EntryTrampolines = ankerl::unordered_dense::map<ModuleTextAddress, PatchTextAddress>;
|
||||||
|
|
||||||
class Patcher {
|
class Patcher {
|
||||||
public:
|
public:
|
||||||
|
|||||||
+2
-1
@@ -17,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"
|
||||||
@@ -396,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;
|
||||||
|
|||||||
+240
-193
@@ -3,12 +3,10 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <span>
|
|
||||||
#include <cctype>
|
|
||||||
#include "common/container/unordered_map.h"
|
|
||||||
|
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -24,30 +22,61 @@ enum class IPSFileType {
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
|
||||||
if (magic.size() >= 5) {
|
{"\\a", "\a"},
|
||||||
if (std::memcmp(magic.data(), "PATCH", 5) == 0)
|
{"\\b", "\b"},
|
||||||
return IPSFileType::IPS;
|
{"\\f", "\f"},
|
||||||
if (std::memcmp(magic.data(), "IPS32", 5) == 0)
|
{"\\n", "\n"},
|
||||||
return IPSFileType::IPS32;
|
{"\\r", "\r"},
|
||||||
|
{"\\t", "\t"},
|
||||||
|
{"\\v", "\v"},
|
||||||
|
{"\\\\", "\\"},
|
||||||
|
{"\\\'", "\'"},
|
||||||
|
{"\\\"", "\""},
|
||||||
|
{"\\\?", "\?"},
|
||||||
|
}};
|
||||||
|
|
||||||
|
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
|
||||||
|
if (magic.size() != 5) {
|
||||||
|
return IPSFileType::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
|
||||||
|
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
|
||||||
|
return IPSFileType::IPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
|
||||||
|
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
|
||||||
|
return IPSFileType::IPS32;
|
||||||
|
}
|
||||||
|
|
||||||
return IPSFileType::Error;
|
return IPSFileType::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
|
||||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
static constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
|
||||||
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
|
||||||
|
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||||
if (in == nullptr || ips == nullptr)
|
if (in == nullptr || ips == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||||
auto const type = IdentifyMagic(in_data);
|
|
||||||
if (type == IPSFileType::Error)
|
if (type == IPSFileType::Error)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
auto in_data = in->ReadAllBytes();
|
||||||
|
if (in_data.size() == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||||
u64 offset = 5; // After header
|
u64 offset = 5; // After header
|
||||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||||
@@ -56,9 +85,12 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 real_offset = (type == IPSFileType::IPS32)
|
u32 real_offset{};
|
||||||
? ((temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3])
|
if (type == IPSFileType::IPS32)
|
||||||
: ((temp[0] << 16) | (temp[1] << 8) | temp[2]);
|
real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];
|
||||||
|
else
|
||||||
|
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
|
||||||
|
|
||||||
if (real_offset > in_data.size()) {
|
if (real_offset > in_data.size()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -81,35 +113,34 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (real_offset + rle_size > in_data.size())
|
if (real_offset + rle_size > in_data.size())
|
||||||
rle_size = u16(in_data.size() - real_offset);
|
rle_size = static_cast<u16>(in_data.size() - real_offset);
|
||||||
std::memset(in_data.data() + real_offset, *data, rle_size);
|
std::memset(in_data.data() + real_offset, *data, rle_size);
|
||||||
} else { // Standard Patch
|
} else { // Standard Patch
|
||||||
auto read = data_size;
|
auto read = data_size;
|
||||||
if (real_offset + read > in_data.size())
|
if (real_offset + read > in_data.size())
|
||||||
read = u16(in_data.size() - real_offset);
|
read = static_cast<u16>(in_data.size() - real_offset);
|
||||||
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
offset += data_size;
|
offset += data_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsEOF(type, temp)) {
|
|
||||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
if (!IsEOF(type, temp)) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
|
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||||
|
in->GetContainingDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct IPSwitchRecord {
|
|
||||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
|
||||||
size_t count;
|
|
||||||
};
|
|
||||||
struct IPSwitchCompiler::IPSwitchPatch {
|
struct IPSwitchCompiler::IPSwitchPatch {
|
||||||
::Common::unordered_map<u32, IPSwitchRecord> records;
|
std::string name;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
std::map<u32, std::vector<u8>> records;
|
||||||
};
|
};
|
||||||
|
|
||||||
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
|
||||||
Parse(patch_text->ReadAllBytes());
|
Parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
IPSwitchCompiler::~IPSwitchCompiler() = default;
|
||||||
@@ -118,185 +149,201 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
|||||||
return nso_build_id;
|
return nso_build_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
bool IPSwitchCompiler::IsValid() const {
|
||||||
IPSwitchRecord r{};
|
return valid;
|
||||||
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
|
||||||
switch (it[1]) {
|
|
||||||
case 'a': r.data[r.count] = '\a'; break;
|
|
||||||
case 'b': r.data[r.count] = '\b'; break;
|
|
||||||
case 'e': r.data[r.count] = '\e'; break;
|
|
||||||
case 'f': r.data[r.count] = '\f'; break;
|
|
||||||
case 'n': r.data[r.count] = '\n'; break;
|
|
||||||
case 'r': r.data[r.count] = '\r'; break;
|
|
||||||
case 't': r.data[r.count] = '\t'; break;
|
|
||||||
case 'v': r.data[r.count] = '\v'; break;
|
|
||||||
case '?': r.data[r.count] = '\?'; break;
|
|
||||||
default: r.data[r.count] = it[1]; break;
|
|
||||||
}
|
|
||||||
++r.count;
|
|
||||||
it += 2;
|
|
||||||
} else {
|
|
||||||
++r.count;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
static bool StartsWith(std::string_view base, std::string_view check) {
|
||||||
LOG_INFO(Loader, "IPSwitchCompiler: '{}'", patch_text->GetName());
|
return base.size() >= check.size() && base.substr(0, check.size()) == check;
|
||||||
bool is_little_endian = true;
|
}
|
||||||
s64 offset_shift = 0;
|
|
||||||
//bool print_values = false;
|
|
||||||
auto const parse_line = [&](std::string_view const line) {
|
|
||||||
// Keep in mind lines have trimmed spaces (at the end & start)!
|
|
||||||
LOG_INFO(Loader, "<{}>", line);
|
|
||||||
// IPSwitch is case insensitive
|
|
||||||
// Yes this is how the logic goes for the main reference parsers!
|
|
||||||
if (line.size() > 2 && line[0] == '@') {
|
|
||||||
switch (line[1]) {
|
|
||||||
// yes, @nsobid too -- NSO Build ID Specifier
|
|
||||||
case 'n':
|
|
||||||
case 'N':
|
|
||||||
nso_build_id = Common::HexStringToArray<0x20>(fmt::format("{:0<64}", line.substr(8)));
|
|
||||||
break;
|
|
||||||
// @stop
|
|
||||||
case 's':
|
|
||||||
case 'S':
|
|
||||||
return false;
|
|
||||||
// @enabled
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
patches.push_back({{}, true});
|
|
||||||
break;
|
|
||||||
// @disabled
|
|
||||||
case 'd':
|
|
||||||
case 'D':
|
|
||||||
patches.push_back({{}, false});
|
|
||||||
break;
|
|
||||||
// @flag
|
|
||||||
case 'f':
|
|
||||||
case 'F': {
|
|
||||||
if (line.starts_with("@flag offset_shift")) {
|
|
||||||
offset_shift = std::strtoll(line.data() + 19, nullptr, 0); // Offset Shift Flag
|
|
||||||
} else if (line.starts_with("@flag print_values")) {
|
|
||||||
//print_values = true; // Force printing of applied values
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'l':
|
|
||||||
case 'L':
|
|
||||||
is_little_endian = true;
|
|
||||||
break;
|
|
||||||
// IPS parsers dont support big endian no more, we do due to backcompat
|
|
||||||
case 'b':
|
|
||||||
case 'B':
|
|
||||||
is_little_endian = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
|
||||||
offset += size_t(offset_shift);
|
|
||||||
if (auto const first_quote = line.find_first_of("\"\'"); first_quote != std::string::npos) {
|
|
||||||
// string replacement
|
|
||||||
char quote = line[first_quote];
|
|
||||||
auto const start = line.cbegin() + first_quote + 1;
|
|
||||||
auto end = start;
|
|
||||||
for (; end < line.cend() && *end != quote; )
|
|
||||||
end += (*end == '\\') ? 2 : 1;
|
|
||||||
if (start <= line.cend() && end <= line.cend()) {
|
|
||||||
LOG_INFO(Loader, "[S] value @ {:#08X} ", offset);
|
|
||||||
patches.back().records.insert_or_assign(u32(offset), EscapeStringSequences({start, end}));
|
|
||||||
} else {
|
|
||||||
LOG_WARNING(Loader, "invalid string");
|
|
||||||
}
|
|
||||||
} else if (auto const first_space = line.find_last_of(" /\t\r\n"); first_space != std::string::npos) {
|
|
||||||
IPSwitchRecord r{}; // hex replacement
|
|
||||||
auto const start = line.cbegin() + first_space + 1;
|
|
||||||
auto const end = line.cend();
|
|
||||||
if (start <= line.cend() && end <= line.cend()) {
|
|
||||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
|
||||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
|
||||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
|
||||||
r.count = hs.size();
|
|
||||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
|
||||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
|
||||||
} else {
|
|
||||||
LOG_WARNING(Loader, "invalid line");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG_WARNING(Loader, "unhandled line!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; //continue
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto it = bytes.begin(); it < bytes.end(); ) {
|
static std::string EscapeStringSequences(std::string in) {
|
||||||
auto const start = it;
|
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
|
||||||
auto end = start;
|
for (auto index = in.find(seq.first); index != std::string::npos;
|
||||||
for (; end < bytes.end() && *end != '\n' && *end != '\r'; ++end)
|
index = in.find(seq.first, index)) {
|
||||||
;
|
in.replace(index, std::strlen(seq.first), seq.second);
|
||||||
it = end + 1; //prepare for next line
|
index += std::strlen(seq.second);
|
||||||
std::string_view const sline{
|
|
||||||
reinterpret_cast<const char*>(bytes.data() + std::distance(bytes.begin(), start)),
|
|
||||||
size_t(std::distance(start, end))
|
|
||||||
};
|
|
||||||
if (sline.size() > 0) {
|
|
||||||
auto p = sline.cbegin();
|
|
||||||
// skip space off line
|
|
||||||
for (; p < sline.cend() && std::isspace(*p); ++p)
|
|
||||||
;
|
|
||||||
// now make a nominal preprocessed line: remove comments
|
|
||||||
char quote = '\0';
|
|
||||||
auto const sline_start = p;
|
|
||||||
for (; p < sline.cend(); ) {
|
|
||||||
// we dont check for "//", IPS checks for '/' only...
|
|
||||||
if ((!quote && p[0] == '/')
|
|
||||||
|| (!quote && p[0] == '#')) {
|
|
||||||
break;
|
|
||||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
|
||||||
quote = (p[0] == quote) ? '\0' : p[0];
|
|
||||||
++p;
|
|
||||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
|
||||||
p += 2;
|
|
||||||
} else {
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now we have the preprocessed string ;)
|
|
||||||
std::string_view pp_str(sline_start, p);
|
|
||||||
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPSwitchCompiler::ParseFlag(const std::string& line) {
|
||||||
|
if (StartsWith(line, "@flag offset_shift ")) {
|
||||||
|
// Offset Shift Flag
|
||||||
|
offset_shift = std::strtoll(line.substr(19).c_str(), nullptr, 0);
|
||||||
|
} else if (StartsWith(line, "@little-endian")) {
|
||||||
|
// Set values to read as little endian
|
||||||
|
is_little_endian = true;
|
||||||
|
} else if (StartsWith(line, "@big-endian")) {
|
||||||
|
// Set values to read as big endian
|
||||||
|
is_little_endian = false;
|
||||||
|
} else if (StartsWith(line, "@flag print_values")) {
|
||||||
|
// Force printing of applied values
|
||||||
|
print_values = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPSwitchCompiler::Parse() {
|
||||||
|
const auto bytes = patch_text->ReadAllBytes();
|
||||||
|
std::stringstream s;
|
||||||
|
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||||
|
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
std::string stream_line;
|
||||||
|
while (std::getline(s, stream_line)) {
|
||||||
|
// Remove a trailing \r
|
||||||
|
if (!stream_line.empty() && stream_line.back() == '\r')
|
||||||
|
stream_line.pop_back();
|
||||||
|
lines.push_back(std::move(stream_line));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||||
|
auto line = lines[i];
|
||||||
|
|
||||||
|
// Remove midline comments
|
||||||
|
std::size_t comment_index = std::string::npos;
|
||||||
|
bool within_string = false;
|
||||||
|
for (std::size_t k = 0; k < line.size(); ++k) {
|
||||||
|
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
|
||||||
|
within_string = !within_string;
|
||||||
|
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
|
||||||
|
comment_index = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StartsWith(line, "//") && comment_index != std::string::npos) {
|
||||||
|
last_comment = line.substr(comment_index + 2);
|
||||||
|
line = line.substr(0, comment_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(line, "@stop")) {
|
||||||
|
// Force stop
|
||||||
|
break;
|
||||||
|
} else if (StartsWith(line, "@nsobid-")) {
|
||||||
|
// NSO Build ID Specifier
|
||||||
|
const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
|
||||||
|
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
|
||||||
|
} else if (StartsWith(line, "#")) {
|
||||||
|
// Mandatory Comment
|
||||||
|
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
|
||||||
|
patch_text->GetName(), line.substr(1));
|
||||||
|
} else if (StartsWith(line, "//")) {
|
||||||
|
// Normal Comment
|
||||||
|
last_comment = line.substr(2);
|
||||||
|
if (last_comment.find_first_not_of(' ') == std::string::npos)
|
||||||
|
continue;
|
||||||
|
if (last_comment.find_first_not_of(' ') != 0)
|
||||||
|
last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
|
||||||
|
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
|
||||||
|
// Start of patch
|
||||||
|
const auto enabled = StartsWith(line, "@enabled");
|
||||||
|
if (i == 0)
|
||||||
|
return;
|
||||||
|
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
|
||||||
|
patch_text->GetName(), last_comment, line.substr(1));
|
||||||
|
|
||||||
|
IPSwitchPatch patch{last_comment, enabled, {}};
|
||||||
|
|
||||||
|
// Read rest of patch
|
||||||
|
while (true) {
|
||||||
|
if (i + 1 >= lines.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& patch_line = lines[++i];
|
||||||
|
|
||||||
|
// Patch line may contain comments
|
||||||
|
if (StartsWith(patch_line, "//") || StartsWith(patch_line, "#")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start of new patch
|
||||||
|
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
|
||||||
|
--i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a flag
|
||||||
|
if (StartsWith(patch_line, "@")) {
|
||||||
|
ParseFlag(patch_line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11 - 8 hex digit offset + space + minimum two digit overwrite val
|
||||||
|
if (patch_line.length() < 11)
|
||||||
|
break;
|
||||||
|
auto offset = std::strtoul(patch_line.substr(0, 8).c_str(), nullptr, 16);
|
||||||
|
offset += static_cast<unsigned long>(offset_shift);
|
||||||
|
|
||||||
|
std::vector<u8> replace;
|
||||||
|
// 9 - first char of replacement val
|
||||||
|
if (patch_line[9] == '\"') {
|
||||||
|
// string replacement
|
||||||
|
auto end_index = patch_line.find('\"', 10);
|
||||||
|
if (end_index == std::string::npos || end_index < 10)
|
||||||
|
return;
|
||||||
|
while (patch_line[end_index - 1] == '\\') {
|
||||||
|
end_index = patch_line.find('\"', end_index + 1);
|
||||||
|
if (end_index == std::string::npos || end_index < 10)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto value = patch_line.substr(10, end_index - 10);
|
||||||
|
value = EscapeStringSequences(value);
|
||||||
|
replace.reserve(value.size());
|
||||||
|
std::copy(value.begin(), value.end(), std::back_inserter(replace));
|
||||||
|
} else {
|
||||||
|
// hex replacement
|
||||||
|
const auto value =
|
||||||
|
patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
|
||||||
|
replace = Common::HexStringToVector(value, is_little_endian);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (print_values) {
|
||||||
|
LOG_INFO(Loader,
|
||||||
|
"[IPSwitchCompiler ('{}')] - Patching value at offset {:#08x} "
|
||||||
|
"with byte string '{}'",
|
||||||
|
patch_text->GetName(), offset, Common::HexToString(replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
patch.records.insert_or_assign(static_cast<u32>(offset), std::move(replace));
|
||||||
|
}
|
||||||
|
|
||||||
|
patches.push_back(std::move(patch));
|
||||||
|
} else if (StartsWith(line, "@")) {
|
||||||
|
ParseFlag(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
||||||
if (in == nullptr)
|
if (in == nullptr || !valid)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
auto in_data = in->ReadAllBytes();
|
||||||
|
|
||||||
for (const auto& patch : patches) {
|
for (const auto& patch : patches) {
|
||||||
if (patch.enabled) {
|
if (!patch.enabled)
|
||||||
for (const auto& record : patch.records) {
|
continue;
|
||||||
if (record.first < in_data.size()) {
|
|
||||||
auto replace_size = record.second.count;
|
for (const auto& record : patch.records) {
|
||||||
if (record.first + replace_size > in_data.size())
|
if (record.first >= in_data.size())
|
||||||
replace_size = in_data.size() - record.first;
|
continue;
|
||||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
auto replace_size = record.second.size();
|
||||||
} else {
|
if (record.first + replace_size > in_data.size())
|
||||||
LOG_WARNING(Loader, "record offs={:x},size={:x}", record.first, record.second.data.size());
|
replace_size = in_data.size() - record.first;
|
||||||
}
|
for (std::size_t i = 0; i < replace_size; ++i)
|
||||||
}
|
in_data[i + record.first] = record.second[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(), in->GetContainingDirectory());
|
|
||||||
|
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
|
||||||
|
in->GetContainingDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
// 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
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
@@ -23,17 +20,24 @@ public:
|
|||||||
~IPSwitchCompiler();
|
~IPSwitchCompiler();
|
||||||
|
|
||||||
std::array<u8, 0x20> GetBuildID() const;
|
std::array<u8, 0x20> GetBuildID() const;
|
||||||
|
bool IsValid() const;
|
||||||
VirtualFile Apply(const VirtualFile& in) const;
|
VirtualFile Apply(const VirtualFile& in) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct IPSwitchPatch;
|
struct IPSwitchPatch;
|
||||||
|
|
||||||
void ParseFlag(const std::string& flag);
|
void ParseFlag(const std::string& flag);
|
||||||
void Parse(std::span<u8 const> bytes);
|
void Parse();
|
||||||
|
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
VirtualFile patch_text;
|
VirtualFile patch_text;
|
||||||
std::vector<IPSwitchPatch> patches;
|
std::vector<IPSwitchPatch> patches;
|
||||||
std::array<u8, 0x20> nso_build_id{};
|
std::array<u8, 0x20> nso_build_id{};
|
||||||
|
bool is_little_endian = false;
|
||||||
|
s64 offset_shift = 0;
|
||||||
|
bool print_values = false;
|
||||||
|
std::string last_comment = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -345,8 +377,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
return exefs;
|
return exefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const std::string& build_id) const {
|
||||||
|
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;
|
||||||
@@ -360,11 +393,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
|||||||
for (const auto& file : exefs_dir->GetFiles()) {
|
for (const auto& file : exefs_dir->GetFiles()) {
|
||||||
if (file->GetExtension() == "ips") {
|
if (file->GetExtension() == "ips") {
|
||||||
auto name = file->GetName();
|
auto name = file->GetName();
|
||||||
const auto this_build_id = fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
|
||||||
|
const auto this_build_id =
|
||||||
|
fmt::format("{:0<64}", name.substr(0, name.find('.')));
|
||||||
if (nso_build_id == this_build_id)
|
if (nso_build_id == this_build_id)
|
||||||
out.push_back(file);
|
out.push_back(file);
|
||||||
} else if (file->GetExtension() == "pchtxt") {
|
} else if (file->GetExtension() == "pchtxt") {
|
||||||
IPSwitchCompiler compiler{file};
|
IPSwitchCompiler compiler{file};
|
||||||
|
if (!compiler.IsValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
const auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||||
if (nso_build_id == this_build_id)
|
if (nso_build_id == this_build_id)
|
||||||
out.push_back(file);
|
out.push_back(file);
|
||||||
@@ -372,6 +410,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,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;
|
||||||
@@ -448,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;
|
||||||
@@ -486,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;
|
||||||
@@ -590,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;
|
||||||
@@ -698,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;
|
||||||
@@ -710,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;
|
||||||
|
|
||||||
@@ -862,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};
|
||||||
|
|
||||||
@@ -888,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();
|
||||||
@@ -956,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");
|
||||||
@@ -992,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,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 {};
|
||||||
}
|
}
|
||||||
@@ -1155,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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<NcaID> CheckMapForContentRecord(const ::Common::unordered_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) {
|
||||||
auto cmnt_iter = map.find(title_id);
|
auto cmnt_iter = map.find(title_id);
|
||||||
u8 id_offset = 0;
|
u8 id_offset = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
@@ -209,11 +209,11 @@ private:
|
|||||||
ContentProviderParsingFunction parser;
|
ContentProviderParsingFunction parser;
|
||||||
|
|
||||||
// maps tid -> NcaID of meta
|
// maps tid -> NcaID of meta
|
||||||
::Common::unordered_map<u64, NcaID> meta_id;
|
ankerl::unordered_dense::map<u64, NcaID> meta_id;
|
||||||
// maps tid -> meta
|
// maps tid -> meta
|
||||||
::Common::unordered_map<u64, CNMT> meta;
|
ankerl::unordered_dense::map<u64, CNMT> meta;
|
||||||
// maps tid -> meta for CNMT in yuzu_meta
|
// maps tid -> meta for CNMT in yuzu_meta
|
||||||
::Common::unordered_map<u64, CNMT> yuzu_meta;
|
ankerl::unordered_dense::map<u64, CNMT> yuzu_meta;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ContentProviderUnionSlot {
|
enum class ContentProviderUnionSlot {
|
||||||
@@ -313,8 +313,8 @@ private:
|
|||||||
void ProcessXCI(const VirtualFile& file);
|
void ProcessXCI(const VirtualFile& file);
|
||||||
|
|
||||||
std::vector<VirtualDir> load_dirs;
|
std::vector<VirtualDir> load_dirs;
|
||||||
::Common::unordered_map<std::tuple<u64, ContentRecordType, TitleType>, VirtualFile> entries;
|
ankerl::unordered_dense::map<std::tuple<u64, ContentRecordType, TitleType>, VirtualFile> entries;
|
||||||
::Common::unordered_map<u64, u32> versions;
|
ankerl::unordered_dense::map<u64, u32> versions;
|
||||||
std::vector<ExternalUpdateEntry> multi_version_entries;
|
std::vector<ExternalUpdateEntry> multi_version_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "common/container/unordered_set.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "core/file_sys/vfs/vfs_layered.h"
|
#include "core/file_sys/vfs/vfs_layered.h"
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ std::string LayeredVfsDirectory::GetFullPath() const {
|
|||||||
|
|
||||||
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||||
std::vector<VirtualFile> out;
|
std::vector<VirtualFile> out;
|
||||||
::Common::unordered_set<std::string> out_names;
|
ankerl::unordered_dense::set<std::string> out_names;
|
||||||
|
|
||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
for (auto& file : layer->GetFiles()) {
|
for (auto& file : layer->GetFiles()) {
|
||||||
@@ -79,7 +79,7 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
|||||||
|
|
||||||
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||||
std::vector<VirtualDir> out;
|
std::vector<VirtualDir> out;
|
||||||
::Common::unordered_set<std::string> out_names;
|
ankerl::unordered_dense::set<std::string> out_names;
|
||||||
|
|
||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
for (const auto& sd : layer->GetSubdirectories()) {
|
for (const auto& sd : layer->GetSubdirectories()) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ private:
|
|||||||
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> m_watchpoints{};
|
||||||
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
|
std::map<KProcessAddress, u64> m_debug_page_refcounts{};
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
::Common::unordered_map<u64, u64> m_post_handlers{};
|
ankerl::unordered_dense::map<u64, u64> m_post_handlers{};
|
||||||
#endif
|
#endif
|
||||||
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
|
std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
|
||||||
Core::Memory::Memory m_memory;
|
Core::Memory::Memory m_memory;
|
||||||
@@ -494,7 +494,7 @@ public:
|
|||||||
static void Switch(KernelCore& kernel, KProcess* cur_process, KProcess* next_process);
|
static void Switch(KernelCore& kernel, KProcess* cur_process, KProcess* next_process);
|
||||||
|
|
||||||
#ifdef HAS_NCE
|
#ifdef HAS_NCE
|
||||||
::Common::unordered_map<u64, u64>& GetPostHandlers() noexcept {
|
ankerl::unordered_dense::map<u64, u64>& GetPostHandlers() noexcept {
|
||||||
return m_post_handlers;
|
return m_post_handlers;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1216,7 +1216,7 @@ Result KServerSession::ReceiveRequest(KernelCore& kernel, uintptr_t server_messa
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle, bool session_closed) {
|
KPhysicalAddress server_message_paddr, bool is_hle) {
|
||||||
// Lock the session.
|
// Lock the session.
|
||||||
KScopedLightLock lk{m_lock};
|
KScopedLightLock lk{m_lock};
|
||||||
|
|
||||||
@@ -1248,7 +1248,7 @@ Result KServerSession::SendReply(KernelCore& kernel, uintptr_t server_message, u
|
|||||||
KEvent* event = request->GetEvent();
|
KEvent* event = request->GetEvent();
|
||||||
|
|
||||||
// Check whether we're closed.
|
// Check whether we're closed.
|
||||||
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed() || session_closed);
|
const bool closed = (client_thread == nullptr || m_parent->IsClientClosed());
|
||||||
|
|
||||||
Result result = ResultSuccess;
|
Result result = ResultSuccess;
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
|||||||
@@ -54,14 +54,14 @@ public:
|
|||||||
|
|
||||||
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
Result OnRequest(KernelCore& kernel, KSessionRequest* request);
|
||||||
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result SendReply(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr, bool is_hle = false, bool session_closed = false);
|
KPhysicalAddress server_message_paddr, bool is_hle = false);
|
||||||
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
Result ReceiveRequest(KernelCore& kernel, uintptr_t server_message, uintptr_t server_buffer_size,
|
||||||
KPhysicalAddress server_message_paddr,
|
KPhysicalAddress server_message_paddr,
|
||||||
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
|
||||||
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
std::weak_ptr<Service::SessionRequestManager> manager = {});
|
||||||
|
|
||||||
Result SendReplyHLE(KernelCore& kernel, bool session_closed = false) {
|
Result SendReplyHLE(KernelCore& kernel) {
|
||||||
R_RETURN(this->SendReply(kernel, 0, 0, 0, true, session_closed));
|
R_RETURN(this->SendReply(kernel, 0, 0, 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
Result ReceiveRequestHLE(KernelCore& kernel, std::shared_ptr<Service::HLERequestContext>* out_context,
|
||||||
|
|||||||
@@ -10,8 +10,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include "common/container/unordered_set.h"
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@@ -793,8 +792,8 @@ struct KernelCore::Impl {
|
|||||||
|
|
||||||
std::optional<KObjectNameGlobalData> object_name_global_data;
|
std::optional<KObjectNameGlobalData> object_name_global_data;
|
||||||
|
|
||||||
::Common::unordered_set<KAutoObject*> registered_objects;
|
ankerl::unordered_dense::set<KAutoObject*> registered_objects;
|
||||||
::Common::unordered_set<KAutoObject*> registered_in_use_objects;
|
ankerl::unordered_dense::set<KAutoObject*> registered_in_use_objects;
|
||||||
|
|
||||||
std::mutex server_lock;
|
std::mutex server_lock;
|
||||||
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
|
|||||||
@@ -17,12 +17,56 @@
|
|||||||
|
|
||||||
namespace Kernel::Svc {
|
namespace Kernel::Svc {
|
||||||
|
|
||||||
|
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||||
|
const auto MAX_MSG_SIZE = 0x1000;
|
||||||
|
|
||||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||||
|
static struct DebugFlusher {
|
||||||
|
std::string msg_buffer;
|
||||||
|
std::mutex msg_mutex;
|
||||||
|
std::condition_variable msg_cv;
|
||||||
|
std::chrono::steady_clock::time_point last_msg_time;
|
||||||
|
std::optional<std::jthread> thread;
|
||||||
|
} flusher_data;
|
||||||
R_SUCCEED_IF(len == 0);
|
R_SUCCEED_IF(len == 0);
|
||||||
std::string msg_buffer(len, 0);
|
// Only start the thread the very first time this function is called
|
||||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
if (!flusher_data.thread) {
|
||||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
std::unique_lock lock(flusher_data.msg_mutex);
|
||||||
|
flusher_data.msg_cv.wait(lock, [&stop_token] {
|
||||||
|
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
|
||||||
|
});
|
||||||
|
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
|
||||||
|
break;
|
||||||
|
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
|
||||||
|
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
|
||||||
|
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
|
||||||
|
});
|
||||||
|
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
|
||||||
|
if (!flusher_data.msg_buffer.empty()) {
|
||||||
|
// Remove trailing newline as LOG_INFO adds that anyways
|
||||||
|
if (flusher_data.msg_buffer.back() == '\n')
|
||||||
|
flusher_data.msg_buffer.pop_back();
|
||||||
|
|
||||||
|
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
|
||||||
|
flusher_data.msg_buffer.clear();
|
||||||
|
}
|
||||||
|
if (stop_token.stop_requested()) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flusher_data.msg_cv.notify_all();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard lock(flusher_data.msg_mutex);
|
||||||
|
const auto old_size = flusher_data.msg_buffer.size();
|
||||||
|
flusher_data.msg_buffer.resize(old_size + len);
|
||||||
|
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
|
||||||
|
flusher_data.last_msg_time = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
flusher_data.msg_cv.notify_one();
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "common/container/unordered_map.h"
|
#include <ankerl/unordered_dense.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
@@ -176,6 +176,6 @@ struct WebCommonReturnValue {
|
|||||||
};
|
};
|
||||||
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
|
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
|
||||||
|
|
||||||
using WebArgInputTLVMap = ::Common::unordered_map<WebArgInputTLVType, std::vector<u8>>;
|
using WebArgInputTLVMap = ankerl::unordered_dense::map<WebArgInputTLVType, std::vector<u8>>;
|
||||||
|
|
||||||
} // namespace Service::AM::Frontend
|
} // namespace Service::AM::Frontend
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -158,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;
|
||||||
|
|
||||||
@@ -238,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.
|
||||||
@@ -252,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;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user