Compare commits
320 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f6ea921e0d | |||
| 339877ea7d | |||
| ba9464c547 | |||
| e7165cbe99 | |||
| d85f1c7155 | |||
| 9e2c41bd48 | |||
| 2d1be09180 | |||
| d25972e196 | |||
| 62d32ab45e | |||
| 1ca995cc96 | |||
| 3164d44952 | |||
| 546f3082a2 | |||
| 8d9abd396b | |||
| 8112c7cc19 | |||
| bf85a3fdd9 | |||
| 8e370c2cf1 | |||
| 6349ea2ee9 | |||
| 2f2ac69d60 | |||
| 4a92d96cf4 | |||
| 9a2243bfcb | |||
| 97704e35e0 | |||
| 40e9ee5af0 | |||
| 3fa1241ac0 | |||
| 62747bb40d | |||
| 44b33f1962 | |||
| cc999d4210 | |||
| 7c2bd56776 | |||
| 5fc7450865 | |||
| 4ca0f0b11d | |||
| 66de4022b1 | |||
| 2b9368abf5 | |||
| f8a5a6ad93 | |||
| fa1aa9dcf3 | |||
| 883a6889e0 | |||
| 1b5a895346 | |||
| 2187f8ae51 | |||
| 17f1fc31bc | |||
| ecf77ecef0 | |||
| db474c292f | |||
| b08e805020 | |||
| 7b540cdb20 | |||
| 059e70069d | |||
| 9c719a5857 | |||
| 375eb21c07 | |||
| 1bd246d2d3 | |||
| 8c82ce8bed | |||
| eafa6a9a25 | |||
| b9d027a76c | |||
| 0a45c84e16 | |||
| a88951baf3 | |||
| 66349fcb5b | |||
| c6602f673b | |||
| 7c6b3dcdb9 | |||
| c11979d124 | |||
| 3058a71d72 | |||
| b41b10a031 | |||
| a87d175295 | |||
| dff29a7864 | |||
| dd81c92a02 | |||
| 64ba0dd5e6 | |||
| 0a5b88ac38 | |||
| f63a34dcc2 | |||
| d8710c431d | |||
| 67464f97df | |||
| 190378bc33 | |||
| da599f7286 | |||
| 73c8c270a6 | |||
| d7da6a713b | |||
| 6a2ad0a0aa | |||
| 84b61e3850 | |||
| 7e436f9bf1 | |||
| 2613d7eee7 | |||
| 55e17ccd3b | |||
| f368a56c66 | |||
| 79cb35daa6 | |||
| b0c1791a0c | |||
| 61a2604824 | |||
| 08c926622d | |||
| b533a560e6 | |||
| e4a49cd425 | |||
| fdce82fc24 | |||
| 5f00b87a44 | |||
| 53180b8141 | |||
| 4cc0e4c7fc | |||
| 46364856ae | |||
| f5c420994d | |||
| c5ee48f54a | |||
| 6f50f0854f | |||
| 53d76db753 | |||
| c860aa2107 | |||
| a545fe92a0 | |||
| ee436307cf | |||
| 33016742f1 | |||
| 320e8d634a | |||
| e52b6ecbbf | |||
| a4712a283f | |||
| 6da3021158 | |||
| 0064ecb85b | |||
| 1d71854c7f | |||
| a9be80da6f | |||
| 2387af0854 | |||
| 021f16f775 | |||
| fb1c1eeaef | |||
| 6fa7efbcc3 | |||
| c534da86c3 | |||
| 6b5a2f781a | |||
| cd7cb1cc11 | |||
| c0b358003f | |||
| 29b4577fdb | |||
| b34eba2fb3 | |||
| 6850e2e5a8 | |||
| 3c2815e89c | |||
| 4312221498 | |||
| c102d1ec0d | |||
| 98e9b3cf30 | |||
| f9ffce76f2 | |||
| e2e1cf02f4 | |||
| 026297334f | |||
| 4f47fee360 | |||
| 9c143d3d45 | |||
| 58eb7d2c04 | |||
| 2bb533274c | |||
| 3fc41b6d66 | |||
| c350c02592 | |||
| 9dde2bf04e | |||
| 4e160320ef | |||
| ca762d5b84 | |||
| d93f5f9f84 | |||
| ac28c391f6 | |||
| d519571e18 | |||
| 99e6b19025 | |||
| 3d6773dd25 | |||
| d36a4bd858 | |||
| 26db44b3b3 | |||
| a6c3ef350a | |||
| f3d0c3ba28 | |||
| 97c7d069ac | |||
| af7ae45888 | |||
| 5c4b0a165b | |||
| af2af23306 | |||
| 59f54caf08 | |||
| b3ac8127ea | |||
| 90ea92d93b | |||
| f694000454 | |||
| b2daaff29f | |||
| d93d9b2c5a | |||
| 4f23f5505a | |||
| 69bc124930 | |||
| 056a8ba1b1 | |||
| 350e7b09f5 | |||
| 1899b61fa5 | |||
| 773808169e | |||
| a6fb0c8931 | |||
| 155883ea2a | |||
| ca87d103fd | |||
| 5dd7792c3d | |||
| e8cd6f4ef6 | |||
| b6ed4201df | |||
| c25e3e33ac | |||
| a8dd053593 | |||
| 66d2bc3dd5 | |||
| d773a3f94e | |||
| 4e0995744a | |||
| 80c1c5dd6d | |||
| 91a8c56c37 | |||
| 5144a999b3 | |||
| 3b1abec405 | |||
| 8b02f46e67 | |||
| 41e6cbbb5a | |||
| ed7f1aa17c | |||
| 5839ad9c70 | |||
| 9c725935dc | |||
| 108276c076 | |||
| 82a4ff051e | |||
| d1c8073106 | |||
| 3829802699 | |||
| 46bbbd2056 | |||
| cbbf52878a | |||
| 278daddd2a | |||
| 48e2d808c9 | |||
| 28ab991302 | |||
| d40a4ee04e | |||
| 7ea0a96d02 | |||
| 985703058b | |||
| 14ca11fdf4 | |||
| 7bc08d05c6 | |||
| 79d0e0eb3c | |||
| 570b2bddca | |||
| f3cf1da7b7 | |||
| 3c9a4417f2 | |||
| b212935c70 | |||
| e05d359721 | |||
| 74bcbd9816 | |||
| b14c76c1c5 | |||
| 6d058586c4 | |||
| 771837ee55 | |||
| d2fc5a5b81 | |||
| 3b21833b8e | |||
| ed02f3a2ce | |||
| 7028e85fac | |||
| 96f73f4497 | |||
| 3ff4f188f4 | |||
| 61e1c0f1fb | |||
| 631e7ef979 | |||
| 7dce197ec4 | |||
| 9e0824112f | |||
| e1734b51c3 | |||
| 4cac76caad | |||
| 077f2a73e1 | |||
| a60734e27b | |||
| 3574677b66 | |||
| b3cc01387f | |||
| e26c80c129 | |||
| 80b6faef10 | |||
| 7d0df300ea | |||
| 1ca8ab393a | |||
| 3cca094d2d | |||
| a088b18e95 | |||
| e5af69d7b2 | |||
| 814821d760 | |||
| 7a8c88056a | |||
| b6b043b23d | |||
| c5db9899e4 | |||
| 081c094c5a | |||
| b6c8374aa5 | |||
| 06a6880c6c | |||
| bd5c10fd48 | |||
| d3804de243 | |||
| 36cd81fb5f | |||
| 7a4d7aa936 | |||
| aff082e0c1 | |||
| 2b144101ab | |||
| d4b7c31ec8 | |||
| 6c9445ff28 | |||
| 2d6ca912fe | |||
| 3b9cc0bc3a | |||
| 88175aa84f | |||
| a7c606c8ac | |||
| 48acbbe4f5 | |||
| b729772041 | |||
| ea0d8a0d78 | |||
| e79f56bfdc | |||
| a543f38704 | |||
| 1cdc401cc5 | |||
| 50d6396f99 | |||
| 7d41bbdd2b | |||
| d53a6a87f6 | |||
| 1e63385dfc | |||
| 4ffeee0344 | |||
| 0b9c53e254 | |||
| 27f857a8fe | |||
| c340eb2f17 | |||
| e43435e152 | |||
| ac3d14cf5c | |||
| 426f3c445b | |||
| bbeb7c4cd7 | |||
| 6c87413f48 | |||
| b10c742f10 | |||
| 9e3e01d8f6 | |||
| bcd9663349 | |||
| 72fa4098dc | |||
| 6dc95dd078 | |||
| 11050a7032 | |||
| f826f95c70 | |||
| 934bc34685 | |||
| f463c128e3 | |||
| 7168cd566a | |||
| 110c786d80 | |||
| fb194241d5 | |||
| 09554c43ba | |||
| 1b1143094e | |||
| 8121bd443c | |||
| a1a140db91 | |||
| e9fb3572f9 | |||
| 1d85de6236 | |||
| 6981e308a0 | |||
| 8e80ddd99f | |||
| beac01d5d1 | |||
| d7f8e25cca | |||
| e0c3df5328 | |||
| 59468f1e1e | |||
| 8ab0ceaa67 | |||
| b9f05ba71b | |||
| 5311f004d0 | |||
| cb276f0da7 | |||
| 34c26eff68 | |||
| b700a7abd9 | |||
| c279583f1f | |||
| ce12c29441 | |||
| e95a980651 | |||
| 87fc45d661 | |||
| 93a2f1edc8 | |||
| 22fe8648ef | |||
| f42b09d1fc | |||
| b8e6785789 | |||
| afdb57bee9 | |||
| b22d0a0b4a | |||
| ec989781a3 | |||
| 110ef818b3 | |||
| aa841ac332 | |||
| ceb2168375 | |||
| 02eb549208 | |||
| 13de8233b0 | |||
| ff4444b18e | |||
| 555ace0955 | |||
| a03a78dbd2 | |||
| f63b1b5dc1 | |||
| ac104a519e | |||
| bd95c728f0 | |||
| 122ccca50e | |||
| 83bfbc1399 | |||
| 80f972cd38 | |||
| 615f416af3 | |||
| 1c37f64a58 | |||
| 7c9261a461 | |||
| cb3a83cba7 | |||
| b0dbac9a07 | |||
| f92677fa7e | |||
| 789bab97be | |||
| 3e60bd2aa6 |
@@ -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 \
|
||||
|
||||
+1
-1
@@ -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 \
|
||||
|
||||
@@ -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*
|
||||
|
||||
|
||||
+39
-21
@@ -4,11 +4,16 @@
|
||||
cd build || exit 1
|
||||
|
||||
cd bin
|
||||
git clone --revision=a075e5e417f87675ea3137b7365f3e5a99608d72 https://github.com/KhronosGroup/MoltenVK.git
|
||||
cd MoltenVK
|
||||
./fetchDependencies --macos
|
||||
sudo xcode-select -switch /Applications/Xcode_16.2.app/Contents/Developer
|
||||
make macos MVK_USE_METAL_PRIVATE_API=1
|
||||
cd ../
|
||||
|
||||
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
|
||||
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar
|
||||
tar -xvf MoltenVK-macos-privateapi.tar
|
||||
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
|
||||
cp "MoltenVK/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"
|
||||
@@ -27,21 +32,18 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
|
||||
# Download translations
|
||||
mkdir -p "rpcs3.app/Contents/translations"
|
||||
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
|
||||
| grep "browser_download_url" \
|
||||
| grep "RPCS3-languages.zip" \
|
||||
| cut -d '"' -f 4)
|
||||
if [ -z "$ZIP_URL" ]; then
|
||||
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
|
||||
else
|
||||
echo "Downloading translations from: $ZIP_URL"
|
||||
curl -L -o translations.zip "$ZIP_URL" || {
|
||||
echo "Failed to download translations.zip. Continuing without translations."
|
||||
exit 0
|
||||
}
|
||||
unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \
|
||||
ZIP_URL="https://github.com/RPCS3/rpcs3_translations/releases/latest/download/RPCS3-languages.zip"
|
||||
echo "Downloading translations from: $ZIP_URL"
|
||||
if curl -fsSL "$ZIP_URL" -o "translations.zip"; then
|
||||
echo "Successfully downloaded translations."
|
||||
if unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1; then
|
||||
rm -f translations.zip
|
||||
else
|
||||
echo "Failed to extract translations.zip. Continuing without translations."
|
||||
rm -f translations.zip
|
||||
rm -f translations.zip
|
||||
fi
|
||||
else
|
||||
echo "Warning: Failed to download translations. Skipping..."
|
||||
fi
|
||||
|
||||
# Copy Qt translations manually
|
||||
@@ -55,9 +57,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
|
||||
|
||||
@@ -24,6 +24,7 @@ mkdir ./bin/config
|
||||
mkdir ./bin/config/input_configs
|
||||
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
|
||||
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -f ISO-8859-1 -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
|
||||
curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -f ISO-8859-1 -t UTF-8 1> ./bin/GuiConfigs/config_database.dat
|
||||
|
||||
# Download translations
|
||||
mkdir -p ./bin/share/qt6/translations
|
||||
|
||||
@@ -14,6 +14,7 @@ mkdir ./bin/config
|
||||
mkdir ./bin/config/input_configs
|
||||
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
|
||||
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
|
||||
curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -t UTF-8 1> ./bin/GuiConfigs/config_database.dat
|
||||
|
||||
# Download translations
|
||||
mkdir -p ./bin/qt6/translations
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
+11
-11
@@ -30,23 +30,23 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
|
||||
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.10"
|
||||
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.10"
|
||||
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.10"
|
||||
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 }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
runs-on: macos-14
|
||||
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
|
||||
@@ -209,16 +209,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 +235,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
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/7zip/7zip updated: 839151eaaa...8c63d71ff8
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: dc034fc671...75c79d4c8a
Vendored
+1
-1
Submodule 3rdparty/OpenAL/openal-soft updated: c41d64c6a3...b2c48f7718
Vendored
+1
-1
Submodule 3rdparty/SoundTouch/soundtouch updated: 3982730833...a0fba77b6f
Vendored
+15
-15
@@ -7,24 +7,24 @@ if(USE_SYSTEM_CURL)
|
||||
target_link_libraries(3rdparty_libcurl INTERFACE CURL::libcurl)
|
||||
else()
|
||||
message(STATUS "RPCS3: building libcurl + wolfssl submodules")
|
||||
set(BUILD_CURL_EXE OFF CACHE BOOL "Set to ON to build curl executable.")
|
||||
set(BUILD_STATIC_CURL OFF CACHE BOOL "Set to ON to build curl executable with static libcurl.")
|
||||
set(BUILD_STATIC_LIBS ON CACHE BOOL "Set to ON to build static libcurl.")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Set to ON to build shared libcurl.")
|
||||
set(BUILD_CURL_EXE OFF CACHE INTERNAL "")
|
||||
set(BUILD_STATIC_CURL OFF CACHE INTERNAL "")
|
||||
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
|
||||
find_package(WolfSSL REQUIRED)
|
||||
set(CURL_USE_WOLFSSL ON CACHE BOOL "enable wolfSSL for SSL/TLS")
|
||||
set(CURL_USE_OPENSSL OFF CACHE BOOL "Use OpenSSL code. Experimental")
|
||||
set(HTTP_ONLY ON CACHE BOOL "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)")
|
||||
set(USE_LIBIDN2 OFF CACHE BOOL "Use libidn2 for IDN support") # Disabled because MacOS CI doesn't work otherwise
|
||||
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
|
||||
option(CURL_DISABLE_INSTALL "Disable installation targets" ON)
|
||||
set(CURL_USE_WOLFSSL ON CACHE INTERNAL "")
|
||||
set(CURL_USE_OPENSSL OFF CACHE INTERNAL "")
|
||||
set(HTTP_ONLY ON CACHE INTERNAL "")
|
||||
set(USE_LIBIDN2 OFF CACHE INTERNAL "") # Disabled because MacOS CI doesn't work otherwise
|
||||
set(CURL_CA_PATH "none" CACHE INTERNAL "")
|
||||
set(CURL_DISABLE_INSTALL ON CACHE INTERNAL "")
|
||||
if(WIN32)
|
||||
set(ENABLE_UNICODE ON CACHE BOOL "enable Unicode")
|
||||
set(ENABLE_UNICODE ON CACHE INTERNAL "")
|
||||
endif()
|
||||
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2")
|
||||
set(CURL_USE_LIBPSL OFF CACHE BOOL "Use libPSL")
|
||||
option(BUILD_TESTING "Build tests" OFF)
|
||||
option(BUILD_EXAMPLES "Build libcurl examples" OFF)
|
||||
set(CURL_USE_LIBSSH2 OFF CACHE INTERNAL "")
|
||||
set(CURL_USE_LIBPSL OFF CACHE INTERNAL "")
|
||||
set(BUILD_TESTING OFF CACHE INTERNAL "")
|
||||
set(BUILD_EXAMPLES OFF CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(curl EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 8c908d2d0a...a05f34973e
Vendored
+11
-3
@@ -66,6 +66,7 @@
|
||||
<ClCompile Include="curl\lib\asyn-thrdd.c" />
|
||||
<ClCompile Include="curl\lib\bufq.c" />
|
||||
<ClCompile Include="curl\lib\bufref.c" />
|
||||
<ClCompile Include="curl\lib\cf-dns.c" />
|
||||
<ClCompile Include="curl\lib\cf-h1-proxy.c" />
|
||||
<ClCompile Include="curl\lib\cf-h2-proxy.c" />
|
||||
<ClCompile Include="curl\lib\cf-haproxy.c" />
|
||||
@@ -107,7 +108,6 @@
|
||||
<ClCompile Include="curl\lib\curl_memrchr.c" />
|
||||
<ClCompile Include="curl\lib\curl_ntlm_core.c" />
|
||||
<ClCompile Include="curl\lib\curl_range.c" />
|
||||
<ClCompile Include="curl\lib\curl_rtmp.c" />
|
||||
<ClCompile Include="curl\lib\curl_sasl.c" />
|
||||
<ClCompile Include="curl\lib\curl_sha512_256.c" />
|
||||
<ClCompile Include="curl\lib\curl_share.c" />
|
||||
@@ -118,6 +118,7 @@
|
||||
<ClCompile Include="curl\lib\cw-pause.c" />
|
||||
<ClCompile Include="curl\lib\dict.c" />
|
||||
<ClCompile Include="curl\lib\dllmain.c" />
|
||||
<ClCompile Include="curl\lib\dnscache.c" />
|
||||
<ClCompile Include="curl\lib\doh.c" />
|
||||
<ClCompile Include="curl\lib\dynhds.c" />
|
||||
<ClCompile Include="curl\lib\easy.c" />
|
||||
@@ -172,6 +173,7 @@
|
||||
<ClCompile Include="curl\lib\pingpong.c" />
|
||||
<ClCompile Include="curl\lib\pop3.c" />
|
||||
<ClCompile Include="curl\lib\progress.c" />
|
||||
<ClCompile Include="curl\lib\protocol.c" />
|
||||
<ClCompile Include="curl\lib\psl.c" />
|
||||
<ClCompile Include="curl\lib\rand.c" />
|
||||
<ClCompile Include="curl\lib\ratelimit.c" />
|
||||
@@ -195,6 +197,8 @@
|
||||
<ClCompile Include="curl\lib\system_win32.c" />
|
||||
<ClCompile Include="curl\lib\telnet.c" />
|
||||
<ClCompile Include="curl\lib\tftp.c" />
|
||||
<ClCompile Include="curl\lib\thrdpool.c" />
|
||||
<ClCompile Include="curl\lib\thrdqueue.c" />
|
||||
<ClCompile Include="curl\lib\transfer.c" />
|
||||
<ClCompile Include="curl\lib\uint-bset.c" />
|
||||
<ClCompile Include="curl\lib\uint-hash.c" />
|
||||
@@ -255,6 +259,7 @@
|
||||
<ClInclude Include="curl\lib\asyn.h" />
|
||||
<ClInclude Include="curl\lib\bufq.h" />
|
||||
<ClInclude Include="curl\lib\bufref.h" />
|
||||
<ClInclude Include="curl\lib\cf-dns.h" />
|
||||
<ClInclude Include="curl\lib\cf-h1-proxy.h" />
|
||||
<ClInclude Include="curl\lib\cf-h2-proxy.h" />
|
||||
<ClInclude Include="curl\lib\cf-haproxy.h" />
|
||||
@@ -275,7 +280,6 @@
|
||||
<ClInclude Include="curl\lib\curlx\base64.h" />
|
||||
<ClInclude Include="curl\lib\curlx\basename.h" />
|
||||
<ClInclude Include="curl\lib\curlx\binmode.h" />
|
||||
<ClInclude Include="curl\lib\curlx\curlx.h" />
|
||||
<ClInclude Include="curl\lib\curlx\dynbuf.h" />
|
||||
<ClInclude Include="curl\lib\curlx\fopen.h" />
|
||||
<ClInclude Include="curl\lib\curlx\inet_ntop.h" />
|
||||
@@ -309,7 +313,6 @@
|
||||
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
|
||||
<ClInclude Include="curl\lib\curl_printf.h" />
|
||||
<ClInclude Include="curl\lib\curl_range.h" />
|
||||
<ClInclude Include="curl\lib\curl_rtmp.h" />
|
||||
<ClInclude Include="curl\lib\curl_sasl.h" />
|
||||
<ClInclude Include="curl\lib\curl_setup.h" />
|
||||
<ClInclude Include="curl\lib\curl_setup_once.h" />
|
||||
@@ -322,6 +325,7 @@
|
||||
<ClInclude Include="curl\lib\cw-out.h" />
|
||||
<ClInclude Include="curl\lib\cw-pause.h" />
|
||||
<ClInclude Include="curl\lib\dict.h" />
|
||||
<ClInclude Include="curl\lib\dnscache.h" />
|
||||
<ClInclude Include="curl\lib\doh.h" />
|
||||
<ClInclude Include="curl\lib\dynhds.h" />
|
||||
<ClInclude Include="curl\lib\easyif.h" />
|
||||
@@ -332,6 +336,7 @@
|
||||
<ClInclude Include="curl\lib\file.h" />
|
||||
<ClInclude Include="curl\lib\fileinfo.h" />
|
||||
<ClInclude Include="curl\lib\formdata.h" />
|
||||
<ClInclude Include="curl\lib\ftp-int.h" />
|
||||
<ClInclude Include="curl\lib\ftp.h" />
|
||||
<ClInclude Include="curl\lib\ftplistparser.h" />
|
||||
<ClInclude Include="curl\lib\functypes.h" />
|
||||
@@ -368,6 +373,7 @@
|
||||
<ClInclude Include="curl\lib\pingpong.h" />
|
||||
<ClInclude Include="curl\lib\pop3.h" />
|
||||
<ClInclude Include="curl\lib\progress.h" />
|
||||
<ClInclude Include="curl\lib\protocol.h" />
|
||||
<ClInclude Include="curl\lib\psl.h" />
|
||||
<ClInclude Include="curl\lib\rand.h" />
|
||||
<ClInclude Include="curl\lib\ratelimit.h" />
|
||||
@@ -395,6 +401,8 @@
|
||||
<ClInclude Include="curl\lib\system_win32.h" />
|
||||
<ClInclude Include="curl\lib\telnet.h" />
|
||||
<ClInclude Include="curl\lib\tftp.h" />
|
||||
<ClInclude Include="curl\lib\thrdpool.h" />
|
||||
<ClInclude Include="curl\lib\thrdqueue.h" />
|
||||
<ClInclude Include="curl\lib\transfer.h" />
|
||||
<ClInclude Include="curl\lib\uint-bset.h" />
|
||||
<ClInclude Include="curl\lib\uint-hash.h" />
|
||||
|
||||
+33
-9
@@ -66,9 +66,6 @@
|
||||
<ClCompile Include="curl\lib\curl_range.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\curl_rtmp.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\curl_sasl.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -552,6 +549,21 @@
|
||||
<ClCompile Include="curl\lib\curlx\strdup.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\cf-dns.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\dnscache.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\protocol.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\thrdpool.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="curl\lib\thrdqueue.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="curl\include\curl\curl.h">
|
||||
@@ -668,9 +680,6 @@
|
||||
<ClInclude Include="curl\lib\curl_range.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\curl_rtmp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\curl_sasl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -1040,9 +1049,6 @@
|
||||
<ClInclude Include="curl\lib\curlx\base64.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\curlx\curlx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\curlx\dynbuf.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -1121,6 +1127,24 @@
|
||||
<ClInclude Include="curl\lib\curlx\strdup.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\cf-dns.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\dnscache.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\ftp-int.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\protocol.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\thrdpool.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="curl\lib\thrdqueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="curl\lib\libcurl.rc">
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/ffmpeg updated: ce81114ed9...42300548cb
Vendored
+1
-1
Submodule 3rdparty/glslang/glslang updated: fc9889c889...f0bd0257c3
Vendored
+1
-1
Submodule 3rdparty/libpng/libpng updated: d5515b5b8b...3061454d98
Vendored
+1
-1
Submodule 3rdparty/libsdl-org/SDL updated: 683181b47c...8e37db5e79
Vendored
+1
-1
Submodule 3rdparty/libusb/libusb updated: 15a7ebb4d4...87a55632db
+5
@@ -54,6 +54,9 @@
|
||||
<ClCompile Condition="'$(Configuration)'=='Release'">
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'">
|
||||
<PreprocessorDefinitions>LIBUSB_WINDOWS_HOTPLUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
|
||||
</Lib>
|
||||
@@ -64,6 +67,7 @@
|
||||
<ClCompile Include="libusb\libusb\hotplug.c" />
|
||||
<ClCompile Include="libusb\libusb\io.c" />
|
||||
<ClCompile Include="libusb\libusb\os\events_windows.c" />
|
||||
<ClCompile Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'" Include="libusb\libusb\os\windows_hotplug.c" />
|
||||
<ClCompile Include="libusb\libusb\strerror.c" />
|
||||
<ClCompile Include="libusb\libusb\sync.c" />
|
||||
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
|
||||
@@ -72,6 +76,7 @@
|
||||
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'" Include="libusb\libusb\os\windows_hotplug.h" />
|
||||
<ClInclude Include="libusb\msvc\config.h" />
|
||||
<ClInclude Include="libusb\libusb\hotplug.h" />
|
||||
<ClInclude Include="libusb\libusb\libusb.h" />
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/opencv/opencv updated: 67f53c26a7...6900a1265b
Vendored
+6
-3
@@ -119,7 +119,8 @@ echo -e "\n\nResult:\n"
|
||||
|
||||
# Find the max length of the paths (before '->')
|
||||
max_len=0
|
||||
while IFS='->' read -r left _; do
|
||||
while read -r line; do
|
||||
left="${line%%->*}"
|
||||
len=$(echo -n "$left" | wc -c)
|
||||
if (( len > max_len )); then
|
||||
max_len=$len
|
||||
@@ -127,8 +128,10 @@ while IFS='->' read -r left _; do
|
||||
done < "$resultfile"
|
||||
|
||||
# Print with padding so '->' lines up
|
||||
while IFS='->' read -r left right; do
|
||||
right=$(echo "$right" | sed 's/^[[:space:]]*>*[[:space:]]*//')
|
||||
while read -r line; do
|
||||
left="${line%%->*}"
|
||||
right="${line#*->}"
|
||||
right=$(echo "$right" | sed 's/^[[:space:]]*//')
|
||||
printf "%-${max_len}s -> %s\n" "$left" "$right"
|
||||
done < "$resultfile"
|
||||
|
||||
|
||||
Vendored
+15
-16
@@ -4,23 +4,22 @@ if(USE_SYSTEM_WOLFSSL)
|
||||
add_library(wolfssl INTERFACE)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
option(WOLFSSL_TLS13 "Enable wolfSSL TLS v1.3 (default: enabled)" OFF)
|
||||
set(WOLFSSL_SHA3 ON CACHE STRING "Enable wolfSSL SHA-3 support (default: enabled on x86_64/aarch64)")
|
||||
set(WOLFSSL_SHAKE256 ON CACHE STRING "Enable wolfSSL SHAKE256 support (default: enabled on x86_64/aarch64)")
|
||||
option(WOLFSSL_BASE64_ENCODE "Enable Base64 encoding (default: enabled on x86_64)" OFF)
|
||||
option(WOLFSSL_DES3 "Enable DES3 (default: disabled)" ON)
|
||||
option(WOLFSSL_PWDBASED "Enable PWDBASED (default: disabled)" ON)
|
||||
option(WOLFSSL_FAST_MATH "Enable fast math ops (default: disabled)" ON)
|
||||
option(WOLFSSL_EXAMPLES "Enable examples (default: enabled)" OFF)
|
||||
option(WOLFSSL_CRYPT_TESTS "Enable Crypt Bench/Test (default: enabled)" OFF)
|
||||
option(WOLFSSL_ASYNC_THREADS "Enable Asynchronous Threading (default: enabled)" OFF)
|
||||
option(WOLFSSL_BUILD_OUT_OF_TREE "Don't generate files in the source tree (default: yes)" ON)
|
||||
option(WOLFSSL_SNI "Enable SNI (default: disabled)" ON)
|
||||
option(WOLFSSL_OPENSSLEXTRA "Enable extra OpenSSL API, size+ (default: disabled)" ON)
|
||||
option(WOLFSSL_HARDEN "Enable Hardened build, Enables Timing Resistance and Blinding (default: enabled)" OFF)
|
||||
option(WOLFSSL_ALT_CERT_CHAINS "Enable support for Alternate certification chains (default: disabled)" ON)
|
||||
set(WOLFSSL_TLS13 OFF CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA3 ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHAKE256 ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_BASE64_ENCODE OFF CACHE INTERNAL "")
|
||||
set(WOLFSSL_DES3 ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES OFF CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS OFF CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS OFF CACHE INTERNAL "")
|
||||
set(WOLFSSL_BUILD_OUT_OF_TREE ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_SNI ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_OPENSSLEXTRA ON CACHE INTERNAL "")
|
||||
set(WOLFSSL_ALT_CERT_CHAINS ON CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H)
|
||||
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_USE_OPTIONS_H)
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/wolfssl/wolfssl updated: 922d04b356...1d363f3adc
+7
-7
@@ -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. `<QtInstallFolder>\6.11.0\msvc2022_64\`
|
||||
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\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. `<QtInstallFolder>\6.11.0\msvc2022_64\`
|
||||
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\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. `<QtInstallFolder>\6.11.0\msvc2022_64`, version will fill in automatically
|
||||
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\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**)
|
||||
|
||||
|
||||
+2
-2
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
+118
-50
@@ -40,7 +40,7 @@ namespace cfg
|
||||
owner->m_nodes.emplace_back(this);
|
||||
}
|
||||
|
||||
bool _base::from_string(std::string_view, bool)
|
||||
bool _base::from_string(std::string_view /*value*/, bool /*dynamic*/)
|
||||
{
|
||||
cfg_log.fatal("cfg::_base::from_string() purecall");
|
||||
return false;
|
||||
@@ -68,7 +68,7 @@ namespace cfg
|
||||
|
||||
// Incrementally load config entries from YAML::Node.
|
||||
// The config value is preserved if the corresponding YAML node doesn't exist.
|
||||
static void decode(const YAML::Node& data, class _base& rhs, bool dynamic = false);
|
||||
[[nodiscard]] static bool decode(const YAML::Node& data, class _base& rhs, bool dynamic, bool strict);
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||
@@ -76,11 +76,11 @@ std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max, std::string_view name)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int64(): called with an empty string");
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s'): called with an empty string", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end || (start[0] == '-' && sign < 0))
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s', '%s'): invalid integer", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s'): out of bounds (val=%d, min=%d, max=%d)", value, result, min, max);
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s', '%s'): out of bounds (val=%d, min=%d, max=%d)", value, name, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -128,11 +128,11 @@ std::vector<std::string> cfg::make_uint_range(u64 min, u64 max)
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max, std::string_view name)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint64(): called with an empty string");
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s'): called with an empty string", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -152,13 +152,13 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s', '%s'): invalid integer", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (val=%u, min=%u, max=%u)", value, result, min, max);
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s', '%s'): out of bounds (val=%u, min=%u, max=%u)", value, name, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -166,11 +166,11 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_to_uint128(u128* out, std::string_view value)
|
||||
bool try_to_uint128(u128* out, std::string_view value, std::string_view name)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint128(): called with an empty string");
|
||||
if (out) cfg_log.error("cfg::try_to_uint128('%s'): called with an empty string", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ bool try_to_uint128(u128* out, std::string_view value)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_uint128('%s', '%s'): invalid integer", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ bool try_to_uint128(u128* out, std::string_view value)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != start_low64)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_uint128('%s', '%s'): invalid integer", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -220,11 +220,11 @@ std::vector<std::string> cfg::make_float_range(f64 min, f64 max)
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max, std::string_view name)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float(): called with an empty string");
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s'): called with an empty string", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -237,13 +237,13 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
|
||||
|
||||
if (end_check != str.data() + str.size())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s'): invalid float", value);
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s', '%s'): invalid float", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s'): out of bounds (val=%f, min=%f, max=%f)", value, result, min, max);
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s', '%s'): out of bounds (val=%f, min=%f, max=%f)", value, name, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -251,12 +251,8 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_to_string(std::string* out, const f64& value)
|
||||
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<char, 32> str{};
|
||||
|
||||
if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value, std::chars_format::fixed); ec == std::errc())
|
||||
@@ -266,14 +262,15 @@ bool try_to_string(std::string* out, const f64& value)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_string(): could not convert value '%f' to string. error='%s'", value, std::make_error_code(ec).message());
|
||||
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<int>::format) func, std::string_view value)
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::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<int>::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;
|
||||
@@ -313,13 +310,13 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): invalid enum or integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_enum_value('%s', '%s'): invalid enum or integer", value, name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): out of bounds(val=%u, min=0, max=%u)", value, result, max);
|
||||
if (out) cfg_log.error("cfg::try_to_enum_value('%s', '%s'): out of bounds(val=%u, min=0, max=%u)", value, name, result, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -468,37 +465,50 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
bool cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic, bool strict)
|
||||
{
|
||||
if (dynamic && !rhs.get_is_dynamic())
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
case type::node:
|
||||
{
|
||||
if (data.IsScalar() || data.IsSequence())
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return; // ???
|
||||
cfg_log.error("node node is not a map");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar()) continue;
|
||||
|
||||
// Find the key among existing nodes
|
||||
for (const auto& node : static_cast<node&>(rhs).get_nodes())
|
||||
const auto& nodes = static_cast<node&>(rhs).get_nodes();
|
||||
const auto it = std::find_if(nodes.cbegin(), nodes.cend(), [&pair](const auto& node) { return ensure(node)->get_name() == pair.first.Scalar(); });
|
||||
|
||||
if (it == nodes.cend())
|
||||
{
|
||||
if (node->get_name() == pair.first.Scalar())
|
||||
if (strict)
|
||||
{
|
||||
decode(pair.second, *node, dynamic);
|
||||
cfg_log.error("Unknown key found: '%s'", pair.first.Scalar());
|
||||
success = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!decode(pair.second, *ensure(*it), dynamic, strict) && strict)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
return success;
|
||||
}
|
||||
case type::set:
|
||||
{
|
||||
@@ -506,7 +516,10 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
|
||||
if (YAML::convert<decltype(values)>::decode(data, values))
|
||||
{
|
||||
rhs.from_list(std::move(values));
|
||||
if (!rhs.from_list(std::move(values)) && strict)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -516,7 +529,8 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
{
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return;
|
||||
cfg_log.error("map node is not a map");
|
||||
return false;
|
||||
}
|
||||
|
||||
map_of_type<std::string> values;
|
||||
@@ -533,22 +547,36 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
}
|
||||
case type::log:
|
||||
{
|
||||
if (data.IsScalar() || data.IsSequence())
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return; // ???
|
||||
cfg_log.error("log node is not a map");
|
||||
return false;
|
||||
}
|
||||
|
||||
map_of_type<logs::level> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar() || !pair.second.IsScalar()) continue;
|
||||
if (!pair.first.IsScalar() || !pair.second.IsScalar())
|
||||
{
|
||||
if (strict)
|
||||
{
|
||||
if (!pair.first.IsScalar())
|
||||
cfg_log.error("Key in map is not a scalar");
|
||||
else
|
||||
cfg_log.error("Value in map is not a scalar. key='%s'", pair.first.Scalar());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
u64 value;
|
||||
if (cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar()))
|
||||
if (!cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar(), pair.first.Scalar()) && strict)
|
||||
{
|
||||
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
|
||||
return false;
|
||||
}
|
||||
|
||||
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
|
||||
}
|
||||
|
||||
static_cast<log_entry&>(rhs).set_map(std::move(values));
|
||||
@@ -558,20 +586,43 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
{
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return; // ???
|
||||
cfg_log.error("device node is not a map");
|
||||
return false;
|
||||
}
|
||||
|
||||
map_of_type<device_info> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar() || !pair.second.IsMap()) continue;
|
||||
if (!pair.first.IsScalar() || !pair.second.IsMap())
|
||||
{
|
||||
if (strict)
|
||||
{
|
||||
if (!pair.first.IsScalar())
|
||||
cfg_log.error("Key in device map is not a scalar");
|
||||
else
|
||||
cfg_log.error("Value in device map is not a map. key='%s'", pair.first.Scalar());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
device_info info{};
|
||||
|
||||
for (const auto& key_value : pair.second)
|
||||
{
|
||||
if (!key_value.first.IsScalar() || !key_value.second.IsScalar()) continue;
|
||||
if (!key_value.first.IsScalar() || !key_value.second.IsScalar())
|
||||
{
|
||||
if (strict)
|
||||
{
|
||||
if (!key_value.first.IsScalar())
|
||||
cfg_log.error("Key in device info map is not a scalar");
|
||||
else
|
||||
cfg_log.error("Value in device map is not a scalar. key='%s'", key_value.first.Scalar());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key_value.first.Scalar() == "Path")
|
||||
info.path = key_value.second.Scalar();
|
||||
@@ -598,12 +649,17 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
|
||||
if (YAML::convert<std::string>::decode(data, value))
|
||||
{
|
||||
rhs.from_string(value, dynamic);
|
||||
if (!rhs.from_string(value, dynamic) && strict)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
break; // ???
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cfg::node::to_string() const
|
||||
@@ -620,8 +676,7 @@ bool cfg::node::from_string(std::string_view value, bool dynamic)
|
||||
|
||||
if (error.empty())
|
||||
{
|
||||
cfg::decode(result, *this, dynamic);
|
||||
return true;
|
||||
return cfg::decode(result, *this, dynamic, false);
|
||||
}
|
||||
|
||||
cfg_log.error("Failed to load node: %s", error);
|
||||
@@ -644,6 +699,19 @@ void cfg::node::restore_defaults()
|
||||
}
|
||||
}
|
||||
|
||||
bool cfg::node::validate(std::string_view value)
|
||||
{
|
||||
auto [result, error] = yaml_load(std::string(value));
|
||||
|
||||
if (error.empty())
|
||||
{
|
||||
return cfg::decode(result, *this, false, true);
|
||||
}
|
||||
|
||||
cfg_log.error("Failed to load node: %s", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string cfg::map_entry::get_value(std::string_view key)
|
||||
{
|
||||
if (auto it = m_map.find(key); it != m_map.end())
|
||||
|
||||
+17
-9
@@ -25,7 +25,7 @@ namespace cfg
|
||||
std::vector<std::string> make_float_range(f64 min, f64 max);
|
||||
|
||||
// Internal hack
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view);
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value, std::string_view name = {});
|
||||
|
||||
// Internal hack
|
||||
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
|
||||
@@ -110,7 +110,7 @@ namespace cfg
|
||||
}
|
||||
|
||||
// Try to convert from string (optional)
|
||||
virtual bool from_string(std::string_view, bool /*dynamic*/ = false);
|
||||
virtual bool from_string(std::string_view value, bool dynamic = false);
|
||||
|
||||
// Get string list (optional)
|
||||
virtual std::vector<std::string> to_list() const
|
||||
@@ -161,6 +161,9 @@ namespace cfg
|
||||
|
||||
// Restore default members
|
||||
void restore_defaults() override;
|
||||
|
||||
// Try to convert from string and validate
|
||||
bool validate(std::string_view value);
|
||||
};
|
||||
|
||||
class _bool final : public _base
|
||||
@@ -301,7 +304,7 @@ namespace cfg
|
||||
{
|
||||
u64 result;
|
||||
|
||||
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value))
|
||||
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value, m_name))
|
||||
{
|
||||
// No narrowing check, it's hard to do right there
|
||||
m_value = static_cast<T>(static_cast<std::underlying_type_t<T>>(result));
|
||||
@@ -382,7 +385,7 @@ namespace cfg
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
s64 result;
|
||||
if (try_to_int64(&result, value, Min, Max))
|
||||
if (try_to_int64(&result, value, Min, Max, m_name))
|
||||
{
|
||||
m_value = static_cast<int_type>(result);
|
||||
return true;
|
||||
@@ -451,7 +454,7 @@ namespace cfg
|
||||
std::string to_string() const override
|
||||
{
|
||||
std::string result;
|
||||
if (try_to_string(&result, m_value))
|
||||
if (try_to_string(&result, m_value, m_name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -462,7 +465,7 @@ namespace cfg
|
||||
std::string def_to_string() const override
|
||||
{
|
||||
std::string result;
|
||||
if (try_to_string(&result, def))
|
||||
if (try_to_string(&result, def, m_name))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
@@ -473,7 +476,7 @@ namespace cfg
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
f64 result;
|
||||
if (try_to_float(&result, value, Min, Max))
|
||||
if (try_to_float(&result, value, Min, Max, m_name))
|
||||
{
|
||||
m_value = static_cast<float_type>(result);
|
||||
return true;
|
||||
@@ -560,7 +563,7 @@ namespace cfg
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
u64 result;
|
||||
if (try_to_uint64(&result, value, Min, Max))
|
||||
if (try_to_uint64(&result, value, Min, Max, m_name))
|
||||
{
|
||||
m_value = static_cast<int_type>(result);
|
||||
return true;
|
||||
@@ -646,7 +649,7 @@ namespace cfg
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
u128 result;
|
||||
if (try_to_uint128(&result, value))
|
||||
if (try_to_uint128(&result, value, m_name))
|
||||
{
|
||||
m_value = result;
|
||||
return true;
|
||||
@@ -714,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)
|
||||
|
||||
+99
-7
@@ -38,6 +38,17 @@ static std::unique_ptr<wchar_t[]> to_wchar(std::string_view source)
|
||||
// Buffer for max possible output length
|
||||
std::unique_ptr<wchar_t[]> 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));
|
||||
|
||||
@@ -166,6 +177,7 @@ static fs::error to_error(int e)
|
||||
case ENOTEMPTY: return fs::error::notempty;
|
||||
case EROFS: return fs::error::readonly;
|
||||
case EISDIR: return fs::error::isdir;
|
||||
case ENOTDIR: return fs::error::notdir;
|
||||
case ENOSPC: return fs::error::nospace;
|
||||
case EXDEV: return fs::error::xdev;
|
||||
default: return fs::error::unknown;
|
||||
@@ -399,11 +411,12 @@ namespace fs
|
||||
class windows_file final : public file_base
|
||||
{
|
||||
HANDLE m_handle;
|
||||
bool m_raw_device;
|
||||
atomic_t<u64> 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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -563,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
|
||||
@@ -1090,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))
|
||||
@@ -1657,9 +1728,29 @@ fs::file::file(const std::string& path, bs_t<open_mode> 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<windows_file>(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();
|
||||
@@ -1670,7 +1761,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
g_tls_error = fs::error::isdir;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
g_tls_error = to_error(last_error);
|
||||
return;
|
||||
}
|
||||
@@ -2833,6 +2924,7 @@ void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
|
||||
case fs::error::notempty: return "Not empty";
|
||||
case fs::error::readonly: return "Read only";
|
||||
case fs::error::isdir: return "Is a directory";
|
||||
case fs::error::notdir: return "Not a directory";
|
||||
case fs::error::toolong: return "Path too long";
|
||||
case fs::error::nospace: return "Not enough space on the device";
|
||||
case fs::error::xdev: return "Device mismatch";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -683,6 +689,7 @@ namespace fs
|
||||
notempty,
|
||||
readonly,
|
||||
isdir,
|
||||
notdir,
|
||||
toolong,
|
||||
nospace,
|
||||
xdev,
|
||||
|
||||
@@ -493,6 +493,10 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime*
|
||||
return reinterpret_cast<FT>(uptr(result));
|
||||
}
|
||||
|
||||
#if defined(__INTELLISENSE__) && !defined(LLVM_AVAILABLE)
|
||||
#define LLVM_AVAILABLE
|
||||
#endif
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
namespace llvm
|
||||
@@ -539,9 +543,15 @@ public:
|
||||
// Add module (path to obj cache dir)
|
||||
void add(std::unique_ptr<llvm::Module> _module, const std::string& path);
|
||||
|
||||
// Returns false after LLVM fatal recovery. The compiler must be discarded.
|
||||
bool try_add(std::unique_ptr<llvm::Module> _module, const std::string& path, std::string& error);
|
||||
|
||||
// Add module (not cached)
|
||||
void add(std::unique_ptr<llvm::Module> _module);
|
||||
|
||||
// Returns false after LLVM fatal recovery. The compiler must be discarded.
|
||||
bool try_add(std::unique_ptr<llvm::Module> _module, std::string& error);
|
||||
|
||||
// Add object (path to obj file)
|
||||
bool add(const std::string& path);
|
||||
|
||||
@@ -554,6 +564,9 @@ 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);
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
#include <charconv>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <pthread.h>
|
||||
#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 <typename F>
|
||||
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<F>(func)();
|
||||
|
||||
g_llvm_fatal_message = nullptr;
|
||||
#if defined(__APPLE__)
|
||||
pthread_jit_write_protect_np(true);
|
||||
#endif
|
||||
});
|
||||
|
||||
worker();
|
||||
const bool result = static_cast<thread_state>(worker) == thread_state::finished;
|
||||
|
||||
if (!result && error.empty())
|
||||
{
|
||||
error = "LLVM crash recovery invoked";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const bool jit_initialize = []() -> bool
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
@@ -649,6 +691,13 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _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<std::string, u64>& _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<llvm::Module> _module, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _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<llvm::Module> _module)
|
||||
{
|
||||
const auto ptr = _module.get();
|
||||
@@ -801,6 +886,28 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
|
||||
}
|
||||
}
|
||||
|
||||
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _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);
|
||||
|
||||
+5
-5
@@ -23,19 +23,19 @@ inline void strcpy_trunc(D&& dst, const T& src)
|
||||
}
|
||||
|
||||
// Convert string to signed integer
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max, std::string_view name = {});
|
||||
|
||||
// Convert string to unsigned integer
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max, std::string_view name = {});
|
||||
|
||||
// Convert string to unsigned int128_t
|
||||
bool try_to_uint128(u128* out, std::string_view value);
|
||||
bool try_to_uint128(u128* out, std::string_view value, std::string_view name = {});
|
||||
|
||||
// Convert string to float
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max);
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max, std::string_view name = {});
|
||||
|
||||
// Convert float to string locale independent
|
||||
bool try_to_string(std::string* out, const f64& value);
|
||||
bool try_to_string(std::string* out, f64 value, std::string_view name = {});
|
||||
|
||||
// Get the file extension of a file path ("png", "jpg", etc.)
|
||||
std::string get_file_extension(const std::string& file_path);
|
||||
|
||||
+131
-11
@@ -25,6 +25,7 @@
|
||||
#include <process.h>
|
||||
#include <sysinfoapi.h>
|
||||
|
||||
#include "stack_trace.h"
|
||||
#include "util/dyn_lib.hpp"
|
||||
|
||||
DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDescription", HRESULT(HANDLE hThread, PCWSTR lpThreadDescription));
|
||||
@@ -1383,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)
|
||||
{
|
||||
@@ -1397,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<u32>::swap(value);
|
||||
if (a_size == 4)
|
||||
{
|
||||
value = stx::se_storage<u32>::swap(value);
|
||||
}
|
||||
else if (a_size == 2)
|
||||
{
|
||||
value = stx::se_storage<u16>::swap(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(a_size == 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (op == X64OP_LOAD_CMP)
|
||||
@@ -1439,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<u32>::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<u32>::swap(value);
|
||||
}
|
||||
else if (a_size == 2)
|
||||
{
|
||||
value = stx::se_storage<u16>::swap(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure(a_size == 1);
|
||||
}
|
||||
|
||||
u64 ctrl;
|
||||
if (!get_x64_reg_value(context, s_tls_reg3, d_size, i_size, ctrl))
|
||||
@@ -1470,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))
|
||||
{
|
||||
@@ -1488,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++;
|
||||
@@ -1981,9 +2038,39 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp) noexcept
|
||||
}
|
||||
|
||||
fmt::append(msg, "RPCS3 image base: %p.\n", GetModuleHandle(NULL));
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
fmt::append(msg, "RAX: %016llX RBX: %016llX\n", pExp->ContextRecord->Rax, pExp->ContextRecord->Rbx);
|
||||
fmt::append(msg, "RCX: %016llX RDX: %016llX\n", pExp->ContextRecord->Rcx, pExp->ContextRecord->Rdx);
|
||||
fmt::append(msg, "RSI: %016llX RDI: %016llX\n", pExp->ContextRecord->Rsi, pExp->ContextRecord->Rdi);
|
||||
fmt::append(msg, "RBP: %016llX RSP: %016llX\n", pExp->ContextRecord->Rbp, pExp->ContextRecord->Rsp);
|
||||
fmt::append(msg, "R8: %016llX R9: %016llX\n", pExp->ContextRecord->R8, pExp->ContextRecord->R9);
|
||||
fmt::append(msg, "R10: %016llX R11: %016llX\n", pExp->ContextRecord->R10, pExp->ContextRecord->R11);
|
||||
fmt::append(msg, "R12: %016llX R13: %016llX\n", pExp->ContextRecord->R12, pExp->ContextRecord->R13);
|
||||
fmt::append(msg, "R14: %016llX R15: %016llX\n", pExp->ContextRecord->R14, pExp->ContextRecord->R15);
|
||||
fmt::append(msg, "RFLAGS: %08X\n", pExp->ContextRecord->EFlags);
|
||||
#elif defined(ARCH_ARM64)
|
||||
for (int i = 0; i < 29; i += 2)
|
||||
{
|
||||
if (i + 1 < 29)
|
||||
fmt::append(msg, "X%-2d: %016llX X%-2d: %016llX\n", i, pExp->ContextRecord->X[i], i + 1, pExp->ContextRecord->X[i + 1]);
|
||||
else
|
||||
fmt::append(msg, "X%-2d: %016llX\n", i, pExp->ContextRecord->X[i]);
|
||||
}
|
||||
fmt::append(msg, "SP: %016llX FP: %016llX LR: %016llX\n", pExp->ContextRecord->Sp, pExp->ContextRecord->Fp, pExp->ContextRecord->Lr);
|
||||
fmt::append(msg, "CPSR: %08X\n", pExp->ContextRecord->Cpsr);
|
||||
#endif
|
||||
|
||||
// TODO: print registers and the callstack
|
||||
const auto stack_trace = utils::get_backtrace(64, pExp->ContextRecord);
|
||||
const auto stack_symbols = utils::get_backtrace_symbols(stack_trace);
|
||||
|
||||
msg += "Stack Trace:\n";
|
||||
|
||||
for (const auto& symbol : stack_symbols)
|
||||
{
|
||||
fmt::append(msg, "%s\n", symbol);
|
||||
}
|
||||
|
||||
sys_log.fatal("\n%s", msg);
|
||||
logs::listener::sync_all();
|
||||
|
||||
@@ -2852,6 +2939,13 @@ void thread_base::exec()
|
||||
}
|
||||
}
|
||||
|
||||
void thread_ctrl::set_name(std::string name)
|
||||
{
|
||||
ensure(g_tls_this_thread);
|
||||
g_tls_this_thread->m_tname.store(make_single<std::string>(name));
|
||||
g_tls_this_thread->set_name(std::move(name));
|
||||
}
|
||||
|
||||
[[noreturn]] void thread_ctrl::emergency_exit(std::string_view reason)
|
||||
{
|
||||
// Print stacktrace
|
||||
@@ -2950,6 +3044,32 @@ void thread_base::exec()
|
||||
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))
|
||||
|
||||
+10
-12
@@ -129,7 +129,7 @@ public:
|
||||
const native_entry entry_point;
|
||||
|
||||
// Set name for debugger
|
||||
static void set_name(std::string);
|
||||
static void set_name(std::string name);
|
||||
|
||||
private:
|
||||
// Thread handle (platform-specific)
|
||||
@@ -232,11 +232,7 @@ public:
|
||||
}
|
||||
|
||||
// Set current thread name (not recommended)
|
||||
static void set_name(std::string name)
|
||||
{
|
||||
g_tls_this_thread->m_tname.store(make_single<std::string>(name));
|
||||
g_tls_this_thread->set_name(std::move(name));
|
||||
}
|
||||
static void set_name(std::string name);
|
||||
|
||||
// Set thread name (not recommended)
|
||||
template <typename T>
|
||||
@@ -319,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()
|
||||
{
|
||||
@@ -801,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> context(static_cast<const Context&>(f));
|
||||
|
||||
// Perform the check and additional preparations for each context
|
||||
if (!std::invoke(std::forward<CheckAndPrepare>(check), i, context))
|
||||
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count, context))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_count++;
|
||||
new (static_cast<void*>(m_threads + i)) Thread(std::string(name) + std::to_string(i + 1), std::move(context));
|
||||
new (static_cast<void*>(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> context(std::forward<Context>(f));
|
||||
|
||||
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count - 1, context))
|
||||
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count, context))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new (static_cast<void*>(m_threads + m_count)) Thread(std::string(name) + std::to_string(m_count + 1), std::move(context));
|
||||
m_count++;
|
||||
new (static_cast<void*>(m_threads + m_count - 1)) Thread(std::string(name) + std::to_string(m_count - 1), std::move(context));
|
||||
}
|
||||
|
||||
// Default constructor
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -1027,3 +1028,6 @@ using color1u = color1_base<unsigned int>;
|
||||
using color1i = color1_base<int>;
|
||||
using color1f = color1_base<float>;
|
||||
using color1d = color1_base<double>;
|
||||
|
||||
using mat3f = color3_base<float>[3];
|
||||
static_assert(sizeof(mat3f) == sizeof(float) * 3 * 3);
|
||||
|
||||
@@ -10,6 +10,19 @@ rXmlNode::rXmlNode(const pugi::xml_node& node)
|
||||
handle = node;
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlNode::GetChild(std::string_view name)
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
if (const pugi::xml_node child = handle.child(name))
|
||||
{
|
||||
return std::make_shared<rXmlNode>(child);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlNode::GetChildren()
|
||||
{
|
||||
if (handle)
|
||||
|
||||
@@ -20,6 +20,7 @@ struct rXmlNode
|
||||
{
|
||||
rXmlNode();
|
||||
rXmlNode(const pugi::xml_node& node);
|
||||
std::shared_ptr<rXmlNode> GetChild(std::string_view name);
|
||||
std::shared_ptr<rXmlNode> GetChildren();
|
||||
std::shared_ptr<rXmlNode> GetNext();
|
||||
std::string GetName();
|
||||
|
||||
+31
-12
@@ -30,42 +30,61 @@ namespace utils
|
||||
return out.data();
|
||||
}
|
||||
|
||||
std::vector<void*> get_backtrace(int max_depth)
|
||||
std::vector<void*> get_backtrace(int max_depth, PCONTEXT ctx)
|
||||
{
|
||||
static struct sym_initer_t
|
||||
{
|
||||
sym_initer_t() noexcept
|
||||
{
|
||||
SymInitialize(GetCurrentProcess(), NULL, TRUE);
|
||||
}
|
||||
~sym_initer_t() noexcept
|
||||
{
|
||||
SymCleanup(GetCurrentProcess());
|
||||
}
|
||||
} s_initer{};
|
||||
|
||||
std::vector<void*> result = {};
|
||||
|
||||
const auto hProcess = ::GetCurrentProcess();
|
||||
const auto hThread = ::GetCurrentThread();
|
||||
|
||||
CONTEXT context{};
|
||||
RtlCaptureContext(&context);
|
||||
if (ctx)
|
||||
context = *ctx;
|
||||
else
|
||||
RtlCaptureContext(&context);
|
||||
|
||||
STACKFRAME64 stack = {};
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#if defined(ARCH_X64)
|
||||
const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrFrame.Offset = context.Rbp;
|
||||
#elif defined(ARCH_ARM64)
|
||||
const DWORD machineType = IMAGE_FILE_MACHINE_ARM64;
|
||||
stack.AddrPC.Offset = context.Pc;
|
||||
stack.AddrStack.Offset = context.Sp;
|
||||
stack.AddrFrame.Offset = context.Fp;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
while (max_depth--)
|
||||
{
|
||||
if (!StackWalk64(
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
hProcess,
|
||||
hThread,
|
||||
&stack,
|
||||
&context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL))
|
||||
machineType,
|
||||
hProcess,
|
||||
hThread,
|
||||
&stack,
|
||||
&context,
|
||||
NULL,
|
||||
SymFunctionTableAccess64,
|
||||
SymGetModuleBase64,
|
||||
NULL))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -116,7 +135,7 @@ namespace utils
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(fmt::format("rpcs3@0xp", pointer));
|
||||
result.push_back(fmt::format("rpcs3@0x%p", pointer));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#include <util/types.hpp>
|
||||
#include <util/logs.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
namespace stack_trace
|
||||
@@ -30,7 +35,12 @@ namespace utils
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<void*> get_backtrace(int max_depth = 255, PCONTEXT ctx = nullptr);
|
||||
#else
|
||||
std::vector<void*> get_backtrace(int max_depth = 255);
|
||||
#endif
|
||||
|
||||
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack);
|
||||
|
||||
FORCE_INLINE void print_trace(stack_trace::Logger auto& logger, int max_depth = 255)
|
||||
|
||||
@@ -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<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r)
|
||||
{
|
||||
for (usz i = 0; i < 3; i++)
|
||||
{
|
||||
left[i] = l[i];
|
||||
right[i] = r[i];
|
||||
}
|
||||
}
|
||||
|
||||
atomic_t<bool> stereo_config::s_live_preview_enabled = false;
|
||||
stereo_config::stereo_matrices stereo_config::m_live_matrices = {};
|
||||
|
||||
const std::unordered_map<stereo_render_mode_options, stereo_config::stereo_matrices> stereo_config::m_matrices =
|
||||
{
|
||||
{stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 1, 0),
|
||||
color3_base<f32>(0, 0, 1)
|
||||
}
|
||||
)},
|
||||
{stereo_render_mode_options::anaglyph_red_green, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 1, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
}
|
||||
)},
|
||||
{stereo_render_mode_options::anaglyph_red_blue, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 1)
|
||||
}
|
||||
)},
|
||||
{stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 0.5f)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 1, 0),
|
||||
color3_base<f32>(0, 0, 0.5f)
|
||||
}
|
||||
)},
|
||||
{stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 1, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 0, 0),
|
||||
color3_base<f32>(0, 0, 1)
|
||||
}
|
||||
)},
|
||||
{stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices(
|
||||
{
|
||||
color3_base<f32>(1, 0, 0),
|
||||
color3_base<f32>(0, 1, 0),
|
||||
color3_base<f32>(0, 0, 0)
|
||||
},
|
||||
{
|
||||
color3_base<f32>(0, 0, 1.0f / 3.0f),
|
||||
color3_base<f32>(0, 0, 1.0f / 3.0f),
|
||||
color3_base<f32>(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<float>(1, 0, 0),
|
||||
color3_base<float>(0, 1, 0),
|
||||
color3_base<float>(0, 0, 1)
|
||||
},
|
||||
{
|
||||
color3_base<float>(0, 0, 0),
|
||||
color3_base<float>(0, 0, 0),
|
||||
color3_base<float>(0, 0, 0)
|
||||
});
|
||||
return s_left_only_matrices;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> stereo_config::get_custom_matrix(bool is_left) const
|
||||
{
|
||||
return convert_matrix(is_left ? m_custom_matrices.left : m_custom_matrices.right);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> stereo_config::convert_matrix(const mat3f& mat)
|
||||
{
|
||||
std::map<std::string, std::string> 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;
|
||||
}
|
||||
@@ -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 <map>
|
||||
#include <mutex>
|
||||
|
||||
struct stereo_config
|
||||
{
|
||||
public:
|
||||
struct stereo_matrices
|
||||
{
|
||||
stereo_matrices() = default;
|
||||
stereo_matrices(const std::array<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r);
|
||||
|
||||
mat3f left {};
|
||||
mat3f right {};
|
||||
};
|
||||
|
||||
stereo_config(bool in_emulation) : m_in_emulation(in_emulation) {}
|
||||
|
||||
void set_stereo_mode(stereo_render_mode_options mode) { m_stereo_mode = mode; }
|
||||
stereo_render_mode_options stereo_mode() const { return m_stereo_mode; }
|
||||
|
||||
const stereo_matrices& matrices() const;
|
||||
|
||||
void update_from_config(bool stereo_enabled);
|
||||
|
||||
void set_custom_matrices(const stereo_matrices& matrices) { m_custom_matrices = matrices; }
|
||||
static void set_live_matrices(const stereo_matrices& matrices) { m_live_matrices = matrices; }
|
||||
|
||||
std::map<std::string, std::string> get_custom_matrix(bool is_left) const;
|
||||
|
||||
template <typename T>
|
||||
static void convert_matrix(mat3f& mat, const T& values)
|
||||
{
|
||||
mat[0] = {};
|
||||
mat[1] = {};
|
||||
mat[2] = {};
|
||||
|
||||
if (values.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
const std::string key = fmt::format("%d%d", i, j);
|
||||
const auto it = values.find(key);
|
||||
if (it == values.cend()) continue;
|
||||
|
||||
const std::string& val_s = it->second;
|
||||
if (val_s.empty()) continue;
|
||||
|
||||
if (f64 val = 0.0f; try_to_float(&val, val_s, -10.0f, 10.0f))
|
||||
{
|
||||
mat[i].rgb[j] = static_cast<f32>(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> convert_matrix(const mat3f& mat);
|
||||
|
||||
static atomic_t<bool> s_live_preview_enabled;
|
||||
|
||||
private:
|
||||
stereo_matrices m_custom_matrices {};
|
||||
stereo_render_mode_options m_stereo_mode = stereo_render_mode_options::disabled;
|
||||
bool m_in_emulation = false;
|
||||
|
||||
static stereo_matrices m_live_matrices;
|
||||
static const std::unordered_map<stereo_render_mode_options, stereo_matrices> m_matrices;
|
||||
};
|
||||
+1
-1
@@ -74,7 +74,7 @@ enum
|
||||
};
|
||||
#endif
|
||||
|
||||
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
|
||||
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, [[maybe_unused]] uint mask = 0)
|
||||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
|
||||
|
||||
@@ -349,24 +349,24 @@ QPushButton:disabled {
|
||||
}
|
||||
|
||||
|
||||
QDialog QDialogButtonBox QPushButton {
|
||||
QDialog QDialogButtonBox QPushButton:default {
|
||||
color: #000000;
|
||||
background-color: #4CC2FF;
|
||||
border-color: #5AC7FF;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:hover {
|
||||
QDialog QDialogButtonBox QPushButton:default:hover {
|
||||
background-color: #48B2E9;
|
||||
border-color: #56B8EB;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:pressed {
|
||||
QDialog QDialogButtonBox QPushButton:default:pressed {
|
||||
color: #22526A;
|
||||
background-color: #45A4D5;
|
||||
border-color: #45A4D5;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:disabled {
|
||||
QDialog QDialogButtonBox QPushButton:default:disabled {
|
||||
color: #ABABAB;
|
||||
background-color: #4C4C4C;
|
||||
border-color: #4C4C4C;
|
||||
@@ -614,10 +614,15 @@ QComboBox QAbstractItemView {
|
||||
QComboBox QAbstractItemView::item {
|
||||
color: #FFFFFF;
|
||||
padding: 4px 6px;
|
||||
margin: 2px 0px;
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item:hover {
|
||||
background-color: #383838;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item:selected {
|
||||
background-color: #383838;
|
||||
}
|
||||
@@ -642,7 +647,7 @@ QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
|
||||
border-radius: 4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
padding: 3px 4px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
QSpinBox::up-button:hover, QSpinBox::down-button:hover,
|
||||
@@ -709,6 +714,16 @@ QSlider::handle:horizontal:disabled {
|
||||
image: url("GuiConfigs/dark/slider-handle-disabled.svg");
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal {
|
||||
background-color: #4CC2FF;
|
||||
border-radius: 2px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal:disabled {
|
||||
background-color: #767A7D;
|
||||
}
|
||||
|
||||
|
||||
/* LINE EDIT */
|
||||
QLineEdit {
|
||||
@@ -791,7 +806,7 @@ QMenuBar::item:pressed {
|
||||
|
||||
QMenu {
|
||||
background-color: #2C2C2C;
|
||||
border: 1px solid #131313;
|
||||
border: 1px solid #3E3E3E;
|
||||
padding: 4px 0px;
|
||||
}
|
||||
|
||||
@@ -799,12 +814,12 @@ QMenu::item {
|
||||
color: #FFFFFF;
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
padding: 8px 20px;
|
||||
padding: 4px 20px;
|
||||
margin: 0px 4px;
|
||||
}
|
||||
|
||||
QMenu::item:selected {
|
||||
background-color: #383838;
|
||||
background-color: #353535;
|
||||
}
|
||||
|
||||
QMenu::item:disabled {
|
||||
@@ -814,8 +829,8 @@ QMenu::item:disabled {
|
||||
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background: #131313;
|
||||
margin: 4px 0px;
|
||||
background: #3E3E3E;
|
||||
margin: 4px 8px;
|
||||
}
|
||||
|
||||
QMenu::indicator, QMenu::right-arrow {
|
||||
|
||||
@@ -349,24 +349,24 @@ QPushButton:disabled {
|
||||
}
|
||||
|
||||
|
||||
QDialog QDialogButtonBox QPushButton {
|
||||
QDialog QDialogButtonBox QPushButton:default {
|
||||
color: #FFFFFF;
|
||||
background-color: #0067C0;
|
||||
border-color: #1473C5;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:hover {
|
||||
QDialog QDialogButtonBox QPushButton:default:hover {
|
||||
background-color: #1A76C6;
|
||||
border-color: #2C80CA;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:pressed {
|
||||
QDialog QDialogButtonBox QPushButton:default:pressed {
|
||||
color: #C2DAEF;
|
||||
background-color: #3284CB;
|
||||
border-color: #3284CB;
|
||||
}
|
||||
|
||||
QDialog QDialogButtonBox QPushButton:disabled {
|
||||
QDialog QDialogButtonBox QPushButton:default:disabled {
|
||||
color: #FFFFFF;
|
||||
background-color: #C5C5C5;
|
||||
border-color: #C5C5C5;
|
||||
@@ -614,10 +614,15 @@ QComboBox QAbstractItemView {
|
||||
QComboBox QAbstractItemView::item {
|
||||
color: #1B1B1B;
|
||||
padding: 4px 6px;
|
||||
margin: 2px 0px;
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item:hover {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
|
||||
QComboBox QAbstractItemView::item:selected {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
@@ -642,7 +647,7 @@ QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
|
||||
border-radius: 4px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
padding: 3px 4px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
QSpinBox::up-button:hover, QSpinBox::down-button:hover,
|
||||
@@ -709,6 +714,16 @@ QSlider::handle:horizontal:disabled {
|
||||
image: url("GuiConfigs/light/slider-handle-disabled.svg");
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal {
|
||||
background-color: #1A76C6;
|
||||
border-radius: 2px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal:disabled {
|
||||
background-color: #868788;
|
||||
}
|
||||
|
||||
|
||||
/* LINE EDIT */
|
||||
QLineEdit {
|
||||
@@ -791,7 +806,7 @@ QMenuBar::item:pressed {
|
||||
|
||||
QMenu {
|
||||
background-color: #F9F9F9;
|
||||
border: 1px solid #EEEEEE;
|
||||
border: 1px solid #E5E5E5;
|
||||
padding: 4px 0px;
|
||||
}
|
||||
|
||||
@@ -799,7 +814,7 @@ QMenu::item {
|
||||
color: #1B1B1B;
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
padding: 8px 20px;
|
||||
padding: 4px 20px;
|
||||
margin: 0px 4px;
|
||||
}
|
||||
|
||||
@@ -814,8 +829,8 @@ QMenu::item:disabled {
|
||||
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background: #EEEEEE;
|
||||
margin: 4px 0px;
|
||||
background: #E5E5E5;
|
||||
margin: 4px 8px;
|
||||
}
|
||||
|
||||
QMenu::indicator, QMenu::right-arrow {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
<!-- Common default props for defining properties without macros. Import right before Microsoft.Cpp.Default.props -->
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<EnableLibUsbWindowsHotplug>true</EnableLibUsbWindowsHotplug>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
<GTestPath>$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.targets</GTestPath>
|
||||
<GTestInstalled Condition="Exists('$(GTestPath)')">true</GTestInstalled>
|
||||
<OpenCvBuildDir>$(SolutionDir)3rdparty\opencv\opencv\opencv413\build</OpenCvBuildDir>
|
||||
<OpenCvWorld>opencv_world4130</OpenCvWorld>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Lib>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<Optimization>Full</Optimization>
|
||||
|
||||
@@ -182,11 +182,13 @@ if(BUILD_RPCS3_TESTS)
|
||||
target_sources(rpcs3_test
|
||||
PRIVATE
|
||||
tests/test.cpp
|
||||
tests/test_bit_set.cpp
|
||||
tests/test_fmt.cpp
|
||||
tests/test_pair.cpp
|
||||
tests/test_tuple.cpp
|
||||
tests/test_simple_array.cpp
|
||||
tests/test_address_range.cpp
|
||||
tests/test_sys_fs.cpp
|
||||
tests/test_rsx_cfg.cpp
|
||||
tests/test_rsx_fp_asm.cpp
|
||||
tests/test_dmux_pamf.cpp
|
||||
|
||||
@@ -807,7 +807,8 @@ bool package_reader::set_install_path()
|
||||
}
|
||||
|
||||
// TODO: Verify whether other content types require appending title ID
|
||||
if (m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
|
||||
// Append title ID depending on content type
|
||||
if (m_metadata.content_type != PKG_CONTENT_TYPE_THEME && m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
|
||||
dir += m_install_dir + '/';
|
||||
|
||||
// If false, an existing directory is being overwritten: cannot cancel the operation
|
||||
|
||||
@@ -126,7 +126,7 @@ struct PKGEntry
|
||||
struct PKGMetaData
|
||||
{
|
||||
private:
|
||||
static std::string to_hex_string(u8 buf[], usz size)
|
||||
static std::string to_hex_string(const u8* buf, usz size)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
for (usz i = 0; i < size; i++)
|
||||
@@ -135,7 +135,7 @@ private:
|
||||
}
|
||||
return sstream.str();
|
||||
}
|
||||
static std::string to_hex_string(u8 buf[], usz size, usz dotpos)
|
||||
static std::string to_hex_string(const u8* buf, usz size, usz dotpos)
|
||||
{
|
||||
std::string result = to_hex_string(buf, size);
|
||||
if (result.size() > dotpos)
|
||||
|
||||
+19
-7
@@ -807,6 +807,8 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
|
||||
self_f.seek(0);
|
||||
sce_hdr.Load(self_f);
|
||||
|
||||
const usz self_size = self_f.size();
|
||||
|
||||
if (out_info)
|
||||
{
|
||||
*out_info = {};
|
||||
@@ -880,8 +882,9 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
|
||||
|
||||
for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
|
||||
{
|
||||
if (self_f.pos() >= self_f.size())
|
||||
if (self_f.pos() >= self_size)
|
||||
{
|
||||
// Read out of bounds (file is truncated or corrupted)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -889,15 +892,23 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
|
||||
m_seg_ext_hdr.back().Load(self_f);
|
||||
}
|
||||
|
||||
if (m_ext_hdr.version_hdr_offset == 0 || utils::add_saturate<u64>(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_f.size())
|
||||
if (m_ext_hdr.version_hdr_offset == 0)
|
||||
{
|
||||
// 0.85 Selfs have version_hdr_offset set to 0
|
||||
m_version_hdr = {};
|
||||
}
|
||||
else if (utils::add_saturate<u64>(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_size)
|
||||
{
|
||||
// Read out of bounds (file is truncated or corrupted)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read SCE version info.
|
||||
self_f.seek(m_ext_hdr.version_hdr_offset);
|
||||
|
||||
// Read SCE version info.
|
||||
self_f.seek(m_ext_hdr.version_hdr_offset);
|
||||
|
||||
m_version_hdr.Load(self_f);
|
||||
m_version_hdr.Load(self_f);
|
||||
}
|
||||
|
||||
// Read control info.
|
||||
m_supplemental_hdr_arr.clear();
|
||||
@@ -905,8 +916,9 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
|
||||
|
||||
for (u64 i = 0; i < m_ext_hdr.supplemental_hdr_size;)
|
||||
{
|
||||
if (self_f.pos() >= self_f.size())
|
||||
if (self_f.pos() >= self_size)
|
||||
{
|
||||
// Read out of bounds (file is truncated or corrupted)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+26
-1
@@ -23,7 +23,32 @@
|
||||
|
||||
// Auxiliary functions (endian swap, xor).
|
||||
|
||||
// Hex string conversion auxiliary functions.
|
||||
// Bytes conversion auxiliary function.
|
||||
void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length)
|
||||
{
|
||||
size_t str_length = data_length * 2;
|
||||
|
||||
hex_str.resize(str_length);
|
||||
|
||||
for (size_t i = 0; i < str_length; i += 2)
|
||||
{
|
||||
const auto [ptr, err] = std::to_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16);
|
||||
if (err != std::errc())
|
||||
{
|
||||
fmt::throw_exception("Failed to read bytes: %s", std::make_error_code(err).message());
|
||||
}
|
||||
|
||||
// Padding handling for values < 0x10 (e.g. 0x05 becomes "5" instead of "05")
|
||||
// If to_chars only writes 1 character, we move to the right and put '0'
|
||||
if (ptr == &hex_str[i] + 1)
|
||||
{
|
||||
hex_str[i + 1] = hex_str[i];
|
||||
hex_str[i] = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hex string conversion auxiliary function.
|
||||
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length)
|
||||
{
|
||||
const auto strn_length = (str_length > 0) ? str_length : hex_str.size();
|
||||
|
||||
@@ -15,7 +15,10 @@ char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PA
|
||||
|
||||
std::string sha256_get_hash(const char* data, usz size, bool lower_case);
|
||||
|
||||
// Hex string conversion auxiliary functions.
|
||||
// Bytes conversion auxiliary function.
|
||||
void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length);
|
||||
|
||||
// Hex string conversion auxiliary function.
|
||||
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length);
|
||||
|
||||
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
||||
|
||||
@@ -13,82 +13,112 @@ LOG_CHANNEL(FAudio_, "FAudio");
|
||||
FAudioBackend::FAudioBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
FAudio *instance;
|
||||
FAudio *instance = nullptr;
|
||||
|
||||
if (u32 res = FAudioCreate(&instance, 0, FAUDIO_DEFAULT_PROCESSOR))
|
||||
const u32 res = FAudioCreate(&instance, 0, FAUDIO_DEFAULT_PROCESSOR);
|
||||
if (res != 0)
|
||||
{
|
||||
FAudio_.error("FAudioCreate() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioCreate() failed with error code 0x%08x", res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
FAudio_.error("FAudioCreate() succeeded but returned null instance");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize callbacks
|
||||
OnProcessingPassStart = nullptr;
|
||||
OnProcessingPassEnd = nullptr;
|
||||
OnCriticalError = OnCriticalError_func;
|
||||
|
||||
if (u32 res = FAudio_RegisterForCallbacks(instance, this))
|
||||
const u32 callback_res = FAudio_RegisterForCallbacks(instance, this);
|
||||
if (callback_res != 0)
|
||||
{
|
||||
// Some error recovery functionality will be lost, but otherwise backend is operational
|
||||
FAudio_.error("FAudio_RegisterForCallbacks() failed(0x%08x)", res);
|
||||
FAudio_.warning("FAudio_RegisterForCallbacks() failed with error code 0x%08x - error recovery may be limited", callback_res);
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
// All succeeded, commit the instance
|
||||
m_instance = instance;
|
||||
FAudio_.success("FAudio backend initialized successfully");
|
||||
}
|
||||
|
||||
FAudioBackend::~FAudioBackend()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance)
|
||||
{
|
||||
// Unregister callbacks before stopping engine
|
||||
FAudio_UnregisterForCallbacks(m_instance, this);
|
||||
|
||||
FAudio_StopEngine(m_instance);
|
||||
FAudio_Release(m_instance);
|
||||
m_instance = nullptr;
|
||||
}
|
||||
|
||||
FAudio_.notice("FAudio backend destroyed successfully");
|
||||
}
|
||||
|
||||
void FAudioBackend::Play()
|
||||
{
|
||||
if (m_source_voice == nullptr)
|
||||
if (!m_source_voice)
|
||||
{
|
||||
FAudio_.error("Play() called uninitialized");
|
||||
FAudio_.error("Play() called with uninitialized source voice");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_playing) return;
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = true;
|
||||
if (!m_playing)
|
||||
{
|
||||
m_playing = true;
|
||||
FAudio_.trace("Audio playback started");
|
||||
}
|
||||
}
|
||||
|
||||
void FAudioBackend::Pause()
|
||||
{
|
||||
if (m_source_voice == nullptr)
|
||||
if (!m_source_voice)
|
||||
{
|
||||
FAudio_.error("Pause() called uninitialized");
|
||||
FAudio_.error("Pause() called with uninitialized source voice");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_playing) return;
|
||||
|
||||
bool was_playing = false;
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
was_playing = m_playing;
|
||||
if (m_playing)
|
||||
{
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice))
|
||||
if (was_playing)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_FlushSourceBuffers() failed(0x%08x)", res);
|
||||
const u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice);
|
||||
if (res != 0)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_FlushSourceBuffers() failed with error code 0x%08x", res);
|
||||
}
|
||||
else
|
||||
{
|
||||
FAudio_.trace("Audio playback paused and buffers flushed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FAudioBackend::CloseUnlocked()
|
||||
{
|
||||
if (m_source_voice != nullptr)
|
||||
if (m_source_voice)
|
||||
{
|
||||
if (u32 res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW))
|
||||
const u32 stop_res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
if (stop_res != 0)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_Stop() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioSourceVoice_Stop() failed with error code 0x%08x", stop_res);
|
||||
}
|
||||
|
||||
FAudioVoice_DestroyVoice(m_source_voice);
|
||||
@@ -103,6 +133,10 @@ void FAudioBackend::CloseUnlocked()
|
||||
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
m_data_buf.clear();
|
||||
m_data_buf.shrink_to_fit(); // Free memory
|
||||
|
||||
FAudio_.trace("FAudio voices and resources cleaned up");
|
||||
}
|
||||
|
||||
void FAudioBackend::Close()
|
||||
@@ -118,34 +152,41 @@ bool FAudioBackend::Initialized()
|
||||
|
||||
bool FAudioBackend::Operational()
|
||||
{
|
||||
return m_source_voice != nullptr && !m_reset_req.observe();
|
||||
return m_source_voice != nullptr && m_instance != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
FAudio_.error("Open() called uninitialized");
|
||||
FAudio_.error("Open() called on uninitialized backend");
|
||||
return false;
|
||||
}
|
||||
|
||||
FAudio_.notice("Opening FAudio device: dev_id='%s', freq=%u, sample_size=%u, channels=%u",
|
||||
dev_id.empty() ? "default" : std::string(dev_id).c_str(),
|
||||
static_cast<u32>(freq), static_cast<u32>(sample_size), static_cast<u32>(ch_cnt));
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
|
||||
const bool use_default_dev = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
u64 devid{};
|
||||
u64 devid = 0;
|
||||
|
||||
if (!use_default_dev)
|
||||
{
|
||||
if (!try_to_uint64(&devid, dev_id, 0, UINT32_MAX))
|
||||
{
|
||||
FAudio_.error("Invalid device id - %s", dev_id);
|
||||
FAudio_.error("Invalid device ID format: '%s'", dev_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr))
|
||||
const u32 master_res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice,
|
||||
FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr);
|
||||
if (master_res != 0)
|
||||
{
|
||||
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudio_CreateMasteringVoice() failed with error code 0x%08x", master_res);
|
||||
m_master_voice = nullptr;
|
||||
return false;
|
||||
}
|
||||
@@ -155,19 +196,22 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
||||
|
||||
if (vd.InputChannels == 0)
|
||||
{
|
||||
FAudio_.error("Channel count of 0 is invalid");
|
||||
FAudio_.error("Master voice reports invalid channel count of 0");
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
FAudio_.notice("Channel count is %d", vd.InputChannels);
|
||||
FAudio_.notice("Master voice created with %u input channels", vd.InputChannels);
|
||||
|
||||
// Store audio parameters
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
|
||||
// Setup channel layout with validation
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, FAudio_);
|
||||
|
||||
FAudioWaveFormatEx waveformatex;
|
||||
// Configure wave format
|
||||
FAudioWaveFormatEx waveformatex{};
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = get_channels();
|
||||
waveformatex.nSamplesPerSec = get_sampling_rate();
|
||||
@@ -176,6 +220,11 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
||||
waveformatex.wBitsPerSample = get_sample_size() * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
|
||||
FAudio_.trace("Wave format: %s, %u channels, %u Hz, %u bits per sample",
|
||||
get_convert_to_s16() ? "PCM" : "IEEE_FLOAT",
|
||||
waveformatex.nChannels, waveformatex.nSamplesPerSec, waveformatex.wBitsPerSample);
|
||||
|
||||
// Initialize voice callbacks
|
||||
OnVoiceProcessingPassStart = OnVoiceProcessingPassStart_func;
|
||||
OnVoiceProcessingPassEnd = nullptr;
|
||||
OnStreamEnd = nullptr;
|
||||
@@ -184,92 +233,183 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
||||
OnLoopEnd = nullptr;
|
||||
OnVoiceError = nullptr;
|
||||
|
||||
if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
|
||||
const u32 source_res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex,
|
||||
0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr);
|
||||
if (source_res != 0)
|
||||
{
|
||||
FAudio_.error("FAudio_CreateSourceVoice() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudio_CreateSourceVoice() failed with error code 0x%08x", source_res);
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
|
||||
const u32 start_res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
if (start_res != 0)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_Start() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioSourceVoice_Start() failed with error code 0x%08x", start_res);
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
|
||||
const u32 volume_res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW);
|
||||
if (volume_res != 0)
|
||||
{
|
||||
FAudio_.error("FAudioVoice_SetVolume() failed(0x%08x)", res);
|
||||
FAudio_.warning("FAudioVoice_SetVolume() failed with error code 0x%08x", volume_res);
|
||||
}
|
||||
|
||||
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
|
||||
// Pre-allocate audio buffer with some safety margin
|
||||
const usz buffer_size = static_cast<usz>(get_sampling_rate()) * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000;
|
||||
m_data_buf.reserve(buffer_size + 1024); // Extra safety margin
|
||||
m_data_buf.resize(buffer_size);
|
||||
|
||||
FAudio_.success("FAudio device opened successfully - %u Hz, %u channels, %u-bit %s",
|
||||
get_sampling_rate(), get_channels(), get_sample_size() * 8,
|
||||
get_convert_to_s16() ? "PCM" : "float");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
f64 FAudioBackend::GetCallbackFrameLen()
|
||||
{
|
||||
constexpr f64 _10ms = 0.01;
|
||||
constexpr f64 fallback_10ms = 0.01;
|
||||
|
||||
if (m_instance == nullptr)
|
||||
if (!m_instance)
|
||||
{
|
||||
FAudio_.error("GetCallbackFrameLen() called uninitialized");
|
||||
return _10ms;
|
||||
FAudio_.error("GetCallbackFrameLen() called on uninitialized instance");
|
||||
return fallback_10ms;
|
||||
}
|
||||
|
||||
f64 min_latency{};
|
||||
u32 samples_per_quantum = 0;
|
||||
u32 frequency = 0;
|
||||
|
||||
FAudio_GetProcessingQuantum(m_instance, &samples_per_quantum, &frequency);
|
||||
|
||||
u32 samples_per_q = 0, freq = 0;
|
||||
FAudio_GetProcessingQuantum(m_instance, &samples_per_q, &freq);
|
||||
|
||||
if (freq)
|
||||
if (frequency == 0 || samples_per_quantum == 0)
|
||||
{
|
||||
min_latency = static_cast<f64>(samples_per_q) / freq;
|
||||
FAudio_.warning("Invalid processing quantum values: samples=%u, freq=%u", samples_per_quantum, frequency);
|
||||
return fallback_10ms;
|
||||
}
|
||||
|
||||
return std::max<f64>(min_latency, _10ms);
|
||||
const f64 calculated_latency = static_cast<f64>(samples_per_quantum) / frequency;
|
||||
const f64 final_latency = std::max(calculated_latency, fallback_10ms);
|
||||
|
||||
FAudio_.trace("Calculated frame length: %.3f ms (quantum: %u samples @ %u Hz)",
|
||||
final_latency * 1000.0, samples_per_quantum, frequency);
|
||||
|
||||
return final_latency;
|
||||
}
|
||||
|
||||
void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, u32 BytesRequired)
|
||||
{
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && faudio->m_write_callback && faudio->m_playing)
|
||||
if (!cb_obj || BytesRequired == 0)
|
||||
{
|
||||
ensure(BytesRequired <= faudio->m_data_buf.size(), "FAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
const u32 sample_size = faudio->get_sample_size() * faudio->get_channels();
|
||||
u32 written = std::min(faudio->m_write_callback(BytesRequired, faudio->m_data_buf.data()), BytesRequired);
|
||||
written -= written % sample_size;
|
||||
|
||||
if (written >= sample_size)
|
||||
{
|
||||
memcpy(faudio->m_last_sample.data(), faudio->m_data_buf.data() + written - sample_size, sample_size);
|
||||
}
|
||||
|
||||
for (u32 i = written; i < BytesRequired; i += sample_size)
|
||||
{
|
||||
memcpy(faudio->m_data_buf.data() + i, faudio->m_last_sample.data(), sample_size);
|
||||
}
|
||||
|
||||
FAudioBuffer buffer{};
|
||||
buffer.AudioBytes = BytesRequired;
|
||||
buffer.pAudioData = static_cast<const u8*>(faudio->m_data_buf.data());
|
||||
// Avoid logging in callback and assume that this always succeeds, all errors are caught by error callback anyway
|
||||
FAudioSourceVoice_SubmitSourceBuffer(faudio->m_source_voice, &buffer, nullptr);
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
// Validate backend state
|
||||
if (!faudio->m_source_voice || faudio->m_reset_req.observe())
|
||||
{
|
||||
return; // Backend is in invalid state
|
||||
}
|
||||
|
||||
// Use a shorter timeout to avoid blocking the audio thread
|
||||
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
|
||||
if (!lock.try_lock_for(std::chrono::microseconds{100}))
|
||||
{
|
||||
return; // Could not acquire lock in reasonable time
|
||||
}
|
||||
|
||||
// Double-check state after acquiring lock
|
||||
if (!faudio->m_write_callback || !faudio->m_playing)
|
||||
{
|
||||
return; // No callback or not playing
|
||||
}
|
||||
|
||||
// Validate buffer size
|
||||
if (BytesRequired > faudio->m_data_buf.size())
|
||||
{
|
||||
// This should never happen with proper initialization, but let's be safe
|
||||
FAudio_.error("FAudio internal buffer is too small (%u requested, %zu available). Report to developers!",
|
||||
BytesRequired, faudio->m_data_buf.size());
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 sample_size = faudio->get_sample_size() * faudio->get_channels();
|
||||
if (sample_size == 0)
|
||||
{
|
||||
return; // Invalid configuration
|
||||
}
|
||||
|
||||
// Get audio data from callback
|
||||
u32 written = 0;
|
||||
if (faudio->m_write_callback)
|
||||
{
|
||||
written = faudio->m_write_callback(BytesRequired, faudio->m_data_buf.data());
|
||||
written = std::min(written, BytesRequired);
|
||||
written -= written % sample_size; // Align to sample boundary
|
||||
}
|
||||
|
||||
// Update last sample for padding if we got any data
|
||||
if (written >= sample_size)
|
||||
{
|
||||
const usz last_sample_offset = written - sample_size;
|
||||
if (last_sample_offset + sample_size <= faudio->m_data_buf.size())
|
||||
{
|
||||
std::memcpy(faudio->m_last_sample.data(),
|
||||
faudio->m_data_buf.data() + last_sample_offset,
|
||||
std::min(sample_size, static_cast<u32>(faudio->m_last_sample.size())));
|
||||
}
|
||||
}
|
||||
|
||||
// Pad remaining buffer with last sample to avoid audio glitches
|
||||
for (u32 i = written; i < BytesRequired; i += sample_size)
|
||||
{
|
||||
const u32 copy_size = std::min(sample_size, BytesRequired - i);
|
||||
if (i + copy_size <= faudio->m_data_buf.size())
|
||||
{
|
||||
std::memcpy(faudio->m_data_buf.data() + i, faudio->m_last_sample.data(), copy_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Submit buffer to FAudio
|
||||
FAudioBuffer buffer{};
|
||||
buffer.AudioBytes = BytesRequired;
|
||||
buffer.pAudioData = static_cast<const u8*>(faudio->m_data_buf.data());
|
||||
buffer.Flags = 0;
|
||||
buffer.PlayBegin = 0;
|
||||
buffer.PlayLength = 0;
|
||||
buffer.LoopBegin = 0;
|
||||
buffer.LoopLength = 0;
|
||||
buffer.LoopCount = 0;
|
||||
buffer.pContext = nullptr;
|
||||
|
||||
// Submit buffer - errors are handled by the error callback
|
||||
FAudioSourceVoice_SubmitSourceBuffer(faudio->m_source_voice, &buffer, nullptr);
|
||||
}
|
||||
|
||||
void FAudioBackend::OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error)
|
||||
{
|
||||
FAudio_.error("OnCriticalError() failed(0x%08x)", Error);
|
||||
// Log the critical error
|
||||
FAudio_.error("OnCriticalError() - FAudio engine encountered critical error 0x%08x", Error);
|
||||
|
||||
if (!cb_obj)
|
||||
{
|
||||
FAudio_.error("OnCriticalError() called with null callback object");
|
||||
return;
|
||||
}
|
||||
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
// Use try_lock to avoid potential deadlock in error conditions
|
||||
std::unique_lock lock(faudio->m_state_cb_mutex, std::try_to_lock);
|
||||
if (!lock.owns_lock())
|
||||
{
|
||||
FAudio_.warning("Could not acquire state callback mutex during critical error handling");
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(faudio->m_state_cb_mutex);
|
||||
|
||||
// Set reset request flag and notify application if we haven't already
|
||||
if (!faudio->m_reset_req.test_and_set() && faudio->m_state_callback)
|
||||
{
|
||||
faudio->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
|
||||
@@ -77,6 +77,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../../Utilities/sema.cpp
|
||||
../../Utilities/simple_ringbuf.cpp
|
||||
../../Utilities/stack_trace.cpp
|
||||
../../Utilities/stereo_config.cpp
|
||||
../../Utilities/StrFmt.cpp
|
||||
../../Utilities/Thread.cpp
|
||||
../../Utilities/version.cpp
|
||||
@@ -126,6 +127,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../Loader/PUP.cpp
|
||||
../Loader/TAR.cpp
|
||||
../Loader/ISO.cpp
|
||||
../Loader/iso_cache.cpp
|
||||
../Loader/iso_validation.cpp
|
||||
../Loader/TROPUSR.cpp
|
||||
../Loader/TRP.cpp
|
||||
)
|
||||
@@ -160,7 +163,7 @@ if(WIN32)
|
||||
Audio/XAudio2/xaudio2_enumerator.cpp
|
||||
)
|
||||
target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0A00)
|
||||
target_link_libraries(rpcs3_emu PRIVATE pdh bcrypt)
|
||||
target_link_libraries(rpcs3_emu PRIVATE pdh bcrypt dbghelp)
|
||||
endif()
|
||||
|
||||
# Cell
|
||||
@@ -333,9 +336,17 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/Modules/HLE_PATCHES.cpp
|
||||
Cell/Modules/libad_async.cpp
|
||||
Cell/Modules/libad_core.cpp
|
||||
Cell/Modules/libavcdec.cpp
|
||||
Cell/Modules/libdivx311dec.cpp
|
||||
Cell/Modules/libdivxdec.cpp
|
||||
Cell/Modules/libmedi.cpp
|
||||
Cell/Modules/libmixer.cpp
|
||||
Cell/Modules/libmvcdec.cpp
|
||||
Cell/Modules/libsjvtd.cpp
|
||||
Cell/Modules/libsmvd2.cpp
|
||||
Cell/Modules/libsmvd4.cpp
|
||||
Cell/Modules/libsnd3.cpp
|
||||
Cell/Modules/libsvc1d.cpp
|
||||
Cell/Modules/libsynth2.cpp
|
||||
Cell/Modules/sceNp2.cpp
|
||||
Cell/Modules/sceNpClans.cpp
|
||||
@@ -399,6 +410,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Io/Dimensions.cpp
|
||||
Io/evdev_gun_handler.cpp
|
||||
Io/GameTablet.cpp
|
||||
Io/gem_config.cpp
|
||||
Io/GHLtar.cpp
|
||||
Io/GunCon3.cpp
|
||||
Io/Infinity.cpp
|
||||
@@ -540,6 +552,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Program/GLSLCommon.cpp
|
||||
RSX/Program/ProgramStateCache.cpp
|
||||
RSX/Program/program_util.cpp
|
||||
RSX/Program/ShaderInterpreter.cpp
|
||||
RSX/Program/SPIRVCommon.cpp
|
||||
RSX/Program/VertexProgramDecompiler.cpp
|
||||
RSX/GSFrameBase.cpp
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace aarch64
|
||||
|
||||
if (head->magic == ESR_CTX_MAGIC)
|
||||
{
|
||||
return reinterpret_cast<const aarch64_esr_ctx*>(head);
|
||||
return utils::bless<const aarch64_esr_ctx>(head);
|
||||
}
|
||||
|
||||
offset += head->size;
|
||||
|
||||
@@ -61,6 +61,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::notify: return "ntf";
|
||||
case cpu_flag::yield: return "y";
|
||||
case cpu_flag::preempt: return "PREEMPT";
|
||||
case cpu_flag::req_exit: return "REQ-EXIT";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
@@ -729,8 +730,14 @@ void cpu_thread::operator()()
|
||||
{
|
||||
if (_this)
|
||||
{
|
||||
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
|
||||
cleanup();
|
||||
|
||||
auto log_thread = named_thread("CPU Thread Cleanup Logger", [name = name]()
|
||||
{
|
||||
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
|
||||
});
|
||||
|
||||
log_thread();
|
||||
}
|
||||
}
|
||||
} cleanup;
|
||||
@@ -1502,7 +1509,7 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept
|
||||
{
|
||||
for (u32 i = 0; i < work->prf_size; i++)
|
||||
{
|
||||
utils::prefetch_write(work->prf_list[0]);
|
||||
utils::prefetch_write(work->prf_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -207,10 +207,25 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
||||
{
|
||||
m_use_dotprod = true;
|
||||
}
|
||||
|
||||
if (utils::has_i8mm())
|
||||
{
|
||||
m_use_i8mm = true;
|
||||
}
|
||||
|
||||
if (utils::has_sve() && utils::sve_length() == 128)
|
||||
{
|
||||
m_use_sve_128 = true;
|
||||
}
|
||||
|
||||
if (utils::has_sve2() && utils::sve_length() == 128)
|
||||
{
|
||||
m_use_sve2_128 = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc) const
|
||||
{
|
||||
uint s1 = type->getScalarSizeInBits();
|
||||
uint s2 = val->getType()->getScalarSizeInBits();
|
||||
@@ -222,7 +237,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
|
||||
if (s1 != s2)
|
||||
{
|
||||
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)", s1, s2);
|
||||
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)\nCalled from: %s", s1, s2, src_loc);
|
||||
}
|
||||
|
||||
if (val->getType() == type)
|
||||
@@ -244,36 +259,48 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = source_val->use_begin(); it != source_val->use_end(); ++it)
|
||||
// Skip use iteration for values that don't have use lists
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (source_val->hasUseList())
|
||||
#endif
|
||||
{
|
||||
llvm::Value* it_val = *it;
|
||||
|
||||
if (!it_val)
|
||||
for (llvm::Value* it_val : source_val->uses())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
llvm::CastInst* bci = llvm::dyn_cast_or_null<llvm::CastInst>(it_val);
|
||||
|
||||
// Walk through bitcasts
|
||||
while (bci && bci->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
if (bci->getParent() != m_ir->GetInsertBlock())
|
||||
if (!it_val)
|
||||
{
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bci->getType() == type)
|
||||
{
|
||||
return bci;
|
||||
}
|
||||
llvm::CastInst* bci = llvm::dyn_cast_or_null<llvm::CastInst>(it_val);
|
||||
|
||||
if (bci->use_begin() == bci->use_end())
|
||||
// Walk through bitcasts
|
||||
while (bci && bci->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (bci->getParent() != m_ir->GetInsertBlock())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bci = llvm::dyn_cast_or_null<llvm::CastInst>(*bci->use_begin());
|
||||
if (bci->getType() == type)
|
||||
{
|
||||
return bci;
|
||||
}
|
||||
|
||||
// Check if bci has use list before accessing use_begin()
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!bci->hasUseList())
|
||||
{
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bci->use_begin() == bci->use_end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bci = llvm::dyn_cast_or_null<llvm::CastInst>(*bci->use_begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,14 +580,25 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
|
||||
{
|
||||
for (auto v : args)
|
||||
{
|
||||
for (auto it = v->use_begin(); it != v->use_end(); ++it)
|
||||
// Skip use iteration for values that don't have use lists
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!v->hasUseList())
|
||||
continue;
|
||||
#endif
|
||||
|
||||
for (llvm::Value* i : v->uses())
|
||||
{
|
||||
llvm::Value* i = *it;
|
||||
llvm::CastInst* bci = nullptr;
|
||||
|
||||
// Walk through bitcasts
|
||||
while (i && (bci = llvm::dyn_cast<llvm::CastInst>(i)) && bci->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
// Check if bci has use list before accessing use_begin()
|
||||
#if LLVM_VERSION_MAJOR >= 21
|
||||
if (!bci->hasUseList())
|
||||
break;
|
||||
#endif
|
||||
|
||||
i = *bci->use_begin();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <source_location>
|
||||
|
||||
// Helper function
|
||||
llvm::Value* peek_through_bitcasts(llvm::Value*);
|
||||
@@ -3119,6 +3120,16 @@ protected:
|
||||
|
||||
// ARMv8 SDOT/UDOT
|
||||
bool m_use_dotprod = false;
|
||||
|
||||
// ARMv8.6 SMMLA/UMMLA
|
||||
bool m_use_i8mm = false;
|
||||
|
||||
// Allow direct TBL2/TBX2 emission.
|
||||
bool m_use_tbl2 = true;
|
||||
|
||||
bool m_use_sve_128 = false;
|
||||
|
||||
bool m_use_sve2_128 = false;
|
||||
#else
|
||||
// Allow FMA
|
||||
bool m_use_fma = false;
|
||||
@@ -3239,7 +3250,7 @@ public:
|
||||
}
|
||||
|
||||
// Bitcast with immediate constant folding
|
||||
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type) const;
|
||||
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc = std::source_location::current()) const;
|
||||
|
||||
template <typename T>
|
||||
llvm::Value* bitcast(llvm::Value* val)
|
||||
@@ -3719,8 +3730,170 @@ template <typename T1, typename T2, typename T3>
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_sdot), {data0, data1, data2});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
value_t<u32[4]> ummla(T1 a, T2 b, T3 c)
|
||||
{
|
||||
value_t<u32[4]> result;
|
||||
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
const auto data2 = c.eval(m_ir);
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_ummla), {data0, data1, data2});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
value_t<u32[4]> smmla(T1 a, T2 b, T3 c)
|
||||
{
|
||||
value_t<u32[4]> result;
|
||||
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
const auto data2 = c.eval(m_ir);
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_smmla), {data0, data1, data2});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<s32[4]> smull(T1 a, T2 b)
|
||||
{
|
||||
value_t<s32[4]> result;
|
||||
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<s32[4]>(llvm::Intrinsic::aarch64_neon_smull), {data0, data1});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<u32[4]> umull(T1 a, T2 b)
|
||||
{
|
||||
value_t<u32[4]> result;
|
||||
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u32[4]>(llvm::Intrinsic::aarch64_neon_umull), {data0, data1});
|
||||
return result;
|
||||
}
|
||||
|
||||
llvm::Value* to_sve_vector(llvm::Value* value)
|
||||
{
|
||||
if (llvm::isa<llvm::ScalableVectorType>(value->getType()))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(value->getType());
|
||||
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
|
||||
return m_ir->CreateInsertVector(scalable_type, llvm::UndefValue::get(scalable_type), value, m_ir->getInt64(0));
|
||||
}
|
||||
|
||||
llvm::Value* from_sve_vector(llvm::Value* value, llvm::FixedVectorType* fixed_type)
|
||||
{
|
||||
if (value->getType() == fixed_type)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return m_ir->CreateExtractVector(fixed_type, value, m_ir->getInt64(0));
|
||||
}
|
||||
|
||||
llvm::Value* sve_ptrue(llvm::FixedVectorType* fixed_type)
|
||||
{
|
||||
const auto pred_type = llvm::ScalableVectorType::get(m_ir->getInt1Ty(), fixed_type->getNumElements());
|
||||
return m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_ptrue, {pred_type}, {m_ir->getInt32(31)});
|
||||
}
|
||||
|
||||
llvm::Value* sve_fnmls(llvm::Value* acc, llvm::Value* lhs, llvm::Value* rhs)
|
||||
{
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(acc->getType());
|
||||
const auto vacc = to_sve_vector(acc);
|
||||
const auto vlhs = to_sve_vector(lhs);
|
||||
const auto vrhs = to_sve_vector(rhs);
|
||||
const auto result = m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_fnmls, {vacc->getType()}, {sve_ptrue(fixed_type), vacc, vlhs, vrhs});
|
||||
|
||||
return from_sve_vector(result, fixed_type);
|
||||
}
|
||||
|
||||
template <typename T, typename T1, typename T2>
|
||||
value_t<T> sve_mull(llvm::Intrinsic::ID id, T1 a, T2 b)
|
||||
{
|
||||
value_t<T> result;
|
||||
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
|
||||
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
|
||||
const auto data0 = to_sve_vector(a.eval(m_ir));
|
||||
const auto data1 = to_sve_vector(b.eval(m_ir));
|
||||
const std::array<llvm::Type*, 1> types{scalable_type};
|
||||
|
||||
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1}), fixed_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename T0, typename T1, typename T2>
|
||||
value_t<T> sve_mlal(llvm::Intrinsic::ID id, T0 acc, T1 a, T2 b)
|
||||
{
|
||||
value_t<T> result;
|
||||
|
||||
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
|
||||
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
|
||||
const auto data0 = to_sve_vector(acc.eval(m_ir));
|
||||
const auto data1 = to_sve_vector(a.eval(m_ir));
|
||||
const auto data2 = to_sve_vector(b.eval(m_ir));
|
||||
const std::array<llvm::Type*, 1> types{scalable_type};
|
||||
|
||||
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1, data2}), fixed_type);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smullb(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullb, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smullt(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullt, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umullb(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullb, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umullt(T1 a, T2 b)
|
||||
{
|
||||
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullt, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smlalb(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalb, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<s32[4]> sve_smlalt(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalt, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T0, typename T1, typename T2>
|
||||
value_t<u32[4]> sve_umlalt(T0 acc, T1 a, T2 b)
|
||||
{
|
||||
return sve_mlal<u32[4]>(llvm::Intrinsic::aarch64_sve_umlalt, acc, a, b);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
auto addp(T1 a, T2 b)
|
||||
{
|
||||
using T_vector = typename is_llvm_expr<T1>::type;
|
||||
@@ -4029,6 +4202,131 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef ARCH_ARM64
|
||||
template <typename T1, typename T2>
|
||||
value_t<u8[16]> tbl(T1 a, T2 b)
|
||||
{
|
||||
value_t<u8[16]> result;
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto index = b.eval(m_ir);
|
||||
const auto zeros = llvm::ConstantAggregateZero::get(get_type<u8[16]>());
|
||||
|
||||
if (auto c = llvm::dyn_cast<llvm::Constant>(index))
|
||||
{
|
||||
v128 mask{};
|
||||
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
|
||||
|
||||
if (cv)
|
||||
{
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
{
|
||||
const u64 b_val = cv->getElementAsInteger(i);
|
||||
mask._u8[i] = (b_val < 16) ? static_cast<u8>(b_val) : static_cast<u8>(16);
|
||||
}
|
||||
}
|
||||
|
||||
if (cv || llvm::isa<llvm::ConstantAggregateZero>(c))
|
||||
{
|
||||
result.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&mask), 16));
|
||||
result.value = m_ir->CreateZExt(result.value, get_type<u32[16]>());
|
||||
result.value = m_ir->CreateShuffleVector(data0, zeros, result.value);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data0, index });
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
value_t<u8[16]> tbl2(T1 a, T2 b, T3 indices)
|
||||
{
|
||||
value_t<u8[16]> result;
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
const auto index = indices.eval(m_ir);
|
||||
|
||||
if (m_use_tbl2)
|
||||
{
|
||||
if (auto c = llvm::dyn_cast<llvm::Constant>(index))
|
||||
{
|
||||
v128 mask{};
|
||||
v128 bitmask{};
|
||||
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
|
||||
|
||||
if (cv)
|
||||
{
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
{
|
||||
const u64 b_val = cv->getElementAsInteger(i);
|
||||
mask._u8[i] = (b_val < 32) ? static_cast<u8>(b_val) : static_cast<u8>(0);
|
||||
bitmask._u8[i] = (b_val < 32) ? static_cast<u8>(0xFF) : static_cast<u8>(0x00);
|
||||
}
|
||||
}
|
||||
else if (llvm::isa<llvm::ConstantAggregateZero>(c))
|
||||
{
|
||||
bitmask = v128::from8p(0xFF);
|
||||
}
|
||||
|
||||
if (cv || llvm::isa<llvm::ConstantAggregateZero>(c))
|
||||
{
|
||||
auto m_val = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&mask), 16));
|
||||
auto m_ext = m_ir->CreateZExt(m_val, get_type<u32[16]>());
|
||||
auto lookup = m_ir->CreateShuffleVector(data0, data1, m_ext);
|
||||
|
||||
auto z_mask = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&bitmask), 16));
|
||||
result.value = m_ir->CreateAnd(lookup, z_mask);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl2), { data0, data1, index });
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto data0_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data0, index });
|
||||
const auto data1_index = m_ir->CreateSub(index, llvm::ConstantInt::get(get_type<u8[16]>(), 16));
|
||||
const auto data1_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data1, data1_index });
|
||||
|
||||
result.value = m_ir->CreateOr(data0_lookup, data1_lookup);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
value_t<u8[16]> tbx(T1 fallback, T2 a, T3 indices)
|
||||
{
|
||||
value_t<u8[16]> result;
|
||||
const auto v_fallback = fallback.eval(m_ir);
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto index = indices.eval(m_ir);
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { v_fallback, data0, index });
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename T3, typename T4>
|
||||
value_t<u8[16]> tbx2(T1 fallback, T2 a, T3 b, T4 indices)
|
||||
{
|
||||
value_t<u8[16]> result;
|
||||
const auto v_fallback = fallback.eval(m_ir);
|
||||
const auto data0 = a.eval(m_ir);
|
||||
const auto data1 = b.eval(m_ir);
|
||||
const auto index = indices.eval(m_ir);
|
||||
|
||||
if (m_use_tbl2)
|
||||
{
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx2), { v_fallback, data0, data1, index });
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto first_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { v_fallback, data0, index });
|
||||
const auto data1_index = m_ir->CreateSub(index, llvm::ConstantInt::get(get_type<u8[16]>(), 16));
|
||||
|
||||
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { first_lookup, data1, data1_index });
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// (m << 3) >= 0 ? a : b
|
||||
template <typename T, typename U, typename V>
|
||||
static auto select_by_bit4(T&& m, U&& a, V&& b)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
#if defined(ARCH_x64)
|
||||
#if defined(ARCH_X64)
|
||||
union hypervisor_context_t
|
||||
{
|
||||
u64 regs[1];
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma push_macro("FORCE_INLINE")
|
||||
#pragma push_macro("ALIGN_STRUCT")
|
||||
#undef FORCE_INLINE
|
||||
#undef ALIGN_STRUCT
|
||||
#define FORCE_INLINE static inline __attribute__((always_inline))
|
||||
#define ALIGN_STRUCT(x) __attribute__((aligned(x)))
|
||||
#define _sse2neon_likely(x) __builtin_expect(!!(x), 1)
|
||||
|
||||
@@ -398,7 +398,7 @@ cell_audio_thread::cell_audio_thread(utils::serial& ar)
|
||||
|
||||
ar(key_count, event_period);
|
||||
|
||||
keys.resize(ar);
|
||||
keys.resize(ar.pop<u64>());
|
||||
|
||||
for (key_info& k : keys)
|
||||
{
|
||||
@@ -421,7 +421,7 @@ void cell_audio_thread::save(utils::serial& ar)
|
||||
USING_SERIALIZATION_VERSION(cellAudio);
|
||||
|
||||
ar(key_count, event_period);
|
||||
ar(keys.size());
|
||||
ar(static_cast<u64>(keys.size()));
|
||||
|
||||
for (const key_info& k : keys)
|
||||
{
|
||||
@@ -1662,36 +1662,35 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags)
|
||||
lv2_sleep(20, &ppu);
|
||||
|
||||
// Dirty hack for sound: confirm the creation of _mxr000 event queue by _cellsurMixerMain thread
|
||||
constexpr u64 c_mxr000 = 0x8000cafe0246030;
|
||||
constexpr u64 c_mxr000 = 0x8000cafe02460300;
|
||||
|
||||
if (key == c_mxr000 || key == 0)
|
||||
{
|
||||
bool has_sur_mixer_thread = false;
|
||||
|
||||
for (usz count = 0; !lv2_event_queue::find(c_mxr000) && count < 100; count++)
|
||||
const bool has_sur_mixer_thread = idm::select<named_thread<ppu_thread>>([&](u32 id, named_thread<ppu_thread>& test_ppu)
|
||||
{
|
||||
if (has_sur_mixer_thread || idm::select<named_thread<ppu_thread>>([&](u32 id, named_thread<ppu_thread>& test_ppu)
|
||||
// Confirm thread existence
|
||||
if (id == ppu.id)
|
||||
{
|
||||
// Confirm thread existence
|
||||
if (id == ppu.id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto ptr = test_ppu.ppu_tname.load();
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return *ptr == "_cellsurMixerMain"sv;
|
||||
}).ret)
|
||||
{
|
||||
has_sur_mixer_thread = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
const auto ptr = test_ppu.ppu_tname.load();
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return *ptr == "_cellsurMixerMain"sv;
|
||||
}).ret;
|
||||
|
||||
bool was_mxr000_queue_found = false;
|
||||
|
||||
for (usz count = 0; has_sur_mixer_thread && count < 100; count++)
|
||||
{
|
||||
if (lv2_event_queue::find(c_mxr000))
|
||||
{
|
||||
was_mxr000_queue_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1701,13 +1700,14 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags)
|
||||
return {};
|
||||
}
|
||||
|
||||
cellAudio.error("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count);
|
||||
(count < 3 ? cellAudio.warning : cellAudio.error)("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count);
|
||||
|
||||
lv2_sleep(50'000, &ppu);
|
||||
}
|
||||
|
||||
if (has_sur_mixer_thread && lv2_event_queue::find(c_mxr000))
|
||||
if (key == 0 && was_mxr000_queue_found)
|
||||
{
|
||||
// Correct key value argument
|
||||
key = c_mxr000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,19 +524,11 @@ error_code cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<f32> screenSize)
|
||||
{
|
||||
// Return Playstation 3D display value
|
||||
// Some games call this function when 3D is enabled
|
||||
*screenSize = 24.f;
|
||||
*screenSize = static_cast<f32>(g_cfg.video.screen_size.get());
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// TODO: Use virtual screen size
|
||||
#ifdef _WIN32
|
||||
// HDC screen = GetDC(NULL);
|
||||
// float diagonal = roundf(sqrtf((powf(float(GetDeviceCaps(screen, HORZSIZE)), 2) + powf(float(GetDeviceCaps(screen, VERTSIZE)), 2))) * 0.0393f);
|
||||
#else
|
||||
// TODO: Linux implementation, without using wx
|
||||
// float diagonal = roundf(sqrtf((powf(wxGetDisplaySizeMM().GetWidth(), 2) + powf(wxGetDisplaySizeMM().GetHeight(), 2))) * 0.0393f);
|
||||
#endif
|
||||
|
||||
// Let's just return not set for now
|
||||
return CELL_VIDEO_OUT_ERROR_VALUE_IS_NOT_SET;
|
||||
}
|
||||
|
||||
|
||||
+1085
-1202
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "cellPamf.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Utilities/BitField.h"
|
||||
|
||||
// Error Codes
|
||||
enum CellDmuxError :u32
|
||||
@@ -18,6 +19,10 @@ enum CellDmuxStreamType : s32
|
||||
CELL_DMUX_STREAM_TYPE_UNDEF = 0,
|
||||
CELL_DMUX_STREAM_TYPE_PAMF = 1,
|
||||
CELL_DMUX_STREAM_TYPE_TERMINATOR = 2,
|
||||
|
||||
// Only used in cellSail
|
||||
CELL_DMUX_STREAM_TYPE_MP4 = 0x81,
|
||||
CELL_DMUX_STREAM_TYPE_AVI = 0x82
|
||||
};
|
||||
|
||||
enum CellDmuxMsgType : s32
|
||||
@@ -48,13 +53,14 @@ struct CellDmuxEsMsg
|
||||
struct CellDmuxType
|
||||
{
|
||||
be_t<s32> streamType; // CellDmuxStreamType
|
||||
be_t<u32> reserved[2];
|
||||
be_t<s32> reserved1;
|
||||
be_t<s32> reserved2;
|
||||
};
|
||||
|
||||
struct CellDmuxType2
|
||||
{
|
||||
be_t<s32> streamType; // CellDmuxStreamType
|
||||
be_t<u32> streamSpecificInfo;
|
||||
be_t<s32> streamType;
|
||||
vm::bcptr<void> streamSpecificInfo;
|
||||
};
|
||||
|
||||
struct CellDmuxResource
|
||||
@@ -73,8 +79,8 @@ struct CellDmuxResourceEx
|
||||
be_t<u32> memSize;
|
||||
be_t<u32> ppuThreadPriority;
|
||||
be_t<u32> ppuThreadStackSize;
|
||||
be_t<u32> spurs_addr;
|
||||
u8 priority[8];
|
||||
vm::bptr<void> spurs; // CellSpurs*
|
||||
be_t<u64, 1> priority;
|
||||
be_t<u32> maxContention;
|
||||
};
|
||||
|
||||
@@ -85,33 +91,23 @@ struct CellDmuxResourceSpurs
|
||||
be_t<u32> maxContention;
|
||||
};
|
||||
|
||||
/*
|
||||
struct CellDmuxResource2Ex
|
||||
{
|
||||
b8 isResourceEx; //true
|
||||
CellDmuxResourceEx resourceEx;
|
||||
};
|
||||
|
||||
struct CellDmuxResource2NoEx
|
||||
{
|
||||
b8 isResourceEx; //false
|
||||
CellDmuxResource resource;
|
||||
};
|
||||
*/
|
||||
|
||||
struct CellDmuxResource2
|
||||
{
|
||||
b8 isResourceEx;
|
||||
be_t<u32> memAddr;
|
||||
be_t<u32> memSize;
|
||||
be_t<u32> ppuThreadPriority;
|
||||
be_t<u32> ppuThreadStackSize;
|
||||
be_t<u32> shit[4];
|
||||
|
||||
union
|
||||
{
|
||||
CellDmuxResource resource;
|
||||
CellDmuxResourceEx resourceEx;
|
||||
};
|
||||
};
|
||||
|
||||
using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
|
||||
struct DmuxContext;
|
||||
struct DmuxEsContext;
|
||||
|
||||
using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
|
||||
using CellDmuxCbMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
|
||||
|
||||
using CellDmuxCbEsMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::ptr<DmuxEsContext> esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
|
||||
|
||||
// Used for internal callbacks as well
|
||||
template <typename F>
|
||||
@@ -177,6 +173,70 @@ struct DmuxAuInfo
|
||||
be_t<u32> specific_info_size;
|
||||
};
|
||||
|
||||
struct DmuxAuQueueElement
|
||||
{
|
||||
be_t<u32> index;
|
||||
u8 unk; // unused
|
||||
DmuxAuInfo au_info;
|
||||
};
|
||||
|
||||
CHECK_SIZE(DmuxAuQueueElement, 0x38);
|
||||
|
||||
enum DmuxState : u32
|
||||
{
|
||||
DMUX_STOPPED = 1 << 0,
|
||||
DMUX_RUNNING = 1 << 1,
|
||||
};
|
||||
|
||||
struct alignas(0x10) DmuxContext // CellDmuxHandle = DmuxContext*
|
||||
{
|
||||
vm::bptr<DmuxContext> _this;
|
||||
be_t<u32> _this_size;
|
||||
be_t<u32> version;
|
||||
be_t<u32> dmux_state;
|
||||
CellDmuxType dmux_type;
|
||||
CellDmuxCb dmux_cb;
|
||||
b8 stream_is_set;
|
||||
vm::bptr<void> core_handle;
|
||||
be_t<u32> version_; // Same value as 'version'
|
||||
be_t<u64> user_data;
|
||||
be_t<s32> max_enabled_es_num;
|
||||
be_t<s32> enabled_es_num;
|
||||
be_t<u32> _dx_mhd; // sys_mutex_t
|
||||
u8 reserved[0x7c];
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(DmuxContext, 0xc0, 0x10);
|
||||
|
||||
struct alignas(0x10) DmuxEsContext // CellDmuxEsHandle = DmuxEsContext*
|
||||
{
|
||||
be_t<u32> _dx_mes; // sys_mutex_t
|
||||
be_t<u32> is_enabled;
|
||||
be_t<u32> error_mem_size;
|
||||
be_t<u32> error_count;
|
||||
vm::bptr<void> error_mem_addr;
|
||||
vm::bptr<DmuxEsContext> _this;
|
||||
be_t<u32> _this_size;
|
||||
be_t<s32> _this_index;
|
||||
vm::bptr<DmuxContext> dmux_handle;
|
||||
CellDmuxEsCb es_cb;
|
||||
vm::bptr<void> core_es_handle;
|
||||
bf_t<be_t<u32>, 0, 1> flush_started;
|
||||
|
||||
struct
|
||||
{
|
||||
be_t<s32> max_size;
|
||||
be_t<s32> allocated_size;
|
||||
be_t<s32> size;
|
||||
be_t<s32> front;
|
||||
be_t<s32> back;
|
||||
be_t<s32> allocated_back;
|
||||
}
|
||||
au_queue;
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(DmuxEsContext, 0x50, 0x10);
|
||||
|
||||
using DmuxNotifyDemuxDone = error_code(vm::ptr<void>, u32, vm::ptr<void>);
|
||||
using DmuxNotifyFatalErr = error_code(vm::ptr<void>, u32, vm::ptr<void>);
|
||||
using DmuxNotifyProgEndCode = error_code(vm::ptr<void>, vm::ptr<void>);
|
||||
@@ -194,10 +254,10 @@ using CellDmuxCoreOpSetStream = error_code(vm::ptr<void>, vm::cptr<void>, u32, b
|
||||
using CellDmuxCoreOpReleaseAu = error_code(vm::ptr<void>, vm::ptr<void>, u32);
|
||||
using CellDmuxCoreOpQueryEsAttr = error_code(vm::cptr<void>, vm::cptr<void>, vm::ptr<CellDmuxPamfEsAttr>);
|
||||
using CellDmuxCoreOpEnableEs = error_code(vm::ptr<void>, vm::cptr<void>, vm::cptr<CellDmuxEsResource>, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>>, vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>>, vm::cptr<void>, vm::pptr<void>);
|
||||
using CellDmuxCoreOpDisableEs = u32(vm::ptr<void>);
|
||||
using CellDmuxCoreOpFlushEs = u32(vm::ptr<void>);
|
||||
using CellDmuxCoreOpResetEs = u32(vm::ptr<void>);
|
||||
using CellDmuxCoreOpResetStreamAndWaitDone = u32(vm::ptr<void>);
|
||||
using CellDmuxCoreOpDisableEs = error_code(vm::ptr<void>);
|
||||
using CellDmuxCoreOpFlushEs = error_code(vm::ptr<void>);
|
||||
using CellDmuxCoreOpResetEs = error_code(vm::ptr<void>);
|
||||
using CellDmuxCoreOpResetStreamAndWaitDone = error_code(vm::ptr<void>);
|
||||
|
||||
struct CellDmuxCoreOps
|
||||
{
|
||||
|
||||
@@ -2591,7 +2591,7 @@ template <bool raw_es>
|
||||
error_code _CellDmuxCoreOpEnableEs(ppu_thread& ppu, vm::ptr<CellDmuxPamfHandle> handle, vm::cptr<void> esFilterId, vm::cptr<CellDmuxEsResource> esResource, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>> notifyAuFound,
|
||||
vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>> notifyFlushDone, vm::cptr<void> esSpecificInfo, vm::pptr<CellDmuxPamfEsHandle> esHandle)
|
||||
{
|
||||
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle)",
|
||||
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle=**0x%x)",
|
||||
raw_es, handle, esFilterId, esResource, notifyAuFound, notifyFlushDone, esSpecificInfo, esHandle);
|
||||
|
||||
if (!handle || !esFilterId || !esResource || !esResource->memAddr || esResource->memSize == 0u || !notifyAuFound || !notifyAuFound->cbFunc || !notifyAuFound->cbArg || !notifyFlushDone || !notifyFlushDone->cbFunc || !notifyFlushDone->cbArg)
|
||||
|
||||
@@ -520,10 +520,11 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
|
||||
return CELL_GAMEDATA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (!fs::create_path(vfs::get(usrdir)))
|
||||
{
|
||||
return {CELL_GAME_ERROR_ACCESS_ERROR, usrdir};
|
||||
}
|
||||
// Nuked until correctly reversed engineered
|
||||
//if (!fs::create_path(vfs::get(usrdir)))
|
||||
//{
|
||||
// return {CELL_GAME_ERROR_ACCESS_ERROR, usrdir};
|
||||
//}
|
||||
}
|
||||
|
||||
// Nuked until correctly reversed engineered
|
||||
|
||||
@@ -27,39 +27,6 @@
|
||||
|
||||
LOG_CHANNEL(cellGem);
|
||||
|
||||
template <>
|
||||
void fmt_class_string<gem_btn>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](gem_btn value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case gem_btn::start: return "Start";
|
||||
case gem_btn::select: return "Select";
|
||||
case gem_btn::triangle: return "Triangle";
|
||||
case gem_btn::circle: return "Circle";
|
||||
case gem_btn::cross: return "Cross";
|
||||
case gem_btn::square: return "Square";
|
||||
case gem_btn::move: return "Move";
|
||||
case gem_btn::t: return "T";
|
||||
case gem_btn::x_axis: return "X-Axis";
|
||||
case gem_btn::y_axis: return "Y-Axis";
|
||||
case gem_btn::combo: return "Combo";
|
||||
case gem_btn::combo_start: return "Combo Start";
|
||||
case gem_btn::combo_select: return "Combo Select";
|
||||
case gem_btn::combo_triangle: return "Combo Triangle";
|
||||
case gem_btn::combo_circle: return "Combo Circle";
|
||||
case gem_btn::combo_cross: return "Combo Cross";
|
||||
case gem_btn::combo_square: return "Combo Square";
|
||||
case gem_btn::combo_move: return "Combo Move";
|
||||
case gem_btn::combo_t: return "Combo T";
|
||||
case gem_btn::count: return "Count";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<CellGemError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -234,7 +201,7 @@ public:
|
||||
struct gem_controller
|
||||
{
|
||||
u32 status = CELL_GEM_STATUS_DISCONNECTED; // Connection status (CELL_GEM_STATUS_DISCONNECTED or CELL_GEM_STATUS_READY)
|
||||
u32 ext_status = CELL_GEM_NO_EXTERNAL_PORT_DEVICE; // External port connection status
|
||||
u32 ext_status = 0; // External port connection status
|
||||
u32 ext_id = 0; // External device ID (type). For example SHARP_SHOOTER_DEVICE_ID
|
||||
u32 port = 0; // Assigned port
|
||||
bool enabled_magnetometer = true; // Whether the magnetometer is enabled (probably used for additional rotational precision)
|
||||
@@ -254,6 +221,8 @@ public:
|
||||
u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds
|
||||
u64 calibration_status_flags = 0; // The calibration status flags
|
||||
|
||||
u8 firing_mode = button_flags::ss_firing_mode_1; // The firing mode of the sharpshooter. Used with fake PS-Move. This is a physical switch with 3 positions, not a button that can be pressed.
|
||||
|
||||
static constexpr u64 calibration_time_us = 500000; // The calibration supposedly takes 0.5 seconds (500000 microseconds)
|
||||
};
|
||||
|
||||
@@ -2149,6 +2118,337 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
|
||||
}
|
||||
}
|
||||
|
||||
static const std::unordered_map<gem_btn, u16> ext_btn_map =
|
||||
{
|
||||
{ gem_btn::sharpshooter_firing_mode_1, button_flags::ss_firing_mode_1 },
|
||||
{ gem_btn::sharpshooter_firing_mode_2, button_flags::ss_firing_mode_2 },
|
||||
{ gem_btn::sharpshooter_firing_mode_3, button_flags::ss_firing_mode_3 },
|
||||
{ gem_btn::sharpshooter_trigger, button_flags::ss_trigger },
|
||||
{ gem_btn::sharpshooter_reload, button_flags::ss_reload },
|
||||
{ gem_btn::racing_wheel_d_pad_up, CELL_PAD_CTRL_UP },
|
||||
{ gem_btn::racing_wheel_d_pad_right, CELL_PAD_CTRL_RIGHT },
|
||||
{ gem_btn::racing_wheel_d_pad_down, CELL_PAD_CTRL_DOWN },
|
||||
{ gem_btn::racing_wheel_d_pad_left, CELL_PAD_CTRL_LEFT },
|
||||
{ gem_btn::racing_wheel_throttle, 0 },
|
||||
{ gem_btn::racing_wheel_l1, CELL_PAD_CTRL_L1 },
|
||||
{ gem_btn::racing_wheel_r1, CELL_PAD_CTRL_R1 },
|
||||
{ gem_btn::racing_wheel_l2, 0 },
|
||||
{ gem_btn::racing_wheel_r2, 0 },
|
||||
{ gem_btn::racing_wheel_paddle_l, button_flags::rw_paddle_l },
|
||||
{ gem_btn::racing_wheel_paddle_r, button_flags::rw_paddle_r },
|
||||
{ gem_btn::combo_sharpshooter_firing_mode_1, button_flags::ss_firing_mode_1 },
|
||||
{ gem_btn::combo_sharpshooter_firing_mode_2, button_flags::ss_firing_mode_2 },
|
||||
{ gem_btn::combo_sharpshooter_firing_mode_3, button_flags::ss_firing_mode_3 },
|
||||
{ gem_btn::combo_sharpshooter_trigger, button_flags::ss_trigger },
|
||||
{ gem_btn::combo_sharpshooter_reload, button_flags::ss_reload },
|
||||
{ gem_btn::combo_racing_wheel_d_pad_up, CELL_PAD_CTRL_UP },
|
||||
{ gem_btn::combo_racing_wheel_d_pad_right, CELL_PAD_CTRL_RIGHT },
|
||||
{ gem_btn::combo_racing_wheel_d_pad_down, CELL_PAD_CTRL_DOWN },
|
||||
{ gem_btn::combo_racing_wheel_d_pad_left, CELL_PAD_CTRL_LEFT },
|
||||
{ gem_btn::combo_racing_wheel_throttle, 0 },
|
||||
{ gem_btn::combo_racing_wheel_l1, CELL_PAD_CTRL_L1 },
|
||||
{ gem_btn::combo_racing_wheel_r1, CELL_PAD_CTRL_R1 },
|
||||
{ gem_btn::combo_racing_wheel_l2, 0 },
|
||||
{ gem_btn::combo_racing_wheel_r2, 0 },
|
||||
{ gem_btn::combo_racing_wheel_paddle_l, button_flags::rw_paddle_l },
|
||||
{ gem_btn::combo_racing_wheel_paddle_r, button_flags::rw_paddle_r },
|
||||
};
|
||||
|
||||
static const std::unordered_map<gem_btn, u16> ext_btn_indices =
|
||||
{
|
||||
{ gem_btn::racing_wheel_throttle, 0 },
|
||||
{ gem_btn::racing_wheel_l2, 1 },
|
||||
{ gem_btn::racing_wheel_r2, 2 },
|
||||
{ gem_btn::racing_wheel_paddle_l, 3 },
|
||||
{ gem_btn::racing_wheel_paddle_r, 4 },
|
||||
{ gem_btn::combo_racing_wheel_throttle, 0 },
|
||||
{ gem_btn::combo_racing_wheel_l2, 1 },
|
||||
{ gem_btn::combo_racing_wheel_r2, 2 },
|
||||
{ gem_btn::combo_racing_wheel_paddle_l, 3 },
|
||||
{ gem_btn::combo_racing_wheel_paddle_r, 4 },
|
||||
};
|
||||
|
||||
static u32 ext_device_id(gem_ext_id id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case gem_ext_id::disconnected: return 0;
|
||||
case gem_ext_id::sharpshooter: return SHARP_SHOOTER_DEVICE_ID;
|
||||
case gem_ext_id::racing_wheel: return RACING_WHEEL_DEVICE_ID;
|
||||
}
|
||||
fmt::throw_exception("Mo ID found for id = %d", static_cast<s32>(id));
|
||||
}
|
||||
|
||||
template <bool has_combo, bool is_combo>
|
||||
static void input_to_ext(u32 external_device_id, CellGemExtPortData& ext, std::set<pad_button>& combos, gem_btn btn, pad_button pad_btn, u16 value, bool pressed)
|
||||
{
|
||||
if (!pressed)
|
||||
return;
|
||||
|
||||
const auto set_firing_mode = [&ext](gem_btn btn)
|
||||
{
|
||||
// The firing mode is exclusive
|
||||
ext.custom[0] &= ~button_flags::ss_firing_mode_mask;
|
||||
ext.custom[0] |= ::at32(ext_btn_map, btn);
|
||||
};
|
||||
|
||||
if constexpr (has_combo && is_combo)
|
||||
{
|
||||
if (external_device_id == SHARP_SHOOTER_DEVICE_ID)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case gem_btn::combo_sharpshooter_firing_mode_1:
|
||||
case gem_btn::combo_sharpshooter_firing_mode_2:
|
||||
case gem_btn::combo_sharpshooter_firing_mode_3:
|
||||
set_firing_mode(btn);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
case gem_btn::combo_sharpshooter_trigger:
|
||||
case gem_btn::combo_sharpshooter_reload:
|
||||
ext.custom[0] |= ::at32(ext_btn_map, btn);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (external_device_id == RACING_WHEEL_DEVICE_ID)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case gem_btn::combo_racing_wheel_throttle:
|
||||
case gem_btn::combo_racing_wheel_l2:
|
||||
case gem_btn::combo_racing_wheel_r2:
|
||||
ext.custom[::at32(ext_btn_indices, btn)] = static_cast<u8>(value);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_paddle_l:
|
||||
case gem_btn::combo_racing_wheel_paddle_r:
|
||||
ext.custom[::at32(ext_btn_indices, btn)] |= ::at32(ext_btn_map, btn);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_d_pad_up:
|
||||
case gem_btn::combo_racing_wheel_d_pad_right:
|
||||
case gem_btn::combo_racing_wheel_d_pad_down:
|
||||
case gem_btn::combo_racing_wheel_d_pad_left:
|
||||
ext.digital1 |= ::at32(ext_btn_map, btn);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_l1:
|
||||
case gem_btn::combo_racing_wheel_r1:
|
||||
ext.digital2 |= ::at32(ext_btn_map, btn);
|
||||
combos.insert(pad_btn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr (has_combo)
|
||||
{
|
||||
if (combos.contains(pad_btn))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (external_device_id == SHARP_SHOOTER_DEVICE_ID)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case gem_btn::sharpshooter_firing_mode_1:
|
||||
case gem_btn::sharpshooter_firing_mode_2:
|
||||
case gem_btn::sharpshooter_firing_mode_3:
|
||||
set_firing_mode(btn);
|
||||
break;
|
||||
case gem_btn::sharpshooter_trigger:
|
||||
case gem_btn::sharpshooter_reload:
|
||||
ext.custom[0] |= ::at32(ext_btn_map, btn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (external_device_id == RACING_WHEEL_DEVICE_ID)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case gem_btn::combo_racing_wheel_throttle:
|
||||
case gem_btn::combo_racing_wheel_l2:
|
||||
case gem_btn::combo_racing_wheel_r2:
|
||||
ext.custom[::at32(ext_btn_indices, btn)] = static_cast<u8>(value);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_paddle_l:
|
||||
case gem_btn::combo_racing_wheel_paddle_r:
|
||||
ext.custom[::at32(ext_btn_indices, btn)] |= ::at32(ext_btn_map, btn);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_d_pad_up:
|
||||
case gem_btn::combo_racing_wheel_d_pad_right:
|
||||
case gem_btn::combo_racing_wheel_d_pad_down:
|
||||
case gem_btn::combo_racing_wheel_d_pad_left:
|
||||
ext.digital1 |= ::at32(ext_btn_map, btn);
|
||||
break;
|
||||
case gem_btn::combo_racing_wheel_l1:
|
||||
case gem_btn::combo_racing_wheel_r1:
|
||||
ext.digital2 |= ::at32(ext_btn_map, btn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void real_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!pad->is_connected() || pad->is_copilot())
|
||||
{
|
||||
controller.ext_status = 0;
|
||||
controller.ext_id = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ps_move_data& move_data = pad->move_data;
|
||||
|
||||
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
|
||||
|
||||
if (!move_data.external_device_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// ext.analog_left_x
|
||||
// ext.analog_left_y
|
||||
// ext.analog_right_x
|
||||
// ext.analog_right_y
|
||||
// ext.digital1
|
||||
// ext.digital2
|
||||
|
||||
ext.status = controller.ext_status;
|
||||
std::memcpy(ext.custom, move_data.external_device_data.data(), 5);
|
||||
}
|
||||
|
||||
static void fake_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!pad->is_connected() || pad->is_copilot())
|
||||
{
|
||||
controller.ext_status = 0;
|
||||
controller.ext_id = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ps_move_data& move_data = pad->move_data;
|
||||
const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num);
|
||||
|
||||
move_data.external_device_id = ext_device_id(cfg->external_device);
|
||||
move_data.external_device_connected = move_data.external_device_id != 0;
|
||||
move_data.external_device_data = {};
|
||||
|
||||
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
|
||||
|
||||
if (!move_data.external_device_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore firing mode
|
||||
ext.custom[0] = controller.firing_mode;
|
||||
|
||||
cfg->handle_input(pad, true, [&move_data, &ext](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
|
||||
{
|
||||
static std::set<pad_button> s_combos = {};
|
||||
input_to_ext<false, false>(move_data.external_device_id, ext, s_combos, btn, pad_btn, value, pressed);
|
||||
});
|
||||
|
||||
ext.status = controller.ext_status;
|
||||
|
||||
// Save firing mode
|
||||
controller.firing_mode = ext.custom[0] & button_flags::ss_firing_mode_mask;
|
||||
}
|
||||
|
||||
static void mouse_input_to_ext(u32 mouse_no, gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
auto& handler = g_fxo->get<MouseHandlerBase>();
|
||||
|
||||
std::scoped_lock lock(handler.mutex);
|
||||
|
||||
// Make sure that the mouse handler is initialized
|
||||
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
|
||||
|
||||
if (mouse_no >= handler.GetMice().size())
|
||||
{
|
||||
controller.ext_status = 0;
|
||||
controller.ext_id = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const Mouse& mouse_data = ::at32(handler.GetMice(), mouse_no);
|
||||
const auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
|
||||
|
||||
const u32 external_device_id = ext_device_id(cfg->external_device);
|
||||
const bool external_device_connected = external_device_id != 0;
|
||||
|
||||
controller.ext_status = external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
controller.ext_id = external_device_connected ? external_device_id : 0;
|
||||
|
||||
if (!external_device_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore firing mode
|
||||
ext.custom[0] = controller.firing_mode;
|
||||
|
||||
bool combo_active = false;
|
||||
std::set<pad_button> combos;
|
||||
|
||||
// Check combo button first
|
||||
cfg->handle_input(mouse_data, [&combo_active](gem_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& abort)
|
||||
{
|
||||
if (pressed && btn == gem_btn::combo)
|
||||
{
|
||||
combo_active = true;
|
||||
abort = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Check combos
|
||||
if (combo_active)
|
||||
{
|
||||
cfg->handle_input(mouse_data, [external_device_id, &ext, &combos](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
|
||||
{
|
||||
input_to_ext<true, true>(external_device_id, ext, combos, btn, pad_btn, value, pressed);
|
||||
});
|
||||
}
|
||||
|
||||
// Check normal buttons
|
||||
cfg->handle_input(mouse_data, [external_device_id, &ext, &combos](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
|
||||
{
|
||||
input_to_ext<true, false>(external_device_id, ext, combos, btn, pad_btn, value, pressed);
|
||||
});
|
||||
|
||||
ext.status = controller.ext_status;
|
||||
|
||||
// Save firing mode
|
||||
controller.firing_mode = ext.custom[0] & button_flags::ss_firing_mode_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Maps external Move controller data to DS3 input. (This can be input from any physical pad, not just the DS3)
|
||||
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
|
||||
@@ -2157,7 +2457,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
|
||||
* \param ext External data to modify
|
||||
* \return true on success, false if controller is disconnected
|
||||
*/
|
||||
static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
static void get_external_device_input(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
|
||||
{
|
||||
ext = {};
|
||||
|
||||
@@ -2166,53 +2466,6 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_pad_thread();
|
||||
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
|
||||
|
||||
if (!pad->is_connected() || pad->is_copilot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& move_data = pad->move_data;
|
||||
|
||||
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
|
||||
|
||||
ext.status = controller.ext_status;
|
||||
|
||||
for (const AnalogStick& stick : pad->m_sticks_external)
|
||||
{
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: ext.analog_left_x = stick.m_value; break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: ext.analog_left_y = stick.m_value; break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: ext.analog_right_x = stick.m_value; break;
|
||||
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: ext.analog_right_y = stick.m_value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Button& button : pad->m_buttons_external)
|
||||
{
|
||||
if (!button.m_pressed)
|
||||
continue;
|
||||
|
||||
switch (button.m_offset)
|
||||
{
|
||||
case CELL_PAD_BTN_OFFSET_DIGITAL1: ext.digital1 |= button.m_outKeyCode; break;
|
||||
case CELL_PAD_BTN_OFFSET_DIGITAL2: ext.digital2 |= button.m_outKeyCode; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!move_data.external_device_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The sharpshooter only sets the custom bytes as follows:
|
||||
// custom[0] (0x01): Firing mode selector is in position 1.
|
||||
// custom[0] (0x02): Firing mode selector is in position 2.
|
||||
@@ -2227,7 +2480,21 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
|
||||
// custom[3] (0x01): Left paddle
|
||||
// custom[3] (0x02): Right paddle
|
||||
|
||||
std::memcpy(ext.custom, move_data.external_device_data.data(), 5);
|
||||
switch (g_cfg.io.move)
|
||||
{
|
||||
case move_handler::real:
|
||||
real_input_to_ext(gem_num, controller, ext);
|
||||
break;
|
||||
case move_handler::fake:
|
||||
fake_input_to_ext(gem_num, controller, ext);
|
||||
break;
|
||||
case move_handler::mouse:
|
||||
case move_handler::raw_mouse:
|
||||
mouse_input_to_ext(gem_num, controller, ext);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2260,7 +2527,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t<u16>& digital_buttons, be_t<u1
|
||||
}
|
||||
|
||||
const Mouse& mouse_data = ::at32(handler.GetMice(), mouse_no);
|
||||
auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
|
||||
const auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
|
||||
|
||||
bool combo_active = false;
|
||||
std::set<pad_button> combos;
|
||||
@@ -2998,7 +3265,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
|
||||
|
||||
if (g_cfg.io.move != move_handler::null)
|
||||
{
|
||||
ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext);
|
||||
get_external_device_input(gem_num, gem.controllers[gem_num], inertial_state->ext);
|
||||
|
||||
inertial_state->timestamp = (get_guest_system_time() - gem.start_timestamp_us);
|
||||
inertial_state->counter = gem.inertial_counter++;
|
||||
@@ -3192,7 +3459,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
|
||||
|
||||
if (g_cfg.io.move != move_handler::null)
|
||||
{
|
||||
ds3_input_to_ext(gem_num, controller, gem_state->ext);
|
||||
get_external_device_input(gem_num, controller, gem_state->ext);
|
||||
|
||||
if (controller.enabled_tracking)
|
||||
{
|
||||
@@ -3609,7 +3876,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr<u32> ext_id, v
|
||||
{
|
||||
// Get external device status
|
||||
CellGemExtPortData ext_port_data{};
|
||||
ds3_input_to_ext(gem_num, controller, ext_port_data);
|
||||
get_external_device_input(gem_num, controller, ext_port_data);
|
||||
}
|
||||
|
||||
if (!(controller.ext_status & CELL_GEM_EXT_CONNECTED))
|
||||
|
||||
@@ -278,3 +278,38 @@ struct CellGemVideoConvertAttribute
|
||||
|
||||
ENABLE_BITWISE_SERIALIZATION;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
enum button_flags : u16
|
||||
{
|
||||
select = 0x01,
|
||||
start = 0x08,
|
||||
triangle = 0x10,
|
||||
circle = 0x20,
|
||||
cross = 0x40,
|
||||
square = 0x80,
|
||||
ps = 0x0001,
|
||||
move = 0x4008,
|
||||
t = 0x8010,
|
||||
ext_dev = 0x1000,
|
||||
|
||||
// Sharpshooter
|
||||
ss_firing_mode_1 = 0x01,
|
||||
ss_firing_mode_2 = 0x02,
|
||||
ss_firing_mode_3 = 0x04,
|
||||
ss_firing_mode_mask = ss_firing_mode_1 | ss_firing_mode_2 | ss_firing_mode_3,
|
||||
ss_trigger = 0x40,
|
||||
ss_reload = 0x80,
|
||||
|
||||
// Racing Wheel
|
||||
rw_d_pad_up = 0x10,
|
||||
rw_d_pad_right = 0x20,
|
||||
rw_d_pad_down = 0x40,
|
||||
rw_d_pad_left = 0x80,
|
||||
rw_l1 = 0x04,
|
||||
rw_r1 = 0x08,
|
||||
rw_paddle_l = 0x01,
|
||||
rw_paddle_r = 0x02,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Memory/vm_ref.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -66,6 +66,7 @@ void fmt_class_string<CellMusic2Error>::format(std::string& out, u64 arg)
|
||||
|
||||
struct music_state
|
||||
{
|
||||
public:
|
||||
shared_mutex mutex;
|
||||
|
||||
vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{};
|
||||
@@ -79,33 +80,58 @@ struct music_state
|
||||
music_state()
|
||||
{
|
||||
handler = Emu.GetCallbacks().get_music_handler();
|
||||
handler->set_status_callback([this](music_handler_base::player_status status)
|
||||
handler->set_event_status_callback([this](u32 status)
|
||||
{
|
||||
// TODO: disabled until I find a game that uses CELL_MUSIC_EVENT_STATUS_NOTIFICATION
|
||||
return;
|
||||
|
||||
if (!func)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s32 result = CELL_OK;
|
||||
|
||||
// Known to be used by NFS: Hot Pursuit
|
||||
sysutil_register_cb([this, state = status](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cellMusic.notice("Sending status notification %d", state);
|
||||
func(ppu, CELL_MUSIC_EVENT_STATUS_NOTIFICATION, vm::addr_t(state), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
});
|
||||
handler->set_playback_status_callback([this](music_handler_base::player_status status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case music_handler_base::player_status::end_of_media:
|
||||
result = CELL_MUSIC_PLAYBACK_FINISHED;
|
||||
// Let's just play the next song for now if we are in list mode.
|
||||
// Due to potential main thread recursion this may cause a deadlock with the internal mutex of the handler.
|
||||
// Let's just call it from another thread instead.
|
||||
m_wake_up_thread = 1;
|
||||
m_wake_up_thread.notify_one();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
sysutil_register_cb([this, &result](ppu_thread& ppu) -> s32
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("cellMusic State", [this]()
|
||||
{
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
cellMusic.notice("Sending status notification %d", result);
|
||||
func(ppu, CELL_MUSIC_EVENT_STATUS_NOTIFICATION, vm::addr_t(result), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
while (thread_ctrl::state() != thread_state::aborting && !m_wake_up_thread)
|
||||
{
|
||||
thread_ctrl::wait_on(m_wake_up_thread, 0);
|
||||
}
|
||||
m_wake_up_thread = 0;
|
||||
|
||||
if (thread_ctrl::state() == thread_state::aborting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Play the next song
|
||||
if (const error_code error = set_playback_command(CELL_MUSIC_PB_CMD_NEXT_TRACK))
|
||||
{
|
||||
cellMusic.error("Failed to play next track. error=0x%x", +error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,6 +141,19 @@ struct music_state
|
||||
save(ar);
|
||||
}
|
||||
|
||||
~music_state()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
auto& thread = *m_thread;
|
||||
thread = thread_state::aborting;
|
||||
m_wake_up_thread = 1;
|
||||
m_wake_up_thread.notify_one();
|
||||
thread();
|
||||
m_thread.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void save(utils::serial& ar)
|
||||
{
|
||||
ar(func);
|
||||
@@ -130,7 +169,7 @@ struct music_state
|
||||
}
|
||||
|
||||
// NOTE: This function only uses CELL_MUSIC enums. CELL_MUSIC2 enums are identical.
|
||||
error_code set_playback_command(s32 command)
|
||||
error_code set_playback_command(u32 command)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
@@ -145,11 +184,27 @@ struct music_state
|
||||
case CELL_MUSIC_PB_CMD_FASTREVERSE:
|
||||
case CELL_MUSIC_PB_CMD_NEXT:
|
||||
case CELL_MUSIC_PB_CMD_PREV:
|
||||
case CELL_MUSIC_PB_CMD_NEXT_TRACK:
|
||||
{
|
||||
std::string path;
|
||||
bool automatic = false;
|
||||
bool no_more_tracks = false;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
|
||||
// Handle auto-play of the next track in the current playlist.
|
||||
if (command == CELL_MUSIC_PB_CMD_NEXT_TRACK)
|
||||
{
|
||||
// We only auto-play the next song if we are in list mode and the music is playing.
|
||||
if (current_selection_context.content_type != CELL_SEARCH_CONTENTTYPE_MUSICLIST || handler->get_state() != CELL_MUSIC_PB_STATUS_PLAY)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
command = CELL_MUSIC_PB_CMD_NEXT;
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& playlist = current_selection_context.playlist;
|
||||
const u32 current_track = current_selection_context.current_track;
|
||||
u32 next_track = current_track;
|
||||
@@ -191,7 +246,7 @@ struct music_state
|
||||
handler->fast_reverse(path);
|
||||
break;
|
||||
default:
|
||||
handler->play(path);
|
||||
handler->play(path, automatic);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -203,6 +258,10 @@ struct music_state
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||
atomic_t<u32> m_wake_up_thread{0};
|
||||
};
|
||||
|
||||
error_code cell_music_select_contents()
|
||||
|
||||
@@ -90,6 +90,8 @@ enum
|
||||
CELL_MUSIC_PB_CMD_PREV = 4,
|
||||
CELL_MUSIC_PB_CMD_FASTFORWARD = 5,
|
||||
CELL_MUSIC_PB_CMD_FASTREVERSE = 6,
|
||||
|
||||
CELL_MUSIC_PB_CMD_NEXT_TRACK = 7, // RPCS3 helper for auto-play of the next track in the current playlist
|
||||
};
|
||||
|
||||
enum
|
||||
|
||||
@@ -74,6 +74,7 @@ struct music_decode
|
||||
cellMusicDecode.notice("set_decode_command(START): context: %s", current_selection_context.to_string());
|
||||
|
||||
music_selection_context context = current_selection_context;
|
||||
context.current_track = context.first_track;
|
||||
|
||||
for (usz i = 0; i < context.playlist.size(); i++)
|
||||
{
|
||||
@@ -90,6 +91,7 @@ struct music_decode
|
||||
case CELL_MUSIC_DECODE_CMD_PREV:
|
||||
{
|
||||
decoder.stop();
|
||||
read_pos = 0;
|
||||
|
||||
if (decoder.set_next_index(command == CELL_MUSIC_DECODE_CMD_NEXT) == umax)
|
||||
{
|
||||
@@ -195,9 +197,10 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
|
||||
if (dec.decoder.m_size == 0)
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA;
|
||||
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "m_size == 0" };
|
||||
}
|
||||
|
||||
ensure(dec.decoder.m_size >= dec.read_pos);
|
||||
const u64 size_left = dec.decoder.m_size - dec.read_pos;
|
||||
|
||||
if (dec.read_pos == 0)
|
||||
@@ -229,10 +232,10 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
|
||||
if (size_to_read == 0)
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA; // TODO: speculative
|
||||
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "size_to_read == 0" }; // TODO: speculative
|
||||
}
|
||||
|
||||
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.read_pos], size_to_read);
|
||||
std::memcpy(buf.get_ptr(), &::at32(dec.decoder.data, dec.read_pos), size_to_read);
|
||||
|
||||
if (size_to_read < reqSize)
|
||||
{
|
||||
@@ -384,6 +387,12 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
//sysutil_register_cb([&dec, command](ppu_thread& ppu) -> s32
|
||||
//{
|
||||
// dec.func(ppu, CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION, vm::addr_t(command), dec.userData);
|
||||
// return CELL_OK;
|
||||
//});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,9 +267,10 @@ void music_selection_context::set_track(std::string_view track)
|
||||
|
||||
for (usz i = 0; i < playlist.size(); i++)
|
||||
{
|
||||
cellMusicSelectionContext.error("set_track: Comparing track '%s' vs '%s'", track, playlist[i]);
|
||||
cellMusicSelectionContext.notice("set_track: Comparing track '%s' vs '%s'", track, playlist[i]);
|
||||
if (track.ends_with(playlist[i]))
|
||||
{
|
||||
cellMusicSelectionContext.notice("set_track: Found track '%s': '%s'", track, playlist[i]);
|
||||
first_track = current_track = static_cast<u32>(i);
|
||||
return;
|
||||
}
|
||||
@@ -287,6 +288,8 @@ u32 music_selection_context::step_track(bool next)
|
||||
return umax;
|
||||
}
|
||||
|
||||
const std::string last_track = (current_track < playlist.size()) ? playlist[current_track] : "";
|
||||
|
||||
switch (repeat_mode)
|
||||
{
|
||||
case CELL_SEARCH_REPEATMODE_NONE:
|
||||
@@ -298,7 +301,7 @@ u32 music_selection_context::step_track(bool next)
|
||||
{
|
||||
// We are at the end of the playlist.
|
||||
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
|
||||
current_track = umax;
|
||||
current_track = ::size32(playlist) - 1; // NOTE: We could use size instead of size - 1 to allow to use PREV to play the last track again.
|
||||
return umax;
|
||||
}
|
||||
}
|
||||
@@ -309,7 +312,6 @@ u32 music_selection_context::step_track(bool next)
|
||||
{
|
||||
// We are at the start of the playlist.
|
||||
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
|
||||
current_track = umax;
|
||||
return umax;
|
||||
}
|
||||
|
||||
@@ -334,7 +336,7 @@ u32 music_selection_context::step_track(bool next)
|
||||
// Play the previous track. Start with the last track if we reached the start of the playlist.
|
||||
if (current_track == 0)
|
||||
{
|
||||
current_track = ::narrow<u32>(playlist.size() - 1);
|
||||
current_track = ::size32(playlist) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -362,8 +364,15 @@ u32 music_selection_context::step_track(bool next)
|
||||
{
|
||||
// We reached the first or last track again. Let's shuffle!
|
||||
cellMusicSelectionContext.notice("step_track: Shuffling playlist...");
|
||||
auto engine = std::default_random_engine{};
|
||||
std::random_device rd;
|
||||
auto engine = std::default_random_engine{rd()};
|
||||
std::shuffle(std::begin(playlist), std::end(playlist), engine);
|
||||
|
||||
// Don't play the same track twice
|
||||
if (last_track == ::at32(playlist, current_track))
|
||||
{
|
||||
current_track = (current_track + 1) % playlist.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,46 @@ error_code cellNetCtlDelHandler(s32 hid)
|
||||
|
||||
error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
|
||||
{
|
||||
cellNetCtl.warning("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
|
||||
bool log_it_once = false;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case CELL_NET_CTL_INFO_ETHER_ADDR:
|
||||
case CELL_NET_CTL_INFO_DEVICE:
|
||||
case CELL_NET_CTL_INFO_MTU:
|
||||
case CELL_NET_CTL_INFO_LINK_TYPE:
|
||||
case CELL_NET_CTL_INFO_IP_CONFIG:
|
||||
case CELL_NET_CTL_INFO_IP_ADDRESS:
|
||||
case CELL_NET_CTL_INFO_NETMASK:
|
||||
case CELL_NET_CTL_INFO_DEFAULT_ROUTE:
|
||||
case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG:
|
||||
case CELL_NET_CTL_INFO_UPNP_CONFIG:
|
||||
{
|
||||
log_it_once = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool log_it = true;
|
||||
|
||||
if (log_it_once && vm::check_addr(info.addr()))
|
||||
{
|
||||
struct logged_t
|
||||
{
|
||||
std::array<atomic_t<bool>, 256> logged_code{};
|
||||
};
|
||||
|
||||
if (g_fxo->get<logged_t>().logged_code[::narrow<u8>(code)].exchange(true))
|
||||
{
|
||||
log_it = false;
|
||||
}
|
||||
}
|
||||
|
||||
(log_it ? cellNetCtl.warning : cellNetCtl.trace)("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ void fmt_class_string<CellPadFilterError>::format(std::string& out, u64 arg)
|
||||
extern void sys_io_serialize(utils::serial& ar);
|
||||
|
||||
pad_info::pad_info(utils::serial& ar)
|
||||
: max_connect(ar)
|
||||
, port_setting(ar)
|
||||
, reported_info(ar)
|
||||
: max_connect(ar.pop<u32>())
|
||||
, port_setting(ar.pop<decltype(port_setting)>())
|
||||
, reported_info(ar.pop<decltype(reported_info)>())
|
||||
{
|
||||
//reported_info = {};
|
||||
sys_io_serialize(ar);
|
||||
@@ -418,7 +418,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
|
||||
}
|
||||
};
|
||||
|
||||
for (const Button& button : pad->m_buttons_external)
|
||||
for (const ButtonExternal& button : pad->m_buttons_external)
|
||||
{
|
||||
// here we check btns, and set pad accordingly,
|
||||
// if something changed, set btnChanged
|
||||
@@ -497,7 +497,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
|
||||
}
|
||||
}
|
||||
|
||||
for (const AnalogStick& stick : pad->m_sticks_external)
|
||||
for (const AnalogStickExternal& stick : pad->m_sticks_external)
|
||||
{
|
||||
switch (stick.m_offset)
|
||||
{
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "util/bit_set.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include "cellPamf.h"
|
||||
|
||||
const std::function<bool()> SQUEUE_ALWAYS_EXIT = []() { return true; };
|
||||
const std::function<bool()> SQUEUE_NEVER_EXIT = []() { return false; };
|
||||
|
||||
bool squeue_test_exit()
|
||||
{
|
||||
return Emu.IsStopped();
|
||||
}
|
||||
|
||||
LOG_CHANNEL(cellPamf);
|
||||
|
||||
template<>
|
||||
@@ -508,7 +500,7 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
|
||||
|
||||
const auto streams = &pAddr->seq_info.grouping_periods.groups.streams;
|
||||
|
||||
std::bitset<16> channels_used[6]{};
|
||||
std::array<bit_set<16>, 6> channels_used{};
|
||||
|
||||
u32 end_of_streams_addr = 0;
|
||||
u32 next_ep_table_addr = 0;
|
||||
@@ -524,14 +516,16 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
|
||||
return CELL_PAMF_ERROR_UNKNOWN_STREAM;
|
||||
}
|
||||
|
||||
bit_set<16>& used_channels = ::at32(channels_used, *type);
|
||||
|
||||
// Every channel may only be used once per type
|
||||
if (channels_used[*type].test(*ch))
|
||||
if (used_channels.test(*ch))
|
||||
{
|
||||
return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid channel" };
|
||||
}
|
||||
|
||||
// Mark channel as used
|
||||
channels_used[*type].set(*ch);
|
||||
used_channels.set(*ch, true);
|
||||
|
||||
const u32 ep_offset = streams[stream_idx].ep_offset;
|
||||
const u32 ep_num = streams[stream_idx].ep_num;
|
||||
|
||||
@@ -595,345 +595,3 @@ struct CellPamfReader
|
||||
CHECK_SIZE(CellPamfReader, 128);
|
||||
|
||||
error_code cellPamfReaderInitialize(vm::ptr<CellPamfReader> pSelf, vm::cptr<PamfHeader> pAddr, u64 fileSize, u32 attribute);
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
|
||||
extern const std::function<bool()> SQUEUE_NEVER_EXIT;
|
||||
|
||||
bool squeue_test_exit();
|
||||
|
||||
// TODO: eliminate this boolshit
|
||||
template<typename T, u32 sq_size = 256>
|
||||
class squeue_t
|
||||
{
|
||||
struct squeue_sync_var_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 position : 31;
|
||||
u32 pop_lock : 1;
|
||||
};
|
||||
struct
|
||||
{
|
||||
u32 count : 31;
|
||||
u32 push_lock : 1;
|
||||
};
|
||||
};
|
||||
|
||||
atomic_t<squeue_sync_var_t> m_sync;
|
||||
|
||||
mutable std::mutex m_rcv_mutex;
|
||||
mutable std::mutex m_wcv_mutex;
|
||||
mutable std::condition_variable m_rcv;
|
||||
mutable std::condition_variable m_wcv;
|
||||
|
||||
T m_data[sq_size];
|
||||
|
||||
enum squeue_sync_var_result : u32
|
||||
{
|
||||
SQSVR_OK = 0,
|
||||
SQSVR_LOCKED = 1,
|
||||
SQSVR_FAILED = 2,
|
||||
};
|
||||
|
||||
public:
|
||||
squeue_t()
|
||||
: m_sync(squeue_sync_var_t{})
|
||||
{
|
||||
}
|
||||
|
||||
static u32 get_max_size()
|
||||
{
|
||||
return sq_size;
|
||||
}
|
||||
|
||||
bool is_full() const
|
||||
{
|
||||
return m_sync.load().count == sq_size;
|
||||
}
|
||||
|
||||
bool push(const T& data, const std::function<bool()>& test_exit)
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
|
||||
if (sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
if (sync.count == sq_size)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
|
||||
sync.push_lock = 1;
|
||||
pos = sync.position + sync.count;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> wcv_lock(m_wcv_mutex);
|
||||
m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
m_data[pos >= sq_size ? pos - sq_size : pos] = data;
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
ensure(!!sync.push_lock);
|
||||
sync.push_lock = 0;
|
||||
sync.count++;
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
m_wcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push(const T& data, const volatile bool* do_exit)
|
||||
{
|
||||
return push(data, [do_exit]() { return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
bool push(const T& data)
|
||||
{
|
||||
return push(data, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
bool try_push(const T& data)
|
||||
{
|
||||
return push(data, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
bool pop(T& data, const std::function<bool()>& test_exit)
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
|
||||
if (!sync.count)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
if (sync.pop_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
pos = sync.position;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
data = m_data[pos];
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
ensure(!!sync.pop_lock);
|
||||
sync.pop_lock = 0;
|
||||
sync.position++;
|
||||
sync.count--;
|
||||
if (sync.position == sq_size)
|
||||
{
|
||||
sync.position = 0;
|
||||
}
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
m_wcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(T& data, const volatile bool* do_exit)
|
||||
{
|
||||
return pop(data, [do_exit]() { return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
bool pop(T& data)
|
||||
{
|
||||
return pop(data, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
bool try_pop(T& data)
|
||||
{
|
||||
return pop(data, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
bool peek(T& data, u32 start_pos, const std::function<bool()>& test_exit)
|
||||
{
|
||||
ensure(start_pos < sq_size);
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
|
||||
if (sync.count <= start_pos)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
if (sync.pop_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
pos = sync.position + start_pos;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
data = m_data[pos >= sq_size ? pos - sq_size : pos];
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
ensure(!!sync.pop_lock);
|
||||
sync.pop_lock = 0;
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool peek(T& data, u32 start_pos, const volatile bool* do_exit)
|
||||
{
|
||||
return peek(data, start_pos, [do_exit]() { return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
bool peek(T& data, u32 start_pos = 0)
|
||||
{
|
||||
return peek(data, start_pos, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
bool try_peek(T& data, u32 start_pos = 0)
|
||||
{
|
||||
return peek(data, start_pos, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
class squeue_data_t
|
||||
{
|
||||
T* const m_data;
|
||||
const u32 m_pos;
|
||||
const u32 m_count;
|
||||
|
||||
squeue_data_t(T* data, u32 pos, u32 count)
|
||||
: m_data(data)
|
||||
, m_pos(pos)
|
||||
, m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
T& operator [] (u32 index)
|
||||
{
|
||||
ensure(index < m_count);
|
||||
index += m_pos;
|
||||
index = index < sq_size ? index : index - sq_size;
|
||||
return m_data[index];
|
||||
}
|
||||
};
|
||||
|
||||
void process(void(*proc)(squeue_data_t data))
|
||||
{
|
||||
u32 pos, count;
|
||||
|
||||
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
|
||||
if (sync.pop_lock || sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
pos = sync.position;
|
||||
count = sync.count;
|
||||
sync.pop_lock = 1;
|
||||
sync.push_lock = 1;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
proc(squeue_data_t(m_data, pos, count));
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
ensure(!!sync.pop_lock);
|
||||
ensure(!!sync.push_lock);
|
||||
sync.pop_lock = 0;
|
||||
sync.push_lock = 0;
|
||||
});
|
||||
|
||||
m_wcv.notify_one();
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
ensure(sync.count <= sq_size);
|
||||
ensure(sync.position < sq_size);
|
||||
|
||||
if (sync.pop_lock || sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
sync.push_lock = 1;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
m_sync.exchange({});
|
||||
m_wcv.notify_one();
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1711,12 +1711,6 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
auto content = std::find_if(searchObject->content_ids.begin(), searchObject->content_ids.end(), [&content_hash](const content_id_type& cid){ return cid.first == content_hash; });
|
||||
if (content != searchObject->content_ids.cend() && content->second)
|
||||
{
|
||||
// Check if the type of the found content is correct
|
||||
if (content->second->type != CELL_SEARCH_CONTENTTYPE_MUSIC)
|
||||
{
|
||||
return { CELL_SEARCH_ERROR_INVALID_CONTENTTYPE, "Type: %d, Expected: CELL_SEARCH_CONTENTTYPE_MUSIC"};
|
||||
}
|
||||
|
||||
// Check if the type of the found content matches our search content type
|
||||
if (content->second->type != first_content->type)
|
||||
{
|
||||
@@ -1724,8 +1718,36 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
}
|
||||
|
||||
// Use the found content
|
||||
context.playlist.push_back(content->second->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path=%s", content_hash, +content->second->type, context.playlist.back());
|
||||
if (content->second->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
const std::string path = content->second->infoPath.contentPath;
|
||||
const std::string vfs_path = vfs::get(path);
|
||||
if (!fs::is_dir(vfs_path))
|
||||
{
|
||||
return { CELL_SEARCH_ERROR_CONTENT_NOT_FOUND, "Not a directory: Path='%s'", vfs_path };
|
||||
}
|
||||
|
||||
for (auto&& dir_entry : fs::dir{vfs_path})
|
||||
{
|
||||
if (dir_entry.name == "." || dir_entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string track = path + "/" + dir_entry.name;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path='%s'", content_hash, +content->second->type, track);
|
||||
context.playlist.push_back(std::move(track));
|
||||
}
|
||||
}
|
||||
else if (content->second->type == CELL_SEARCH_CONTENTTYPE_MUSIC)
|
||||
{
|
||||
context.playlist.push_back(content->second->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path='%s'", content_hash, +content->second->type, context.playlist.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
return { CELL_SEARCH_ERROR_INVALID_CONTENTTYPE, "Type: %d, Expected: CELL_SEARCH_CONTENTTYPE_MUSIC or CELL_SEARCH_CONTENTTYPE_MUSICLIST", +content->second->type };
|
||||
}
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
@@ -1738,14 +1760,14 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
// TODO: whole playlist
|
||||
shared_ptr<search_content_t> content = get_random_content();
|
||||
context.playlist.push_back(content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path=%s", content_hash, +content->type, context.playlist.back());
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path='%s'", content_hash, +content->type, context.playlist.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select the first track by default
|
||||
// TODO: whole playlist
|
||||
context.playlist.push_back(first_content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path=%s", content_hash, +first_content->type, context.playlist.back());
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path='%s'", content_hash, +first_content->type, context.playlist.back());
|
||||
}
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
@@ -1759,14 +1781,14 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
// TODO: whole playlist
|
||||
shared_ptr<search_content_t> content = get_random_content();
|
||||
context.playlist.push_back(content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path=%s", +content->type, context.playlist.back());
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path='%s'", +content->type, context.playlist.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select the first track by default
|
||||
// TODO: whole playlist
|
||||
context.playlist.push_back(first_content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path=%s", +first_content->type, context.playlist.back());
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path='%s'", +first_content->type, context.playlist.back());
|
||||
}
|
||||
|
||||
context.content_type = first_content->type;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <string>
|
||||
|
||||
#include "cellSsl.h"
|
||||
@@ -8,6 +7,7 @@
|
||||
#include "Utilities/File.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "util/bit_set.hpp"
|
||||
|
||||
#include "cellRtc.h"
|
||||
|
||||
@@ -92,7 +92,7 @@ std::string getCert(const std::string& certPath, const int certID, const bool is
|
||||
newID = certID - 1;
|
||||
}
|
||||
|
||||
std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID);
|
||||
const std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID);
|
||||
|
||||
if (!fs::exists(filePath))
|
||||
{
|
||||
@@ -106,7 +106,7 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
|
||||
{
|
||||
cellSsl.trace("cellSslCertificateLoader(flag=%llu, buffer=*0x%x, size=%zu, required=*0x%x)", flag, buffer, size, required);
|
||||
|
||||
const std::bitset<58> flagBits(flag);
|
||||
const bit_set<58> flagBits(flag);
|
||||
const std::string certPath = vfs::get("/dev_flash/data/cert/");
|
||||
|
||||
if (required)
|
||||
@@ -114,10 +114,11 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
|
||||
*required = 0;
|
||||
for (uint i = 1; i <= flagBits.size(); i++)
|
||||
{
|
||||
if (!flagBits[i-1])
|
||||
if (!flagBits.test(i - 1))
|
||||
continue;
|
||||
|
||||
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
|
||||
*required += ::size32(getCert(certPath, i, flagBits[BaltimoreCert-1]));
|
||||
*required += ::size32(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -125,14 +126,15 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
|
||||
std::string final;
|
||||
for (uint i = 1; i <= flagBits.size(); i++)
|
||||
{
|
||||
if (!flagBits[i-1])
|
||||
if (!flagBits.test(i - 1))
|
||||
continue;
|
||||
|
||||
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
|
||||
final.append(getCert(certPath, i, flagBits[BaltimoreCert-1]));
|
||||
final.append(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
|
||||
}
|
||||
|
||||
memset(buffer.get_ptr(), '\0', size - 1);
|
||||
memcpy(buffer.get_ptr(), final.c_str(), final.size());
|
||||
std::memset(buffer.get_ptr(), '\0', size - 1);
|
||||
std::memcpy(buffer.get_ptr(), final.c_str(), final.size());
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
||||
+679
-270
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,11 @@ enum CellVdecError : u32
|
||||
CELL_VDEC_ERROR_EMPTY = 0x80610104,
|
||||
CELL_VDEC_ERROR_AU = 0x80610105,
|
||||
CELL_VDEC_ERROR_PIC = 0x80610106,
|
||||
CELL_VDEC_ERROR_UNK = 0x80610110,
|
||||
CELL_VDEC_ERROR_FATAL = 0x80610180,
|
||||
};
|
||||
|
||||
enum CellVdecCodecType : s32
|
||||
enum CellVdecCodecType : u32
|
||||
{
|
||||
CELL_VDEC_CODEC_TYPE_MPEG2 = 0,
|
||||
CELL_VDEC_CODEC_TYPE_AVC = 1,
|
||||
@@ -96,7 +97,7 @@ struct CellVdecTypeEx
|
||||
{
|
||||
be_t<s32> codecType; // CellVdecCodecType
|
||||
be_t<u32> profileLevel;
|
||||
be_t<u32> codecSpecificInfo_addr;
|
||||
vm::bcptr<void> codecSpecificInfo;
|
||||
};
|
||||
|
||||
// Library Attributes
|
||||
@@ -220,7 +221,7 @@ enum
|
||||
CELL_VDEC_AVC_CCD_MAX = 128,
|
||||
};
|
||||
|
||||
enum AVC_level : u8
|
||||
enum AVC_level : u32
|
||||
{
|
||||
CELL_VDEC_AVC_LEVEL_1P0 = 10,
|
||||
CELL_VDEC_AVC_LEVEL_1P1 = 11,
|
||||
@@ -235,6 +236,7 @@ enum AVC_level : u8
|
||||
CELL_VDEC_AVC_LEVEL_4P0 = 40,
|
||||
CELL_VDEC_AVC_LEVEL_4P1 = 41,
|
||||
CELL_VDEC_AVC_LEVEL_4P2 = 42,
|
||||
CELL_VDEC_AVC_LEVEL_UNK = 1042 // Same as 4.2, but disables the deblocking filter
|
||||
};
|
||||
|
||||
struct CellVdecAvcSpecificInfo
|
||||
@@ -399,6 +401,7 @@ enum DIVX_level : u8
|
||||
CELL_VDEC_DIVX_HOME_THEATER = 12,
|
||||
CELL_VDEC_DIVX_HD_720 = 13,
|
||||
CELL_VDEC_DIVX_HD_1080 = 14,
|
||||
CELL_VDEC_DIVX_UNK = 15 // Only used for DivX version 3.11
|
||||
};
|
||||
|
||||
struct CellVdecDivxSpecificInfo
|
||||
@@ -695,3 +698,118 @@ struct CellVdecMpeg4SpecificInfo
|
||||
be_t<u16> maxDecodedFrameWidth;
|
||||
be_t<u16> maxDecodedFrameHeight;
|
||||
};
|
||||
|
||||
enum VC1_level
|
||||
{
|
||||
CELL_VDEC_VC1_SP_LL,
|
||||
CELL_VDEC_VC1_SP_ML,
|
||||
CELL_VDEC_VC1_MP_LL,
|
||||
CELL_VDEC_VC1_MP_ML,
|
||||
CELL_VDEC_VC1_MP_HL,
|
||||
CELL_VDEC_VC1_AP_L0,
|
||||
CELL_VDEC_VC1_AP_L1,
|
||||
CELL_VDEC_VC1_AP_L2,
|
||||
CELL_VDEC_VC1_AP_L3,
|
||||
CELL_VDEC_VC1_AP_L4
|
||||
};
|
||||
|
||||
struct CellVdecVc1SpecificInfo
|
||||
{
|
||||
be_t<u32> thisSize;
|
||||
be_t<u16> maxDecodedFrameWidth;
|
||||
be_t<u16> maxDecodedFrameHeight;
|
||||
};
|
||||
|
||||
struct VdecDecoderAttr
|
||||
{
|
||||
be_t<u32> mem_size;
|
||||
be_t<u32> unk1;
|
||||
u8 cmd_depth;
|
||||
be_t<u32> unk2;
|
||||
be_t<u32> decoder_version;
|
||||
};
|
||||
|
||||
struct VdecDecoderSpecificOps
|
||||
{
|
||||
error_code (&query_attr)(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* codec_specific_info);
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
// Abstraction layer for decoders with "S...D" in their names
|
||||
|
||||
enum class VdecSceDecoderType : u8
|
||||
{
|
||||
mpeg2,
|
||||
mpeg4,
|
||||
vc1,
|
||||
jvt
|
||||
};
|
||||
|
||||
template <typename param_t>
|
||||
struct VdecSceDecoderOps
|
||||
{
|
||||
error_code (&get_memory_size)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
|
||||
error_code (&get_memory_size_2)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<param_t> params);
|
||||
error_code (&get_version_number)(ppu_thread& ppu, vm::ptr<u32> version);
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
struct VdecSceDecoderUnk
|
||||
{
|
||||
u32 unk1;
|
||||
u32 unk2;
|
||||
u32 unk3;
|
||||
u32 unk4;
|
||||
};
|
||||
|
||||
constexpr VdecSceDecoderOps<Smvd2Params> VDEC_SCE_DECODER_OPS_MPEG2 =
|
||||
{
|
||||
.get_memory_size = ppu_execute<&smvd2GetMemorySize>,
|
||||
.get_memory_size_2 = ppu_execute<&smvd2GetMemorySize2>,
|
||||
.get_version_number = ppu_execute<&smvd2GetVersionNumber>,
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
constexpr VdecSceDecoderOps<Smvd4Params> VDEC_SCE_DECODER_OPS_MPEG4 =
|
||||
{
|
||||
.get_memory_size = ppu_execute<&smvd4GetMemorySize>,
|
||||
.get_memory_size_2 = ppu_execute<&smvd4GetMemorySize2>,
|
||||
.get_version_number = ppu_execute<&smvd4GetVersionNumber>,
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
constexpr VdecSceDecoderOps<Svc1dParams> VDEC_SCE_DECODER_OPS_VC1 =
|
||||
{
|
||||
.get_memory_size = ppu_execute<&svc1dGetMemorySize>,
|
||||
.get_memory_size_2 = ppu_execute<&svc1dGetMemorySize2>,
|
||||
.get_version_number = ppu_execute<&svc1dGetVersionNumber>,
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
constexpr VdecSceDecoderOps<SjvtdParams> VDEC_SCE_DECODER_OPS_JVT =
|
||||
{
|
||||
.get_memory_size = ppu_execute<&sjvtdGetMemorySize>,
|
||||
.get_memory_size_2 = ppu_execute<&sjvtdGetMemorySize2>,
|
||||
.get_version_number = ppu_execute<&sjvtdGetVersionNumber>,
|
||||
// TODO remaining functions
|
||||
};
|
||||
|
||||
constexpr std::array VDEC_SCE_DECODER_ERROR_BASE_MAP = { 0x80615000u, 0x80615100u, 0x80615300u, 0x80615800u };
|
||||
|
||||
constexpr std::array VDEC_SCE_DECODER_MAX_MEM_SIZE_MAP =
|
||||
{
|
||||
std::array<u32, 13>{ 0xc8400, 0x295680, 0x9b8200, 0xce4f00 },
|
||||
std::array<u32, 13>{ 0x44480, 0x44480, 0xa8b00, 0x1e6800, 0x1b4d00, 0x23e400 },
|
||||
std::array<u32, 13>{ 0x1b44f0, 0x2dfef0, 0x38f9f0, 0x999e30, 0x2827f70, 0x2dfef0, 0x754130, 0x11bd9f0, 0x1fdee70, 0x3900070 },
|
||||
std::array<u32, 13>{ 0x380fd0, 0x380fd0, 0x380fd0, 0x380fd0, 0x780950, 0xcb6d50, 0xcb6d50, 0x17a4950, 0x3245ed0, 0x4499fd0, 0x4499fd0, 0x4499fd0, 0x6b979d0 }
|
||||
};
|
||||
|
||||
constexpr std::array VDEC_SCE_DECODER_UNK_MAP =
|
||||
{
|
||||
VdecSceDecoderUnk{ .unk1 = 2, .unk2 = 0, .unk4 = 0x1c8 },
|
||||
VdecSceDecoderUnk{ .unk1 = 1, .unk2 = 6, .unk4 = 0x38 },
|
||||
VdecSceDecoderUnk{ .unk1 = 3, .unk2 = 6, .unk4 = 0x170 },
|
||||
VdecSceDecoderUnk{ .unk1 = 4, .unk2 = 6, .unk4 = 0x160 }
|
||||
};
|
||||
|
||||
error_code cellVdecQueryAttrEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr);
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libavcdec.h"
|
||||
|
||||
LOG_CHANNEL(libavcdec)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<AvcDecError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(AVC_DEC_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code avcDecQueryMemory([[maybe_unused]] ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr)
|
||||
{
|
||||
libavcdec.todo("avcDecQueryMemory(params=*0x%x, attr=*0x%x)", params, attr);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
void avcDecGetVersion([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
|
||||
{
|
||||
libavcdec.notice("avcDecGetVersion(version=*0x%x)", version);
|
||||
|
||||
if (version)
|
||||
{
|
||||
*version = 0x11300;
|
||||
}
|
||||
}
|
||||
|
||||
error_code avcDecQueryCharacteristics([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> unk)
|
||||
{
|
||||
libavcdec.notice("avcDecGetVersion(unk=*0x%x)", unk);
|
||||
|
||||
if (!unk)
|
||||
{
|
||||
return AVC_DEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
unk[0] = 5; // Command queue depth
|
||||
unk[1] = 20;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecOpen()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecClose()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecReset()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecFlush()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecAccessUnit()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecRlsFrame()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code avcDecGetStatus()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libavcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libavcdec)("libavcdec", []
|
||||
{
|
||||
REG_FUNC(libavcdec, avcDecQueryMemory);
|
||||
REG_FUNC(libavcdec, avcDecGetVersion);
|
||||
REG_FUNC(libavcdec, avcDecQueryCharacteristics);
|
||||
REG_FUNC(libavcdec, avcDecOpen);
|
||||
REG_FUNC(libavcdec, avcDecClose);
|
||||
REG_FUNC(libavcdec, avcDecReset);
|
||||
REG_FUNC(libavcdec, avcDecFlush);
|
||||
REG_FUNC(libavcdec, avcDecAccessUnit);
|
||||
REG_FUNC(libavcdec, avcDecRlsFrame);
|
||||
REG_FUNC(libavcdec, avcDecGetStatus);
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
enum AvcDecError : u32
|
||||
{
|
||||
AVC_DEC_ERROR_ARG = 0x80615201,
|
||||
};
|
||||
|
||||
struct AvcDecParams
|
||||
{
|
||||
u8 profile_level;
|
||||
b8 disable_deblocking_filter;
|
||||
u8 number_of_decoded_frame_buffer;
|
||||
be_t<u16> max_decoded_frame_width;
|
||||
be_t<u16> max_decoded_frame_height;
|
||||
};
|
||||
|
||||
struct AvcDecAttr
|
||||
{
|
||||
u32 unk;
|
||||
be_t<u32> mem_size;
|
||||
be_t<u32> mem_align;
|
||||
};
|
||||
|
||||
error_code avcDecQueryMemory(ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr);
|
||||
void avcDecGetVersion(ppu_thread& ppu, vm::ptr<u32> version);
|
||||
error_code avcDecQueryCharacteristics(ppu_thread& ppu, vm::ptr<u32> unk);
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libdivx311dec.h"
|
||||
|
||||
LOG_CHANNEL(libdivx311dec)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<Divx311DecError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(DIVX311_DEC_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code divx311DecQueryAttr([[maybe_unused]] ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version)
|
||||
{
|
||||
libdivx311dec.todo("divx311DecQueryAttr(params=*0x%x, memSize=*0x%x, version=*0x%x)", params, memSize, version);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecOpen()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecClose()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecReset()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecFlushPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecGetNumOfPictures()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecGetPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecDecodeAu()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divx311DecReleasePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivx311dec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libdivx311dec)("libdivx311dec", []
|
||||
{
|
||||
REG_FUNC(libdivx311dec, divx311DecQueryAttr);
|
||||
REG_FUNC(libdivx311dec, divx311DecOpen);
|
||||
REG_FUNC(libdivx311dec, divx311DecClose);
|
||||
REG_FUNC(libdivx311dec, divx311DecReset);
|
||||
REG_FUNC(libdivx311dec, divx311DecFlushPicture);
|
||||
REG_FUNC(libdivx311dec, divx311DecGetNumOfPictures);
|
||||
REG_FUNC(libdivx311dec, divx311DecGetPicture);
|
||||
REG_FUNC(libdivx311dec, divx311DecDecodeAu);
|
||||
REG_FUNC(libdivx311dec, divx311DecReleasePicture);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
enum Divx311DecError : u32
|
||||
{
|
||||
DIVX311_DEC_ERROR_ARG = 0x80615502,
|
||||
};
|
||||
|
||||
struct DivxDecParams;
|
||||
|
||||
error_code divx311DecQueryAttr(ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version);
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libdivxdec.h"
|
||||
|
||||
LOG_CHANNEL(libdivxdec)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<DivxDecError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(DIVX_DEC_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code divxDecQueryAttr([[maybe_unused]] ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version)
|
||||
{
|
||||
libdivxdec.todo("divxDecQueryAttr(params=*0x%x, memSize=*0x%x, version=*0x%x)", params, memSize, version);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecOpen()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecClose()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecReset()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecFlushPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecGetNumOfPictures()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecGetPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecDecodeAu()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code divxDecReleasePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libdivxdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libdivxdec)("libdivxdec", []
|
||||
{
|
||||
REG_FUNC(libdivxdec, divxDecQueryAttr);
|
||||
REG_FUNC(libdivxdec, divxDecOpen);
|
||||
REG_FUNC(libdivxdec, divxDecClose);
|
||||
REG_FUNC(libdivxdec, divxDecReset);
|
||||
REG_FUNC(libdivxdec, divxDecFlushPicture);
|
||||
REG_FUNC(libdivxdec, divxDecGetNumOfPictures);
|
||||
REG_FUNC(libdivxdec, divxDecGetPicture);
|
||||
REG_FUNC(libdivxdec, divxDecDecodeAu);
|
||||
REG_FUNC(libdivxdec, divxDecReleasePicture);
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
enum DivxDecError : u32
|
||||
{
|
||||
DIVX_DEC_ERROR_ARG = 0x80615502,
|
||||
};
|
||||
|
||||
struct DivxDecParams
|
||||
{
|
||||
be_t<u32> profile_level;
|
||||
be_t<u32> max_decoded_frame_width;
|
||||
be_t<u32> max_decoded_frame_height;
|
||||
be_t<u32> number_of_decoded_frame_buffer;
|
||||
};
|
||||
|
||||
error_code divxDecQueryAttr(ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version);
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libmvcdec.h"
|
||||
|
||||
LOG_CHANNEL(libmvcdec)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<MvcDecError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(MVC_DEC_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code mvcDecQueryMemory([[maybe_unused]] ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr)
|
||||
{
|
||||
libmvcdec.todo("mvcDecQueryMemory(params=*0x%x, attr=*0x%x)", params, attr);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
void mvcDecGetVersion([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
|
||||
{
|
||||
libmvcdec.notice("mvcDecGetVersion(version=*0x%x)", version);
|
||||
|
||||
if (version)
|
||||
{
|
||||
*version = 0x10804;
|
||||
}
|
||||
}
|
||||
|
||||
error_code mvcDecQueryCharacteristics([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> unk)
|
||||
{
|
||||
libmvcdec.notice("mvcDecGetVersion(unk=*0x%x)", unk);
|
||||
|
||||
if (!unk)
|
||||
{
|
||||
return MVC_DEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
unk[0] = 5; // Command queue depth
|
||||
unk[1] = 36;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecOpen()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecClose()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecReset()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecFlush()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecAccessUnit()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code mvcDecRlsFrame()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libmvcdec);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libmvcdec)("libmvcdec", []
|
||||
{
|
||||
REG_FUNC(libmvcdec, mvcDecQueryMemory);
|
||||
REG_FUNC(libmvcdec, mvcDecGetVersion);
|
||||
REG_FUNC(libmvcdec, mvcDecQueryCharacteristics);
|
||||
REG_FUNC(libmvcdec, mvcDecOpen);
|
||||
REG_FUNC(libmvcdec, mvcDecClose);
|
||||
REG_FUNC(libmvcdec, mvcDecReset);
|
||||
REG_FUNC(libmvcdec, mvcDecFlush);
|
||||
REG_FUNC(libmvcdec, mvcDecAccessUnit);
|
||||
REG_FUNC(libmvcdec, mvcDecRlsFrame);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
enum MvcDecError : u32
|
||||
{
|
||||
MVC_DEC_ERROR_ARG = 0x80615901,
|
||||
};
|
||||
|
||||
struct AvcDecAttr;
|
||||
struct AvcDecParams;
|
||||
|
||||
error_code mvcDecQueryMemory(ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr);
|
||||
void mvcDecGetVersion(ppu_thread& ppu, vm::ptr<u32> version);
|
||||
error_code mvcDecQueryCharacteristics(ppu_thread& ppu, vm::ptr<u32> unk);
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libsjvtd.h"
|
||||
|
||||
LOG_CHANNEL(libsjvtd)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<SjvtdError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(SJVTD_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code sjvtdGetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
|
||||
{
|
||||
libsjvtd.todo("sjvtdGetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<SjvtdParams> params)
|
||||
{
|
||||
libsjvtd.todo("sjvtdGetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
|
||||
{
|
||||
libsjvtd.notice("sjvtdGetVersionNumber(version=*0x%x)", version);
|
||||
|
||||
if (!version)
|
||||
{
|
||||
return SJVTD_ERROR_ARG;
|
||||
}
|
||||
|
||||
*version = 0x1050000;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdInitialize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdCreateInstance2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdReleaseInstance()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdCancelDecode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdFlushPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetNumPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdReleasePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetSideInfoSeq()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetSideInfoPic()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdSetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdSetDecodeMode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdDecodePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdGetExistenceFlag()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sjvtdDiscontinue()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsjvtd);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libsjvtd)("libsjvtd", []
|
||||
{
|
||||
REG_FUNC(libsjvtd, sjvtdGetMemorySize);
|
||||
REG_FUNC(libsjvtd, sjvtdGetMemorySize2);
|
||||
REG_FUNC(libsjvtd, sjvtdGetVersionNumber);
|
||||
REG_FUNC(libsjvtd, sjvtdInitialize);
|
||||
REG_FUNC(libsjvtd, sjvtdCreateInstance2);
|
||||
REG_FUNC(libsjvtd, sjvtdReleaseInstance);
|
||||
REG_FUNC(libsjvtd, sjvtdCancelDecode);
|
||||
REG_FUNC(libsjvtd, sjvtdFlushPicture);
|
||||
REG_FUNC(libsjvtd, sjvtdGetNumPicture);
|
||||
REG_FUNC(libsjvtd, sjvtdGetPicture);
|
||||
REG_FUNC(libsjvtd, sjvtdReleasePicture);
|
||||
REG_FUNC(libsjvtd, sjvtdGetAuxData);
|
||||
REG_FUNC(libsjvtd, sjvtdGetSideInfoSeq);
|
||||
REG_FUNC(libsjvtd, sjvtdGetSideInfoPic);
|
||||
REG_FUNC(libsjvtd, sjvtdSetAuxData);
|
||||
REG_FUNC(libsjvtd, sjvtdSetDecodeMode);
|
||||
REG_FUNC(libsjvtd, sjvtdDecodePicture);
|
||||
REG_FUNC(libsjvtd, sjvtdGetExistenceFlag);
|
||||
REG_FUNC(libsjvtd, sjvtdDiscontinue);
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
enum SjvtdError : u32
|
||||
{
|
||||
SJVTD_ERROR_ARG = 0x80615841,
|
||||
};
|
||||
|
||||
enum SjvtdProfileLevel : u8
|
||||
{
|
||||
SJVTD_LEVEL_1P0 = 1,
|
||||
SJVTD_LEVEL_1P1,
|
||||
SJVTD_LEVEL_1P2,
|
||||
SJVTD_LEVEL_1P3,
|
||||
SJVTD_LEVEL_2P0,
|
||||
SJVTD_LEVEL_2P1,
|
||||
SJVTD_LEVEL_2P2,
|
||||
SJVTD_LEVEL_3P0,
|
||||
SJVTD_LEVEL_3P1,
|
||||
SJVTD_LEVEL_3P2,
|
||||
SJVTD_LEVEL_4P0,
|
||||
SJVTD_LEVEL_4P1,
|
||||
SJVTD_LEVEL_4P2
|
||||
};
|
||||
|
||||
struct SjvtdParams
|
||||
{
|
||||
be_t<u32> unk1;
|
||||
be_t<u32> unk2;
|
||||
be_t<u32> unk3;
|
||||
be_t<s32> max_decoded_frame_width;
|
||||
be_t<s32> max_decoded_frame_height;
|
||||
be_t<u32> enable_deblocking_filter;
|
||||
be_t<u32> unk;
|
||||
be_t<u32> number_of_decoded_frame_buffer;
|
||||
};
|
||||
|
||||
error_code sjvtdGetMemorySize(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
|
||||
error_code sjvtdGetMemorySize2(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<SjvtdParams> params);
|
||||
error_code sjvtdGetVersionNumber(ppu_thread& ppu, vm::ptr<u32> version);
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libsmvd2.h"
|
||||
|
||||
LOG_CHANNEL(libsmvd2)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<Smvd2Error>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(SMVD2_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code smvd2GetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
|
||||
{
|
||||
libsmvd2.todo("smvd2GetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd2Params> params)
|
||||
{
|
||||
libsmvd2.todo("smvd2GetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
|
||||
{
|
||||
libsmvd2.notice("smvd2GetVersionNumber(version=*0x%x)", version);
|
||||
|
||||
if (!version)
|
||||
{
|
||||
return SMVD2_ERROR_ARG;
|
||||
}
|
||||
|
||||
*version = 0x1030000;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2Initialize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2CreateInstance2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2ReleaseInstance()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2CancelDecode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2FlushPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetNumPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2ReleasePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetSideInfoSeq()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetBnrData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetSideInfoPic()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2SetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2SetDecodeMode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2DecodePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2DecodePicture2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2GetExistenceFlag()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd2Discontinue()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libsmvd2)("libsmvd2", []
|
||||
{
|
||||
REG_FUNC(libsmvd2, smvd2GetMemorySize);
|
||||
REG_FUNC(libsmvd2, smvd2GetMemorySize2);
|
||||
REG_FUNC(libsmvd2, smvd2GetVersionNumber);
|
||||
REG_FUNC(libsmvd2, smvd2Initialize);
|
||||
REG_FUNC(libsmvd2, smvd2CreateInstance2);
|
||||
REG_FUNC(libsmvd2, smvd2ReleaseInstance);
|
||||
REG_FUNC(libsmvd2, smvd2CancelDecode);
|
||||
REG_FUNC(libsmvd2, smvd2FlushPicture);
|
||||
REG_FUNC(libsmvd2, smvd2GetNumPicture);
|
||||
REG_FUNC(libsmvd2, smvd2GetPicture);
|
||||
REG_FUNC(libsmvd2, smvd2ReleasePicture);
|
||||
REG_FUNC(libsmvd2, smvd2GetAuxData);
|
||||
REG_FUNC(libsmvd2, smvd2GetSideInfoSeq);
|
||||
REG_FUNC(libsmvd2, smvd2GetBnrData);
|
||||
REG_FUNC(libsmvd2, smvd2GetSideInfoPic);
|
||||
REG_FUNC(libsmvd2, smvd2SetAuxData);
|
||||
REG_FUNC(libsmvd2, smvd2SetDecodeMode);
|
||||
REG_FUNC(libsmvd2, smvd2DecodePicture);
|
||||
REG_FUNC(libsmvd2, smvd2DecodePicture2);
|
||||
REG_FUNC(libsmvd2, smvd2GetExistenceFlag);
|
||||
REG_FUNC(libsmvd2, smvd2Discontinue);
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
enum Smvd2Error : u32
|
||||
{
|
||||
SMVD2_ERROR_ARG = 0x80615041,
|
||||
};
|
||||
|
||||
enum Smvd2ProfileLevel : u32
|
||||
{
|
||||
SMVD2_MP_LL = 1,
|
||||
SMVD2_MP_ML,
|
||||
SMVD2_MP_H14,
|
||||
SMVD2_MP_HL
|
||||
};
|
||||
|
||||
struct Smvd2Params
|
||||
{
|
||||
be_t<u32> unk1;
|
||||
be_t<u32> unk2;
|
||||
be_t<u32> unk3;
|
||||
be_t<u32> max_decoded_frame_width;
|
||||
be_t<u32> max_decoded_frame_height;
|
||||
};
|
||||
|
||||
error_code smvd2GetMemorySize(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
|
||||
error_code smvd2GetMemorySize2(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd2Params> params);
|
||||
error_code smvd2GetVersionNumber(ppu_thread& ppu, vm::ptr<u32> version);
|
||||
@@ -0,0 +1,158 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "libsmvd4.h"
|
||||
|
||||
LOG_CHANNEL(libsmvd4)
|
||||
|
||||
template<>
|
||||
void fmt_class_string<Smvd4Error>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(SMVD4_ERROR_ARG);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
error_code smvd4GetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
|
||||
{
|
||||
libsmvd4.todo("smvd4GetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd4Params> params)
|
||||
{
|
||||
libsmvd4.todo("smvd4GetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
|
||||
{
|
||||
libsmvd4.notice("smvd4GetVersionNumber(version=*0x%x)", version);
|
||||
|
||||
if (!version)
|
||||
{
|
||||
return SMVD4_ERROR_ARG;
|
||||
}
|
||||
|
||||
*version = 0x1080000;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4Initialize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4CreateInstance2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4ReleaseInstance()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4CancelDecode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4FlushPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetNumPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetPicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4ReleasePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetSideInfoSeq()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetSideInfoPic()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4SetAuxData()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4DecodePicture()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4GetExistenceFlag()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code smvd4Discontinue()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(libsmvd4);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::libsmvd4)("libsmvd4", []
|
||||
{
|
||||
REG_FUNC(libsmvd4, smvd4GetMemorySize);
|
||||
REG_FUNC(libsmvd4, smvd4GetMemorySize2);
|
||||
REG_FUNC(libsmvd4, smvd4GetVersionNumber);
|
||||
REG_FUNC(libsmvd4, smvd4Initialize);
|
||||
REG_FUNC(libsmvd4, smvd4CreateInstance2);
|
||||
REG_FUNC(libsmvd4, smvd4ReleaseInstance);
|
||||
REG_FUNC(libsmvd4, smvd4CancelDecode);
|
||||
REG_FUNC(libsmvd4, smvd4FlushPicture);
|
||||
REG_FUNC(libsmvd4, smvd4GetNumPicture);
|
||||
REG_FUNC(libsmvd4, smvd4GetPicture);
|
||||
REG_FUNC(libsmvd4, smvd4ReleasePicture);
|
||||
REG_FUNC(libsmvd4, smvd4GetAuxData);
|
||||
REG_FUNC(libsmvd4, smvd4GetSideInfoSeq);
|
||||
REG_FUNC(libsmvd4, smvd4GetSideInfoPic);
|
||||
REG_FUNC(libsmvd4, smvd4SetAuxData);
|
||||
REG_FUNC(libsmvd4, smvd4DecodePicture);
|
||||
REG_FUNC(libsmvd4, smvd4GetExistenceFlag);
|
||||
REG_FUNC(libsmvd4, smvd4Discontinue);
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user