diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh
index 1fe640809c..26fc1dc9d9 100755
--- a/.ci/build-linux-aarch64.sh
+++ b/.ci/build-linux-aarch64.sh
@@ -35,7 +35,7 @@ cmake .. \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SDL=ON \
-DUSE_SYSTEM_SDL=ON \
- -DUSE_SYSTEM_FFMPEG=OFF \
+ -DUSE_SYSTEM_FFMPEG=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh
index 13a9e802f3..d9f66f6a04 100755
--- a/.ci/build-linux.sh
+++ b/.ci/build-linux.sh
@@ -46,7 +46,7 @@ cmake .. \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SDL=ON \
-DUSE_SYSTEM_SDL=ON \
- -DUSE_SYSTEM_FFMPEG=OFF \
+ -DUSE_SYSTEM_FFMPEG=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh
index 9e16fce4ec..ae0341cc0e 100755
--- a/.ci/build-mac.sh
+++ b/.ci/build-mac.sh
@@ -13,31 +13,18 @@ export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
-brew update
brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
-if [ "$AARCH64" -eq 1 ]; then
- brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk
- brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true
-else
- arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader molten-vk
- arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative protobuf || true
-fi
+brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk
+brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true
export CXX=clang++
export CC=clang
export BREW_PATH;
-if [ "$AARCH64" -eq 1 ]; then
- BREW_PATH="$(brew --prefix)"
- export BREW_BIN="/opt/homebrew/bin"
- export BREW_SBIN="/opt/homebrew/sbin"
-else
- BREW_PATH="$("/usr/local/bin/brew" --prefix)"
- export BREW_BIN="/usr/local/bin"
- export BREW_SBIN="/usr/local/sbin"
-fi
+BREW_PATH="$(brew --prefix)"
+export BREW_BIN="$BREW_PATH/bin"
+export BREW_SBIN="$BREW_PATH/sbin"
export WORKDIR;
WORKDIR="$(pwd)"
@@ -68,7 +55,7 @@ ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
export SDL3_DIR="$BREW_PATH/opt/sdl3/lib/cmake/SDL3"
-export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$PATH"
+export PATH="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$PATH"
export LDFLAGS="-L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++ -L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/unwind -lunwind"
export VULKAN_SDK
@@ -82,12 +69,13 @@ LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER"
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules)
mkdir build && cd build || exit 1
+# The below should be uncommented once bugs with Qt 6 QListWidgets when using the OS 26 visual style are resolved.
+# sudo xcode-select -switch /Applications/Xcode_26.3.app/Contents/Developer
-if [ "$AARCH64" -eq 1 ]; then
cmake .. \
-DBUILD_RPCS3_TESTS="${RUN_UNIT_TESTS}" \
-DRUN_RPCS3_TESTS="${RUN_UNIT_TESTS}" \
- -DCMAKE_OSX_DEPLOYMENT_TARGET=14.4 \
+ -DCMAKE_OSX_DEPLOYMENT_TARGET=15.0 \
-DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \
-DMACOSX_BUNDLE_SHORT_VERSION_STRING="${COMM_TAG}" \
-DMACOSX_BUNDLE_BUNDLE_VERSION="${COMM_COUNT}" \
@@ -102,31 +90,8 @@ cmake .. \
-DUSE_SYSTEM_SDL=ON \
-DUSE_SYSTEM_OPENCV=ON \
-G Ninja
-else
-cmake .. \
- -DBUILD_RPCS3_TESTS=OFF \
- -DRUN_RPCS3_TESTS=OFF \
- -DCMAKE_OSX_ARCHITECTURES=x86_64 \
- -DCMAKE_SYSTEM_PROCESSOR=x86_64 \
- -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinX86_64.cmake \
- -DCMAKE_OSX_DEPLOYMENT_TARGET=14.4 \
- -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \
- -DMACOSX_BUNDLE_SHORT_VERSION_STRING="${COMM_TAG}" \
- -DMACOSX_BUNDLE_BUNDLE_VERSION="${COMM_COUNT}"\
- -DSTATIC_LINK_LLVM=ON \
- -DUSE_SDL=ON \
- -DUSE_DISCORD_RPC=ON \
- -DUSE_AUDIOUNIT=ON \
- -DUSE_SYSTEM_FFMPEG=OFF \
- -DUSE_NATIVE_INSTRUCTIONS=OFF \
- -DUSE_PRECOMPILED_HEADERS=OFF \
- -DUSE_SYSTEM_MVK=ON \
- -DUSE_SYSTEM_SDL=ON \
- -DUSE_SYSTEM_OPENCV=ON \
- -G Ninja
-fi
-ninja; build_status=$?;
+ninja -j4; build_status=$?;
cd ..
diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh
index b0599400d2..3309eb89e0 100755
--- a/.ci/deploy-linux.sh
+++ b/.ci/deploy-linux.sh
@@ -19,6 +19,9 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt
+ # Restore App Icon
+ ln -sr ./AppDir/rpcs3.svg ./AppDir/.DirIcon
+
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
rm -f ./AppDir/usr/lib/libwayland-client.so*
diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh
index 0b8c1994e0..045b93ac9f 100755
--- a/.ci/deploy-mac.sh
+++ b/.ci/deploy-mac.sh
@@ -4,15 +4,15 @@
cd build || exit 1
cd bin
-git clone --revision=32dceb35e2c95b46cec501033cbc3a1ddf32d6e8 https://github.com/KhronosGroup/MoltenVK.git
+git clone --revision=a075e5e417f87675ea3137b7365f3e5a99608d72 https://github.com/KhronosGroup/MoltenVK.git
cd MoltenVK
./fetchDependencies --macos
make macos MVK_USE_METAL_PRIVATE_API=1
cd ../
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
-cp "MoltenVK/Package/Latest/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
-cp "MoltenVK/Package/Latest/MoltenVK/dynamic/dylib/macOS/MoltenVK_icd.json" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
+cp "MoltenVK/Package/Release/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
+cp "MoltenVK/Package/Release/MoltenVK/dynamic/dylib/macOS/MoltenVK_icd.json" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
sed -i '' "s/.\//..\/..\/..\/Frameworks\//g" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
cp "$(realpath $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
@@ -56,9 +56,25 @@ rm -f rpcs3.app/Contents/translations/qt_help_*.qm || true
mv rpcs3.app RPCS3_.app
mv RPCS3_.app RPCS3.app
-# Hack
-install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || true
-install_name_tool -delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 || true
+# Hack to fix rpath issues
+BIN="RPCS3.app/Contents/MacOS/rpcs3"
+install_name_tool -delete_rpath /opt/homebrew/lib $BIN || true
+install_name_tool -delete_rpath /usr/local/lib $BIN || true
+
+# Fix dylib IDs
+for lib in RPCS3.app/Contents/Frameworks/*.dylib; do
+ name=$(basename "$lib")
+ install_name_tool -id "@rpath/$name" "$lib"
+done
+
+# Rewrite any hardcoded Homebrew paths to use @rpath
+find "RPCS3.app/Contents/" -type f \( -perm +111 -o -name "*.dylib" \) | while read -r bin; do
+ otool -L "$bin" | grep -E "/opt/homebrew|/usr/local" | awk '{print $1}' | while read -r dep; do
+ base=$(basename "$dep")
+ echo "Fixing $dep -> @rpath/$base in $bin"
+ install_name_tool -change "$dep" "@rpath/$base" "$bin"
+ done
+done
# NOTE: "--deep" is deprecated
codesign --deep -fs - RPCS3.app
@@ -67,7 +83,7 @@ echo "[InternetShortcut]" > Quickstart.url
echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url
echo "IconIndex=0" >> Quickstart.url
-if [ "$AARCH64" -eq 1 ]; then
+if [ "$(arch)" = "arm64" ]; then
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${LVER}_macos_aarch64.7z"
else
ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${LVER}_macos.7z"
diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh
index d8016d8c13..9008860e75 100755
--- a/.ci/setup-windows.sh
+++ b/.ci/setup-windows.sh
@@ -16,7 +16,7 @@ QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia$
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}"
LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
-VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
+VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/olpvqk4346ig7i8btadk5/vulkansdk-windows-X64-${VULKAN_VER}.exe?rlkey=huvssch6sy904qkg8ti9p65br&st=pztptdin&dl=1"
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip"
DEP_URLS=" \
@@ -88,7 +88,7 @@ for url in $DEP_URLS; do
*qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;;
*llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./build/lib_ext/Release-x64" ;;
*ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;;
- *Vulkan*)
+ *vulkansdk*)
# Vulkan setup needs to be run in batch environment
# Need to subshell this or else it doesn't wait
download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName"
diff --git a/.github/workflows/llvm.yml b/.github/workflows/llvm.yml
index 3ed584437a..3ac6b37c46 100644
--- a/.github/workflows/llvm.yml
+++ b/.github/workflows/llvm.yml
@@ -17,7 +17,7 @@ jobs:
Windows_Build:
if: github.event_name == 'workflow_dispatch'
name: LLVM Windows (MSVC)
- runs-on: windows-2025
+ runs-on: windows-2025-vs2026
env:
COMPILER: msvc
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml
index eb42b7aae1..515555a224 100644
--- a/.github/workflows/rpcs3.yml
+++ b/.github/workflows/rpcs3.yml
@@ -30,23 +30,23 @@ jobs:
matrix:
include:
- os: ubuntu-24.04
- docker_img: "rpcs3/rpcs3-ci-jammy:1.11"
+ docker_img: "rpcs3/rpcs3-ci-jammy:1.14"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: clang
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
- os: ubuntu-24.04
- docker_img: "rpcs3/rpcs3-ci-jammy:1.11"
+ docker_img: "rpcs3/rpcs3-ci-jammy:1.14"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
- docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.11"
+ docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.14"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: clang
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
- os: ubuntu-24.04-arm
- docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.11"
+ docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.14"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
@@ -123,25 +123,24 @@ jobs:
matrix:
include:
- name: Intel
- AARCH64: 0
+ runs-on: macos-15-intel
UPLOAD_COMMIT_HASH: 51ae32f468089a8169aaf1567de355ff4a3e0842
UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac
- name: Apple Silicon
- AARCH64: 1
+ runs-on: macos-15
UPLOAD_COMMIT_HASH: 8e21bdbc40711a3fccd18fbf17b742348b0f4281
UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac-arm64
name: RPCS3 Mac ${{ matrix.name }}
- runs-on: macos-14
+ runs-on: ${{ matrix.runs-on }}
env:
CCACHE_DIR: /tmp/ccache_dir
- QT_VER: '6.11.0'
+ QT_VER: '6.11.1'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }}
UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }}
- AARCH64: ${{ matrix.AARCH64 }}
- RUN_UNIT_TESTS: github.event_name == 'pull_request' && 'ON' || 'OFF'
+ RUN_UNIT_TESTS: ${{ matrix.name == 'Apple Silicon' && (github.event_name == 'pull_request' && 'ON') || 'OFF' }}
steps:
- name: Checkout repository
uses: actions/checkout@main
@@ -209,16 +208,16 @@ jobs:
# Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 Windows
- runs-on: windows-2025
+ runs-on: windows-2025-vs2026
env:
COMPILER: msvc
QT_VER_MAIN: '6'
- QT_VER: '6.11.0'
+ QT_VER: '6.11.1'
QT_VER_MSVC: 'msvc2022'
- QT_DATE: '202603180535'
+ QT_DATE: '202605090529'
LLVM_VER: '19.1.7'
- VULKAN_VER: '1.3.268.0'
- VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
+ VULKAN_VER: '1.4.341.1'
+ VULKAN_SDK_SHA: 'bcf2d75aa9556889ab974858666e20b3655b6055a0db704ccb47279ff33b5bfe'
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
CCACHE_BIN_DIR: 'C:\ccache_bin'
CCACHE_DIR: 'C:\ccache'
@@ -235,7 +234,7 @@ jobs:
fetch-depth: 0
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
- name: Restore NuGet packages
run: nuget restore rpcs3.sln
diff --git a/3rdparty/7zip/7zip b/3rdparty/7zip/7zip
index 839151eaaa..f9d78aff31 160000
--- a/3rdparty/7zip/7zip
+++ b/3rdparty/7zip/7zip
@@ -1 +1 @@
-Subproject commit 839151eaaad24771892afaae6bac690e31e58384
+Subproject commit f9d78aff31a5f2521ae7ddbdc97c4a8855808959
diff --git a/3rdparty/FAudio b/3rdparty/FAudio
index 0372329dbb..75c79d4c8a 160000
--- a/3rdparty/FAudio
+++ b/3rdparty/FAudio
@@ -1 +1 @@
-Subproject commit 0372329dbb56e7814d0dea7b6eafa7a613bd8042
+Subproject commit 75c79d4c8ab59dfc4313e40c1997e03f7d32229f
diff --git a/3rdparty/OpenAL/openal-soft b/3rdparty/OpenAL/openal-soft
index c41d64c6a3..b2c48f7718 160000
--- a/3rdparty/OpenAL/openal-soft
+++ b/3rdparty/OpenAL/openal-soft
@@ -1 +1 @@
-Subproject commit c41d64c6a35f6174bf4a27010aeac52a8d3bb2c6
+Subproject commit b2c48f7718ef3fcf67921a8b6534c4914e328970
diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl
index 8c908d2d0a..a05f34973e 160000
--- a/3rdparty/curl/curl
+++ b/3rdparty/curl/curl
@@ -1 +1 @@
-Subproject commit 8c908d2d0a6d32abdedda2c52e90bd56ec76c24d
+Subproject commit a05f34973e6c4bb629d018f7cb51487be1c904d8
diff --git a/3rdparty/curl/libcurl.vcxproj b/3rdparty/curl/libcurl.vcxproj
index 4db28782a3..9acb78a8b7 100644
--- a/3rdparty/curl/libcurl.vcxproj
+++ b/3rdparty/curl/libcurl.vcxproj
@@ -66,6 +66,7 @@
+
@@ -107,7 +108,6 @@
-
@@ -118,6 +118,7 @@
+
@@ -172,6 +173,7 @@
+
@@ -195,6 +197,8 @@
+
+
@@ -255,6 +259,7 @@
+
@@ -275,7 +280,6 @@
-
@@ -309,7 +313,6 @@
-
@@ -322,6 +325,7 @@
+
@@ -332,6 +336,7 @@
+
@@ -368,6 +373,7 @@
+
@@ -395,6 +401,8 @@
+
+
diff --git a/3rdparty/curl/libcurl.vcxproj.filters b/3rdparty/curl/libcurl.vcxproj.filters
index d38316e767..482b2f1452 100644
--- a/3rdparty/curl/libcurl.vcxproj.filters
+++ b/3rdparty/curl/libcurl.vcxproj.filters
@@ -66,9 +66,6 @@
Source Files
-
- Source Files
-
Source Files
@@ -552,6 +549,21 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -668,9 +680,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -1040,9 +1049,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -1121,6 +1127,24 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
diff --git a/3rdparty/ffmpeg b/3rdparty/ffmpeg
index ce81114ed9..649d9d9353 160000
--- a/3rdparty/ffmpeg
+++ b/3rdparty/ffmpeg
@@ -1 +1 @@
-Subproject commit ce81114ed99e5510f6cd983f59a1eac9f33bb73c
+Subproject commit 649d9d9353184a17ae78d9df4a6fbbe7456f0428
diff --git a/3rdparty/glslang/glslang b/3rdparty/glslang/glslang
index fc9889c889..f0bd0257c3 160000
--- a/3rdparty/glslang/glslang
+++ b/3rdparty/glslang/glslang
@@ -1 +1 @@
-Subproject commit fc9889c889561c5882e83819dcaffef5ed45529b
+Subproject commit f0bd0257c308b9a26562c1a30c4748a0219cc951
diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL
index 5848e584a1..8e37db5e79 160000
--- a/3rdparty/libsdl-org/SDL
+++ b/3rdparty/libsdl-org/SDL
@@ -1 +1 @@
-Subproject commit 5848e584a1b606de26e3dbd1c7e4ecbc34f807a6
+Subproject commit 8e37db5e797b6167f3a00d697d816a684bd259c7
diff --git a/3rdparty/libusb/libusb b/3rdparty/libusb/libusb
index 15a7ebb4d4..87a55632db 160000
--- a/3rdparty/libusb/libusb
+++ b/3rdparty/libusb/libusb
@@ -1 +1 @@
-Subproject commit 15a7ebb4d426c5ce196684347d2b7cafad862626
+Subproject commit 87a55632db62c9bdc58cd31d3ccfa673f1bb017f
diff --git a/3rdparty/libusb/libusb_static.vcxproj b/3rdparty/libusb/libusb_static.vcxproj
index 66dfc2ac29..79e0cfb4b2 100644
--- a/3rdparty/libusb/libusb_static.vcxproj
+++ b/3rdparty/libusb/libusb_static.vcxproj
@@ -54,6 +54,9 @@
NDEBUG;%(PreprocessorDefinitions)
+
+ LIBUSB_WINDOWS_HOTPLUG;%(PreprocessorDefinitions)
+
$(OutDir)libusb-1.0.lib
@@ -64,6 +67,7 @@
+
@@ -72,6 +76,7 @@
+
diff --git a/3rdparty/opencv/opencv b/3rdparty/opencv/opencv
index 67f53c26a7..6900a1265b 160000
--- a/3rdparty/opencv/opencv
+++ b/3rdparty/opencv/opencv
@@ -1 +1 @@
-Subproject commit 67f53c26a701c2aeefd8033ec2f2079e04c438ca
+Subproject commit 6900a1265b8e1b0a19d7955b5bd6599a4a8b8576
diff --git a/3rdparty/pine/pine_server.h b/3rdparty/pine/pine_server.h
index fe88ddb474..5c59e87da3 100644
--- a/3rdparty/pine/pine_server.h
+++ b/3rdparty/pine/pine_server.h
@@ -164,14 +164,17 @@ namespace pine
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
};
- const auto write_string = [&](const std::string& str)
+ const auto write_string = [&](std::string_view str)
{
if (!SafetyChecks(buf_cnt, 0, ret_cnt, str.size() + 1 + sizeof(u32), buf_size))
return false;
ToArray(ret_buffer, ::narrow(str.size() + 1), ret_cnt);
ret_cnt += sizeof(u32);
- memcpy(&ret_buffer[ret_cnt], str.data(), str.size());
- ret_cnt += str.size();
+ if (str.size())
+ {
+ std::memcpy(&ret_buffer[ret_cnt], str.data(), str.size());
+ ret_cnt += str.size();
+ }
ret_buffer[ret_cnt++] = '\0';
return true;
};
@@ -512,7 +515,7 @@ namespace pine
*/
static inline bool SafetyChecks(usz command_len, usz command_size, usz reply_len, usz reply_size = 0, usz buf_size = MAX_IPC_SIZE - 1)
{
- bool res = ((command_len + command_size) > buf_size ||
+ const bool res = ((command_len + command_size) > buf_size ||
(reply_len + reply_size) >= MAX_IPC_RETURN_SIZE);
if (res) [[unlikely]]
return false;
@@ -524,8 +527,8 @@ namespace pine
pine_server() noexcept
{
#ifdef _WIN32
- WSADATA wsa;
- struct sockaddr_in server;
+ WSADATA wsa {};
+ struct sockaddr_in server {};
m_sock = INVALID_SOCKET;
m_msgsock = INVALID_SOCKET;
@@ -579,7 +582,7 @@ namespace pine
fmt::append(m_socket_name, ".%d", slot);
}
- struct sockaddr_un server;
+ struct sockaddr_un server {};
m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (m_sock < 0)
diff --git a/3rdparty/pugixml b/3rdparty/pugixml
index ee86beb30e..c8033ce9d0 160000
--- a/3rdparty/pugixml
+++ b/3rdparty/pugixml
@@ -1 +1 @@
-Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38
+Subproject commit c8033ce9d039e7f9d134877c363397b3cfe20816
diff --git a/3rdparty/wolfssl/wolfssl b/3rdparty/wolfssl/wolfssl
index 1d363f3adc..ac01707f55 160000
--- a/3rdparty/wolfssl/wolfssl
+++ b/3rdparty/wolfssl/wolfssl
@@ -1 +1 @@
-Subproject commit 1d363f3adceba9d1478230ede476a37b0dcdef24
+Subproject commit ac01707f552c611fbd135cc723b2682b3e7f80f2
diff --git a/3rdparty/wolfssl/wolfssl.vcxproj b/3rdparty/wolfssl/wolfssl.vcxproj
index 90da8b7f38..66f8a5ef1f 100644
--- a/3rdparty/wolfssl/wolfssl.vcxproj
+++ b/3rdparty/wolfssl/wolfssl.vcxproj
@@ -71,15 +71,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -139,6 +164,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -166,8 +262,6 @@
-
-
@@ -180,7 +274,6 @@
-
@@ -194,6 +287,7 @@
+
@@ -212,7 +306,6 @@
-
@@ -221,12 +314,16 @@
+
+
+
+
-
+
false
false
@@ -247,6 +344,7 @@
true
true
+
diff --git a/BUILDING.md b/BUILDING.md
index c1774908fd..fb8a8144cf 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -20,27 +20,27 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
-- [Qt 6.11.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
-- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
+- [Qt 6.11.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
+- [Vulkan SDK 1.4.341.1](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)). Note that future SDKs may not work.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
-- add and set the `QTDIR` environment variable, e.g. `\6.11.0\msvc2022_64\`
+- add and set the `QTDIR` environment variable, e.g. `\6.11.1\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
-- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.11.0\msvc2022_64\`
+- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.11.1\msvc2022_64\`
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/)
-- [Qt 6.11.0](https://www.qt.io/download-qt-installer)
-- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
+- [Qt 6.11.1](https://www.qt.io/download-qt-installer)
+- [Vulkan SDK 1.4.341.1](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)). Note that future SDKs may not work.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
@@ -123,7 +123,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions`
-2) add the path to your Qt installation with compiler e.g. `\6.11.0\msvc2022_64`, version will fill in automatically
+2) add the path to your Qt installation with compiler e.g. `\6.11.1\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 217c40f341..cbb2f66fc8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -94,8 +94,8 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
endif()
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
- include_directories(/opt/homebrew/include)
- link_directories(/opt/homebrew/lib)
+ list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew")
+ list(APPEND CMAKE_SYSTEM_PREFIX_PATH "/opt/homebrew")
endif()
if(MSVC)
diff --git a/README.md b/README.md
index ac6e3cd594..351688be64 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ The world's first free and open-source PlayStation 3 emulator/debugger, written
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
-[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
+[**Support the Lead Developers on Patreon**](https://rpcs3.net/patreon)
## Contributing
@@ -23,6 +23,16 @@ If you want to contribute as a developer, please take a look at the following pa
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
+### AI Use
+
+Use of AI tools for research and reverse engineering purposes is permitted. However, contributors are expected to fully own and understand all code they submit. Any communication with the team — including code, code comments, and GitHub comments — must come from the human contributor, not an AI agent acting autonomously.
+
+We have unfortunately seen a rise in untested and unverified AI-generated slop being submitted to this project. This wastes maintainer time and, in worse cases, such changes get merged and break functionality for all users. Repeated violations will result in a ban from the repository. Please be respectful of everyone's time.
+
+**Pull requests opened by AI agents or automated tools must include a disclosure in the PR description** stating the scope of AI involvement — which parts were AI-generated and what human testing or review was performed prior to submission. PRs that omit this disclosure may be closed without review.
+
+If you are unsure about your work, open a discussion issue to talk it through with the team, or reach out to a maintainer on [Discord](https://discord.gg/RPCS3).
+
## Building
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
diff --git a/Utilities/Config.cpp b/Utilities/Config.cpp
index f242bd6172..6b2e3bf841 100644
--- a/Utilities/Config.cpp
+++ b/Utilities/Config.cpp
@@ -253,10 +253,6 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max, std::strin
bool try_to_string(std::string* out, f64 value, std::string_view name)
{
-#ifdef __APPLE__
- if (out) *out = std::to_string(value);
- return true;
-#else
std::array str{};
if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value, std::chars_format::fixed); ec == std::errc())
@@ -269,11 +265,12 @@ bool try_to_string(std::string* out, f64 value, std::string_view name)
if (out) cfg_log.error("cfg::try_to_string('%s'): could not convert value '%f' to string. error='%s'", name, value, std::make_error_code(ec).message());
return false;
}
-#endif
}
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string::format) func, std::string_view value, std::string_view name)
{
+ ensure(func);
+
u64 max = umax;
for (u64 i = 0;; i++)
@@ -302,7 +299,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string::format) f
const char* end = start + value.size();
int base = 10;
- if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
+ if (value.size() >= 2 && start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
{
// Limited hex support
base = 16;
@@ -760,7 +757,7 @@ void cfg::log_entry::from_default()
std::pair cfg::device_info::get_usb_ids() const
{
- auto string_to_hex = [](const std::string& str) -> u16
+ auto string_to_hex = [](std::string_view str) -> u16
{
u16 value = 0x0000;
if (!str.empty() && std::from_chars(str.data(), str.data() + str.size(), value, 16).ec != std::errc{})
diff --git a/Utilities/Config.h b/Utilities/Config.h
index 4f8e578fde..d17f970dc5 100644
--- a/Utilities/Config.h
+++ b/Utilities/Config.h
@@ -717,6 +717,11 @@ namespace cfg
m_value = std::string(value);
return true;
}
+
+ void set(std::string_view value)
+ {
+ m_value = std::string(value);
+ }
};
// Simple set entry (TODO: template for various types)
diff --git a/Utilities/File.cpp b/Utilities/File.cpp
index aff4537dea..20e25794d2 100644
--- a/Utilities/File.cpp
+++ b/Utilities/File.cpp
@@ -38,6 +38,17 @@ static std::unique_ptr to_wchar(std::string_view source)
// Buffer for max possible output length
std::unique_ptr buffer(new wchar_t[buf_size + 8 + 32768]);
+ // If path points to an optical raw device, copy it AS IS
+ if (fs::is_optical_raw_device(std::string(source)))
+ {
+ ensure(MultiByteToWideChar(CP_UTF8, 0, source.data(), size, buffer.get() + 32768, size)); // "to_wchar"
+
+ // Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc)
+ ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar"
+
+ return buffer;
+ }
+
// Prepend wide path prefix (4 characters)
std::memcpy(buffer.get() + 32768, L"\\\\\?\\", 4 * sizeof(wchar_t));
@@ -400,11 +411,12 @@ namespace fs
class windows_file final : public file_base
{
HANDLE m_handle;
+ bool m_raw_device;
atomic_t m_pos {0};
public:
- windows_file(HANDLE handle)
- : m_handle(handle)
+ windows_file(HANDLE handle, bool raw_device = false)
+ : m_handle(handle), m_raw_device(raw_device)
{
}
@@ -564,11 +576,20 @@ namespace fs
u64 size() override
{
- // NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
- LARGE_INTEGER size;
- ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
+ if (!m_raw_device)
+ {
+ // NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
+ LARGE_INTEGER size;
- return size.QuadPart;
+ ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
+ return size.QuadPart;
+ }
+
+ // For a raw device, we need to use DeviceIoControl.
+ DISK_GEOMETRY_EX geometry;
+
+ ensure(DeviceIoControl(m_handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, &geometry, sizeof(geometry), nullptr, nullptr));
+ return geometry.DiskSize.QuadPart;
}
native_handle get_handle() override
@@ -1091,6 +1112,55 @@ bool fs::is_symlink(const std::string& path)
return true;
}
+bool fs::is_optical_raw_device([[maybe_unused]] const std::string& path)
+{
+#ifdef _WIN32
+ if (path.starts_with("\\\\.\\"))
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+
+bool fs::get_optical_raw_device(const std::string& path, std::string* raw_device)
+{
+ if (fs::is_optical_raw_device(path))
+ {
+ if (raw_device)
+ {
+ *raw_device = path;
+ }
+
+ return true;
+ }
+
+#ifdef _WIN32
+ // Skip a useless check to detect an optical raw device if navigating on subfolders (e.g. C:\subfolder_1\subfolder_2\),
+ // it means we are on a HDD/SSD. A path for an optical drive should include only the drive letter (e.g. E:\)
+ const size_t drive_delim_pos = path.find_first_of(":");
+
+ if (drive_delim_pos != 1 || drive_delim_pos != path.find_last_not_of(delim))
+ {
+ return false;
+ }
+
+ const std::string drive_letter = path.substr(0, drive_delim_pos + 1); // e.g. "E:"
+ const std::string drive_path = drive_letter + "\\"; // e.g. "E:\"
+
+ if (GetDriveTypeA(drive_path.c_str()) == DRIVE_CDROM)
+ {
+ if (raw_device)
+ {
+ *raw_device = "\\\\.\\" + drive_letter;
+ }
+
+ return true;
+ }
+#endif
+ return false;
+}
+
bool fs::statfs(const std::string& path, fs::device_stat& info)
{
if (auto device = get_virtual_device(path))
@@ -1658,9 +1728,29 @@ fs::file::file(const std::string& path, bs_t mode)
return;
}
+ // If path points to an optical raw device, complete the file opening
+ // (the following GetFileInformationByHandle() would always fail on a raw device).
+ if (is_optical_raw_device(path))
+ {
+ DISK_GEOMETRY_EX geometry;
+
+ // Try to retrieve information on content. If it fails, no disc is probably mounted so abort the file opening
+ if (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, &geometry, sizeof(geometry), nullptr, nullptr))
+ {
+ const DWORD last_error = GetLastError();
+ CloseHandle(handle);
+ g_tls_error = to_error(last_error);
+ return;
+ }
+
+ m_file = std::make_unique(handle, true);
+ return;
+ }
+
// Check if the handle is actually valid.
// This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION).
BY_HANDLE_FILE_INFORMATION info{};
+
if (!GetFileInformationByHandle(handle, &info))
{
const DWORD last_error = GetLastError();
@@ -1671,7 +1761,7 @@ fs::file::file(const std::string& path, bs_t mode)
g_tls_error = fs::error::isdir;
return;
}
-
+
g_tls_error = to_error(last_error);
return;
}
diff --git a/Utilities/File.h b/Utilities/File.h
index 3d332dd0be..778bfacd48 100644
--- a/Utilities/File.h
+++ b/Utilities/File.h
@@ -213,6 +213,12 @@ namespace fs
// Check whether the path points to an existing symlink
bool is_symlink(const std::string& path);
+ // Check whether the path points to a raw device
+ bool is_optical_raw_device(const std::string& path);
+
+ // Check whether the path points to an optical drive. If so, provide the raw device in "raw_device" if requested
+ bool get_optical_raw_device(const std::string& path, std::string* raw_device = nullptr);
+
// Get filesystem information
bool statfs(const std::string& path, device_stat& info);
diff --git a/Utilities/JIT.h b/Utilities/JIT.h
index 86fc72ed55..aba30c3400 100644
--- a/Utilities/JIT.h
+++ b/Utilities/JIT.h
@@ -525,7 +525,7 @@ class jit_compiler final
atomic_t m_disk_space = umax;
public:
- jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags = 0, std::function symbols_cement = {}) noexcept;
+ jit_compiler(const std::unordered_map& _link, std::string_view _cpu, u32 flags = 0, std::function symbols_cement = {}) noexcept;
jit_compiler& operator=(thread_state) noexcept;
~jit_compiler() noexcept;
@@ -543,9 +543,15 @@ public:
// Add module (path to obj cache dir)
void add(std::unique_ptr _module, const std::string& path);
+ // Returns false after LLVM fatal recovery. The compiler must be discarded.
+ bool try_add(std::unique_ptr _module, const std::string& path, std::string& error);
+
// Add module (not cached)
void add(std::unique_ptr _module);
+ // Returns false after LLVM fatal recovery. The compiler must be discarded.
+ bool try_add(std::unique_ptr _module, std::string& error);
+
// Add object (path to obj file)
bool add(const std::string& path);
@@ -558,11 +564,14 @@ public:
// Finalize
void fin();
+ // Returns false after LLVM fatal recovery. The compiler must be discarded.
+ bool try_fin(std::string& error);
+
// Get compiled function address
u64 get(const std::string& name);
// Get CPU info
- static std::string cpu(const std::string& _cpu);
+ static std::string cpu(std::string_view _cpu);
// Get system triple (PPU)
static std::string triple1();
diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp
index 34e1572185..3bfb30cea2 100644
--- a/Utilities/JITLLVM.cpp
+++ b/Utilities/JITLLVM.cpp
@@ -12,6 +12,10 @@
#include
+#if defined(__APPLE__)
+#include
+#endif
+
LOG_CHANNEL(jit_log, "JIT");
#ifdef LLVM_AVAILABLE
@@ -50,6 +54,44 @@ LOG_CHANNEL(jit_log, "JIT");
#include "Emu/CPU/Backends/AArch64/AArch64Common.h"
#endif
+namespace
+{
+ thread_local std::string* g_llvm_fatal_message = nullptr;
+
+ template
+ bool run_recoverable_llvm(F&& func, std::string& error)
+ {
+ error.clear();
+
+ // Run LLVM codegen in a disposable thread. If LLVM invokes the fatal
+ // handler, only this helper thread exits.
+ named_thread worker("LLVM JIT", [&]()
+ {
+#if defined(__APPLE__)
+ pthread_jit_write_protect_np(false);
+#endif
+ g_llvm_fatal_message = &error;
+
+ std::forward(func)();
+
+ g_llvm_fatal_message = nullptr;
+#if defined(__APPLE__)
+ pthread_jit_write_protect_np(true);
+#endif
+ });
+
+ worker();
+ const bool result = static_cast(worker) == thread_state::finished;
+
+ if (!result && error.empty())
+ {
+ error = "LLVM crash recovery invoked";
+ }
+
+ return result;
+ }
+}
+
const bool jit_initialize = []() -> bool
{
llvm::InitializeNativeTarget();
@@ -509,9 +551,9 @@ public:
}
};
-std::string jit_compiler::cpu(const std::string& _cpu)
+std::string jit_compiler::cpu(std::string_view _cpu)
{
- std::string m_cpu = _cpu;
+ std::string m_cpu = std::string(_cpu);
if (m_cpu.empty())
{
@@ -639,7 +681,7 @@ bool jit_compiler::add_sub_disk_space(ssz space)
}).second;
}
-jit_compiler::jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags, std::function symbols_cement) noexcept
+jit_compiler::jit_compiler(const std::unordered_map& _link, std::string_view _cpu, u32 flags, std::function symbols_cement) noexcept
: m_context(new llvm::LLVMContext)
, m_cpu(cpu(_cpu))
{
@@ -649,6 +691,13 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co
llvm::install_fatal_error_handler([](void*, const char* msg, bool)
{
const std::string_view out = msg ? msg : "";
+
+ if (g_llvm_fatal_message)
+ {
+ *g_llvm_fatal_message = out;
+ thread_ctrl::silent_exit();
+ }
+
fmt::throw_exception("LLVM Emergency Exit Invoked: '%s'", out);
}, nullptr);
@@ -701,6 +750,15 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co
else
attributes.push_back("-dotprod");
+ // The recompilers emit i8mm intrinsics (e.g. ummla) gated on utils::has_i8mm().
+ // The JIT target features must advertise i8mm too, otherwise the backend fails
+ // with "Cannot select: intrinsic %llvm.aarch64.neon.ummla" whenever the resolved
+ // -mcpu does not already imply it (e.g. the cortex-a78 fallback on Apple silicon).
+ if (utils::has_i8mm())
+ attributes.push_back("+i8mm");
+ else
+ attributes.push_back("-i8mm");
+
if (utils::has_sve())
attributes.push_back("+sve");
else
@@ -788,6 +846,33 @@ void jit_compiler::add(std::unique_ptr _module, const std::string&
}
}
+bool jit_compiler::try_add(std::unique_ptr _module, const std::string& path, std::string& error)
+{
+ ObjectCache cache{path, this};
+ m_engine->setObjectCache(&cache);
+
+ const auto ptr = _module.get();
+ m_engine->addModule(std::move(_module));
+
+ if (!run_recoverable_llvm([&]()
+ {
+ m_engine->generateCodeForModule(ptr);
+ }, error))
+ {
+ return false;
+ }
+
+ m_engine->setObjectCache(nullptr);
+
+ for (auto& func : ptr->functions())
+ {
+ // Delete IR to lower memory consumption
+ func.deleteBody();
+ }
+
+ return true;
+}
+
void jit_compiler::add(std::unique_ptr _module)
{
const auto ptr = _module.get();
@@ -801,6 +886,28 @@ void jit_compiler::add(std::unique_ptr _module)
}
}
+bool jit_compiler::try_add(std::unique_ptr _module, std::string& error)
+{
+ const auto ptr = _module.get();
+ m_engine->addModule(std::move(_module));
+
+ if (!run_recoverable_llvm([&]()
+ {
+ m_engine->generateCodeForModule(ptr);
+ }, error))
+ {
+ return false;
+ }
+
+ for (auto& func : ptr->functions())
+ {
+ // Delete IR to lower memory consumption
+ func.deleteBody();
+ }
+
+ return true;
+}
+
bool jit_compiler::add(const std::string& path)
{
auto cache = ObjectCache::load(path);
@@ -852,6 +959,14 @@ void jit_compiler::fin()
m_engine->finalizeObject();
}
+bool jit_compiler::try_fin(std::string& error)
+{
+ return run_recoverable_llvm([&]()
+ {
+ m_engine->finalizeObject();
+ }, error);
+}
+
u64 jit_compiler::get(const std::string& name)
{
return m_engine->getGlobalValueAddress(name);
diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp
index d68ef51cb5..39a2cb9233 100644
--- a/Utilities/StrFmt.cpp
+++ b/Utilities/StrFmt.cpp
@@ -630,6 +630,13 @@ namespace fmt
thread_ctrl::emergency_exit(out);
}
+ [[noreturn]] void raw_verify_error(std::source_location loc, std::source_location propagated_loc, const char8_t* msg, usz object)
+ {
+ std::string out;
+ fmt::append(out, "%s (object: 0x%x)%s%s", msg ? msg : u8"Verification failed", object, loc, propagated_loc);
+ thread_ctrl::emergency_exit(out);
+ }
+
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size)
{
std::string out;
@@ -921,6 +928,13 @@ std::string_view fmt::trim_front_sv(std::string_view source, std::string_view va
void fmt::trim_back(std::string& source, std::string_view values)
{
const usz index = source.find_last_not_of(values);
+
+ if (index == source.npos)
+ {
+ source.clear();
+ return;
+ }
+
source.resize(index + 1);
}
diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp
index 57d7446daf..abe6a108f8 100644
--- a/Utilities/Thread.cpp
+++ b/Utilities/Thread.cpp
@@ -1384,11 +1384,7 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
return false;
}
- if (a_size != 4)
- {
- // Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
- break;
- }
+ bool handled = true;
switch (op)
{
@@ -1398,14 +1394,37 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_LOAD_TEST:
{
u32 value;
- if (is_writing || !thread->read_reg(addr, value))
+ const u32 addr_aligned = addr & -4;
+
+ if (addr % 4 + a_size > 4)
+ {
+ handled = false;
+ break;
+ }
+
+ if (is_writing || !thread->read_reg(addr_aligned, value))
{
return false;
}
+ // Adjust value for 8-bit and 16-bit reads
+ value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
+ value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
+
if (op != X64OP_LOAD_BE)
{
- value = stx::se_storage::swap(value);
+ if (a_size == 4)
+ {
+ value = stx::se_storage::swap(value);
+ }
+ else if (a_size == 2)
+ {
+ value = stx::se_storage::swap(value);
+ }
+ else
+ {
+ ensure(a_size == 1);
+ }
}
if (op == X64OP_LOAD_CMP)
@@ -1440,12 +1459,35 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_BEXTR:
{
u32 value;
- if (is_writing || !thread->read_reg(addr, value))
+ const u32 addr_aligned = addr & -4;
+
+ if (addr % 4 + a_size > 4)
+ {
+ handled = false;
+ break;
+ }
+
+ if (is_writing || !thread->read_reg(addr_aligned, value))
{
return false;
}
- value = stx::se_storage::swap(value);
+ // Adjust value for 8-bit and 16-bit reads
+ value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
+ value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
+
+ if (a_size == 4)
+ {
+ value = stx::se_storage::swap(value);
+ }
+ else if (a_size == 2)
+ {
+ value = stx::se_storage::swap(value);
+ }
+ else
+ {
+ ensure(a_size == 1);
+ }
u64 ctrl;
if (!get_x64_reg_value(context, s_tls_reg3, d_size, i_size, ctrl))
@@ -1471,6 +1513,13 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STORE:
case X64OP_STORE_BE:
{
+ if (a_size != 4)
+ {
+ // Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
+ handled = false;
+ break;
+ }
+
u64 reg_value;
if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value))
{
@@ -1489,12 +1538,19 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STOS:
default:
{
- sig_log.error("Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, i_size=%lld)", +op, +reg, d_size, i_size);
+ sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
report_opcode();
return false;
}
}
+ if (!handled)
+ {
+ sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
+ report_opcode();
+ break;
+ }
+
// skip processed instruction
RIP(context) += i_size;
g_tls_fault_spu++;
@@ -2988,6 +3044,32 @@ void thread_ctrl::set_name(std::string name)
report_fatal_error(reason);
}
+void thread_ctrl::silent_exit() noexcept
+{
+ if (const auto _this = g_tls_this_thread)
+ {
+ g_tls_error_callback();
+
+ u64 _self = _this->finalize(thread_state::errored);
+
+ if (_self == umax)
+ {
+ // Unused, detached thread support remnant
+ delete _this;
+ }
+
+ thread_base::finalize(umax);
+ }
+
+#ifdef _WIN32
+ _endthreadex(0);
+#else
+ pthread_exit(nullptr);
+#endif
+
+ std::abort();
+}
+
void thread_ctrl::detect_cpu_layout()
{
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
diff --git a/Utilities/Thread.h b/Utilities/Thread.h
index bafcea0b9f..5d6d07c870 100644
--- a/Utilities/Thread.h
+++ b/Utilities/Thread.h
@@ -315,6 +315,9 @@ public:
// Exit.
[[noreturn]] static void emergency_exit(std::string_view reason);
+ // Exit the current named thread as errored without reporting a fatal error.
+ [[noreturn]] static void silent_exit() noexcept;
+
// Get current thread (may be nullptr)
static thread_base* get_current()
{
@@ -797,31 +800,30 @@ public:
m_count = 0;
// Create all threads
- for (u32 i = 0; i < count - 1; i++)
+ for (; m_count < count - 1; m_count++)
{
// Copy the context
std::remove_cvref_t context(static_cast(f));
// Perform the check and additional preparations for each context
- if (!std::invoke(std::forward(check), i, context))
+ if (!std::invoke(std::forward(check), m_count, context))
{
return;
}
- m_count++;
- new (static_cast(m_threads + i)) Thread(std::string(name) + std::to_string(i + 1), std::move(context));
+ new (static_cast(m_threads + m_count)) Thread(std::string(name) + std::to_string(m_count + 1), std::move(context));
}
// Move the context (if movable)
std::remove_cvref_t context(std::forward(f));
- if (!std::invoke(std::forward(check), m_count - 1, context))
+ if (!std::invoke(std::forward(check), m_count, context))
{
return;
}
+ new (static_cast(m_threads + m_count)) Thread(std::string(name) + std::to_string(m_count + 1), std::move(context));
m_count++;
- new (static_cast(m_threads + m_count - 1)) Thread(std::string(name) + std::to_string(m_count - 1), std::move(context));
}
// Default constructor
diff --git a/Utilities/cheat_info.cpp b/Utilities/cheat_info.cpp
index 7745d26732..e67e103214 100644
--- a/Utilities/cheat_info.cpp
+++ b/Utilities/cheat_info.cpp
@@ -29,7 +29,7 @@ void fmt_class_string::format(std::string& out, u64 arg)
bool cheat_info::from_str(std::string_view cheat_line)
{
- const auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false);
+ auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false);
s64 val64 = 0;
if (cheat_vec.size() != 5 || !try_to_int64(&val64, cheat_vec[2], 0, cheat_type_max - 1))
@@ -38,11 +38,11 @@ bool cheat_info::from_str(std::string_view cheat_line)
return false;
}
- game = cheat_vec[0];
- description = cheat_vec[1];
+ game = std::move(cheat_vec[0]);
+ description = std::move(cheat_vec[1]);
type = cheat_type{::narrow(val64)};
offset = std::stoul(cheat_vec[3]);
- red_script = cheat_vec[4];
+ red_script = std::move(cheat_vec[4]);
return true;
}
diff --git a/Utilities/geometry.h b/Utilities/geometry.h
index 3ffbc04dd3..a5881d1cd5 100644
--- a/Utilities/geometry.h
+++ b/Utilities/geometry.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
@@ -1027,3 +1028,6 @@ using color1u = color1_base;
using color1i = color1_base;
using color1f = color1_base;
using color1d = color1_base;
+
+using mat3f = color3_base[3];
+static_assert(sizeof(mat3f) == sizeof(float) * 3 * 3);
diff --git a/Utilities/sema.cpp b/Utilities/sema.cpp
index ee3dd98ffa..81e3616f53 100644
--- a/Utilities/sema.cpp
+++ b/Utilities/sema.cpp
@@ -41,7 +41,13 @@ void semaphore_base::imp_wait()
// Add waiter
value += c_waiter;
}
- });
+ else
+ {
+ return false;
+ }
+
+ return true;
+ }).first;
if (value & c_value_mask)
{
diff --git a/Utilities/stack_trace.cpp b/Utilities/stack_trace.cpp
index 049e0f1805..0a95269848 100644
--- a/Utilities/stack_trace.cpp
+++ b/Utilities/stack_trace.cpp
@@ -119,23 +119,23 @@ namespace utils
if (sym->NameLen)
{
- const auto function_name = wstr_to_utf8(sym->Name, static_cast(sym->NameLen));
+ std::string function_name = wstr_to_utf8(sym->Name, static_cast(sym->NameLen));
// Attempt to get file and line information if available
DWORD unused2;
if (SymGetLineFromAddrW64(hProcess, reinterpret_cast(pointer), &unused2, &line_info))
{
- const auto full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name);
- result.push_back(full_path);
+ std::string full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name);
+ result.push_back(std::move(full_path));
}
else
{
- result.push_back(function_name);
+ result.push_back(std::move(function_name));
}
}
else
{
- result.push_back(fmt::format("rpcs3@0xp", pointer));
+ result.push_back(fmt::format("rpcs3@0x%p", pointer));
}
}
diff --git a/Utilities/stack_trace.h b/Utilities/stack_trace.h
index d0cec0cf4c..d186a698f4 100644
--- a/Utilities/stack_trace.h
+++ b/Utilities/stack_trace.h
@@ -14,7 +14,7 @@ namespace utils
// Printing utilities
template
- concept Logger = requires (T& t, const std::string& msg)
+ concept Logger = requires (T& t, std::string_view msg)
{
{ t.print(msg) };
};
@@ -28,7 +28,7 @@ namespace utils
: log(chan)
{}
- void print(const std::string& s)
+ void print(std::string_view s)
{
log.error("%s", s);
}
diff --git a/Utilities/stereo_config.cpp b/Utilities/stereo_config.cpp
new file mode 100644
index 0000000000..186b8ef5e3
--- /dev/null
+++ b/Utilities/stereo_config.cpp
@@ -0,0 +1,159 @@
+#include "stdafx.h"
+#include "stereo_config.h"
+#include "Emu/system_config.h"
+
+stereo_config::stereo_matrices::stereo_matrices(const std::array, 3>& l, const std::array, 3>& r)
+{
+ for (usz i = 0; i < 3; i++)
+ {
+ left[i] = l[i];
+ right[i] = r[i];
+ }
+}
+
+atomic_t stereo_config::s_live_preview_enabled = false;
+stereo_config::stereo_matrices stereo_config::m_live_matrices = {};
+
+const std::unordered_map stereo_config::m_matrices =
+{
+ {stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0)
+ },
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 1)
+ }
+ )},
+ {stereo_render_mode_options::anaglyph_red_green, stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0)
+ },
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 0)
+ }
+ )},
+ {stereo_render_mode_options::anaglyph_red_blue, stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0)
+ },
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 1)
+ }
+ )},
+ {stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0.5f)
+ },
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 0.5f)
+ }
+ )},
+ {stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices(
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 0)
+ },
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 1)
+ }
+ )},
+ {stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 0)
+ },
+ {
+ color3_base(0, 0, 1.0f / 3.0f),
+ color3_base(0, 0, 1.0f / 3.0f),
+ color3_base(0, 0, 1.0f / 3.0f)
+ }
+ )},
+};
+
+void stereo_config::update_from_config(bool stereo_enabled)
+{
+ m_stereo_mode = stereo_enabled ? g_cfg.video.stereo_render_mode.get() : stereo_render_mode_options::disabled;
+
+ if (m_stereo_mode == stereo_render_mode_options::anaglyph_custom)
+ {
+ stereo_config::convert_matrix(m_custom_matrices.left, g_cfg.video.custom_anaglyph_matrices.left.get_map());
+ stereo_config::convert_matrix(m_custom_matrices.right, g_cfg.video.custom_anaglyph_matrices.right.get_map());
+ }
+}
+
+const stereo_config::stereo_matrices& stereo_config::matrices() const
+{
+ if (m_in_emulation && s_live_preview_enabled)
+ {
+ return m_live_matrices;
+ }
+
+ switch (m_stereo_mode)
+ {
+ case stereo_render_mode_options::disabled:
+ case stereo_render_mode_options::side_by_side:
+ case stereo_render_mode_options::over_under:
+ case stereo_render_mode_options::interlaced:
+ break;
+ case stereo_render_mode_options::anaglyph_red_green:
+ case stereo_render_mode_options::anaglyph_red_blue:
+ case stereo_render_mode_options::anaglyph_red_cyan:
+ case stereo_render_mode_options::anaglyph_magenta_cyan:
+ case stereo_render_mode_options::anaglyph_trioscopic:
+ case stereo_render_mode_options::anaglyph_amber_blue:
+ return ::at32(m_matrices, m_stereo_mode);
+ case stereo_render_mode_options::anaglyph_custom:
+ return m_custom_matrices;
+ }
+
+ static const stereo_matrices s_left_only_matrices = stereo_matrices(
+ {
+ color3_base(1, 0, 0),
+ color3_base(0, 1, 0),
+ color3_base(0, 0, 1)
+ },
+ {
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0),
+ color3_base(0, 0, 0)
+ });
+ return s_left_only_matrices;
+}
+
+std::map stereo_config::get_custom_matrix(bool is_left) const
+{
+ return convert_matrix(is_left ? m_custom_matrices.left : m_custom_matrices.right);
+}
+
+std::map stereo_config::convert_matrix(const mat3f& mat)
+{
+ std::map values {};
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ values[fmt::format("%d%d", i, j)] = fmt::format("%f", mat[i].rgb[j]);
+ }
+ }
+ return values;
+}
diff --git a/Utilities/stereo_config.h b/Utilities/stereo_config.h
new file mode 100644
index 0000000000..e1ea7fce03
--- /dev/null
+++ b/Utilities/stereo_config.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#include "geometry.h"
+#include "Emu/system_config_types.h"
+#include "util/atomic.hpp"
+#include "Utilities/StrFmt.h"
+#include "Utilities/StrUtil.h"
+#include