diff --git a/.ci/ps4/build.sh b/.ci/ps4/build.sh new file mode 100755 index 0000000000..8a5a64c5ec --- /dev/null +++ b/.ci/ps4/build.sh @@ -0,0 +1,67 @@ +#!/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 + +[ -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") +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") + +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 " -o -lc -lkernel -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o ") +set(CMAKE_CXX_LINK_EXECUTABLE " -o -lc -lkernel -lc++ -lSceUserService -lSceSysmodule -lSceNet -lSceLibcInternal $OO_PS4_TOOLCHAIN/lib/crt1.o ") + +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 + +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[@]}" $@) +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_OPENSSL=OFF \ + -DENABLE_WEB_SERVICE=OFF \ + -DUSE_DISCORD_PRESENCE=OFF \ + -DCPMUTIL_FORCE_BUNDLED=ON \ + -DOPENSSL_ROOT_DIR="$OO_PS4_TOOLCHAIN" \ + -DOPENSSL_SSL_LIBRARY="$OO_PS4_TOOLCHAIN/lib/libssl.a" \ + -DOPENSSL_CRYPTO_LIBRARY="$OO_PS4_TOOLCHAIN/lib/libcrypto.a" \ + -DOPENSSL_INCLUDE_DIR="$OO_PS4_TOOLCHAIN/include/openssl" \ + -DYUZU_USE_EXTERNAL_FFMPEG=ON \ + -DYUZU_USE_CPM=ON \ + -DDYNARMIC_ENABLE_NO_EXECUTE_SUPPORT=OFF \ + -DDYNARMIC_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 diff --git a/.ci/ps4/make-toolchain.sh b/.ci/ps4/make-toolchain.sh new file mode 100755 index 0000000000..2b4f994b79 --- /dev/null +++ b/.ci/ps4/make-toolchain.sh @@ -0,0 +1,176 @@ +#!/usr/local/bin/bash -ex + +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +# Define global vars +# These flags are used everywhere, so let's reuse them. +export OO_PS4_TOOLCHAIN="$PWD/prefix" +export PREFIX="$OO_PS4_TOOLCHAIN" +export CC="clang" +export CXX="clang++" +export AR="llvm-ar" +export CFLAGS="-fPIC -DPS4 -D_LIBUNWIND_IS_BAREMETAL=1" +export CXXFLAGS="$CFLAGS -D__STDC_VERSION__=0" +export TARGET="x86_64-scei-ps4" +export LLVM_ROOT="$PWD/llvm-project" +export LLVM_PATH="$PWD/llvm-project/llvm" +export WORK_PATH="$PWD" + +prepare_prefix() { + [ -d OpenOrbis-PS4-Toolchain ] || git clone --depth=1 https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain + [ -d musl ] || git clone --depth=1 https://github.com/OpenOrbis/musl + [ -d llvm-project ] || git clone --depth=1 --branch openorbis/20.x https://github.com/seuros/llvm-project + [ -d create-fself ] || git clone --depth=1 https://github.com/OpenOrbis/create-fself + [ -d create-gp4 ] || git clone --depth=1 https://github.com/OpenOrbis/create-gp4 + [ -d readoelf ] || git clone --depth=1 https://github.com/OpenOrbis/readoelf + [ -d LibOrbisPkg ] || git clone --depth=1 https://github.com/maxton/LibOrbisPkg + + mkdir -p $PREFIX "$PREFIX/bin" "$PREFIX/include" + [ -f "$PREFIX/include/orbis/libkernel.h" ] || cp -r OpenOrbis-PS4-Toolchain/include/* "$PREFIX/include/" + mkdir -p $PREFIX/usr + [ -L "$PREFIX/usr/include" ] || ln -s $PREFIX/include $PREFIX/usr/include || echo 1 + [ -L "$PREFIX/usr/share" ] || ln -s $PREFIX/share $PREFIX/usr/share || echo 1 + [ -L "$PREFIX/usr/lib" ] || ln -s $PREFIX/lib $PREFIX/usr/lib || echo 1 + [ -L "$PREFIX/usr/bin" ] || ln -s $PREFIX/bin $PREFIX/usr/bin || echo 1 +} + +build_musl() { + mkdir -p musl-build + cd musl-build + ../musl/configure --target=$TARGET --disable-shared CC="$CC" CFLAGS="$CFLAGS" --prefix=$PREFIX + gmake -j8 && gmake install + cd .. +} + +build_llvm() { + # Build compiler-rt + cmake "$LLVM_ROOT/compiler-rt" -B "$WORK_PATH/llvm-build/compiler-rt" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ + -DCMAKE_C_FLAGS="$CFLAGS" -DCMAKE_CXX_FLAGS="$CXXFLAGS" \ + -DCMAKE_ASM_COMPILER="$CC" -DCMAKE_ASM_FLAGS="$CFLAGS -x assembler-with-cpp" \ + -DLLVM_PATH="$LLVM_PATH" -DCOMPILER_RT_DEFAULT_TARGET_TRIPLE="$TARGET" \ + -DCOMPILER_RT_BAREMETAL_BUILD=YES -DCOMPILER_RT_BUILD_BUILTINS=ON \ + -DCOMPILER_RT_BUILD_CRT=OFF -DCOMPILER_RT_BUILD_SANITIZERS=OFF \ + -DCOMPILER_RT_BUILD_XRAY=OFF -DCOMPILER_RT_BUILD_LIBFUZZER=OFF \ + -DCOMPILER_RT_BUILD_PROFILE=OFF -DCOMPILER_RT_STANDALONE_BUILD=ON + # Build libunwind + cmake "$LLVM_ROOT/libunwind" -B "$WORK_PATH/llvm-build/libunwind" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ + -DCMAKE_C_FLAGS="$CFLAGS -fcxx-exceptions" -DCMAKE_CXX_FLAGS="$CXXFLAGS -fcxx-exceptions" \ + -DCMAKE_ASM_COMPILER="$CC" -DCMAKE_ASM_FLAGS="$CFLAGS -x assembler-with-cpp" \ + -DLLVM_PATH="$LLVM_PATH" -DLIBUNWIND_USE_COMPILER_RT=YES \ + -DLIBUNWIND_BUILD_32_BITS=NO -DLIBUNWIND_ENABLE_STATIC=ON \ + -DLIBUNWIND_ENABLE_SHARED=OFF -DLIBUNWIND_IS_BAREMETAL=ON + # Build libcxxabi + cmake "$LLVM_ROOT/libcxxabi" -B "$WORK_PATH/llvm-build/libcxxabi" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ + -DCMAKE_C_FLAGS="$CFLAGS -D_GNU_SOURCE=1 -isysroot $PREFIX -isystem $LLVM_ROOT/libcxx/include -isystem $PREFIX/include -isystem $WORK_PATH/llvm-build/libcxx/include/c++/v1" \ + -DCMAKE_CXX_FLAGS="$CXXFLAGS -D_GNU_SOURCE=1 -isysroot $PREFIX -isystem $LLVM_ROOT/libcxx/include -isystem $PREFIX/include -isystem $WORK_PATH/llvm-build/libcxx/include/c++/v1" \ + -DCMAKE_ASM_COMPILER="$CC" -DCMAKE_ASM_FLAGS="$CFLAGS -x assembler-with-cpp" \ + -DLLVM_PATH="$LLVM_PATH" -DLIBCXXABI_ENABLE_SHARED=NO \ + -DLLVM_ENABLE_RUNTIMES="rt;libunwind" \ + -DLIBCXXABI_ENABLE_STATIC=YES -DLIBCXXABI_ENABLE_EXCEPTIONS=YES \ + -DLIBCXXABI_USE_COMPILER_RT=YES -DLIBCXXABI_USE_LLVM_UNWINDER=YES \ + -DLIBCXXABI_LIBUNWIND_PATH="$LLVM_ROOT/libunwind" \ + -DLIBCXXABI_LIBCXX_INCLUDES="$LLVM_ROOT/libcxx/include" \ + -DLIBCXXABI_ENABLE_PIC=YES + # Build libcxx + cmake "$LLVM_ROOT/libcxx" -B "$WORK_PATH/llvm-build/libcxx" \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DCMAKE_C_COMPILER="$CC" -DCMAKE_CXX_COMPILER="$CXX" \ + -DCMAKE_C_FLAGS="$CFLAGS -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 -isysroot $PREFIX -isystem $PREFIX/include/c++/v1 -isystem $PREFIX/include" \ + -DCMAKE_CXX_FLAGS="$CXXFLAGS -D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1 -isysroot $PREFIX -isystem $PREFIX/include/c++/v1 -isystem $PREFIX/include" \ + -DCMAKE_ASM_COMPILER="$CC" -DCMAKE_ASM_FLAGS="$CFLAGS -x assembler-with-cpp" \ + -DLLVM_PATH="$LLVM_PATH" -DLIBCXX_ENABLE_RTTI=YES \ + -DLIBCXX_HAS_MUSL_LIBC=YES -DLIBCXX_ENABLE_SHARED=NO \ + -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$LLVM_ROOT/libcxxabi/include" \ + -DLIBCXX_CXX_ABI_LIBRARY_PATH="$LLVM_ROOT/libcxxabi/build/lib" + + cmake --build "$WORK_PATH/llvm-build/compiler-rt" --parallel + cmake --install "$WORK_PATH/llvm-build/compiler-rt" + + cmake --build "$WORK_PATH/llvm-build/libunwind" --parallel + cmake --install "$WORK_PATH/llvm-build/libunwind" + + cmake --build "$WORK_PATH/llvm-build/libcxxabi" --parallel + cmake --install "$WORK_PATH/llvm-build/libcxxabi" + + touch "$WORK_PATH/llvm-build/libcxx/include/c++/v1/libcxx.imp" + cmake --build "$WORK_PATH/llvm-build/libcxx" --parallel + cmake --install "$WORK_PATH/llvm-build/libcxx" +} + +build_tools() { + + # Build create-fself + cd create-fself/cmd/create-fself + cp go-linux.mod go.mod + go build -ldflags "-linkmode external -extldflags -static" -o create-fself + mv ./create-fself $PREFIX/bin/create-fself + cd ../../../ + + # Build create-gp4 + cd create-gp4/cmd/create-gp4 + go build -ldflags "-linkmode external -extldflags -static" -o create-gp4 + mv ./create-gp4 $PREFIX/bin/create-gp4 + cd ../../../ + + # Build readoelf + cd readoelf/cmd/readoelf + go build -ldflags "-linkmode external -extldflags -static" -o readoelf + mv ./readoelf $PREFIX/bin/readoelf + cd ../../../ + + # # Pull maxton's publishing tools (<3) + # # Sadly maxton has passed on, we have forked the repository and will continue to update it in the future. RIP <3 + # cd $PREFIX/bin + # [ -f PkgTool.Core-linux-x64-0.2.231.zip ] || wget https://github.com/maxton/LibOrbisPkg/releases/download/v0.2/PkgTool.Core-linux-x64-0.2.231.zip + # [ -f PkgTool.Core ] || unzip PkgTool.Core-linux-x64-0.2.231.zip + # chmod +x PkgTool.Core +} + +finish_prefix() { + as $WORK_PATH/OpenOrbis-PS4-Toolchain/src/crt/crtlib.S -o $PREFIX/lib/crtlib.o + cp -a $WORK_PATH/OpenOrbis-PS4-Toolchain/link.x $PREFIX/ + + cp -a ~/OpenOrbis/PS4Toolchain/lib/libkernel* $PREFIX/lib/ + cp -a ~/OpenOrbis/PS4Toolchain/lib/libSce* $PREFIX/lib/ + + cp -a ~/OpenOrbis/PS4Toolchain/lib/libSDL* $PREFIX/lib/ + cp -r ~/OpenOrbis/PS4Toolchain/include/SDL2 $PREFIX/include/SDL2 + + cp $WORK_PATH/llvm-build/compiler-rt/lib/freebsd/libclang_rt.builtins-x86_64.a $PREFIX/lib/ + + # Combine libc++, libc++abi and libunwind into a single archive + cat << EOF >"mri.txt" +CREATE $PREFIX/lib/libc++M.a +ADDLIB $PREFIX/lib/libunwind.a +ADDLIB $PREFIX/lib/libc++abi.a +ADDLIB $PREFIX/lib/libc++.a +SAVE +END +EOF + $AR -M < mri.txt + cp $PREFIX/lib/libc++M.a $PREFIX/lib/libc++.a + # Merge compiler-rt into libc + cat << EOF >"mri.txt" +CREATE $PREFIX/lib/libcM.a +ADDLIB $PREFIX/lib/libc.a +ADDLIB $PREFIX/lib/libclang_rt.builtins-x86_64.a +SAVE +END +EOF + $AR -M < mri.txt + cp $PREFIX/lib/libcM.a $PREFIX/lib/libc.a + rm mri.txt +} + +prepare_prefix +build_musl +build_llvm +build_tools +finish_prefix diff --git a/.gitignore b/.gitignore index 67bdd8adf4..b304411646 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,6 @@ artifacts /install* vulkansdk*.exe *.tar.zst + +# PS4 toolchain stuff +ps4-toolchain.cmake diff --git a/.patch/boost/0004-openorbis.patch b/.patch/boost/0004-openorbis.patch new file mode 100644 index 0000000000..09071b599c --- /dev/null +++ b/.patch/boost/0004-openorbis.patch @@ -0,0 +1,17 @@ +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 + + #include diff --git a/.patch/enet/0001-openorbis.patch b/.patch/enet/0001-openorbis.patch new file mode 100644 index 0000000000..744e2f879d --- /dev/null +++ b/.patch/enet/0001-openorbis.patch @@ -0,0 +1,13 @@ +diff --git a/unix.c b/unix.c +index 6669216..86a2faa 100644 +--- a/unix.c ++++ b/unix.c +@@ -53,7 +53,7 @@ + #include + #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 + diff --git a/.patch/mbedtls/0003-openorbis.patch b/.patch/mbedtls/0003-openorbis.patch new file mode 100644 index 0000000000..46cfbd408f --- /dev/null +++ b/.patch/mbedtls/0003-openorbis.patch @@ -0,0 +1,13 @@ +diff --git a/library/entropy_poll.c b/library/entropy_poll.c +index 611768c..8950ee4 100644 +--- a/library/entropy_poll.c ++++ b/library/entropy_poll.c +@@ -118,7 +118,7 @@ static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) + * + * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7 + */ +-#if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM) ++#if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM) && !defined(__OPENORBIS__) + #include + #include + #if defined(KERN_ARND) diff --git a/.patch/sdl2_ps4/0001-ps4.patch b/.patch/sdl2_ps4/0001-ps4.patch new file mode 100644 index 0000000000..cb5ed901cb --- /dev/null +++ b/.patch/sdl2_ps4/0001-ps4.patch @@ -0,0 +1,359 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7c230473ac..b1275edb61 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -331,6 +331,13 @@ if(CYGWIN) + list(APPEND SDL_CFLAGS "-I/usr/include/mingw") + endif() + ++######### *FIXME* ++if(PS4 OR ORBIS) ++ set(USE_GENERATED_CONFIG Off) ++else() ++ set(USE_GENERATED_CONFIG On) ++endif() ++ + # General includes + target_compile_definitions(sdl-build-options INTERFACE "-DUSING_GENERATED_CONFIG_H") + target_include_directories(sdl-build-options BEFORE INTERFACE "${SDL2_BINARY_DIR}/include" "${SDL2_BINARY_DIR}/include-config-$>") +@@ -359,6 +366,15 @@ if(EMSCRIPTEN) + set(SDL_CPUINFO_ENABLED_BY_DEFAULT OFF) + endif() + ++if(PS4 OR ORBIS) ++ set(SDL_ATOMIC_ENABLED_BY_DEFAULT ON) ++ set(SDL_SHARED_ENABLED_BY_DEFAULT OFF) ++ set(SDL_THREADS_ENABLED_BY_DEFAULT ON) ++ set(SDL_PTHREADS_ENABLED_BY_DEFAULT ON) ++ set(SDL_LOADSO_ENABLED_BY_DEFAULT OFF) ++ set(SDL_DLOPEN_ENABLED_BY_DEFAULT OFF) ++endif() ++ + if(VITA OR PSP OR PS2 OR N3DS) + set(SDL_SHARED_ENABLED_BY_DEFAULT OFF) + set(SDL_LOADSO_ENABLED_BY_DEFAULT OFF) +@@ -1478,7 +1494,42 @@ elseif(EMSCRIPTEN) + + CheckPTHREAD() + CheckLibUnwind() ++elseif(PS4 OR ORBIS) ++ CheckPTHREAD() ++ if(SDL_AUDIO) ++ set(SDL_AUDIO_DRIVER_PS4 1) ++ file(GLOB PS4_AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/ps4/*.c) ++ set(SOURCE_FILES ${SOURCE_FILES} ${PS4_AUDIO_SOURCES}) ++ set(HAVE_SDL_AUDIO TRUE) ++ endif() ++# if(SDL_FILESYSTEM) ++# set(SDL_FILESYSTEM_PS4 1) ++# file(GLOB PS4_FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/ps4/*.c) ++# set(SOURCE_FILES ${SOURCE_FILES} ${PS4_FILESYSTEM_SOURCES}) ++# set(HAVE_SDL_FILESYSTEM TRUE) ++# endif() ++ if(SDL_JOYSTICK) ++ set(SDL_JOYSTICK_PS4 1) ++ file(GLOB PS4_JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/ps4/*.c) ++ set(SOURCE_FILES ${SOURCE_FILES} ${PS4_JOYSTICK_SOURCES}) ++ set(HAVE_SDL_JOYSTICK TRUE) ++ endif() ++ if(SDL_TIMERS) ++ set(SDL_TIMER_UNIX 1) ++ file(GLOB TIMER_SOURCES ${SDL2_SOURCE_DIR}/src/timer/unix/*.c) ++ set(SOURCE_FILES ${SOURCE_FILES} ${TIMER_SOURCES}) ++ set(HAVE_SDL_TIMERS TRUE) + ++ if(CLOCK_GETTIME) ++ set(HAVE_CLOCK_GETTIME 1) ++ endif() ++ endif() ++ if(SDL_VIDEO) ++ set(SDL_VIDEO_DRIVER_PS4 1) ++ file(GLOB PS4_VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/ps4/*.c) ++ set(SOURCE_FILES ${SOURCE_FILES} ${PS4_VIDEO_SOURCES}) ++ set(HAVE_SDL_VIDEO TRUE) ++ endif() + elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) + if(SDL_AUDIO) + if(SYSV5 OR SOLARIS OR HPUX) +@@ -3039,7 +3090,7 @@ endif() + # We always need to have threads and timers around + if(NOT HAVE_SDL_THREADS) + # The emscripten platform has been carefully vetted to work without threads +- if (EMSCRIPTEN) ++ if (EMSCRIPTEN OR PS4 OR ORBIS) + set(SDL_THREADS_DISABLED 1) + file(GLOB THREADS_SOURCES ${SDL2_SOURCE_DIR}/src/thread/generic/*.c) + list(APPEND SOURCE_FILES ${THREADS_SOURCES}) +diff --git a/README.md b/README.md +index fa7f7ba0b5..8d5a375694 100644 +--- a/README.md ++++ b/README.md +@@ -1,5 +1,5 @@ + +-# Simple DirectMedia Layer (SDL) Version 2.0 ++# Simple DirectMedia Layer (SDL) Version 2.0 (For OpenOrbis PS4 SDK) + + https://www.libsdl.org/ + +diff --git a/include/SDL_config.h b/include/SDL_config.h +index a628d86252..a101532d16 100644 +--- a/include/SDL_config.h ++++ b/include/SDL_config.h +@@ -41,6 +41,10 @@ + #include "SDL_config_iphoneos.h" + #elif defined(__ANDROID__) + #include "SDL_config_android.h" ++#elif defined(__PSP__) ++#include "SDL_config_psp.h" ++#elif defined(__OPENORBIS__) ++#include "SDL_config_ps4.h" + #elif defined(__OS2__) + #include "SDL_config_os2.h" + #elif defined(__EMSCRIPTEN__) +diff --git a/include/SDL_platform.h b/include/SDL_platform.h +index 36df782a4e..0cc20dc4e2 100644 +--- a/include/SDL_platform.h ++++ b/include/SDL_platform.h +@@ -214,6 +214,10 @@ + #if defined(PS2) + #define __PS2__ 1 + #endif ++#if defined(__OPENORBIS__) ++#undef __PS4__ ++#define __PS4__ 1 ++#endif + + /* The NACL compiler defines __native_client__ and __pnacl__ + * Ref: http://www.chromium.org/nativeclient/pnacl/stability-of-the-pnacl-bitcode-abi +diff --git a/src/SDL.c b/src/SDL.c +index cfeea077e7..33fce965c0 100644 +--- a/src/SDL.c ++++ b/src/SDL.c +@@ -642,6 +642,8 @@ const char *SDL_GetPlatform(void) + return "Nokia N-Gage"; + #elif defined(__3DS__) + return "Nintendo 3DS"; ++#elif defined(__PS4__) ++ return "PlayStation4"; + #else + return "Unknown (see SDL_platform.h)"; + #endif +diff --git a/src/SDL_error.c b/src/SDL_error.c +index 993f5bac55..083ebf3027 100644 +--- a/src/SDL_error.c ++++ b/src/SDL_error.c +@@ -50,11 +50,14 @@ int SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...) + va_end(ap); + } + } +- ++#ifndef __OPENORBIS__ // Yeah this is stupid but whatever + if (SDL_LogGetPriority(SDL_LOG_CATEGORY_ERROR) <= SDL_LOG_PRIORITY_DEBUG) { ++#endif + /* If we are in debug mode, print out the error message */ + SDL_LogDebug(SDL_LOG_CATEGORY_ERROR, "%s", error->str); ++#ifndef __OPENORBIS__ // Yeah this is stupid but whatever + } ++#endif + } + + return -1; +diff --git a/src/SDL_log.c b/src/SDL_log.c +index 7a5f1dbc03..a7f3d85782 100644 +--- a/src/SDL_log.c ++++ b/src/SDL_log.c +@@ -390,10 +390,12 @@ void SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va + int len; + va_list aq; + ++#ifndef __OPENORBIS__ + /* Nothing to do if we don't have an output function */ + if (!SDL_log_function) { + return; + } ++#endif + + /* Make sure we don't exceed array bounds */ + if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) { +@@ -442,7 +444,11 @@ void SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va + } + + SDL_LockMutex(log_function_mutex); ++#ifdef __OPENORBIS__ ++ printf("%s\n", message); // just fucking do it ++#else + SDL_log_function(SDL_log_userdata, category, priority, message); ++#endif + SDL_UnlockMutex(log_function_mutex); + + /* Free only if dynamically allocated */ +diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c +index 421adbc76a..82d087a2f8 100644 +--- a/src/audio/SDL_audio.c ++++ b/src/audio/SDL_audio.c +@@ -114,7 +114,10 @@ static const AudioBootStrap *const bootstrap[] = { + #ifdef SDL_AUDIO_DRIVER_N3DS + &N3DSAUDIO_bootstrap, + #endif +-#ifdef SDL_AUDIO_DRIVER_EMSCRIPTEN ++#if SDL_AUDIO_DRIVER_PS4 ++ &PS4AUDIO_bootstrap, ++#endif ++#if SDL_AUDIO_DRIVER_EMSCRIPTEN + &EMSCRIPTENAUDIO_bootstrap, + #endif + #ifdef SDL_AUDIO_DRIVER_JACK +diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h +index 87387692ce..b1c00ba9dc 100644 +--- a/src/audio/SDL_sysaudio.h ++++ b/src/audio/SDL_sysaudio.h +@@ -207,6 +207,7 @@ extern AudioBootStrap PS2AUDIO_bootstrap; + extern AudioBootStrap PSPAUDIO_bootstrap; + extern AudioBootStrap VITAAUD_bootstrap; + extern AudioBootStrap N3DSAUDIO_bootstrap; ++extern AudioBootStrap PS4AUDIO_bootstrap; + extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap; + extern AudioBootStrap OS2AUDIO_bootstrap; + +diff --git a/src/dynapi/SDL_dynapi.h b/src/dynapi/SDL_dynapi.h +index 178218c053..a6e298a9fe 100644 +--- a/src/dynapi/SDL_dynapi.h ++++ b/src/dynapi/SDL_dynapi.h +@@ -69,6 +69,8 @@ + #define SDL_DYNAMIC_API 0 /* devkitARM doesn't support dynamic linking */ + #elif defined(DYNAPI_NEEDS_DLOPEN) && !defined(HAVE_DLOPEN) + #define SDL_DYNAMIC_API 0 /* we need dlopen(), but don't have it.... */ ++#elif defined(__OPENORBIS__) // Apparently __PS4__ getting defined is missed somewhere, I get broken static builds so force the issue ++#define SDL_DYNAMIC_API 0 + #endif + + /* everyone else. This is where we turn on the API if nothing forced it off. */ +diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c +index 60b0daf790..34433166e8 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 d36f784143..6671aff2fd 100644 +--- a/src/joystick/SDL_sysjoystick.h ++++ b/src/joystick/SDL_sysjoystick.h +@@ -243,6 +243,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_render.c b/src/render/SDL_render.c +index 35f80664ab..cd8f9d3615 100644 +--- a/src/render/SDL_render.c ++++ b/src/render/SDL_render.c +@@ -137,6 +137,10 @@ static const SDL_RenderDriver *render_drivers[] = { + #if SDL_VIDEO_RENDER_VITA_GXM + &VITA_GXM_RenderDriver, + #endif ++#if SDL_VIDEO_RENDER_PS4 && 0 // *FIXME* PS4_RenderDriver Disabled, it's software anyhow lets not reinvent... ++#error Use SoftRender for PS4 currently! ++ &PS4_RenderDriver, ++#endif + #if SDL_VIDEO_RENDER_SW + &SW_RenderDriver + #endif +diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h +index ac5426b676..d9b5bfbc39 100644 +--- a/src/render/SDL_sysrender.h ++++ b/src/render/SDL_sysrender.h +@@ -307,6 +307,7 @@ extern SDL_RenderDriver DirectFB_RenderDriver; + extern SDL_RenderDriver METAL_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; + +diff --git a/src/thread/pthread/SDL_systhread.c b/src/thread/pthread/SDL_systhread.c +index 212fe9c000..a920afba0b 100644 +--- a/src/thread/pthread/SDL_systhread.c ++++ b/src/thread/pthread/SDL_systhread.c +@@ -29,8 +29,10 @@ + #include + #endif + ++#ifdef HAVE_SIGNAL_H + #include + #include ++#endif + + #ifdef __LINUX__ + #include +@@ -60,7 +62,7 @@ + #endif + + +-#ifndef __NACL__ ++#if !defined(__NACL__) && !defined(__OPENORBIS__) + /* List of signals to mask in the subthreads */ + static const int sig_list[] = { + SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGWINCH, +@@ -162,7 +164,7 @@ void SDL_SYS_SetupThread(const char *name) + } + + /* NativeClient does not yet support signals.*/ +-#if !defined(__NACL__) ++#if !defined(__NACL__) && !defined(__OPENORBIS__) + /* Mask asynchronous signals for this thread */ + sigemptyset(&mask); + for (i = 0; sig_list[i]; ++i) { +diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h +index badb1a3edc..e17beb9f5c 100644 +--- a/src/video/SDL_sysvideo.h ++++ b/src/video/SDL_sysvideo.h +@@ -471,6 +471,7 @@ extern VideoBootStrap PSP_bootstrap; + extern VideoBootStrap VITA_bootstrap; + extern VideoBootStrap RISCOS_bootstrap; + extern VideoBootStrap N3DS_bootstrap; ++extern VideoBootStrap PS4_bootstrap; + extern VideoBootStrap RPI_bootstrap; + extern VideoBootStrap KMSDRM_bootstrap; + extern VideoBootStrap KMSDRM_LEGACY_bootstrap; +diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c +index 134cc05e13..f40d6104e2 100644 +--- a/src/video/SDL_video.c ++++ b/src/video/SDL_video.c +@@ -121,6 +121,9 @@ static VideoBootStrap *bootstrap[] = { + #ifdef SDL_VIDEO_DRIVER_N3DS + &N3DS_bootstrap, + #endif ++#ifdef SDL_VIDEO_DRIVER_PS4 ++ &PS4_bootstrap, ++#endif + #ifdef SDL_VIDEO_DRIVER_KMSDRM + &KMSDRM_bootstrap, + #endif +@@ -241,6 +244,7 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U + SDL_GetWindowSizeInPixels(window, &w, &h); + + if (!data) { ++ + SDL_Renderer *renderer = NULL; + const char *render_driver = NULL; + const char *hint; +@@ -297,7 +301,7 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U + SDL_assert(renderer != NULL); /* should have explicitly checked this above. */ + + /* Create the data after we successfully create the renderer (bug #1116) */ +- data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data)); ++ data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(SDL_WindowTextureData)); + if (!data) { + SDL_DestroyRenderer(renderer); + return SDL_OutOfMemory(); diff --git a/.patch/spirv-tools/0003-openorbis.patch b/.patch/spirv-tools/0003-openorbis.patch new file mode 100644 index 0000000000..f2c19d893f --- /dev/null +++ b/.patch/spirv-tools/0003-openorbis.patch @@ -0,0 +1,25 @@ +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 +@@ -19,6 +25,7 @@ + #include + #include + #include ++#include + + #include "source/opt/instruction.h" + #include "source/opt/scalar_analysis_nodes.h" diff --git a/.patch/xbyak/0001-macro-stl.patch b/.patch/xbyak/0001-macro-stl.patch new file mode 100644 index 0000000000..5cfb9eeec6 --- /dev/null +++ b/.patch/xbyak/0001-macro-stl.patch @@ -0,0 +1,21 @@ +diff --git a/xbyak/xbyak.h b/xbyak/xbyak.h +index ed7706a..51b520d 100644 +--- a/xbyak/xbyak.h ++++ b/xbyak/xbyak.h +@@ -37,6 +37,7 @@ + #define XBYAK_GNUC_PREREQ(major, minor) 0 + #endif + ++#if !defined(XBYAK_STD_UNORDERED_SET) + // This covers -std=(gnu|c)++(0x|11|1y), -stdlib=libc++, and modern Microsoft. + #if ((defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(_LIBCPP_VERSION) ||\ + ((__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__))) +@@ -71,6 +72,8 @@ + #define XBYAK_STD_UNORDERED_MAP std::map + #define XBYAK_STD_UNORDERED_MULTIMAP std::multimap + #endif ++#endif ++ + #ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN diff --git a/.patch/xbyak_sun/0001-macro-stl.patch b/.patch/xbyak_sun/0001-macro-stl.patch new file mode 100644 index 0000000000..5cfb9eeec6 --- /dev/null +++ b/.patch/xbyak_sun/0001-macro-stl.patch @@ -0,0 +1,21 @@ +diff --git a/xbyak/xbyak.h b/xbyak/xbyak.h +index ed7706a..51b520d 100644 +--- a/xbyak/xbyak.h ++++ b/xbyak/xbyak.h +@@ -37,6 +37,7 @@ + #define XBYAK_GNUC_PREREQ(major, minor) 0 + #endif + ++#if !defined(XBYAK_STD_UNORDERED_SET) + // This covers -std=(gnu|c)++(0x|11|1y), -stdlib=libc++, and modern Microsoft. + #if ((defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(_LIBCPP_VERSION) ||\ + ((__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__))) +@@ -71,6 +72,8 @@ + #define XBYAK_STD_UNORDERED_MAP std::map + #define XBYAK_STD_UNORDERED_MULTIMAP std::multimap + #endif ++#endif ++ + #ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN diff --git a/CMakeLists.txt b/CMakeLists.txt index 4490df21cb..fa5a85c724 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ include(UseCcache) include(CMakeDependentOption) include(CTest) include(CPMUtil) +include(OpenOrbis) if (NOT DEFINED ARCHITECTURE) message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?") diff --git a/CMakeModules/GenerateSCMRev.cmake b/CMakeModules/GenerateSCMRev.cmake index 5b0adad8dd..0c8c9cedec 100644 --- a/CMakeModules/GenerateSCMRev.cmake +++ b/CMakeModules/GenerateSCMRev.cmake @@ -18,10 +18,13 @@ if (DEFINED GIT_RELEASE) set(BUILD_VERSION "${GIT_TAG}") set(GIT_REFSPEC "${GIT_RELEASE}") set(IS_DEV_BUILD false) -else() +elseif(DEFINED GIT_COMMIT) string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION) set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}") set(IS_DEV_BUILD true) +else() + set(BUILD_VERSION "NoGitInfo") + set(IS_DEV_BUILD true) endif() if (NIGHTLY_BUILD) diff --git a/CMakeModules/OpenOrbis.cmake b/CMakeModules/OpenOrbis.cmake new file mode 100644 index 0000000000..6babccdd6d --- /dev/null +++ b/CMakeModules/OpenOrbis.cmake @@ -0,0 +1,46 @@ +# 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 () diff --git a/cpmfile.json b/cpmfile.json index c938e67e88..e9884910a5 100644 --- a/cpmfile.json +++ b/cpmfile.json @@ -17,7 +17,8 @@ "version": "1.57", "find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem", "patches": [ - "0001-clang-cl.patch" + "0001-clang-cl.patch", + "0004-openorbis.patch" ] }, "fmt": { diff --git a/dist/icon_variations/ps4.svg b/dist/icon_variations/ps4.svg new file mode 100644 index 0000000000..0ee32c660c --- /dev/null +++ b/dist/icon_variations/ps4.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Caveats.md b/docs/Caveats.md index d554f3ff77..a5203775e7 100644 --- a/docs/Caveats.md +++ b/docs/Caveats.md @@ -11,6 +11,7 @@ - [NetBSD](#netbsd) - [MSYS2](#msys2) - [RedoxOS](#redoxos) +- [PlayStation 4](#playstation-4) - [Windows](#windows) - [Windows 7, Windows 8 and Windows 8.1](#windows-7-windows-8-and-windows-81) - [Windows Vista and below](#windows-vista-and-below) @@ -220,6 +221,17 @@ 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. +## 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 7, Windows 8 and Windows 8.1 diff --git a/docs/user/Basics.md b/docs/user/Basics.md index 5101f4d9c3..b8719a9310 100644 --- a/docs/user/Basics.md +++ b/docs/user/Basics.md @@ -26,6 +26,7 @@ Eden will store configuration files in the following directories: - **Android**: Data is stored internally. - **Linux, macOS, FreeBSD, Solaris, OpenBSD**: `$XDG_DATA_HOME`, `$XDG_CACHE_HOME`, `$XDG_CONFIG_HOME`. - **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. diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 7b4c481ba5..c324e35f73 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -159,6 +159,10 @@ if (NOT ANDROID) if ("${YUZU_SYSTEM_PROFILE}" STREQUAL "steamdeck") set(SDL_PIPEWIRE OFF) # build errors out with this on AddJsonPackage("sdl2_steamdeck") + elseif (PLATFORM_PS4) + set(PS4 ON) + set(ORBIS ON) + AddJsonPackage("sdl2_ps4") else() AddJsonPackage("sdl2_generic") endif() @@ -259,6 +263,11 @@ target_include_directories(tz PUBLIC ./tz) add_library(bc_decoder bc_decoder/bc_decoder.cpp) target_include_directories(bc_decoder PUBLIC ./bc_decoder) +if (PLATFORM_PS4) + add_library(ps4sup ps4sup/emutls.c ps4sup/stub.cpp) + target_include_directories(ps4sup PUBLIC ./ps4sup) +endif() + if (NOT TARGET RenderDoc::API) add_library(renderdoc INTERFACE) target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc) diff --git a/externals/cmake-modules/DetectPlatform.cmake b/externals/cmake-modules/DetectPlatform.cmake index 6475884f1f..5d305438b9 100644 --- a/externals/cmake-modules/DetectPlatform.cmake +++ b/externals/cmake-modules/DetectPlatform.cmake @@ -17,6 +17,8 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") set(PLATFORM_SUN ON) +elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenOrbis") + set(PLATFORM_PS4 ON) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") set(PLATFORM_FREEBSD ON) elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") diff --git a/externals/cpmfile.json b/externals/cpmfile.json index de024fd40e..a35a3db599 100644 --- a/externals/cpmfile.json +++ b/externals/cpmfile.json @@ -98,7 +98,10 @@ "hash": "a0d2fa8c957704dd49e00a726284ac5ca034b50b00d2b20a94fa1bbfbb80841467834bfdc84aa0ed0d6aab894608fd6c86c3b94eee46343f0e6d9c22e391dbf9", "version": "1.3", "git_version": "1.3.18", - "find_args": "MODULE" + "find_args": "MODULE", + "patches": [ + "0001-openorbis.patch" + ] }, "spirv-tools": { "package": "SPIRV-Tools", @@ -111,7 +114,8 @@ ], "patches": [ "0001-netbsd-fix.patch", - "0002-allow-static-only.patch" + "0002-allow-static-only.patch", + "0003-openorbis.patch" ] }, "spirv-headers": { @@ -186,6 +190,18 @@ "bundled": true, "skip_updates": "true" }, + "sdl2_ps4": { + "package": "SDL2", + "repo": "libsdl-org/SDL", + "sha": "0c7042477a", + "hash": "91d897257fe1134e65234618a96bc8f4f9b61dd3ba42241a65c293cd406139e308a6c79eadfa7c3969271c88d73149e75c4310fff37c79387c80d9c982c1f322", + "key": "ps4", + "bundled": true, + "skip_updates": true, + "patches": [ + "0001-ps4.patch" + ] + }, "moltenvk": { "repo": "V380-Ori/Ryujinx.MoltenVK", "tag": "v%VERSION%-ryujinx", diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 3140f8e545..45697d23f4 100644 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -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-FileCopyrightText: 2021 yuzu Emulator Project @@ -11,63 +11,106 @@ set(FFmpeg_HWACCEL_FLAGS) set(FFmpeg_HWACCEL_INCLUDE_DIRS) set(FFmpeg_HWACCEL_LDFLAGS) -if (UNIX AND NOT ANDROID) - find_package(PkgConfig REQUIRED) - if (NOT ANDROID) - pkg_check_modules(LIBVA libva) - pkg_check_modules(CUDA cuda) - pkg_check_modules(FFNVCODEC ffnvcodec) - pkg_check_modules(VDPAU vdpau) +if (NOT YUZU_USE_BUNDLED_FFMPEG) + set(FFmpeg_CROSS_COMPILE_FLAGS "") + if (ANDROID) + # TODO: Maybe use CMAKE_SYSROOT? and probably provide a toolchain file for android + # I mean isn't that the "proper" way anyways? + string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" FFmpeg_HOST_SYSTEM_NAME) + set(TOOLCHAIN "${ANDROID_NDK}/toolchains/llvm/prebuilt/${FFmpeg_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}") + set(SYSROOT "${TOOLCHAIN}/sysroot") + set(FFmpeg_CPU "armv8-a") + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS + --enable-cross-compile + --arch=arm64 + #--cpu=${FFmpeg_CPU} + --cross-prefix="${TOOLCHAIN}/bin/aarch64-linux-android-" + --sysroot="${SYSROOT}" + --target-os=android + --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" + --extra-ldflags="-nostdlib" + ) + set(FFmpeg_IS_CROSS_COMPILING TRUE) + # User attempts to do a FFmpeg cross compilation because... + # Here we just quickly test against host/system processors not matching + # TODO: Test for versions not matching as well? + elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR + AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME)) + string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME) + if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis") + set(FFmpeg_SYSTEM_NAME "freebsd") # Emulates FBSD :) + endif() + # TODO: Can we really do better? Auto-detection? Something clever? + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS + --enable-cross-compile + --arch="${CMAKE_SYSTEM_PROCESSOR}" + --target-os="${FFmpeg_SYSTEM_NAME}" + --sysroot="${CMAKE_SYSROOT}" + ) + if (DEFINED FFmpeg_CROSS_PREFIX) + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}") + else() + message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: \${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}-") + endif() + set(FFmpeg_IS_CROSS_COMPILING TRUE) endif() +endif() - if (NOT APPLE) - # In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so - if(PLATFORM_SUN) - find_library(LIBDRM_LIB libdrm PATHS /usr/lib/64 /usr/lib/amd64 /usr/lib) - if(LIBDRM_LIB) +if (PLATFORM_PS4) + list(APPEND FFmpeg_HWACCEL_FLAGS + --disable-vaapi + ) +elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING) + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBVA libva) + pkg_check_modules(CUDA cuda) + pkg_check_modules(FFNVCODEC ffnvcodec) + pkg_check_modules(VDPAU vdpau) + + find_package(X11) + if(X11_FOUND) + if (NOT APPLE) + # In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so + if(PLATFORM_SUN) list(APPEND FFmpeg_HWACCEL_LIBRARIES X11 - "${LIBDRM_LIB}") - message(STATUS "Found libdrm at: ${LIBDRM_LIB}") + "${CMAKE_SYSROOT}/usr/lib/xorg/amd64/libdrm.so") else() - message(WARNING "libdrm not found, disabling libdrm support") - list(APPEND FFmpeg_HWACCEL_FLAGS - --disable-libdrm) + pkg_check_modules(LIBDRM libdrm REQUIRED) + list(APPEND FFmpeg_HWACCEL_LIBRARIES + ${LIBDRM_LIBRARIES}) + list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS + ${LIBDRM_INCLUDE_DIRS}) endif() - else() - pkg_check_modules(LIBDRM libdrm REQUIRED) - list(APPEND FFmpeg_HWACCEL_LIBRARIES - ${LIBDRM_LIBRARIES}) - list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS - ${LIBDRM_INCLUDE_DIRS}) list(APPEND FFmpeg_HWACCEL_FLAGS --enable-libdrm) endif() - endif() - - if(LIBVA_FOUND) - find_package(X11 REQUIRED) - pkg_check_modules(LIBVA-DRM libva-drm REQUIRED) - pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED) - list(APPEND FFmpeg_HWACCEL_LIBRARIES - ${X11_LIBRARIES} - ${LIBVA-DRM_LIBRARIES} - ${LIBVA-X11_LIBRARIES} - ${LIBVA_LIBRARIES}) - list(APPEND FFmpeg_HWACCEL_FLAGS - --enable-hwaccel=h264_vaapi - --enable-hwaccel=vp8_vaapi - --enable-hwaccel=vp9_vaapi) - list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS - ${X11_INCLUDE_DIRS} - ${LIBVA-DRM_INCLUDE_DIRS} - ${LIBVA-X11_INCLUDE_DIRS} - ${LIBVA_INCLUDE_DIRS} - ) - message(STATUS "ffmpeg: va-api libraries version ${LIBVA_VERSION} found") + if(LIBVA_FOUND) + pkg_check_modules(LIBVA-DRM libva-drm REQUIRED) + pkg_check_modules(LIBVA-X11 libva-x11 REQUIRED) + list(APPEND FFmpeg_HWACCEL_LIBRARIES + ${X11_LIBRARIES} + ${LIBVA-DRM_LIBRARIES} + ${LIBVA-X11_LIBRARIES} + ${LIBVA_LIBRARIES}) + list(APPEND FFmpeg_HWACCEL_FLAGS + --enable-hwaccel=h264_vaapi + --enable-hwaccel=vp8_vaapi + --enable-hwaccel=vp9_vaapi) + list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS + ${X11_INCLUDE_DIRS} + ${LIBVA-DRM_INCLUDE_DIRS} + ${LIBVA-X11_INCLUDE_DIRS} + ${LIBVA_INCLUDE_DIRS} + ) + message(STATUS "ffmpeg: va-api libraries version ${LIBVA_VERSION} found") + else() + list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) + message(WARNING "ffmpeg: libva-dev not found, disabling Video Acceleration API (VA-API)...") + endif() else() list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) - message(WARNING "ffmpeg: libva-dev not found, disabling Video Acceleration API (VA-API)...") + message(WARNING "ffmpeg: X11 libraries not found, disabling VA-API...") endif() if (FFNVCODEC_FOUND) @@ -111,6 +154,23 @@ if (UNIX AND NOT ANDROID) endif() endif() +if (PLATFORM_PS4) + list(APPEND FFmpeg_CROSS_COMPILE_LIBS + -lc + -lkernel + -lSceUserService + -lSceSysmodule + -lSceNet + -lSceLibcInternal + ) + list(APPEND FFmpeg_CROSS_COMPILE_FLAGS + --disable-pthreads + --extra-cflags=${CMAKE_SYSROOT}/usr/include + --extra-cxxflags=${CMAKE_SYSROOT}/usr/include + --extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}" + ) +endif() + if (YUZU_USE_BUNDLED_FFMPEG) AddJsonPackage(ffmpeg-ci) @@ -181,24 +241,6 @@ else() find_program(BASH_PROGRAM bash REQUIRED) - set(FFmpeg_CROSS_COMPILE_FLAGS "") - if (ANDROID) - string(TOLOWER "${CMAKE_HOST_SYSTEM_NAME}" FFmpeg_HOST_SYSTEM_NAME) - set(TOOLCHAIN "${ANDROID_NDK}/toolchains/llvm/prebuilt/${FFmpeg_HOST_SYSTEM_NAME}-${CMAKE_HOST_SYSTEM_PROCESSOR}") - set(SYSROOT "${TOOLCHAIN}/sysroot") - set(FFmpeg_CPU "armv8-a") - list(APPEND FFmpeg_CROSS_COMPILE_FLAGS - --arch=arm64 - #--cpu=${FFmpeg_CPU} - --enable-cross-compile - --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- - --sysroot=${SYSROOT} - --target-os=android - --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" - --extra-ldflags="-nostdlib" - ) - endif() - # `configure` parameters builds only exactly what yuzu needs from FFmpeg # `--disable-vdpau` is needed to avoid linking issues set(FFmpeg_CC ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER}) @@ -221,8 +263,12 @@ else() --enable-decoder=vp9 --enable-filter=yadif,scale --enable-pic - --cc="${FFmpeg_CC}" - --cxx="${FFmpeg_CXX}" + --cc=${FFmpeg_CC} + --cxx=${FFmpeg_CXX} + --ld=${CMAKE_LINKER} + --extra-cflags=${CMAKE_C_FLAGS} + --extra-cxxflags=${CMAKE_CXX_FLAGS} + --extra-ldflags=${CMAKE_C_LINK_FLAGS} ${FFmpeg_HWACCEL_FLAGS} ${FFmpeg_CROSS_COMPILE_FLAGS} WORKING_DIRECTORY @@ -254,7 +300,7 @@ else() OUTPUT ${FFmpeg_BUILD_LIBRARIES} COMMAND - make ${FFmpeg_MAKE_ARGS} + gmake ${FFmpeg_MAKE_ARGS} WORKING_DIRECTORY ${FFmpeg_BUILD_DIR} ) diff --git a/externals/ps4sup/emutls.c b/externals/ps4sup/emutls.c new file mode 100644 index 0000000000..3b0a0cc909 --- /dev/null +++ b/externals/ps4sup/emutls.c @@ -0,0 +1,181 @@ +/* ===---------- 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 +#include +#include +#include + +//#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]; +} diff --git a/externals/ps4sup/stub.cpp b/externals/ps4sup/stub.cpp new file mode 100644 index 0000000000..ebaef27acc --- /dev/null +++ b/externals/ps4sup/stub.cpp @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#define STUB_WEAK(name) \ + extern "C" void name() { \ + printf("called " #name); \ + asm volatile("ud2"); \ + } + +extern "C" int __pthread_cxa_finalize(); +extern "C" void __cxa_thread_atexit_impl() { + //printf("__cxa_thread_atexit_impl called!\n"); + //__pthread_cxa_finalize(); +} + +STUB_WEAK(__assert) +STUB_WEAK(ZSTD_trace_compress_begin) +STUB_WEAK(ZSTD_trace_compress_end) +STUB_WEAK(ZSTD_trace_decompress_begin) +STUB_WEAK(ZSTD_trace_decompress_end) + +FILE* __stderrp = stdout; +FILE* __stdinp = stdin; + +#undef STUB_WEAK + +// THIS MAKES STD::COUT AND SUCH WORK :) +#include +std::ios_base::Init init; diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 4f8cc7d1ca..3a3f937107 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2018 yuzu Emulator Project @@ -256,4 +256,10 @@ else() target_compile_definitions(audio_core PRIVATE HAVE_SDL2) endif() +if(PLATFORM_PS4) + target_sources(audio_core PRIVATE + sink/ps4_sink.cpp + sink/ps4_sink.h) +endif() + create_target_directory_groups(audio_core) diff --git a/src/audio_core/sink/ps4_sink.cpp b/src/audio_core/sink/ps4_sink.cpp new file mode 100644 index 0000000000..42b151f49f --- /dev/null +++ b/src/audio_core/sink/ps4_sink.cpp @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include +#include + +#include + +#include "audio_core/common/common.h" +#include "audio_core/sink/ps4_sink.h" +#include "audio_core/sink/sink_stream.h" +#include "common/logging/log.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_; + + auto const length = 0x800; + auto const sample_rate = 48000; + auto const num_channels = this->GetDeviceChannels(); + output_buffer.resize(length * num_channels * sizeof(s16)); + + auto const param_type = num_channels == 1 ? ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO : ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO; + audio_dev = sceAudioOutOpen(ORBIS_USER_SERVICE_USER_ID_SYSTEM, ORBIS_AUDIO_OUT_PORT_TYPE_MAIN, 0, length, sample_rate, param_type); + if (audio_dev > 0) { + audio_thread = std::jthread([=, this](std::stop_token stop_token) { + while (!stop_token.stop_requested()) { + if (this->type == StreamType::In) { + // this->ProcessAudioIn(input_buffer, length); + } else { + sceAudioOutOutput(audio_dev, nullptr); + this->ProcessAudioOutAndRender(output_buffer, length); + sceAudioOutOutput(audio_dev, output_buffer.data()); + } + } + }); + } 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); + sceAudioOutClose(audio_dev); + if (audio_thread.joinable()) { + audio_thread.request_stop(); + audio_thread.join(); + } + } + + void Finalize() override { + if (audio_dev > 0) { + Stop(); + sceAudioOutClose(audio_dev); + } + } + + void Start(bool resume = false) override { + if (audio_dev > 0 && paused) { + paused = false; + } + } + + void Stop() override { + if (audio_dev > 0 && !paused) { + + } + } + + std::vector output_buffer; + 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(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 ListPS4SinkDevices(bool capture) { + return {{"Default"}}; +} + +u32 GetPS4Latency() { + return TargetSampleCount * 2; +} + +} // namespace AudioCore::Sink diff --git a/src/audio_core/sink/ps4_sink.h b/src/audio_core/sink/ps4_sink.h new file mode 100644 index 0000000000..cfc8c293bb --- /dev/null +++ b/src/audio_core/sink/ps4_sink.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2025 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 +#include + +#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 sink_streams; +}; + +std::vector ListPS4SinkDevices(bool capture); +u32 GetPS4Latency(); + +} // namespace AudioCore::Sink diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 70bf75018b..d8a80b9319 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp @@ -19,6 +19,9 @@ #ifdef HAVE_SDL2 #include "audio_core/sink/sdl2_sink.h" #endif +#ifdef __OPENORBIS__ +#include "audio_core/sink/ps4_sink.h" +#endif #include "audio_core/sink/null_sink.h" #include "common/logging/log.h" #include "common/settings_enums.h" @@ -51,6 +54,16 @@ struct SinkDetails { // sink_details is ordered in terms of desirability, with the best choice at the top. constexpr SinkDetails sink_details[] = { +#ifdef __OPENORBIS__ + SinkDetails{ + Settings::AudioEngine::Ps4, + [](std::string_view device_id) -> std::unique_ptr { + return std::make_unique(device_id); + }, + &ListPS4SinkDevices, + &GetPS4Latency, + }, +#endif #ifdef HAVE_OBOE SinkDetails{ Settings::AudioEngine::Oboe, @@ -115,7 +128,9 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) { // 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 // causes audio issues, in that case go with SDL. -#if defined(HAVE_CUBEB) && defined(HAVE_SDL2) +#if defined(__OPENORBIS__) + iter = find_backend(Settings::AudioEngine::Ps4); +#elif defined(HAVE_CUBEB) && defined(HAVE_SDL2) iter = find_backend(Settings::AudioEngine::Cubeb); if (iter->latency() > TargetSampleCount * 3) { iter = find_backend(Settings::AudioEngine::Sdl2); diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index 69eca732eb..4f3dad7e88 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -16,7 +16,7 @@ namespace Common { #ifdef __OPENORBIS__ -constexpr size_t DEFAULT_STACK_SIZE = 128 * 4096; +constexpr size_t DEFAULT_STACK_SIZE = 32 * 4096; #else constexpr size_t DEFAULT_STACK_SIZE = 512 * 4096; #endif diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp index 174aed49b8..bdeeae911d 100644 --- a/src/common/fs/fs.cpp +++ b/src/common/fs/fs.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -171,11 +174,14 @@ bool CreateDir(const fs::path& path) { return false; } + // TODO: Maybe this is what causes death? +#ifndef __OPENORBIS__ if (!Exists(path.parent_path())) { LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist", PathToUTF8String(path)); return false; } +#endif if (IsDir(path)) { LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory", diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 3f928ac327..0cacb50596 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -130,6 +130,10 @@ public: ASSERT(!eden_path.empty()); eden_path_cache = eden_path / CACHE_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 eden_path = GetCurrentDir() / PORTABLE_DIR; if (!Exists(eden_path) || !IsDir(eden_path)) { diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index a75152eec0..6de3e64474 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -30,19 +30,21 @@ #include #include #include +#elif defined(__OPENORBIS__) +#include #endif // FreeBSD #ifndef MAP_NORESERVE -#define MAP_NORESERVE 0 +# define MAP_NORESERVE 0 #endif // Solaris 11 and illumos #ifndef MAP_ALIGNED_SUPER -#define MAP_ALIGNED_SUPER 0 +# define MAP_ALIGNED_SUPER 0 #endif // macOS #ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON +# define MAP_ANONYMOUS MAP_ANON #endif #endif // ^^^ POSIX ^^^ @@ -433,13 +435,15 @@ static void* ChooseVirtualBase(size_t virtual_size) { #else -static void* ChooseVirtualBase(size_t virtual_size) { +static void* ChooseVirtualBase(size_t size) { #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__) - void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0); + void* virtual_base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0); if (virtual_base != MAP_FAILED) 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 - return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); } #endif @@ -495,9 +499,10 @@ class HostMemory::Impl { public: explicit Impl(size_t backing_size_, size_t virtual_size_) : backing_size{backing_size_}, virtual_size{virtual_size_} { +#if !defined(__OPENORBIS__) && !defined(__APPLE__) long page_size = sysconf(_SC_PAGESIZE); - ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", - page_size); + ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size); +#endif // Backing memory initialization #if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__) fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600); @@ -670,6 +675,13 @@ private: #endif // ^^^ POSIX ^^^ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) : backing_size(backing_size_), virtual_size(virtual_size_) { +#ifdef __OPENORBIS__ + Common::InitSwap(); + LOG_WARNING(HW_Memory, "Platform doesn't support fastmem"); + fallback_buffer.emplace(backing_size); + backing_base = fallback_buffer->data(); + virtual_base = nullptr; +#else // Try to allocate a fastmem arena. // The implementation will fail with std::bad_alloc on errors. impl = std::make_unique(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize); @@ -680,6 +692,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) : backing_siz virtual_base = reinterpret_cast(Common::AlignUp(uintptr_t(virtual_base), HugePageSize)); virtual_base_offset = virtual_base - impl->virtual_base; } +#endif } HostMemory::~HostMemory() = default; diff --git a/src/common/host_memory.h b/src/common/host_memory.h index 8dd30aa9de..6095230154 100644 --- a/src/common/host_memory.h +++ b/src/common/host_memory.h @@ -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-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -7,6 +7,7 @@ #pragma once #include +#include #include "common/common_funcs.h" #include "common/common_types.h" #include "common/virtual_buffer.h" @@ -82,6 +83,9 @@ private: u8* backing_base{}; u8* virtual_base{}; size_t virtual_base_offset{}; +#ifdef __OPENORBIS__ + std::optional> fallback_buffer; +#endif }; } // namespace Common diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index dee1f770bc..3a0e69e79d 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -41,6 +41,9 @@ static void PrintMessage(const Entry& entry) noexcept { #ifdef _WIN32 auto const str = FormatLogMessage(entry).append(1, '\n'); fwrite(str.c_str(), 1, str.size(), stderr); +#elif defined(__OPENORBIS__) + auto const str = FormatLogMessage(entry).append(1, '\n'); + fwrite(str.c_str(), 1, str.size(), stderr); #else #define ESC "\x1b" auto const color_str = [&entry]() -> const char* { diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index 86a3abcc6d..5063b28b2d 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ // clang-format on #else #include -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || (defined(__FreeBSD__) && !defined(__OPENORBIS__)) #include #elif defined(__linux__) #include @@ -43,6 +46,8 @@ static MemoryInfo Detect() { sysctlbyname("vm.swapusage", &vmusage, &sizeof_vmusage, nullptr, 0); mem_info.TotalPhysicalMemory = ramsize; mem_info.TotalSwapMemory = vmusage.xsu_total; +#elif defined(__OPENORBIS__) + mem_info.TotalPhysicalMemory = mem_info.TotalSwapMemory = 0; #elif defined(__FreeBSD__) u_long physmem, swap_total; std::size_t sizeof_u_long = sizeof(u_long); diff --git a/src/common/settings.h b/src/common/settings.h index 8cd55bcdd3..e2d91783d3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -196,7 +196,14 @@ struct Values { linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; // Core - SwitchableSetting use_multi_core{linkage, true, "use_multi_core", Category::Core}; + SwitchableSetting use_multi_core{linkage, +#ifdef __OPENORBIS__ + // Re-enable once proper TLS support is added + false, +#else + true, +#endif + "use_multi_core", Category::Core}; SwitchableSetting memory_layout_mode{linkage, MemoryLayout::Memory_4Gb, "memory_layout_mode", @@ -332,7 +339,7 @@ struct Values { // Renderer SwitchableSetting renderer_backend{linkage, -#if defined(__sun__) || defined(__managarm__) +#if defined(__sun__) || defined(__managarm__) || defined(__OPENORBIS__) RendererBackend::OpenGL_GLSL, #else RendererBackend::Vulkan, diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 638be4127f..d82f4dc90a 100644 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -92,12 +92,13 @@ struct EnumMetadata { // AudioEngine must be specified discretely due to having existing but slightly different // canonicalizations // TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id -enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, }; +enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, Ps4 }; template<> inline std::vector> EnumMetadata::Canonicalizations() { return { {"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2}, {"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 diff --git a/src/common/virtual_buffer.cpp b/src/common/virtual_buffer.cpp index 55ddfc243a..8acb75fc57 100644 --- a/src/common/virtual_buffer.cpp +++ b/src/common/virtual_buffer.cpp @@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -10,30 +10,191 @@ #include #endif +#ifdef __OPENORBIS__ +#include +#include +#include +#include +#include +#include +typedef void (*SceKernelExceptionHandler)(int32_t, void*); +extern "C" int32_t sceKernelInstallExceptionHandler(int32_t signum, SceKernelExceptionHandler handler); +#endif + #include "common/assert.h" #include "common/virtual_buffer.h" +#include "common/logging/log.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 { -void* AllocateMemoryPages(std::size_t size) noexcept { -#ifdef _WIN32 - void* base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); -#else - void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - if (base == MAP_FAILED) - base = nullptr; -#endif - ASSERT(base); - return base; +#ifdef __OPENORBIS__ + +namespace Orbis { +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]; +}; } -void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept { - if (!base) +static boost::container::static_vector, 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 = 0x200000; //2M + size_t const page_mask = ~(page_size - 1); + // should replace the existing mapping... ugh + void* aligned_addr = reinterpret_cast(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_TRACE(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 { +#ifdef _WIN32 + void* addr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); + ASSERT(addr != nullptr); +#elif defined(__OPENORBIS__) + bool use_void_mem = true; + void* addr = nullptr; + if (size <= 8192 * 4096) { + size_t align = 16384; + 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 { + sceKernelReleaseDirectMemory(offset, size); + 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 + void* addr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + ASSERT(addr != MAP_FAILED); +#endif + return addr; +} + +void FreeMemoryPages(void* addr, [[maybe_unused]] std::size_t size) noexcept { + if (!addr) return; #ifdef _WIN32 - ASSERT(VirtualFree(base, 0, MEM_RELEASE)); + VirtualFree(addr, 0, MEM_RELEASE); +#elif defined(__OPENORBIS__) + if (size <= 8192 * 4096) { + sceKernelCheckedReleaseDirectMemory(off_t(addr), size_t(size)); + } else { + int rc = munmap(addr, size); + ASSERT(rc == 0); + } #else - ASSERT(munmap(base, size) == 0); + int rc = munmap(addr, size); + ASSERT(rc == 0); #endif } diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h index 4f6e3e6e5c..8094df14c7 100644 --- a/src/common/virtual_buffer.h +++ b/src/common/virtual_buffer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,6 +12,7 @@ namespace Common { void* AllocateMemoryPages(std::size_t size) noexcept; void FreeMemoryPages(void* base, std::size_t size) noexcept; +void InitSwap() noexcept; template class VirtualBuffer final { @@ -32,9 +36,10 @@ public: VirtualBuffer(const VirtualBuffer&) = delete; VirtualBuffer& operator=(const VirtualBuffer&) = delete; - VirtualBuffer(VirtualBuffer&& other) noexcept - : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), - nullptr} {} + VirtualBuffer(VirtualBuffer&& other) noexcept { + alloc_size = std::exchange(other.alloc_size, 0); + base_ptr = std::exchange(other.base_ptr, nullptr); + } VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { alloc_size = std::exchange(other.alloc_size, 0); diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 912a15475b..5f082e38bf 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -202,7 +202,9 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) { config.enable_cycle_counting = !m_uses_wall_clock; // Code cache size -#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +#if defined(__OPENORBIS__) + 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); #else config.code_cache_size = std::uint32_t(512_MiB); @@ -286,7 +288,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) { // Curated optimizations case Settings::CpuAccuracy::Auto: config.unsafe_optimizations = true; -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OPENORBIS__) config.fastmem_pointer = std::nullopt; config.fastmem_exclusive_access = false; #endif diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 55584d0e38..0842f439b8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -254,7 +254,9 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s config.enable_cycle_counting = !m_uses_wall_clock; // Code cache size -#if defined(ARCHITECTURE_arm64) || defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) +#if defined(__OPENORBIS__) + 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); #else config.code_cache_size = std::uint32_t(512_MiB); diff --git a/src/core/hle/service/ns/platform_service_manager.cpp b/src/core/hle/service/ns/platform_service_manager.cpp index e91e6451a8..4caea67d2c 100644 --- a/src/core/hle/service/ns/platform_service_manager.cpp +++ b/src/core/hle/service/ns/platform_service_manager.cpp @@ -34,12 +34,13 @@ struct FontRegion { // The below data is specific to shared font data dumped from Switch on f/w 2.2 // Virtual address and offsets/sizes likely will vary by dump -[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; -constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be -constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be -constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; +[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR = 0x00000009d3016000ULL; +constexpr u32 EXPECTED_RESULT = 0x7f9a0218; // What we expect the decrypted bfttf first 4 bytes to be +constexpr u32 EXPECTED_MAGIC = 0x36f81a1e; // What we expect the encrypted bfttf first 4 bytes to be +constexpr u64 SHARED_FONT_MEM_SIZE = 0x1100000; constexpr FontRegion EMPTY_REGION{0, 0}; +#ifndef __OPENORBIS__ static void DecryptSharedFont(const std::span input, std::span output, std::size_t& offset) { ASSERT(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE && "Shared fonts exceeds 17mb!"); ASSERT(input[0] == EXPECTED_MAGIC && "Failed to derive key, unexpected magic number"); @@ -51,6 +52,7 @@ static void DecryptSharedFont(const std::span input, std::span ou std::memcpy(output.data() + offset, transformed_font.data(), transformed_font.size() * sizeof(u32)); offset += transformed_font.size() * sizeof(u32); } +#endif void DecryptSharedFontToTTF(const std::vector& input, std::vector& output) { ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); @@ -70,7 +72,7 @@ void EncryptSharedFont(const std::vector& input, std::vector& output, s const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC); std::vector transformed_font(input.size() + 2); transformed_font[0] = Common::swap32(EXPECTED_MAGIC); - transformed_font[1] = Common::swap32(static_cast(input.size() * sizeof(u32))) ^ key; + transformed_font[1] = Common::swap32(u32(input.size() * sizeof(u32))) ^ key; std::transform(input.begin(), input.end(), transformed_font.begin() + 2, [key](u32 in) { return in ^ key; }); std::memcpy(output.data() + offset, transformed_font.data(), transformed_font.size() * sizeof(u32)); offset += transformed_font.size() * sizeof(u32); @@ -83,8 +85,10 @@ struct IPlatformServiceManager::Impl { // Automatically populated based on shared_fonts dump or system archives. // 6 builtin fonts + extra 2 for whatever may come after boost::container::static_vector shared_font_regions; +#ifndef __OPENORBIS__ /// Backing memory for the shared font data std::array shared_font; +#endif }; IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_) @@ -111,49 +115,40 @@ IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const ch // clang-format on RegisterHandlers(functions); +#ifndef __OPENORBIS__ auto& fsc = system.GetFileSystemController(); - // Attempt to load shared font data from disk const auto* nand = fsc.GetSystemNANDContents(); std::size_t offset = 0; // Rebuild shared fonts from data ncas or synthesize for (auto& font : SHARED_FONTS) { FileSys::VirtualFile romfs; - const auto nca = - nand->GetEntry(static_cast(font.first), FileSys::ContentRecordType::Data); - if (nca) { + if (auto const nca = nand->GetEntry(u64(font.first), FileSys::ContentRecordType::Data); nca) romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast(font.first)); - } - - if (!romfs) { + if (!romfs) + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(u64(font.first)); + if (romfs) { + if (auto const extracted_romfs = FileSys::ExtractRomFS(romfs); extracted_romfs) { + if (auto const font_fp = extracted_romfs->GetFile(font.second); font_fp) { + std::vector font_data_u32(font_fp->GetSize() / sizeof(u32)); + font_fp->ReadBytes(font_data_u32.data(), font_fp->GetSize()); + // We need to be BigEndian as u32s for the xor encryption + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), Common::swap32); + // Font offset and size do not account for the header + const FontRegion region{u32(offset + 8), u32((font_data_u32.size() * sizeof(u32)) - 8)}; + DecryptSharedFont(font_data_u32, impl->shared_font, offset); + impl->shared_font_regions.push_back(region); + } else { + LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); + } + } else { + LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); + } + } else { LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first); - continue; } - - const auto extracted_romfs = FileSys::ExtractRomFS(romfs); - if (!extracted_romfs) { - LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first); - continue; - } - const auto font_fp = extracted_romfs->GetFile(font.second); - if (!font_fp) { - LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second); - continue; - } - std::vector font_data_u32(font_fp->GetSize() / sizeof(u32)); - font_fp->ReadBytes(font_data_u32.data(), font_fp->GetSize()); - // We need to be BigEndian as u32s for the xor encryption - std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), - Common::swap32); - // Font offset and size do not account for the header - const FontRegion region{u32(offset + 8), u32((font_data_u32.size() * sizeof(u32)) - 8)}; - DecryptSharedFont(font_data_u32, impl->shared_font, offset); - impl->shared_font_regions.push_back(region); } +#endif } IPlatformServiceManager::~IPlatformServiceManager() = default; @@ -187,8 +182,10 @@ Result IPlatformServiceManager::GetSharedMemoryNativeHandle(OutCopyHandleshared_font.data(), impl->shared_font.size()); +#endif // FIXME: this shouldn't belong to the kernel *out_shared_memory_native_handle = &kernel.GetFontSharedMem(); diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index 636f54ad49..bf0a89dfef 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -73,6 +73,13 @@ Services::Services(std::shared_ptr& sm, Core::System& system system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); +#ifdef __OPENORBIS__ + // PS4 requires us to run this on a single thread so we don't immediately die + bool const run_on_host = false; +#else + bool const run_on_host = true; +#endif + // Just a quick C++ lesson // Capturing lambdas will silently create new variables for the objects referenced via = // and create a `auto&` sorts of for `&`; with all your usual reference shenanigans. @@ -92,9 +99,12 @@ Services::Services(std::shared_ptr& sm, Core::System& system {"Loader", &LDR::LoopProcess}, {"nvservices", &Nvidia::LoopProcess}, {"bsdsocket", &Sockets::LoopProcess}, - }) - kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach(); - kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); + }) { + if (run_on_host) kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach(); + else kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); + } + if (run_on_host) kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); + else kernel.RunOnGuestCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }); // Avoid cold clones of lambdas -- succintly for (auto const& e : std::vector>{ {"sm", &SM::LoopProcess}, diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index dd6becc02d..fef83b2d4b 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -60,7 +63,11 @@ private: std::size_t current_index{0}; /// Stores an hour of historical frametime data useful for processing and tracking performance /// regressions with code changes. +#ifdef __OPENORBIS__ + std::array perf_history{}; +#else std::array perf_history{}; +#endif /// Point when the cumulative counters were reset Clock::time_point reset_point = Clock::now(); diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp index 9f508f72e5..cf56288a82 100644 --- a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp +++ b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp @@ -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 /* This file is part of the dynarmic project. @@ -59,12 +59,16 @@ public: signal_stack_size = std::max(SIGSTKSZ, 2 * 1024 * 1024); signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#ifdef __OPENORBIS__ + fmt::print(stderr, "no fastmem on PS4\n"); + supports_fast_mem = false; +#else stack_t signal_stack{}; signal_stack.ss_sp = signal_stack_memory; signal_stack.ss_size = signal_stack_size; signal_stack.ss_flags = 0; if (sigaltstack(&signal_stack, nullptr) != 0) { - fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n"); + fmt::print(stderr, "POSIX SigHandler: init failure at sigaltstack\n"); supports_fast_mem = false; return; } @@ -75,16 +79,17 @@ public: sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART; sigemptyset(&sa.sa_mask); if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) { - fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n"); + fmt::print(stderr, "POSIX SigHandler: could not set SIGSEGV handler\n"); supports_fast_mem = false; return; } -#ifdef __APPLE__ +# ifdef __APPLE__ if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) { - fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n"); + fmt::print(stderr, "POSIX SigHandler: could not set SIGBUS handler\n"); supports_fast_mem = false; return; } +# endif #endif } @@ -145,6 +150,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { # error "Invalid architecture" #endif +#ifdef __OPENORBIS__ + // No fastmem +#else struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus; if (retry_sa->sa_flags & SA_SIGINFO) { retry_sa->sa_sigaction(sig, info, raw_context); @@ -158,6 +166,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { return; } retry_sa->sa_handler(sig); +#endif } } // anonymous namespace diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h index 5ec78ff50e..6f5a97a903 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h @@ -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 /* This file is part of the dynarmic project. @@ -66,8 +66,14 @@ public: const void* code_ptr = nullptr; }; static_assert(sizeof(FastDispatchEntry) == 0x10); +#ifdef __OPENORBIS__ + static constexpr u64 fast_dispatch_table_mask = 0xFF0; + static constexpr size_t fast_dispatch_table_size = 0x100; +#else static constexpr u64 fast_dispatch_table_mask = 0xFFFF0; static constexpr size_t fast_dispatch_table_size = 0x10000; +#endif + void ClearFastDispatchTable(); void GenFastmemFallbacks(); void GenTerminalHandlers(); diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h index dd556e36ce..e8f46d0580 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h @@ -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 /* This file is part of the dynarmic project. @@ -59,8 +59,13 @@ public: const void* code_ptr = nullptr; }; static_assert(sizeof(FastDispatchEntry) == 0x10); +#ifdef __OPENORBIS__ + static constexpr u64 fast_dispatch_table_mask = 0xFF0; + static constexpr size_t fast_dispatch_table_size = 0x100; +#else static constexpr u64 fast_dispatch_table_mask = 0xFFFFF0; static constexpr size_t fast_dispatch_table_size = 0x100000; +#endif void ClearFastDispatchTable(); void GenMemory128Accessors(); diff --git a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp index 28c821ab59..9b0e44b19f 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.cpp @@ -67,8 +67,12 @@ public: uint8_t* alloc(size_t size) override { void* p = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); if (p == nullptr) { +#ifndef XBYAK_NO_EXCEPTION using Xbyak::Error; XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); +#else + std::abort(); +#endif } return static_cast(p); } @@ -106,8 +110,12 @@ public: #endif void* p = mmap(nullptr, size, prot, mode, -1, 0); if (p == MAP_FAILED) { +#ifndef XBYAK_NO_EXCEPTION using Xbyak::Error; XBYAK_THROW(Xbyak::ERR_CANT_ALLOC); +#else + std::abort(); +#endif } std::memcpy(p, &size, sizeof(size_t)); return static_cast(p) + DYNARMIC_PAGE_SIZE; @@ -233,18 +241,16 @@ bool IsUnderRosetta() { } // anonymous namespace -#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT -static const auto default_cg_mode = Xbyak::DontSetProtectRWE; +BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function rcp) noexcept +#if defined(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT) + : Xbyak::CodeGenerator(total_code_size, Xbyak::DontSetProtectRWE, &s_allocator) #else -static const auto default_cg_mode = nullptr; //Allow RWE + : Xbyak::CodeGenerator(total_code_size, nullptr, &s_allocator) #endif - -BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function rcp) - : Xbyak::CodeGenerator(total_code_size, default_cg_mode, &s_allocator) - , cb(std::move(cb)) - , jsi(jsi) - , constant_pool(*this, CONSTANT_POOL_SIZE) - , host_features(GetHostFeatures()) { + , cb(std::move(cb)) + , jsi(jsi) + , constant_pool(*this, CONSTANT_POOL_SIZE) + , host_features(GetHostFeatures()) { EnableWriting(); EnsureMemoryCommitted(PRELUDE_COMMIT_SIZE); GenRunCode(rcp); @@ -533,8 +539,12 @@ size_t BlockOfCode::GetTotalCodeSize() const { void* BlockOfCode::AllocateFromCodeSpace(size_t alloc_size) { if (size_ + alloc_size >= maxSize_) { +#ifndef XBYAK_NO_EXCEPTION using Xbyak::Error; XBYAK_THROW(Xbyak::ERR_CODE_IS_TOO_BIG); +#else + std::abort(); +#endif } EnsureMemoryCommitted(alloc_size); diff --git a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h index 5ccab7a3ed..315833645c 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h +++ b/src/dynarmic/src/dynarmic/backend/x64/block_of_code.h @@ -38,8 +38,8 @@ struct RunCodeCallbacks { class BlockOfCode final : public Xbyak::CodeGenerator { public: - BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function rcp); - BlockOfCode(const BlockOfCode&) = delete; + BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function rcp) noexcept; + BlockOfCode(const BlockOfCode&) noexcept = delete; /// Call when external emitters have finished emitting their preludes. void PreludeComplete(); diff --git a/src/dynarmic/src/dynarmic/common/context.h b/src/dynarmic/src/dynarmic/common/context.h index 941289cb94..cce77a6a97 100644 --- a/src/dynarmic/src/dynarmic/common/context.h +++ b/src/dynarmic/src/dynarmic/common/context.h @@ -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 #pragma once @@ -60,7 +60,7 @@ # elif defined(__linux__) # define CTX_RIP (mctx.gregs[REG_RIP]) # define CTX_RSP (mctx.gregs[REG_RSP]) -# elif defined(__FreeBSD__) +# elif defined(__FreeBSD__) || defined(__DragonFly__) # define CTX_RIP (mctx.mc_rip) # define CTX_RSP (mctx.mc_rsp) # elif defined(__NetBSD__) @@ -72,9 +72,9 @@ # elif defined(__sun__) # define CTX_RIP (mctx.gregs[REG_RIP]) # define CTX_RSP (mctx.gregs[REG_RSP]) -# elif defined(__DragonFly__) -# define CTX_RIP (mctx.mc_rip) -# define CTX_RSP (mctx.mc_rsp) +# elif defined(__OPENORBIS__) +# define CTX_RIP (mctx.gregs[REG_RIP]) +# define CTX_RSP (mctx.gregs[REG_RSP]) # else # error "Unknown platform" # endif @@ -97,7 +97,7 @@ # define CTX_Q(i) (fpctx->vregs[i]) # define CTX_FPSR (fpctx->fpsr) # define CTX_FPCR (fpctx->fpcr) -# elif defined(__FreeBSD__) +# elif defined(__FreeBSD__) || defined(__DragonFly__) # define CTX_PC (mctx.mc_gpregs.gp_elr) # define CTX_SP (mctx.mc_gpregs.gp_sp) # define CTX_LR (mctx.mc_gpregs.gp_lr) diff --git a/src/dynarmic/tests/CMakeLists.txt b/src/dynarmic/tests/CMakeLists.txt index 395181efe3..a31d88dfd0 100644 --- a/src/dynarmic/tests/CMakeLists.txt +++ b/src/dynarmic/tests/CMakeLists.txt @@ -134,3 +134,9 @@ target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS}) target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1) add_test(NAME dynarmic_tests COMMAND dynarmic_tests --durations yes) + +if (PLATFORM_PS4) + target_link_libraries(dynarmic_tests PRIVATE SceVideoOut SceAudioOut ScePad SceSystemService) + target_link_libraries(dynarmic_tests PRIVATE ps4sup) + create_ps4_eboot(dynarmic_tests dynarmic_tests IV0000-BREW00090_00-DYNARMICTS000000) +endif() diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index ef1308b1d0..c7c4b11800 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2018 yuzu Emulator Project diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index b04a70590a..3cbc6bbed5 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -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-FileCopyrightText: 2017 Citra Emulator Project @@ -82,7 +82,10 @@ struct InputSubsystem::Impl { #ifdef ENABLE_LIBUSB RegisterEngine("gcpad", gcadapter); #endif +#ifndef __OPENORBIS__ + // TODO: Issue in PS4, crash for UDP_client RegisterEngine("cemuhookudp", udp_client); +#endif RegisterEngine("tas", tas_input); RegisterEngine("camera", camera); #ifdef ANDROID @@ -116,7 +119,9 @@ struct InputSubsystem::Impl { #ifdef ENABLE_LIBUSB UnregisterEngine(gcadapter); #endif +#ifndef __OPENORBIS__ UnregisterEngine(udp_client); +#endif UnregisterEngine(tas_input); UnregisterEngine(camera); #ifdef ANDROID @@ -152,8 +157,10 @@ struct InputSubsystem::Impl { auto gcadapter_devices = gcadapter->GetInputDevices(); devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end()); #endif +#ifndef __OPENORBIS__ auto udp_devices = udp_client->GetInputDevices(); devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); +#endif #ifdef HAVE_SDL2 auto joycon_devices = joycon->GetInputDevices(); devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end()); @@ -186,9 +193,11 @@ struct InputSubsystem::Impl { return gcadapter; } #endif +#ifndef __OPENORBIS__ if (engine == udp_client->GetEngineName()) { return udp_client; } +#endif #ifdef HAVE_SDL2 if (engine == sdl->GetEngineName()) { return sdl; @@ -271,9 +280,11 @@ struct InputSubsystem::Impl { return true; } #endif +#ifndef __OPENORBIS__ if (engine == udp_client->GetEngineName()) { return true; } +#endif if (engine == tas_input->GetEngineName()) { return true; } @@ -300,7 +311,9 @@ struct InputSubsystem::Impl { #ifdef ENABLE_LIBUSB gcadapter->BeginConfiguration(); #endif +#ifndef __OPENORBIS__ udp_client->BeginConfiguration(); +#endif #ifdef HAVE_SDL2 sdl->BeginConfiguration(); joycon->BeginConfiguration(); @@ -316,7 +329,9 @@ struct InputSubsystem::Impl { #ifdef ENABLE_LIBUSB gcadapter->EndConfiguration(); #endif +#ifndef __OPENORBIS__ udp_client->EndConfiguration(); +#endif #ifdef HAVE_SDL2 sdl->EndConfiguration(); joycon->EndConfiguration(); @@ -341,7 +356,9 @@ struct InputSubsystem::Impl { std::shared_ptr mouse; std::shared_ptr touch_screen; std::shared_ptr tas_input; +#ifndef __OPENORBIS__ std::shared_ptr udp_client; +#endif std::shared_ptr camera; std::shared_ptr virtual_amiibo; std::shared_ptr virtual_gamepad; @@ -470,7 +487,9 @@ bool InputSubsystem::IsStickInverted(const Common::ParamPackage& params) const { } void InputSubsystem::ReloadInputDevices() { +#ifndef __OPENORBIS__ impl->udp_client.get()->ReloadSockets(); +#endif } void InputSubsystem::BeginMapping(Polling::InputType type) { diff --git a/src/qt_common/config/uisettings.h b/src/qt_common/config/uisettings.h index 49205c5b84..363b0dad76 100644 --- a/src/qt_common/config/uisettings.h +++ b/src/qt_common/config/uisettings.h @@ -159,7 +159,13 @@ struct Values { Setting enable_discord_presence{linkage, false, "enable_discord_presence", Category::Ui}; // logging - Setting show_console{linkage, false, "showConsole", Category::Ui}; + Setting show_console{linkage, +#ifdef __OPENORBIS__ + true, +#else + false, +#endif + "showConsole", Category::Ui}; // Screenshots Setting enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as", diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h index 2cc0f0d7f0..dbd575bcee 100644 --- a/src/video_core/vulkan_common/vulkan.h +++ b/src/video_core/vulkan_common/vulkan.h @@ -15,6 +15,8 @@ #define VK_USE_PLATFORM_ANDROID_KHR #elif defined(__HAIKU__) #define VK_USE_PLATFORM_XCB_KHR +#elif defined(__OPENORBIS__) +// No fucking vulkan on the PlayStation 4 #else #define VK_USE_PLATFORM_XLIB_KHR #define VK_USE_PLATFORM_WAYLAND_KHR diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp index 47e18dd6a5..2cbc06d80a 100644 --- a/src/video_core/vulkan_common/vulkan_instance.cpp +++ b/src/video_core/vulkan_common/vulkan_instance.cpp @@ -59,6 +59,8 @@ namespace { case Core::Frontend::WindowSystemType::Xcb: extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); break; +#elif defined(__OPENORBIS__) + // No vulkan #else case Core::Frontend::WindowSystemType::X11: extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp index 761b7759c8..4c14c0099d 100644 --- a/src/video_core/vulkan_common/vulkan_surface.cpp +++ b/src/video_core/vulkan_common/vulkan_surface.cpp @@ -76,6 +76,8 @@ vk::SurfaceKHR CreateSurface( throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); } } +#elif defined(__OPENORBIS__) + // No native #else if (window_info.type == Core::Frontend::WindowSystemType::X11) { const VkXlibSurfaceCreateInfoKHR xlib_ci{ diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index a1f16be75c..3b85bc1d28 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -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-FileCopyrightText: 2018 yuzu Emulator Project @@ -39,7 +39,6 @@ target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") target_include_directories(yuzu-cmd PRIVATE ${RESOURCES_DIR}) - target_link_libraries(yuzu-cmd PRIVATE SDL2::SDL2) if(UNIX AND NOT APPLE) @@ -65,3 +64,9 @@ if (NOT MSVC) -Wno-unused-parameter -Wno-missing-field-initializers) endif() + +if (PLATFORM_PS4) + target_link_libraries(yuzu-cmd PRIVATE SceVideoOut SceAudioOut ScePad SceSystemService) + target_link_libraries(yuzu-cmd PRIVATE ps4sup) + create_ps4_eboot(yuzu-cmd eden-cli IV0000-BREW00090_00-EDENEMULAT000000) +endif() diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index f509652bf6..735ef3c327 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -74,6 +77,12 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste window_info.type = Core::Frontend::WindowSystemType::Android; window_info.render_surface = reinterpret_cast(wm.info.android.window); break; +#endif +#ifdef SDL_VIDEO_DRIVER_DIRECTFB + case SDL_SYSWM_TYPE::SDL_SYSWM_DIRECTFB: + window_info.type = Core::Frontend::WindowSystemType::Headless; + window_info.render_surface = reinterpret_cast(wm.info.dfb.window); + break; #endif default: LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index b292b4886b..9e0f2bd57a 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -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-FileCopyrightText: 2014 Citra Emulator Project @@ -16,6 +16,7 @@ #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/settings.h" +#include "common/settings_enums.h" #include "common/string_util.h" #include "core/core.h" #include "core/core_timing.h" @@ -51,7 +52,12 @@ #include #endif -#ifdef _WIN32 +#if defined(__OPENORBIS__) +#include +#include +#include +#include +#elif defined(_WIN32) extern "C" { // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable // graphics @@ -182,6 +188,11 @@ int main(int argc, char** argv) { freopen("CONOUT$", "wb", stderr); } #endif +#ifdef __OPENORBIS__ + // May prevent spurious crashes on swap handlers... + setvbuf(stdout, nullptr, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); +#endif Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true); @@ -224,7 +235,11 @@ int main(int argc, char** argv) { {0, 0, 0, 0}, // clang-format on }; - +#ifdef __OPENORBIS__ + // PS4 will use this path by default UNLESS overriden; this is so users + // can quickly launch whatever they want. + filepath = "/data/eden/games/test.nro"; +#endif while (optind < argc) { int arg = getopt_long(argc, argv, "g:fhvp::c:u:d:", long_options, &option_index); if (arg != -1) { @@ -421,10 +436,16 @@ int main(int argc, char** argv) { [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); } - system.RegisterExitCallback([&] { + auto const exit_fn = [&] { +#ifdef __OPENORBIS__ + sceSystemServiceLoadExec("EXIT", nullptr); +#else // Just exit right away. exit(0); - }); +#endif + }; + system.RegisterExitCallback(exit_fn); + void(system.Run()); if (system.DebuggerEnabled()) { system.InitializeDebugger(); @@ -436,6 +457,7 @@ int main(int argc, char** argv) { void(system.Pause()); system.ShutdownMainProcess(); detached_tasks.WaitForAllTasks(); + exit_fn(); return 0; }