Compare commits
439 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61d9852632 | |||
| cb2748ae08 | |||
| 900d7df40f | |||
| 6837370a62 | |||
| 9cfdd8afea | |||
| 7b24df0957 | |||
| 122555fb66 | |||
| d836033212 | |||
| 510041a873 | |||
| 8b4b6ba946 | |||
| d36a715b24 | |||
| 599e00d6da | |||
| 3cd8891ab8 | |||
| e3e6fb5489 | |||
| a9303acfdf | |||
| b4ec4f46b8 | |||
| 6ee9d0f35f | |||
| a0040e6fb1 | |||
| 28d7af313b | |||
| 39ef39aa4e | |||
| d9a1308624 | |||
| 56dd09f4fe | |||
| de495952fd | |||
| 1ce5349199 | |||
| dcd011048d | |||
| 12e3c9e08b | |||
| 262ff01619 | |||
| 76ccaf5e6f | |||
| 47e4a95d8f | |||
| c0bafbc804 | |||
| 61c64d1060 | |||
| 3e1e1a683c | |||
| 18f59d3be8 | |||
| bf739aa156 | |||
| 43b7d1fe98 | |||
| cc21d1b3cf | |||
| 08333e0876 | |||
| 2f93df480b | |||
| 56fe450959 | |||
| 6f9adbd162 | |||
| f48c535802 | |||
| 5f7e5a9844 | |||
| 3f3b4bc363 | |||
| 68466f05b6 | |||
| e6b4655142 | |||
| f690d55045 | |||
| d523f9cc6b | |||
| 7ca15c60bb | |||
| 8abd3e8bb4 | |||
| 6730dc1dc4 | |||
| 6b40d69a8f | |||
| d6420b8803 | |||
| a4ad0a9468 | |||
| a09fd80905 | |||
| 0617e9e14b | |||
| cb4711203b | |||
| e3e39e8de3 | |||
| 209b14fbac | |||
| 04c9d01390 | |||
| c9d8e59dbf | |||
| 86b194014b | |||
| e7c827f73b | |||
| abe498f35c | |||
| 49c02854f5 | |||
| cccfb89aa0 | |||
| 2981867375 | |||
| 56d922999f | |||
| 813c3298ad | |||
| 02832d9623 | |||
| 0ab36ef44d | |||
| 9bb46aa944 | |||
| 474baca6d8 | |||
| 88d0b7d135 | |||
| aea1ec2594 | |||
| 22a7b026e7 | |||
| c8d4a0dcdc | |||
| f16949c292 | |||
| f910a152f1 | |||
| 270c2e58a0 | |||
| a586d1c529 | |||
| 09b047611b | |||
| e5005597fe | |||
| 38bfefcdfa | |||
| 44fe6f6d39 | |||
| 4fe17fcecf | |||
| 276ee81da8 | |||
| 5e158bd3f2 | |||
| 2b924175aa | |||
| 37a722cc1d | |||
| a84223bdc6 | |||
| 4df1a938b1 | |||
| 94a3b1cfe8 | |||
| a21c6c4628 | |||
| 58f0fa3ca5 | |||
| 97bd8f7bc1 | |||
| 4c0ef37a5c | |||
| 1df8f52a9f | |||
| f9434091c3 | |||
| 59f253ba24 | |||
| c52d447381 | |||
| 524b065bc8 | |||
| f7b55ec8d4 | |||
| e7fc2bfa3b | |||
| 22e3075c64 | |||
| 09ead539a5 | |||
| 5272dd951b | |||
| 7eee9e7b05 | |||
| e5eb727110 | |||
| 7352a5a495 | |||
| 3d859a7493 | |||
| 3587cfd2df | |||
| de2be90636 | |||
| 9f35c3f52d | |||
| 22910c02a9 | |||
| fef8905dc2 | |||
| 0f7534c755 | |||
| 4d0330bf82 | |||
| 026efd96b2 | |||
| f6af5ff13a | |||
| 0d7b7585a8 | |||
| 6283600b84 | |||
| f6e04ffdd2 | |||
| 44b42f68fd | |||
| 56241838c0 | |||
| 26475f8f40 | |||
| ff5e31f396 | |||
| 59b1c324a9 | |||
| 1f627caa81 | |||
| 7dc3a50ddf | |||
| cfe08de951 | |||
| 48ad959697 | |||
| 05881ffa62 | |||
| 8b3ed7b599 | |||
| 48ad7ca3a0 | |||
| cbec04cd0f | |||
| 3fde455932 | |||
| ba727e13ae | |||
| 2248007c9a | |||
| 2359ba9aed | |||
| b736691bde | |||
| 50bf528f3f | |||
| 7e3eab9915 | |||
| dc0793b731 | |||
| c194804fec | |||
| d41e405420 | |||
| 516cb959cb | |||
| 704e74b904 | |||
| b70f08d850 | |||
| 53f2594983 | |||
| 7573d7289b | |||
| f9abe40494 | |||
| 88bb26afb4 | |||
| dac0baac0d | |||
| f7eacf70ec | |||
| 067b35217a | |||
| c3888f5bbb | |||
| af3ef02ca0 | |||
| c49ebba648 | |||
| 933d96af5f | |||
| bf6044fced | |||
| 1a0392bf15 | |||
| ad00c44231 | |||
| 69f321a471 | |||
| 37383f4217 | |||
| b84e95d768 | |||
| 4369fb234e | |||
| 58040d478a | |||
| 46a54f5474 | |||
| e31173e33e | |||
| 5b0ef401f7 | |||
| 1650dd1c7d | |||
| 84f123041a | |||
| f258ae795c | |||
| 33e80a733d | |||
| 20331a77ce | |||
| 4e6b37ca04 | |||
| 646ad3eed4 | |||
| edcc2a9e0d | |||
| 0e20acdf55 | |||
| f262e77fbd | |||
| 244aa6879a | |||
| e9fe90244f | |||
| a90376a7c4 | |||
| aff7d8a994 | |||
| 7f4b6b1ae7 | |||
| 78bcb0fd53 | |||
| c733e794de | |||
| 99fc90648b | |||
| 2587545eed | |||
| ac300c7356 | |||
| 2af2db2cbf | |||
| d5ca9dacb6 | |||
| 5e436984a2 | |||
| be972f04ac | |||
| 00c87a8fc9 | |||
| e51656e070 | |||
| 9dc6d920ce | |||
| 354db953e6 | |||
| e67cf68321 | |||
| 60d35e17ac | |||
| d161d8c545 | |||
| f66b29f043 | |||
| 77f6db2543 | |||
| 5eabe9aa33 | |||
| 76c1681173 | |||
| 67ba381dfe | |||
| 9a6285942b | |||
| d93f16fe87 | |||
| 214c1ccc7e | |||
| d1ac92fd99 | |||
| 263b7854c1 | |||
| 843aed228e | |||
| bf71b1fcf7 | |||
| 81c216f330 | |||
| e4b242955c | |||
| 3fac832567 | |||
| 08011e9b78 | |||
| ee7ed1fdc3 | |||
| d63a712cc5 | |||
| c549d7154b | |||
| 590483b81f | |||
| 9ba9d9424e | |||
| 962e140cea | |||
| 9afa960979 | |||
| 8b4f3fa775 | |||
| 5fb0b2c190 | |||
| 266ab28145 | |||
| 2acac67570 | |||
| 361ef33856 | |||
| c0c52c33b9 | |||
| 4ed92f4155 | |||
| cb953a418f | |||
| 9c1f258be3 | |||
| c6641748ce | |||
| 12efd29121 | |||
| a4bd1755cf | |||
| 683c061e60 | |||
| 0e625e193e | |||
| 43302f937c | |||
| 792ac5ed64 | |||
| 90548468ee | |||
| 1584aba387 | |||
| caea340985 | |||
| ab6c74b3c1 | |||
| f06c8b22e8 | |||
| 68fdc49528 | |||
| cd73d738e5 | |||
| ab50e5483e | |||
| 8a72bdb422 | |||
| df39bd39b0 | |||
| d58df667b9 | |||
| 7a24642c56 | |||
| 1a90adfb5e | |||
| 3832d4fa1c | |||
| 8f328b2624 | |||
| 80ef348600 | |||
| e2a9c0a8f3 | |||
| 780a234467 | |||
| ea9dc9317d | |||
| 15af1bca69 | |||
| 479150b214 | |||
| e1d1d16227 | |||
| 877dd1f98d | |||
| 7df7ac57cc | |||
| e5793acd78 | |||
| 24e76f2960 | |||
| 75e66ab5e0 | |||
| b3725baf5a | |||
| f1d9a014c0 | |||
| 8e3112734e | |||
| eb850681ee | |||
| 70308d05f3 | |||
| c7e5a62909 | |||
| f72d148d37 | |||
| d8435163bb | |||
| af11546b1e | |||
| 63f16d7a46 | |||
| c765de81d4 | |||
| 02e1c49818 | |||
| 71b15c1fa6 | |||
| 65e9388f00 | |||
| 056d43eca7 | |||
| 55ec4808f1 | |||
| 2cfa5406e7 | |||
| 86716dc37b | |||
| f9ab077908 | |||
| 8b3a3e4ac8 | |||
| 45f5819ef3 | |||
| 3f55067414 | |||
| 797b69fe8f | |||
| 019fa390f2 | |||
| 42de2a3e0b | |||
| f90bf2dd40 | |||
| dc8fc9fc79 | |||
| 3d49976b3c | |||
| eed38e1bbc | |||
| 9595297a3a | |||
| 7c5b5d25e3 | |||
| 2e22a0d9bb | |||
| ba2a8ebf2e | |||
| 381c7544fa | |||
| 4752c4014b | |||
| 24642a4c18 | |||
| 62979c7bd9 | |||
| 7b9fb7ad9c | |||
| 7f830d555d | |||
| 9aafd8c09f | |||
| 6781eb7c76 | |||
| 3e09b97f58 | |||
| e7b9513d4a | |||
| 34c36c73ee | |||
| 2821eb7397 | |||
| 65c9cd99cd | |||
| b217e8384c | |||
| df96be199b | |||
| ecc55048c5 | |||
| e58911cc95 | |||
| 269c4604aa | |||
| 8f1dc7a2d4 | |||
| da91a841b7 | |||
| 4323ca4a01 | |||
| fff0c96bf3 | |||
| 7167fbf545 | |||
| e4aff539b0 | |||
| d0e31f4ec3 | |||
| 3e06446297 | |||
| 4237039d53 | |||
| 5ae7dbf77c | |||
| f1037f75d9 | |||
| a7cb513a89 | |||
| e3ec71c683 | |||
| f7870d4968 | |||
| 80ef0ce9b5 | |||
| 696a3c4a42 | |||
| 81a01134bb | |||
| 70c291753c | |||
| 3c7ada8e83 | |||
| 46b3027981 | |||
| 334999f639 | |||
| dabfce5c82 | |||
| 0a8d9a12ab | |||
| 19b2da2590 | |||
| 17a074e1ac | |||
| 38d5f688b3 | |||
| e307a1b4bd | |||
| a50e22a11f | |||
| c995505023 | |||
| 1f8f428a59 | |||
| e10c6cbaf7 | |||
| 5870da0b55 | |||
| 5f729d4dbf | |||
| ebde86b967 | |||
| 14a425e487 | |||
| 1f1d1da87b | |||
| 129ade2f73 | |||
| c37c6fb6de | |||
| 5736fa3351 | |||
| 3e84a2acc0 | |||
| 77c252a4c0 | |||
| e4f92ddf77 | |||
| 1f3b1e1c99 | |||
| 73bdf7481e | |||
| c2ab3c664c | |||
| ddec5d6908 | |||
| 975aae1d13 | |||
| 9e62ca562b | |||
| d28b0ba2fa | |||
| aba332d4c4 | |||
| 543fb7a9cb | |||
| 67b3fc70f8 | |||
| 2268aa9093 | |||
| 4b8ee85995 | |||
| 86ead1b93b | |||
| 7ec7f261b1 | |||
| 1685769bd9 | |||
| 2acb6ed60d | |||
| 144244e902 | |||
| 7cf9d1380b | |||
| f188019244 | |||
| 38dfc88e8d | |||
| 95c36221fa | |||
| b30ac48513 | |||
| 4fcc1c0f31 | |||
| 1a2fad4eac | |||
| da4f4dafbd | |||
| e5876b3a1b | |||
| 9c2d94ca1a | |||
| ae4f837f57 | |||
| c6c4aa4634 | |||
| bcda172ae7 | |||
| d511e76a63 | |||
| 56e982375e | |||
| e3eaf5f29e | |||
| e56609d836 | |||
| 460fb66949 | |||
| b955469721 | |||
| 6378e16fdf | |||
| 8b48fd95fe | |||
| 5600430a05 | |||
| 50ad7ba1f6 | |||
| fe6cb4774e | |||
| 91fefd4671 | |||
| 677d5f09ce | |||
| c7f5475b56 | |||
| 42e3add6a1 | |||
| 8dc98bbc1b | |||
| f98595bee5 | |||
| fafefb2cf5 | |||
| 1cbcf7e1ad | |||
| df080fbc53 | |||
| bd66dfedc9 | |||
| 69faf14a79 | |||
| 1a443cf6b5 | |||
| 2fb68bfc03 | |||
| 4d70c483b2 | |||
| 29256df5b1 | |||
| a74156fe5b | |||
| 57473ff98d | |||
| 00618995d9 | |||
| 6a340afb8a | |||
| dfbc49513d | |||
| 7bb830a228 | |||
| b3f002f68f | |||
| ea949a5f28 | |||
| 53457262d4 | |||
| 472efc08eb | |||
| 0525070898 | |||
| cbc1c472d5 | |||
| aca9884e76 | |||
| 17fb601c02 | |||
| 76ac8ffbd6 | |||
| fe0c164de2 | |||
| ea2c5294df | |||
| 3c0681ad6d | |||
| 0debcfed0a | |||
| 06f733a7f2 | |||
| ad6a72e369 | |||
| b40ed5bdb7 | |||
| ee6e4c493d |
+10
-4
@@ -7,10 +7,16 @@ git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ { print $3 }'
|
||||
|
||||
# Prefer newer Clang than in base system (see also .ci/install-freebsd.sh)
|
||||
# libc++ isn't in llvm* packages, so download manually
|
||||
fetch https://github.com/llvm/llvm-project/releases/download/llvmorg-12.0.0/libcxx-12.0.0.src.tar.xz
|
||||
tar xf libcxx-12.0.0.src.tar.xz
|
||||
export CC=clang12 CXX=clang++12
|
||||
export CXXFLAGS="$CXXFLAGS -nostdinc++ -D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS -isystem $PWD/libcxx-12.0.0.src/include"
|
||||
fetch https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-13.0.1-rc1.tar.gz
|
||||
tar xf llvm*.tar.gz
|
||||
export CC=clang13 CXX=clang++13
|
||||
cmake -B libcxx_build -G Ninja -S llvm*/libcxx \
|
||||
-DLLVM_CCACHE_BUILD=ON \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=libcxx_prefix
|
||||
cmake --build libcxx_build
|
||||
cmake --install libcxx_build
|
||||
export CXXFLAGS="$CXXFLAGS -nostdinc++ -isystem$PWD/libcxx_prefix/include/c++/v1"
|
||||
export LDFLAGS="$LDFLAGS -nostdlib++ -L$PWD/libcxx_prefix/lib -l:libc++.a -lcxxrt"
|
||||
|
||||
CONFIGURE_ARGS="
|
||||
-DWITH_LLVM=OFF
|
||||
|
||||
@@ -18,6 +18,11 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
# Embed newer libstdc++ for distros that don't come with it (ubuntu 16.04)
|
||||
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
|
||||
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
|
||||
|
||||
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
|
||||
rm -f ./appdir/usr/lib/libwayland-client.so*
|
||||
|
||||
# Install latest appimage runner
|
||||
rm ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
|
||||
chmod a+x ./appdir/AppRun
|
||||
|
||||
@@ -9,10 +9,10 @@ export ASSUME_ALWAYS_YES=true
|
||||
pkg info # debug
|
||||
|
||||
# Prefer newer Clang than in base system (see also .ci/build-freebsd.sh)
|
||||
pkg install llvm12
|
||||
pkg install llvm13
|
||||
|
||||
# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets)
|
||||
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent glew openal-soft ffmpeg
|
||||
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent qt5-multimedia qt5-svg glew openal-soft ffmpeg
|
||||
|
||||
# Optional dependencies (libevdev is pulled by qt5-gui)
|
||||
pkg install pkgconf alsa-lib pulseaudio sdl2 evdev-proto vulkan-headers vulkan-loader
|
||||
|
||||
@@ -7,8 +7,8 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
|
||||
|
||||
# Resource/dependency URLs
|
||||
# Qt mirrors can be volatile and slow, so we list 2
|
||||
QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
#QT_HOST="http://qt.mirror.constant.com/"
|
||||
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
QT_HOST="http://qt.mirror.constant.com/"
|
||||
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
|
||||
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
|
||||
@@ -17,6 +17,8 @@ QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}"
|
||||
QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}"
|
||||
QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}"
|
||||
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}qttools${QT_SUFFIX}"
|
||||
QT_MM_URL="${QT_HOST}${QT_PREFIX}qtmultimedia${QT_SUFFIX}"
|
||||
QT_SVG_URL="${QT_HOST}${QT_PREFIX}qtsvg${QT_SUFFIX}"
|
||||
LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win/llvmlibs_mt.7z'
|
||||
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z'
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/s/fjlh97hrz0crqxv/VulkanSDK-1.2.182.0-Installer.exe"
|
||||
@@ -26,6 +28,8 @@ DEP_URLS=" \
|
||||
$QT_WINE_URL \
|
||||
$QT_DECL_URL \
|
||||
$QT_TOOL_URL \
|
||||
$QT_MM_URL \
|
||||
$QT_SVG_URL \
|
||||
$LLVMLIBS_URL \
|
||||
$GLSLANG_URL \
|
||||
$VULKAN_SDK_URL"
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ windows_task:
|
||||
|
||||
linux_task:
|
||||
container:
|
||||
image: rpcs3/rpcs3-ci-bionic:1.1
|
||||
image: rpcs3/rpcs3-ci-bionic:1.3
|
||||
cpu: 4
|
||||
memory: 16G
|
||||
env:
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
Standard: c++20
|
||||
UseTab: ForIndentation
|
||||
UseTab: AlignWithSpaces
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
@@ -16,6 +16,7 @@ AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: Inline
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
|
||||
+6
-2
@@ -5,7 +5,7 @@
|
||||
[submodule "asmjit"]
|
||||
path = 3rdparty/asmjit/asmjit
|
||||
url = ../../asmjit/asmjit.git
|
||||
branch = oldstable
|
||||
branch = aarch64
|
||||
ignore = dirty
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
@@ -61,10 +61,14 @@
|
||||
url = ../../curl/curl.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/wolfssl"]
|
||||
path = 3rdparty/wolfssl
|
||||
path = 3rdparty/wolfssl/wolfssl
|
||||
url = ../../wolfSSL/wolfssl.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/flatbuffers"]
|
||||
path = 3rdparty/flatbuffers
|
||||
url = ../../google/flatbuffers.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/cubeb/cubeb"]
|
||||
path = 3rdparty/cubeb/cubeb
|
||||
url = ../../mozilla/cubeb.git
|
||||
ignore = dirty
|
||||
|
||||
Vendored
+7
@@ -45,7 +45,14 @@ if(WIN32)
|
||||
src/LzmaLib.c
|
||||
src/7zCrc.c
|
||||
src/MtDec.c)
|
||||
target_include_directories(3rdparty_7z INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:/src>)
|
||||
|
||||
target_include_directories(3rdparty_7z INTERFACE 7z)
|
||||
|
||||
set_property(TARGET 3rdparty_7z PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
else()
|
||||
add_library(3rdparty_7z INTERFACE)
|
||||
endif()
|
||||
|
||||
Vendored
+6
-80
@@ -128,38 +128,8 @@ target_include_directories(3rdparty_stblib INTERFACE stblib/include)
|
||||
# DiscordRPC
|
||||
add_subdirectory(discord-rpc)
|
||||
|
||||
|
||||
# ALSA
|
||||
set(ALSA_TARGET 3rdparty_dummy_lib)
|
||||
|
||||
if(USE_ALSA)
|
||||
find_package(ALSA)
|
||||
if(ALSA_FOUND)
|
||||
add_library(3rdparty_alsa INTERFACE)
|
||||
target_compile_definitions(3rdparty_alsa INTERFACE -DHAVE_ALSA)
|
||||
target_include_directories(3rdparty_alsa SYSTEM INTERFACE ${ALSA_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_alsa INTERFACE ${ALSA_LIBRARIES})
|
||||
|
||||
set(ALSA_TARGET 3rdparty_alsa)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Pulse
|
||||
set(PULSE_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_PULSE)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
|
||||
if(PULSE_FOUND)
|
||||
add_library(3rdparty_pulse INTERFACE)
|
||||
target_compile_definitions(3rdparty_pulse INTERFACE -DHAVE_PULSE)
|
||||
target_include_directories(3rdparty_pulse SYSTEM
|
||||
INTERFACE ${PULSE_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_pulse INTERFACE ${PULSE_LDFLAGS})
|
||||
|
||||
set(PULSE_TARGET 3rdparty_pulse)
|
||||
endif()
|
||||
endif()
|
||||
# Cubeb
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
|
||||
# libevdev
|
||||
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
|
||||
@@ -239,6 +209,9 @@ if(USE_FAUDIO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${FAUDIO_TARGET} PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
|
||||
# FFMPEG
|
||||
add_library(3rdparty_ffmpeg INTERFACE)
|
||||
|
||||
@@ -309,52 +282,7 @@ endif()
|
||||
include(llvm.cmake)
|
||||
|
||||
# WOLFSSL
|
||||
if(USE_SYSTEM_WOLFSSL)
|
||||
message("-- RPCS3: using shared wolfssl")
|
||||
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
|
||||
add_library(wolfssl INTERFACE)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
|
||||
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI)
|
||||
|
||||
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# This only works in single-config generator
|
||||
# For a multi-config generator, we need to provide different wolfssl binaries for different config
|
||||
if (GENERATOR_IS_MULTI_CONFIG)
|
||||
message( FATAL_ERROR "RPCS3 can only be configured using single-config generator." )
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/wolfssl.lib")
|
||||
else()
|
||||
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/libwolfssl.a")
|
||||
endif()
|
||||
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
|
||||
# So that curl can be built on an environment where openssl headers are not provided
|
||||
set(WolfSSL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/" "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/")
|
||||
endif()
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
# CURL
|
||||
add_subdirectory(curl EXCLUDE_FROM_ALL)
|
||||
@@ -376,8 +304,6 @@ add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
||||
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
||||
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
||||
add_library(3rdparty::discordRPC ALIAS 3rdparty_discordRPC)
|
||||
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
|
||||
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
|
||||
add_library(3rdparty::faudio ALIAS ${FAUDIO_TARGET})
|
||||
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
|
||||
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/FAudio updated: 1f6daa2382...caf9754455
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
# OpenAL
|
||||
if (MSVC)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS include/)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS libs/Win64/)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
Vendored
+4
-2
@@ -38,8 +38,10 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
|
||||
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
||||
BIN
Binary file not shown.
Vendored
+1
@@ -14,4 +14,5 @@ target_link_libraries(asmjit PRIVATE ${ASMJIT_DEPS})
|
||||
# ASMJIT should have a option for disabling installing and this wouldnt
|
||||
# be required to avoid installing ASMJIT...
|
||||
|
||||
set_property(TARGET asmjit PROPERTY FOLDER "3rdparty/")
|
||||
add_library(3rdparty::asmjit ALIAS asmjit)
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/asmjit/asmjit updated: 723f58581a...eae7197fce
Vendored
+132
-52
@@ -11,66 +11,146 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="asmjit\src\asmjit\base\arch.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codeholder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\constpool.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\globals.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\logging.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\osutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\regalloc.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\runtime.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\string.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\vmem.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\zone.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\archtraits.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\codeholder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\codewriter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\constpool.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\cpuinfo.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emitter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emitterutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\environment.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\errorhandler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\funcargscontext.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\globals.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\jitallocator.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\jitruntime.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\logger.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\osutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\ralocal.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\rastack.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\string.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\support.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\target.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\type.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\virtmem.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zone.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonehash.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonelist.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonestack.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonetree.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonevector.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="asmjit\src\asmjit\base\arch.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codebuilder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codecompiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codeemitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codeholder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\constpool.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\context_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\cpuinfo.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\func.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\logging.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\lock.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\osutils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\regalloc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\runtime.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\string.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\vectypes.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\vmem.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\zone.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-config.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\archcommons.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\archtraits.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codebuffer.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codeholder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codewriter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\compilerdefs.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\constpool.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\cpuinfo.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emitterutils_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\environment.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\errorhandler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\formatter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\funcargscontext_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\func.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\jitallocator.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\jitruntime.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\logger.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\misc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\osutils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\osutils_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\raassignment_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rabuilders_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\radefs_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\ralocal_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rastack_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\string.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\support.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\target.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\type.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\virtmem.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zone.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonehash.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonelist.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonestack.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonestring.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonetree.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonevector.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86archtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86func_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instdb.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instdb_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{AC40FF01-426E-4838-A317-66354CEFAE88}</ProjectGuid>
|
||||
|
||||
+132
-52
@@ -1,65 +1,145 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="asmjit\src\asmjit\base\arch.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\codeholder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\constpool.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\globals.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\logging.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\osutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\regalloc.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\runtime.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\string.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\vmem.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\base\zone.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\archtraits.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\codeholder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\codewriter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\constpool.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\cpuinfo.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emitter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\emitterutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\environment.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\errorhandler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\funcargscontext.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\globals.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\jitallocator.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\jitruntime.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\logger.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\osutils.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\ralocal.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\rastack.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\string.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\support.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\target.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\type.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\virtmem.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zone.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonehash.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonelist.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonestack.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonetree.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\core\zonevector.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86formatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86func.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\x86\x86rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64assembler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64builder.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64compiler.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64emithelper.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instapi.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64instdb.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64operand.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\a64rapass.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armformatter.cpp" />
|
||||
<ClCompile Include="asmjit\src\asmjit\arm\armfunc.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="asmjit\src\asmjit\base\arch.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codebuilder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codecompiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codeemitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\codeholder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\constpool.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\context_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\cpuinfo.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\func.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\logging.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\lock.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\osutils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\regalloc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\runtime.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\string.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\vectypes.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\vmem.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\base\zone.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-build_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\api-config.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\archcommons.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\archtraits.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codebuffer.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codeholder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\codewriter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\compilerdefs.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\constpool.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\cpuinfo.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\emitterutils_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\environment.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\errorhandler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\formatter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\funcargscontext_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\func.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\jitallocator.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\jitruntime.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\logger.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\misc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\osutils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\osutils_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\raassignment_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rabuilders_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\radefs_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\ralocal_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\rastack_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\string.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\support.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\target.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\type.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\virtmem.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zone.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonehash.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonelist.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonestack.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonestring.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonetree.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\core\zonevector.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86archtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86inst.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86formatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86func_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instdb.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86instdb_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86opcode_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\x86\x86rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64assembler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64builder.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64compiler.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emithelper_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64emitter.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64globals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instapi_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64instdb_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64operand.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64rapass_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\a64utils.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armarchtraits_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armformatter_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armfunc_p.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armglobals.h" />
|
||||
<ClInclude Include="asmjit\src\asmjit\arm\armoperand.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
# Cubeb
|
||||
|
||||
function(add_sanitizers ...)
|
||||
endfunction(add_sanitizers)
|
||||
|
||||
set(BUILD_SHARED_LIBS FALSE CACHE BOOL "Don't build shared libs")
|
||||
set(BUILD_TESTS FALSE CACHE BOOL "Don't build tests")
|
||||
set(BUILD_RUST_LIBS FALSE CACHE BOOL "Don't build rust libs")
|
||||
set(BUILD_TOOLS FALSE CACHE BOOL "Don't build tools")
|
||||
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
add_library(3rdparty::cubeb ALIAS cubeb)
|
||||
+1
Submodule 3rdparty/cubeb/cubeb added at d512bfa07a
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#define CUBEB_EXPORT
|
||||
Vendored
+87
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cubeb\src\cubeb.c" />
|
||||
<ClCompile Include="cubeb\src\cubeb_log.cpp" />
|
||||
<ClCompile Include="cubeb\src\cubeb_mixer.cpp" />
|
||||
<ClCompile Include="cubeb\src\cubeb_resampler.cpp" />
|
||||
<ClCompile Include="cubeb\src\cubeb_strings.c" />
|
||||
<ClCompile Include="cubeb\src\cubeb_utils.cpp" />
|
||||
<ClCompile Include="cubeb\src\cubeb_wasapi.cpp" />
|
||||
<ClCompile Include="cubeb\src\cubeb_winmm.c" />
|
||||
<ClCompile Include="cubeb\src\speex\resample.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="cubeb\include\cubeb\cubeb.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb-internal.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb-speex-resampler.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_array_queue.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_assert.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_log.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_mixer.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_resampler.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_resampler_internal.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_ringbuffer.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_ring_array.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_strings.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_utils.h" />
|
||||
<ClInclude Include="cubeb\src\cubeb_utils_win.h" />
|
||||
<ClInclude Include="cubeb\src\speex\arch.h" />
|
||||
<ClInclude Include="cubeb\src\speex\fixed_generic.h" />
|
||||
<ClInclude Include="cubeb\src\speex\resample_neon.h" />
|
||||
<ClInclude Include="cubeb\src\speex\resample_sse.h" />
|
||||
<ClInclude Include="cubeb\src\speex\speex_config_types.h" />
|
||||
<ClInclude Include="cubeb\src\speex\speex_resampler.h" />
|
||||
<ClInclude Include="cubeb\src\speex\stack_alloc.h" />
|
||||
<ClInclude Include="extra\cubeb_export.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{fda7b080-03b0-48c8-b24f-88118981422a}</ProjectGuid>
|
||||
<RootNamespace>libcubeb</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>OUTSIDE_SPEEX;RANDOM_PREFIX=speex;FLOATING_POINT;EXPORT=;USE_WASAPI;USE_WINMM;CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME;_HAS_DEPRECATED_RESULT_OF;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./cubeb/src/speex/;./extra/;./cubeb/src/;./cubeb/include/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cubeb\src\speex\resample.c">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_winmm.c">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_utils.cpp">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_wasapi.cpp">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_strings.c">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_resampler.cpp">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_mixer.cpp">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb_log.cpp">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cubeb\src\cubeb.c">
|
||||
<Filter>Source files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="cubeb\include\cubeb\cubeb.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\arch.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_array_queue.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_assert.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="extra\cubeb_export.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_log.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_mixer.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_resampler.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_resampler_internal.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_ring_array.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_ringbuffer.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_strings.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_utils.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb_utils_win.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb-internal.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\cubeb-speex-resampler.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\fixed_generic.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\resample_neon.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\resample_sse.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\speex_config_types.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\speex_resampler.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cubeb\src\speex\stack_alloc.h">
|
||||
<Filter>Header files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source files">
|
||||
<UniqueIdentifier>{cd86de6d-e811-4c48-9653-af00c7098a45}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header files">
|
||||
<UniqueIdentifier>{566bbf3e-ca28-4390-b054-58c92a66f24e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+7
@@ -7,10 +7,17 @@ if(USE_SYSTEM_CURL)
|
||||
target_link_libraries(libcurl INTERFACE CURL::libcurl)
|
||||
else()
|
||||
message("-- RPCS3: building libcurl + wolfssl submodules")
|
||||
add_compile_definitions(HAVE_SNI)
|
||||
option(BUILD_CURL_EXE "Set to ON to build curl executable." OFF)
|
||||
option(CMAKE_USE_WOLFSSL "enable wolfSSL for SSL/TLS" ON)
|
||||
option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON)
|
||||
option(CURL_ZLIB "Set to ON to enable building curl with zlib support." OFF)
|
||||
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
|
||||
if(USE_MSVC_STATIC_CRT)
|
||||
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl" FORCE)
|
||||
endif()
|
||||
add_subdirectory(curl EXCLUDE_FROM_ALL)
|
||||
|
||||
set_property(TARGET libcurl PROPERTY FOLDER "3rdparty/")
|
||||
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/curl/curl updated: 6b951a6928...c7aef0a945
Vendored
+1
-1
@@ -43,7 +43,7 @@
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/flatbuffers updated: 9e7e8cbe9f...a9a295fecf
Vendored
+1
-1
Submodule 3rdparty/glslang/glslang updated: ae2a562936...c9706bdda0
Vendored
+4
-2
@@ -38,8 +38,10 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
|
||||
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
|
||||
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
|
||||
Vendored
+19
-2
@@ -13,6 +13,15 @@ if(WITH_LLVM)
|
||||
option(LLVM_INCLUDE_UTILS OFF)
|
||||
option(LLVM_CCACHE_BUILD ON)
|
||||
|
||||
if(WIN32)
|
||||
set(LLVM_USE_INTEL_JITEVENTS ON)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
set(LLVM_USE_INTEL_JITEVENTS ON)
|
||||
set(LLVM_USE_PERF ON)
|
||||
endif()
|
||||
|
||||
set(CXX_FLAGS_OLD ${CMAKE_CXX_FLAGS})
|
||||
|
||||
if (MSVC)
|
||||
@@ -26,7 +35,7 @@ if(WITH_LLVM)
|
||||
set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
|
||||
|
||||
# now tries to find LLVM again
|
||||
find_package(LLVM 11.0 CONFIG)
|
||||
find_package(LLVM 13.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
@@ -39,7 +48,7 @@ if(WITH_LLVM)
|
||||
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
|
||||
endif()
|
||||
|
||||
find_package(LLVM 11.0 CONFIG)
|
||||
find_package(LLVM 13.0 CONFIG)
|
||||
|
||||
if (NOT LLVM_FOUND)
|
||||
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 11)
|
||||
@@ -54,6 +63,14 @@ if(WITH_LLVM)
|
||||
|
||||
set(LLVM_LIBS LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
|
||||
|
||||
if(WIN32 OR CMAKE_SYSTEM MATCHES "Linux")
|
||||
set(LLVM_LIBS ${LLVM_LIBS} LLVMIntelJITEvents)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
set(LLVM_LIBS ${LLVM_LIBS} LLVMPerfJITEvents)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_llvm INTERFACE)
|
||||
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
|
||||
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
|
||||
Vendored
+4
-4
@@ -2,17 +2,17 @@ add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
set(QT_MIN_VER 5.15.2)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent)
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg)
|
||||
if(WIN32)
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
else()
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
+81
-21
@@ -1,4 +1,4 @@
|
||||
// stb_truetype.h - v1.25 - public domain
|
||||
// stb_truetype.h - v1.26 - public domain
|
||||
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
|
||||
//
|
||||
// =======================================================================
|
||||
@@ -58,6 +58,7 @@
|
||||
//
|
||||
// VERSION HISTORY
|
||||
//
|
||||
// 1.26 (2021-08-28) fix broken rasterizer
|
||||
// 1.25 (2021-07-11) many fixes
|
||||
// 1.24 (2020-02-05) fix warning
|
||||
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
|
||||
@@ -271,8 +272,8 @@
|
||||
//// SAMPLE PROGRAMS
|
||||
////
|
||||
//
|
||||
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
|
||||
//
|
||||
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
|
||||
// See "tests/truetype_demo_win32.c" for a complete version.
|
||||
#if 0
|
||||
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
|
||||
#include "stb_truetype.h"
|
||||
@@ -307,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
|
||||
if (*text >= 32 && *text < 128) {
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
|
||||
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
|
||||
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
|
||||
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
|
||||
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
|
||||
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
|
||||
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
|
||||
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
|
||||
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
|
||||
}
|
||||
++text;
|
||||
}
|
||||
@@ -3061,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
|
||||
}
|
||||
}
|
||||
|
||||
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
|
||||
{
|
||||
STBTT_assert(top_width >= 0);
|
||||
STBTT_assert(bottom_width >= 0);
|
||||
return (top_width + bottom_width) / 2.0f * height;
|
||||
}
|
||||
|
||||
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
|
||||
{
|
||||
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
|
||||
}
|
||||
|
||||
static float stbtt__sized_triangle_area(float height, float width)
|
||||
{
|
||||
return height * width / 2;
|
||||
}
|
||||
|
||||
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
|
||||
{
|
||||
float y_bottom = y_top+1;
|
||||
@@ -3115,10 +3133,10 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
float height;
|
||||
// simple case, only spans one pixel
|
||||
int x = (int) x_top;
|
||||
height = sy1 - sy0;
|
||||
height = (sy1 - sy0) * e->direction;
|
||||
STBTT_assert(x >= 0 && x < len);
|
||||
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
|
||||
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
|
||||
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
|
||||
scanline_fill[x] += height; // everything right of this pixel is filled
|
||||
} else {
|
||||
int x,x1,x2;
|
||||
float y_crossing, y_final, step, sign, area;
|
||||
@@ -3134,40 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
dy = -dy;
|
||||
t = x0, x0 = xb, xb = t;
|
||||
}
|
||||
assert(dy >= 0);
|
||||
assert(dx >= 0);
|
||||
STBTT_assert(dy >= 0);
|
||||
STBTT_assert(dx >= 0);
|
||||
|
||||
x1 = (int) x_top;
|
||||
x2 = (int) x_bottom;
|
||||
// compute intersection with y axis at x1+1
|
||||
y_crossing = (x1+1 - x0) * dy + y_top;
|
||||
y_crossing = y_top + dy * (x1+1 - x0);
|
||||
|
||||
// compute intersection with y axis at x2
|
||||
y_final = y_top + dy * (x2 - x0);
|
||||
|
||||
// x1 x_top x2 x_bottom
|
||||
// y_top +------|-----+------------+------------+--------|---+------------+
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// sy0 | Txxxxx|............|............|............|............|
|
||||
// y_crossing | *xxxxx.......|............|............|............|
|
||||
// | | xxxxx..|............|............|............|
|
||||
// | | /- xx*xxxx........|............|............|
|
||||
// | | dy < | xxxxxx..|............|............|
|
||||
// y_final | | \- | xx*xxx.........|............|
|
||||
// sy1 | | | | xxxxxB...|............|
|
||||
// | | | | | |
|
||||
// | | | | | |
|
||||
// y_bottom +------------+------------+------------+------------+------------+
|
||||
//
|
||||
// goal is to measure the area covered by '.' in each pixel
|
||||
|
||||
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
|
||||
// @TODO: maybe test against sy1 rather than y_bottom?
|
||||
if (y_crossing > y_bottom)
|
||||
y_crossing = y_bottom;
|
||||
|
||||
sign = e->direction;
|
||||
// area of the rectangle covered from y0..y_crossing
|
||||
|
||||
// area of the rectangle covered from sy0..y_crossing
|
||||
area = sign * (y_crossing-sy0);
|
||||
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
|
||||
scanline[x1] += area * (x1+1 - x_top)/2;
|
||||
|
||||
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
|
||||
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
|
||||
|
||||
// check if final y_crossing is blown up; no test case for this
|
||||
y_final = y_crossing + dy * (x2 - (x1+1)); // advance y by number of steps taken below
|
||||
if (y_final > y_bottom) {
|
||||
y_final = y_bottom;
|
||||
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
|
||||
}
|
||||
|
||||
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, which is also how much pixel area changes for each step in x
|
||||
// in second pixel, area covered by line segment found in first pixel
|
||||
// is always a rectangle 1 wide * the height of that line segment; this
|
||||
// is exactly what the variable 'area' stores. it also gets a contribution
|
||||
// from the line segment within it. the THIRD pixel will get the first
|
||||
// pixel's rectangle contribution, the second pixel's rectangle contribution,
|
||||
// and its own contribution. the 'own contribution' is the same in every pixel except
|
||||
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
|
||||
// the second pixel's contribution to the third pixel will be the
|
||||
// rectangle 1 wide times the height change in the second pixel, which is dy.
|
||||
|
||||
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
|
||||
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
|
||||
// so the area advances by 'step' every time
|
||||
|
||||
for (x = x1+1; x < x2; ++x) {
|
||||
scanline[x] += area + step/2; // area of parallelogram is step/2
|
||||
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
|
||||
area += step;
|
||||
}
|
||||
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
|
||||
STBTT_assert(sy1 > y_final-0.01f);
|
||||
|
||||
// area of the triangle (x2,y_crossing), (x_bottom,y1), (x2,y1)
|
||||
scanline[x2] += area + sign * (x_bottom - x2)/2 * (sy1-y_crossing);
|
||||
// area covered in the last pixel is the rectangle from all the pixels to the left,
|
||||
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
|
||||
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
|
||||
|
||||
// the rest of the line is filled based on the total height of the line segment in this pixel
|
||||
scanline_fill[x2] += sign * (sy1-sy0);
|
||||
}
|
||||
} else {
|
||||
@@ -3175,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
|
||||
// clipping logic. since this does not match the intended use
|
||||
// of this library, we use a different, very slow brute
|
||||
// force implementation
|
||||
// note though that this does happen some of the time because
|
||||
// x_top and x_bottom can be extrapolated at the top & bottom of
|
||||
// the shape and actually lie outside the bounding box
|
||||
int x;
|
||||
for (x=0; x < len; ++x) {
|
||||
// cases:
|
||||
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
if(USE_SYSTEM_WOLFSSL)
|
||||
message("-- RPCS3: using shared wolfssl")
|
||||
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
|
||||
add_library(wolfssl INTERFACE)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
|
||||
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
|
||||
|
||||
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
|
||||
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
|
||||
|
||||
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
|
||||
|
||||
set(WolfSSL_LIBRARY "$<TARGET_FILE:wolfssl>" CACHE INTERNAL "")
|
||||
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
|
||||
# So that curl can be built on an environment where openssl headers are not provided
|
||||
set(WolfSSL_INCLUDE_DIR
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/wolfssl/"
|
||||
CACHE INTERNAL "")
|
||||
endif()
|
||||
@@ -48,7 +48,7 @@
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;ECC_TIMING_RESISTANT;WC_RSA_BLINDING;HAVE_AESGCM;WOLFSSL_SHA512;WOLFSSL_SHA384;NO_DSA;HAVE_ECC;TFM_ECC256;ECC_SHAMIR;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_TLS_EXTENSIONS;HAVE_SNI;HAVE_SUPPORTED_CURVES;HAVE_EXTENDED_MASTER;NO_RC4;HAVE_ENCRYPT_THEN_MAC;NO_PSK;NO_MD4;WC_NO_ASYNC_THREADING;OPENSSL_EXTRA;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
@@ -62,7 +62,7 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;WC_NO_HARDEN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
Vendored
+1
-1
Submodule 3rdparty/xxHash updated: 94e5f23e73...35b0373c69
Vendored
+1
-1
Submodule 3rdparty/yaml-cpp/yaml-cpp updated: 6a211f0bc7...0b67821f30
Vendored
+1
@@ -52,6 +52,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\convert.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\depthguard.cpp" />
|
||||
<ClCompile Include="yaml-cpp\src\directives.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emit.cpp">
|
||||
|
||||
+3
@@ -91,5 +91,8 @@
|
||||
<ClCompile Include="yaml-cpp\src\exceptions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\depthguard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+4
-4
@@ -29,11 +29,11 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative sdl2
|
||||
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative sdl2 sndio jack2
|
||||
|
||||
#### Debian & Ubuntu
|
||||
|
||||
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev
|
||||
sudo apt-get install build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git libevdev-dev libsdl2-2.0 libsdl2-dev libjack-dev libsndio-dev
|
||||
|
||||
Ubuntu is usually horrendously out of date, and some packages need to be downloaded by hand. This part is for Qt, GCC, Vulkan, and CMake
|
||||
##### Qt PPA
|
||||
@@ -82,11 +82,11 @@ sudo apt-get install cmake
|
||||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-qtbase-devel qt5-qtbase-private-devel vulkan-devel
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-qtbase-devel qt5-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt5-qtmultimedia-devel qt5-qtsvg-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libqt5gui-private-headers-devel libevdev-devel
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libqt5-qtmultimedia-devel libqt5-qtsvg-devel libQt5Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel
|
||||
|
||||
## Setup the project
|
||||
|
||||
|
||||
+50
-5
@@ -15,8 +15,6 @@ endif()
|
||||
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
|
||||
option(WITH_LLVM "Enable usage of LLVM library" ON)
|
||||
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
|
||||
option(USE_ALSA "ALSA audio backend" ON)
|
||||
option(USE_PULSE "PulseAudio audio backend" ON)
|
||||
option(USE_FAUDIO "FAudio audio backend" ON)
|
||||
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
|
||||
@@ -28,17 +26,64 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/buildfiles/cmake")
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# Currently cmake config scripts are single-config generator only.
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(IS_MULTI_CONFIG)
|
||||
message(FATAL_ERROR "Multi-config generators are not supported.")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
elseif(NOT MSVC)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT MSVC)
|
||||
add_definitions(-D_DEBUG)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
option(USE_MSVC_STATIC_CRT "Use static MSVC C runtime" OFF)
|
||||
|
||||
# TODO(cjj19970505@live.cno)
|
||||
# DiscordRPC binary in 3rdparty is compiled /MT
|
||||
# So theoretically we should enable DiscordRPC in Release and static CRT build
|
||||
# since we might encounter some rumtime issues when more than one CRT version are presented.
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
# Add other DiscordRPC binaries(compiled with /MTd, /MD, /MDd) or compile it from source may address this issue.
|
||||
if(NOT(CMAKE_BUILD_TYPE MATCHES "Release" AND USE_MSVC_STATIC_CRT))
|
||||
set(USE_DISCORD_RPC OFF CACHE BOOL "Discord RPC is only available in Release and static CRT build." FORCE)
|
||||
endif()
|
||||
|
||||
if(USE_MSVC_STATIC_CRT)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
else()
|
||||
# though doc ( https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html )
|
||||
# says if that property is not set then CMake uses the default value MultiThreaded$<$<CONFIG:Debug>:Debug>DLL
|
||||
# to select a MSVC runtime library.
|
||||
# But yaml-cpp set /MT(d) if CMAKE_MSVC_RUNTIME_LIBRARY is undefined
|
||||
# So we have to define it explicitly
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
endif()
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# offical QT uses dynamic CRT.
|
||||
# When building our lib with static CRT and debug build type
|
||||
# and linking with Qt with dynmaic CRT and debug build,
|
||||
# error is encountered in runtime (which is expected).
|
||||
# But building our lib with static CRT and release build type,
|
||||
# and linking with Qt with dynamic CRT and release build seems to be working,
|
||||
# which is the same config with VS solution.
|
||||
# (though technically it might still have some hidden errors).
|
||||
# So we allow static CRT in both relase and debug build, but prompt warning in debug build.
|
||||
# For more info:
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
|
||||
if(USE_MSVC_STATIC_CRT AND CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
message(AUTHOR_WARNING "Debug build currently can not work with static CRT.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
|
||||
@@ -3,7 +3,7 @@ RPCS3
|
||||
|
||||
[](https://dev.azure.com/nekotekina/nekotekina/_build?definitionId=4&branchName=master)
|
||||
[&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
|
||||
[](https://discord.me/rpcs3)
|
||||
[](https://discord.me/rpcs3)
|
||||
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
|
||||
|
||||
|
||||
+30
-17
@@ -19,8 +19,8 @@ namespace cfg
|
||||
}
|
||||
}
|
||||
|
||||
_base::_base(type _type, node* owner, const std::string& name, bool dynamic)
|
||||
: m_type(_type), m_dynamic(dynamic), m_name(name)
|
||||
_base::_base(type _type, node* owner, std::string name, bool dynamic)
|
||||
: m_type(_type), m_dynamic(dynamic), m_name(std::move(name))
|
||||
{
|
||||
for (const auto& node : owner->m_nodes)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace cfg
|
||||
owner->m_nodes.emplace_back(this);
|
||||
}
|
||||
|
||||
bool _base::from_string(const std::string&, bool)
|
||||
bool _base::from_string(std::string_view, bool)
|
||||
{
|
||||
cfg_log.fatal("cfg::_base::from_string() purecall");
|
||||
return false;
|
||||
@@ -58,7 +58,7 @@ 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, const std::string& value, s64 min, s64 max)
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
{
|
||||
s64 result;
|
||||
const char* start = &value.front();
|
||||
@@ -104,7 +104,7 @@ 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, const std::string& value, u64 min, u64 max)
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
{
|
||||
u64 result;
|
||||
const char* start = &value.front();
|
||||
@@ -136,7 +136,7 @@ bool try_to_uint64(u64* out, const std::string& value, u64 min, u64 max)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value)
|
||||
{
|
||||
u64 max = umax;
|
||||
|
||||
@@ -320,7 +320,7 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> values;
|
||||
map_of_type<std::string> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
@@ -339,7 +339,7 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
return; // ???
|
||||
}
|
||||
|
||||
std::map<std::string, logs::level> values;
|
||||
map_of_type<logs::level> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
@@ -377,9 +377,9 @@ std::string cfg::node::to_string() const
|
||||
return {out.c_str(), out.size()};
|
||||
}
|
||||
|
||||
bool cfg::node::from_string(const std::string& value, bool dynamic)
|
||||
bool cfg::node::from_string(std::string_view value, bool dynamic)
|
||||
{
|
||||
auto [result, error] = yaml_load(value);
|
||||
auto [result, error] = yaml_load(std::string(value));
|
||||
|
||||
if (error.empty())
|
||||
{
|
||||
@@ -387,7 +387,7 @@ bool cfg::node::from_string(const std::string& value, bool dynamic)
|
||||
return true;
|
||||
}
|
||||
|
||||
cfg_log.fatal("Failed to load node: %s", error);
|
||||
cfg_log.error("Failed to load node: %s", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -414,27 +414,40 @@ void cfg::set_entry::from_default()
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
std::string cfg::map_entry::get_value(const std::string& key)
|
||||
std::string cfg::map_entry::get_value(std::string_view key)
|
||||
{
|
||||
return m_map.contains(key) ? m_map.at(key) : "";
|
||||
if (auto it = m_map.find(key); it != m_map.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void cfg::map_entry::set_value(const std::string& key, const std::string& value)
|
||||
void cfg::map_entry::set_value(std::string key, std::string value)
|
||||
{
|
||||
m_map[key] = value;
|
||||
m_map[std::move(key)] = std::move(value);
|
||||
}
|
||||
|
||||
void cfg::map_entry::set_map(std::map<std::string, std::string>&& map)
|
||||
void cfg::map_entry::set_map(map_of_type<std::string>&& map)
|
||||
{
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
void cfg::map_entry::erase(std::string_view key)
|
||||
{
|
||||
if (auto it = m_map.find(key); it != m_map.end())
|
||||
{
|
||||
m_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::map_entry::from_default()
|
||||
{
|
||||
set_map({});
|
||||
}
|
||||
|
||||
void cfg::log_entry::set_map(std::map<std::string, logs::level>&& map)
|
||||
void cfg::log_entry::set_map(map_of_type<logs::level>&& map)
|
||||
{
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
+27
-22
@@ -21,7 +21,7 @@ namespace cfg
|
||||
std::vector<std::string> make_uint_range(u64 min, u64 max);
|
||||
|
||||
// Internal hack
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string&);
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view);
|
||||
|
||||
// Internal hack
|
||||
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
|
||||
@@ -53,7 +53,7 @@ namespace cfg
|
||||
_base(type _type);
|
||||
|
||||
// Owned entry constructor
|
||||
_base(type _type, class node* owner, const std::string& name, bool dynamic);
|
||||
_base(type _type, class node* owner, std::string name, bool dynamic);
|
||||
|
||||
public:
|
||||
_base(const _base&) = delete;
|
||||
@@ -80,7 +80,7 @@ namespace cfg
|
||||
}
|
||||
|
||||
// Try to convert from string (optional)
|
||||
virtual bool from_string(const std::string&, bool /*dynamic*/ = false);
|
||||
virtual bool from_string(std::string_view, bool /*dynamic*/ = false);
|
||||
|
||||
// Get string list (optional)
|
||||
virtual std::vector<std::string> to_list() const
|
||||
@@ -107,8 +107,8 @@ namespace cfg
|
||||
}
|
||||
|
||||
// Registered node constructor
|
||||
node(node* owner, const std::string& name, bool dynamic = true)
|
||||
: _base(type::node, owner, name, dynamic)
|
||||
node(node* owner, std::string name, bool dynamic = true)
|
||||
: _base(type::node, owner, std::move(name), dynamic)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace cfg
|
||||
std::string to_string() const override;
|
||||
|
||||
// Deserialize node
|
||||
bool from_string(const std::string& value, bool dynamic = false) override;
|
||||
bool from_string(std::string_view value, bool dynamic = false) override;
|
||||
|
||||
// Set default values
|
||||
void from_default() override;
|
||||
@@ -135,8 +135,8 @@ namespace cfg
|
||||
public:
|
||||
bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false, bool dynamic = false)
|
||||
: _base(type::_bool, owner, name, dynamic)
|
||||
_bool(node* owner, std::string name, bool def = false, bool dynamic = false)
|
||||
: _base(type::_bool, owner, std::move(name), dynamic)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
@@ -159,7 +159,7 @@ namespace cfg
|
||||
return m_value ? "true" : "false";
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
if (value == "false")
|
||||
m_value = false;
|
||||
@@ -220,7 +220,7 @@ namespace cfg
|
||||
return result; // TODO: ???
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
u64 result;
|
||||
|
||||
@@ -285,7 +285,7 @@ namespace cfg
|
||||
return std::to_string(m_value);
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
s64 result;
|
||||
if (try_to_int64(&result, value, Min, Max))
|
||||
@@ -359,7 +359,7 @@ namespace cfg
|
||||
return std::to_string(m_value);
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
u64 result;
|
||||
if (try_to_uint64(&result, value, Min, Max))
|
||||
@@ -430,9 +430,9 @@ namespace cfg
|
||||
return *m_value.load().get();
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
m_value = value;
|
||||
m_value = std::string(value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -474,9 +474,12 @@ namespace cfg
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using map_of_type = std::map<std::string, T, std::less<>>;
|
||||
|
||||
class map_entry final : public _base
|
||||
{
|
||||
std::map<std::string, std::string> m_map{};
|
||||
map_of_type<std::string> m_map{};
|
||||
|
||||
public:
|
||||
map_entry(node* owner, const std::string& name)
|
||||
@@ -484,22 +487,24 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string>& get_map() const
|
||||
const map_of_type<std::string>& get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
std::string get_value(const std::string& key);
|
||||
std::string get_value(std::string_view key);
|
||||
|
||||
void set_value(const std::string& key, const std::string& value);
|
||||
void set_map(std::map<std::string, std::string>&& map);
|
||||
void set_value(std::string key, std::string value);
|
||||
void set_map(map_of_type<std::string>&& map);
|
||||
|
||||
void erase(std::string_view key);
|
||||
|
||||
void from_default() override;
|
||||
};
|
||||
|
||||
class log_entry final : public _base
|
||||
{
|
||||
std::map<std::string, logs::level> m_map{};
|
||||
map_of_type<logs::level> m_map{};
|
||||
|
||||
public:
|
||||
log_entry(node* owner, const std::string& name)
|
||||
@@ -507,12 +512,12 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::map<std::string, logs::level>& get_map() const
|
||||
const map_of_type<logs::level>& get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
void set_map(std::map<std::string, logs::level>&& map);
|
||||
void set_map(map_of_type<logs::level>&& map);
|
||||
|
||||
void from_default() override;
|
||||
};
|
||||
|
||||
+31
-6
@@ -9,6 +9,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
#include "util/coro.hpp"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
@@ -383,12 +384,12 @@ shared_ptr<fs::device_base> fs::set_virtual_device(const std::string& name, shar
|
||||
return get_device_manager().set_device(name, std::move(device));
|
||||
}
|
||||
|
||||
std::string fs::get_parent_dir(const std::string& path)
|
||||
std::string fs::get_parent_dir(const std::string& path, u32 levels)
|
||||
{
|
||||
std::string_view result = path;
|
||||
|
||||
// Number of path components to remove
|
||||
usz to_remove = 1;
|
||||
usz to_remove = levels;
|
||||
|
||||
while (to_remove--)
|
||||
{
|
||||
@@ -683,9 +684,9 @@ bool fs::create_path(const std::string& path)
|
||||
|
||||
#ifdef _WIN32
|
||||
// Workaround: don't call is_dir with naked drive letter
|
||||
if (!parent.empty() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
|
||||
if (parent.size() < path.size() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
|
||||
#else
|
||||
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
|
||||
if (parent.size() < path.size() && !is_dir(parent) && !create_path(parent))
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
@@ -1182,7 +1183,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
for (const char* data = static_cast<const char*>(buffer); count;)
|
||||
{
|
||||
const DWORD size = static_cast<DWORD>(std::min<u64>(count, DWORD{umax} & -4096));
|
||||
const DWORD size = static_cast<DWORD>(std::min<u64>(count, DWORD{umax} & -4096));
|
||||
|
||||
DWORD nwritten = 0;
|
||||
ensure(WriteFile(m_handle, data, size, &nwritten, nullptr)); // "file::write"
|
||||
@@ -1658,7 +1659,7 @@ bool fs::dir::open(const std::string& path)
|
||||
|
||||
bool fs::file::strict_read_check(u64 _size, u64 type_size) const
|
||||
{
|
||||
if (usz pos0 = pos(), size0 = size(); pos0 >= size0 || (size0 - pos0) / type_size < _size)
|
||||
if (usz pos0 = pos(), size0 = size(); (pos0 >= size0 ? 0 : (size0 - pos0)) / type_size < _size)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return false;
|
||||
@@ -2039,6 +2040,30 @@ bool fs::pending_file::commit(bool overwrite)
|
||||
return false;
|
||||
}
|
||||
|
||||
stx::generator<fs::dir_entry&> fs::list_dir_recursively(std::string path)
|
||||
{
|
||||
for (auto& entry : fs::dir(path))
|
||||
{
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string new_path = path_append(path, entry.name);
|
||||
|
||||
if (entry.is_directory)
|
||||
{
|
||||
for (auto& nested : fs::list_dir_recursively(new_path))
|
||||
{
|
||||
co_yield nested;
|
||||
}
|
||||
}
|
||||
|
||||
entry.name = std::move(new_path);
|
||||
co_yield entry;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
||||
+45
-56
@@ -45,7 +45,8 @@ namespace fs
|
||||
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
|
||||
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
constexpr auto write_new = write + create + excl;
|
||||
constexpr auto rewrite = write + create + trunc;
|
||||
|
||||
// File seek mode
|
||||
enum class seek_mode : u32
|
||||
@@ -156,7 +157,7 @@ namespace fs
|
||||
shared_ptr<device_base> set_virtual_device(const std::string& name, shared_ptr<device_base> device);
|
||||
|
||||
// Try to get parent directory (returns empty string on failure)
|
||||
std::string get_parent_dir(const std::string& path);
|
||||
std::string get_parent_dir(const std::string& path, u32 levels = 1);
|
||||
|
||||
// Get file information
|
||||
bool stat(const std::string& path, stat_t& info);
|
||||
@@ -204,8 +205,6 @@ namespace fs
|
||||
{
|
||||
std::unique_ptr<file_base> m_file{};
|
||||
|
||||
bool strict_read_check(u64 size, u64 type_size) const;
|
||||
|
||||
public:
|
||||
// Default constructor
|
||||
file() = default;
|
||||
@@ -280,6 +279,9 @@ namespace fs
|
||||
return m_file->sync();
|
||||
}
|
||||
|
||||
// Check if the handle is capable of reading (size * type_size) of bytes at the time of calling
|
||||
bool strict_read_check(u64 size, u64 type_size = 1) const;
|
||||
|
||||
// Read the data from the file and return the amount of data written in buffer
|
||||
u64 read(void* buffer, u64 count,
|
||||
u32 line = __builtin_LINE(),
|
||||
@@ -336,8 +338,8 @@ namespace fs
|
||||
}
|
||||
|
||||
// Write std::basic_string unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const std::basic_string<T>& str,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
const file& write(const std::basic_string<T>& str,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
@@ -348,8 +350,8 @@ namespace fs
|
||||
}
|
||||
|
||||
// Write POD unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const T& data,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
const file& write(const T& data,
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
@@ -361,8 +363,8 @@ namespace fs
|
||||
}
|
||||
|
||||
// Write POD std::vector unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const std::vector<T>& vec,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
const file& write(const std::vector<T>& vec,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
@@ -372,84 +374,69 @@ namespace fs
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Read std::basic_string, size must be set by resize() method
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
return read(&str[0], str.size() * sizeof(T), line, col, file, func) == str.size() * sizeof(T);
|
||||
}
|
||||
|
||||
// Read std::basic_string
|
||||
template <bool IsStrict = false, typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str, usz _size,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
bool read(std::basic_string<T>& str, usz _size = umax,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
if (!_size) return true;
|
||||
|
||||
if constexpr (IsStrict)
|
||||
if (_size != umax)
|
||||
{
|
||||
// If _size arg is too high std::bad_alloc may happen in resize and then we cannot error check
|
||||
if (!strict_read_check(_size, sizeof(T))) return false;
|
||||
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
str.resize(_size);
|
||||
}
|
||||
|
||||
str.resize(_size);
|
||||
return read(str.data(), sizeof(T) * _size, line, col, file, func) == sizeof(T) * _size;
|
||||
return read(str.data(), sizeof(T) * str.size(), line, col, file, func) == sizeof(T) * str.size();
|
||||
}
|
||||
|
||||
// Read POD, sizeof(T) is used
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(T& data,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
bool read(T& data,
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
return read(&data, sizeof(T), line, col, file, func) == sizeof(T);
|
||||
}
|
||||
|
||||
// Read POD std::vector, size must be set by resize() method
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
|
||||
return read(std::addressof(data), sizeof(T), line, col, file, func) == sizeof(T);
|
||||
}
|
||||
|
||||
// Read POD std::vector
|
||||
template <bool IsStrict = false, typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec, usz _size,
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
bool read(std::vector<T>& vec, usz _size = umax,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
if (!_size) return true;
|
||||
|
||||
if constexpr (IsStrict)
|
||||
if (_size != umax)
|
||||
{
|
||||
if (!strict_read_check(_size, sizeof(T))) return false;
|
||||
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
|
||||
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vec.resize(_size);
|
||||
}
|
||||
|
||||
vec.resize(_size);
|
||||
return read(vec.data(), sizeof(T) * _size, line, col, file, func) == sizeof(T) * _size;
|
||||
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
// Read POD (experimental)
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T> read(
|
||||
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
T read(
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
@@ -471,13 +458,13 @@ namespace fs
|
||||
{
|
||||
std::basic_string<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
if (seek(0), !read(result, file, func, line, col)) xfail({line, col, file, func});
|
||||
if (seek(0), !read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read full file to std::vector
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, std::vector<T>> to_vector(
|
||||
template<typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
|
||||
std::vector<T> to_vector(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
@@ -485,7 +472,7 @@ namespace fs
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
if (seek(0), !read(result, file, func, line, col)) xfail({line, col, file, func});
|
||||
if (seek(0), !read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -817,4 +804,6 @@ namespace fs
|
||||
}
|
||||
|
||||
file make_gather(std::vector<file>);
|
||||
|
||||
stx::generator<dir_entry&> list_dir_recursively(std::string path);
|
||||
}
|
||||
|
||||
+123
-78
@@ -16,6 +16,15 @@
|
||||
|
||||
LOG_CHANNEL(jit_log, "JIT");
|
||||
|
||||
void jit_announce(uptr func, usz size, std::string_view name)
|
||||
{
|
||||
#ifdef __linux__
|
||||
static const fs::file s_map(fmt::format("/tmp/perf-%d.map", getpid()), fs::rewrite + fs::append);
|
||||
|
||||
s_map.write(fmt::format("%x %x %s\n", func, size, name));
|
||||
#endif
|
||||
}
|
||||
|
||||
static u8* get_jit_memory()
|
||||
{
|
||||
// Reserve 2G memory (magic static)
|
||||
@@ -104,8 +113,32 @@ static u8* add_jit_memory(usz size, uint align)
|
||||
return pointer + pos;
|
||||
}
|
||||
|
||||
const asmjit::Environment& jit_runtime_base::environment() const noexcept
|
||||
{
|
||||
static const asmjit::Environment g_env = asmjit::Environment::host();
|
||||
|
||||
return g_env;
|
||||
}
|
||||
|
||||
void* jit_runtime_base::_add(asmjit::CodeHolder* code) noexcept
|
||||
{
|
||||
ensure(!code->flatten());
|
||||
ensure(!code->resolveUnresolvedLinks());
|
||||
usz codeSize = ensure(code->codeSize());
|
||||
auto p = ensure(this->_alloc(codeSize, 64));
|
||||
ensure(!code->relocateToBase(uptr(p)));
|
||||
|
||||
asmjit::VirtMem::ProtectJitReadWriteScope rwScope(p, codeSize);
|
||||
|
||||
for (asmjit::Section* section : code->_sections)
|
||||
{
|
||||
std::memcpy(p + section->offset(), section->data(), section->bufferSize());
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
jit_runtime::jit_runtime()
|
||||
: HostRuntime()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -113,38 +146,9 @@ jit_runtime::~jit_runtime()
|
||||
{
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
uchar* jit_runtime::_alloc(usz size, usz align) noexcept
|
||||
{
|
||||
usz codeSize = code->getCodeSize();
|
||||
if (!codeSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
|
||||
void* p = jit_runtime::alloc(codeSize, 16);
|
||||
if (!p) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
usz relocSize = code->relocate(p);
|
||||
if (!relocSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_release(void*) noexcept
|
||||
{
|
||||
return asmjit::kErrorOk;
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* jit_runtime::alloc(usz size, uint align, bool exec) noexcept
|
||||
@@ -191,12 +195,12 @@ void jit_runtime::finalize() noexcept
|
||||
std::memcpy(alloc(s_data_init.size(), 1, false), s_data_init.data(), s_data_init.size());
|
||||
}
|
||||
|
||||
asmjit::Runtime& asmjit::get_global_runtime()
|
||||
jit_runtime_base& asmjit::get_global_runtime()
|
||||
{
|
||||
// 16 MiB for internal needs
|
||||
static constexpr u64 size = 1024 * 1024 * 16;
|
||||
|
||||
struct custom_runtime final : asmjit::HostRuntime
|
||||
struct custom_runtime final : jit_runtime_base
|
||||
{
|
||||
custom_runtime() noexcept
|
||||
{
|
||||
@@ -205,7 +209,7 @@ asmjit::Runtime& asmjit::get_global_runtime()
|
||||
{
|
||||
if (auto ptr = utils::memory_reserve(size, reinterpret_cast<void*>(addr)))
|
||||
{
|
||||
m_pos.raw() = static_cast<std::byte*>(ptr);
|
||||
m_pos.raw() = static_cast<uchar*>(ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -217,50 +221,26 @@ asmjit::Runtime& asmjit::get_global_runtime()
|
||||
utils::memory_commit(m_pos, size, utils::protection::wx);
|
||||
}
|
||||
|
||||
custom_runtime(const custom_runtime&) = delete;
|
||||
|
||||
custom_runtime& operator=(const custom_runtime&) = delete;
|
||||
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override
|
||||
uchar* _alloc(usz size, usz align) noexcept override
|
||||
{
|
||||
usz codeSize = code->getCodeSize();
|
||||
if (!codeSize) [[unlikely]]
|
||||
return m_pos.atomic_op([&](uchar*& pos) -> uchar*
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
const auto r = reinterpret_cast<uchar*>(utils::align(uptr(pos), align));
|
||||
|
||||
void* p = m_pos.fetch_add(utils::align(codeSize, 4096));
|
||||
if (!p || m_pos > m_max) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
jit_log.fatal("Out of memory (static asmjit)");
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
if (r >= pos && r + size > pos && r + size <= m_max)
|
||||
{
|
||||
pos = r + size;
|
||||
return r;
|
||||
}
|
||||
|
||||
usz relocSize = code->relocate(p);
|
||||
if (!relocSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
utils::memory_protect(p, utils::align(codeSize, 4096), utils::protection::rx);
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
asmjit::Error _release(void*) noexcept override
|
||||
{
|
||||
return asmjit::kErrorOk;
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_t<std::byte*> m_pos{};
|
||||
atomic_t<uchar*> m_pos{};
|
||||
|
||||
std::byte* m_max{};
|
||||
uchar* m_max{};
|
||||
};
|
||||
|
||||
// Magic static
|
||||
@@ -268,6 +248,24 @@ asmjit::Runtime& asmjit::get_global_runtime()
|
||||
return g_rt;
|
||||
}
|
||||
|
||||
asmjit::inline_runtime::inline_runtime(uchar* data, usz size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
uchar* asmjit::inline_runtime::_alloc(usz size, usz align) noexcept
|
||||
{
|
||||
ensure(align <= 4096);
|
||||
|
||||
return size <= m_size ? m_data : nullptr;
|
||||
}
|
||||
|
||||
asmjit::inline_runtime::~inline_runtime()
|
||||
{
|
||||
utils::memory_protect(m_data, m_size, utils::protection::rx);
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -293,6 +291,9 @@ asmjit::Runtime& asmjit::get_global_runtime()
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Object/SymbolSize.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
@@ -348,19 +349,19 @@ static u64 make_null_function(const std::string& name)
|
||||
using namespace asmjit;
|
||||
|
||||
// Build a "null" function that contains its name
|
||||
const auto func = build_function_asm<void (*)()>([&](X86Assembler& c, auto& args)
|
||||
const auto func = build_function_asm<void (*)()>("NULL", [&](x86::Assembler& c, auto& args)
|
||||
{
|
||||
Label data = c.newLabel();
|
||||
c.lea(args[0], x86::qword_ptr(data, 0));
|
||||
c.jmp(imm_ptr(&null));
|
||||
c.align(kAlignCode, 16);
|
||||
c.jmp(Imm(&null));
|
||||
c.align(AlignMode::kCode, 16);
|
||||
c.bind(data);
|
||||
|
||||
// Copy function name bytes
|
||||
for (char ch : name)
|
||||
c.db(ch);
|
||||
c.db(0);
|
||||
c.align(kAlignData, 16);
|
||||
c.align(AlignMode::kData, 16);
|
||||
});
|
||||
|
||||
func_ptr = reinterpret_cast<u64>(func);
|
||||
@@ -368,6 +369,42 @@ static u64 make_null_function(const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
struct JITAnnouncer : llvm::JITEventListener
|
||||
{
|
||||
void notifyObjectLoaded(u64, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& info) override
|
||||
{
|
||||
using namespace llvm;
|
||||
|
||||
object::OwningBinary<object::ObjectFile> debug_obj_ = info.getObjectForDebug(obj);
|
||||
if (!debug_obj_.getBinary())
|
||||
{
|
||||
#ifdef __linux__
|
||||
jit_log.error("LLVM: Failed to announce JIT events (no debug object)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
const object::ObjectFile& debug_obj = *debug_obj_.getBinary();
|
||||
|
||||
for (const auto& [sym, size] : computeSymbolSizes(debug_obj))
|
||||
{
|
||||
Expected<object::SymbolRef::Type> type_ = sym.getType();
|
||||
if (!type_ || *type_ != object::SymbolRef::ST_Function)
|
||||
continue;
|
||||
|
||||
Expected<StringRef> name = sym.getName();
|
||||
if (!name)
|
||||
continue;
|
||||
|
||||
Expected<u64> addr = sym.getAddress();
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
jit_announce(*addr, size, {name->data(), name->size()});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
@@ -391,7 +428,8 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
|
||||
~MemoryManager1() override
|
||||
{
|
||||
utils::memory_release(ptr, c_max_size * 2);
|
||||
// Hack: don't release to prevent reuse of address space, see jit_announce
|
||||
utils::memory_decommit(ptr, c_max_size * 2);
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
@@ -725,11 +763,12 @@ std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_cpu(cpu(_cpu))
|
||||
: m_context(new llvm::LLVMContext)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
auto null_mod = std::make_unique<llvm::Module> ("null_", m_context);
|
||||
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
|
||||
|
||||
if (_link.empty())
|
||||
{
|
||||
@@ -773,6 +812,12 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
||||
}
|
||||
}
|
||||
|
||||
if (!_link.empty() || !(flags & 0x1))
|
||||
{
|
||||
m_engine->RegisterJITEventListener(llvm::JITEventListener::createIntelJITEventListener());
|
||||
m_engine->RegisterJITEventListener(new JITAnnouncer);
|
||||
}
|
||||
|
||||
if (!m_engine)
|
||||
{
|
||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
|
||||
+145
-55
@@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
// Include asmjit with warnings ignored
|
||||
#define ASMJIT_EMBED
|
||||
#define ASMJIT_DEBUG
|
||||
#define ASMJIT_STATIC
|
||||
#define ASMJIT_BUILD_DEBUG
|
||||
#undef Bool
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
@@ -27,6 +31,17 @@
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
void jit_announce(uptr func, usz size, std::string_view name);
|
||||
|
||||
void jit_announce(auto* func, usz size, std::string_view name)
|
||||
{
|
||||
jit_announce(uptr(func), size, name);
|
||||
}
|
||||
|
||||
enum class jit_class
|
||||
{
|
||||
@@ -36,17 +51,27 @@ enum class jit_class
|
||||
spu_data,
|
||||
};
|
||||
|
||||
struct jit_runtime_base
|
||||
{
|
||||
jit_runtime_base() noexcept = default;
|
||||
virtual ~jit_runtime_base() = default;
|
||||
|
||||
jit_runtime_base(const jit_runtime_base&) = delete;
|
||||
jit_runtime_base& operator=(const jit_runtime_base&) = delete;
|
||||
|
||||
const asmjit::Environment& environment() const noexcept;
|
||||
void* _add(asmjit::CodeHolder* code) noexcept;
|
||||
virtual uchar* _alloc(usz size, usz align) noexcept = 0;
|
||||
};
|
||||
|
||||
// ASMJIT runtime for emitting code in a single 2G region
|
||||
struct jit_runtime final : asmjit::HostRuntime
|
||||
struct jit_runtime final : jit_runtime_base
|
||||
{
|
||||
jit_runtime();
|
||||
~jit_runtime() override;
|
||||
|
||||
// Allocate executable memory
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override;
|
||||
|
||||
// Do nothing (deallocation is delayed)
|
||||
asmjit::Error _release(void* p) noexcept override;
|
||||
uchar* _alloc(usz size, usz align) noexcept override;
|
||||
|
||||
// Allocate memory
|
||||
static u8* alloc(usz size, uint align, bool exec = true) noexcept;
|
||||
@@ -61,11 +86,25 @@ struct jit_runtime final : asmjit::HostRuntime
|
||||
namespace asmjit
|
||||
{
|
||||
// Should only be used to build global functions
|
||||
asmjit::Runtime& get_global_runtime();
|
||||
jit_runtime_base& get_global_runtime();
|
||||
|
||||
// Don't use directly
|
||||
class inline_runtime : public jit_runtime_base
|
||||
{
|
||||
uchar* m_data;
|
||||
usz m_size;
|
||||
|
||||
public:
|
||||
inline_runtime(uchar* data, usz size);
|
||||
|
||||
~inline_runtime();
|
||||
|
||||
uchar* _alloc(usz size, usz align) noexcept override;
|
||||
};
|
||||
|
||||
// Emit xbegin and adjacent loop, return label at xbegin (don't use xabort please)
|
||||
template <typename F>
|
||||
[[nodiscard]] inline asmjit::Label build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, F func)
|
||||
[[nodiscard]] inline asmjit::Label build_transaction_enter(asmjit::x86::Assembler& c, asmjit::Label fallback, F func)
|
||||
{
|
||||
Label fall = c.newLabel();
|
||||
Label begin = c.newLabel();
|
||||
@@ -80,7 +119,7 @@ namespace asmjit
|
||||
func();
|
||||
|
||||
// Other bad statuses are ignored regardless of repeat flag (TODO)
|
||||
c.align(kAlignCode, 16);
|
||||
c.align(AlignMode::kCode, 16);
|
||||
c.bind(begin);
|
||||
return fall;
|
||||
|
||||
@@ -88,7 +127,7 @@ namespace asmjit
|
||||
}
|
||||
|
||||
// Helper to spill RDX (EDX) register for RDTSC
|
||||
inline void build_swap_rdx_with(asmjit::X86Assembler& c, std::array<X86Gp, 4>& args, const asmjit::X86Gp& with)
|
||||
inline void build_swap_rdx_with(asmjit::x86::Assembler& c, std::array<x86::Gp, 4>& args, const asmjit::x86::Gp& with)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
c.xchg(args[1], with);
|
||||
@@ -100,7 +139,7 @@ namespace asmjit
|
||||
}
|
||||
|
||||
// Get full RDTSC value into chosen register (clobbers rax/rdx or saves only rax with other target)
|
||||
inline void build_get_tsc(asmjit::X86Assembler& c, const asmjit::X86Gp& to = asmjit::x86::rax)
|
||||
inline void build_get_tsc(asmjit::x86::Assembler& c, const asmjit::x86::Gp& to = asmjit::x86::rax)
|
||||
{
|
||||
if (&to != &x86::rax && &to != &x86::rdx)
|
||||
{
|
||||
@@ -127,21 +166,22 @@ namespace asmjit
|
||||
c.or_(to.r64(), x86::rdx);
|
||||
}
|
||||
}
|
||||
|
||||
using imm_ptr = Imm;
|
||||
}
|
||||
|
||||
// Build runtime function with asmjit::X86Assembler
|
||||
template <typename FT, typename F>
|
||||
inline FT build_function_asm(F&& builder)
|
||||
inline FT build_function_asm(std::string_view name, F&& builder)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
auto& rt = get_global_runtime();
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.getCodeInfo());
|
||||
code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign;
|
||||
code.init(rt.environment());
|
||||
|
||||
std::array<X86Gp, 4> args;
|
||||
std::array<x86::Gp, 4> args;
|
||||
#ifdef _WIN32
|
||||
args[0] = x86::rcx;
|
||||
args[1] = x86::rdx;
|
||||
@@ -154,59 +194,109 @@ inline FT build_function_asm(F&& builder)
|
||||
args[3] = x86::rcx;
|
||||
#endif
|
||||
|
||||
X86Assembler compiler(&code);
|
||||
x86::Assembler compiler(&code);
|
||||
compiler.addEncodingOptions(EncodingOptions::kOptimizedAlign);
|
||||
builder(std::ref(compiler), args);
|
||||
ensure(compiler.getLastError() == 0);
|
||||
const auto result = rt._add(&code);
|
||||
jit_announce(result, code.codeSize(), name);
|
||||
return reinterpret_cast<FT>(uptr(result));
|
||||
}
|
||||
|
||||
FT result;
|
||||
#ifdef __APPLE__
|
||||
template <typename FT, usz = 4096>
|
||||
class built_function
|
||||
{
|
||||
FT m_func;
|
||||
|
||||
if (rt.add(&result, &code))
|
||||
public:
|
||||
built_function(const built_function&) = delete;
|
||||
|
||||
built_function& operator=(const built_function&) = delete;
|
||||
|
||||
template <typename F>
|
||||
built_function(std::string_view name, F&& builder)
|
||||
: m_func(ensure(build_function_asm<FT>(name, std::forward<F>(builder))))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
operator FT() const noexcept
|
||||
{
|
||||
return m_func;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const noexcept
|
||||
{
|
||||
return m_func(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
#else
|
||||
template <typename FT, usz Size = 4096>
|
||||
class built_function
|
||||
{
|
||||
alignas(4096) uchar m_data[Size];
|
||||
|
||||
public:
|
||||
built_function(const built_function&) = delete;
|
||||
|
||||
built_function& operator=(const built_function&) = delete;
|
||||
|
||||
template <typename F>
|
||||
built_function(std::string_view name, F&& builder)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
inline_runtime rt(m_data, Size);
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.environment());
|
||||
|
||||
std::array<x86::Gp, 4> args;
|
||||
#ifdef _WIN32
|
||||
args[0] = x86::rcx;
|
||||
args[1] = x86::rdx;
|
||||
args[2] = x86::r8;
|
||||
args[3] = x86::r9;
|
||||
#else
|
||||
args[0] = x86::rdi;
|
||||
args[1] = x86::rsi;
|
||||
args[2] = x86::rdx;
|
||||
args[3] = x86::rcx;
|
||||
#endif
|
||||
|
||||
x86::Assembler compiler(&code);
|
||||
compiler.addEncodingOptions(EncodingOptions::kOptimizedAlign);
|
||||
builder(std::ref(compiler), args);
|
||||
jit_announce(rt._add(&code), code.codeSize(), name);
|
||||
}
|
||||
|
||||
operator FT() const noexcept
|
||||
{
|
||||
return FT(+m_data);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) const noexcept
|
||||
{
|
||||
return FT(+m_data)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-override"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
#endif
|
||||
#endif
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
namespace llvm
|
||||
{
|
||||
class LLVMContext;
|
||||
class ExecutionEngine;
|
||||
class Module;
|
||||
}
|
||||
|
||||
// Temporary compiler interface
|
||||
class jit_compiler final
|
||||
{
|
||||
// Local LLVM context
|
||||
llvm::LLVMContext m_context{};
|
||||
std::unique_ptr<llvm::LLVMContext> m_context{};
|
||||
|
||||
// Execution instance
|
||||
std::unique_ptr<llvm::ExecutionEngine> m_engine{};
|
||||
@@ -221,7 +311,7 @@ public:
|
||||
// Get LLVM context
|
||||
auto& get_context()
|
||||
{
|
||||
return m_context;
|
||||
return *m_context;
|
||||
}
|
||||
|
||||
auto& get_engine() const
|
||||
|
||||
+16
-7
@@ -6,6 +6,8 @@
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string wchar_to_utf8(const wchar_t *src);
|
||||
std::string wchar_path_to_ansi_path(const std::wstring& src);
|
||||
@@ -22,17 +24,17 @@ inline void strcpy_trunc(D& dst, const T& src)
|
||||
}
|
||||
|
||||
// Convert string to signed integer
|
||||
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
|
||||
|
||||
// Convert string to unsigned integer
|
||||
bool try_to_uint64(u64* out, const std::string& value, u64 min, u64 max);
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_all(std::string_view src, std::string_view from, std::string_view to, usz count = -1);
|
||||
|
||||
template <usz list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::string> (&list)[list_size])
|
||||
std::string replace_all(std::string src, const std::pair<std::string_view, std::string> (&list)[list_size])
|
||||
{
|
||||
for (usz pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
@@ -41,11 +43,14 @@ namespace fmt
|
||||
const usz comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (src.substr(pos, comp_length) == list[i].first)
|
||||
{
|
||||
src = (pos ? src.substr(0, pos) + list[i].second : list[i].second) + src.substr(pos + comp_length);
|
||||
src.erase(pos, comp_length);
|
||||
src.insert(pos, list[i].second.data(), list[i].second.length());
|
||||
pos += list[i].second.length() - 1;
|
||||
break;
|
||||
}
|
||||
@@ -56,7 +61,7 @@ namespace fmt
|
||||
}
|
||||
|
||||
template <usz list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>> (&list)[list_size])
|
||||
std::string replace_all(std::string src, const std::pair<std::string_view, std::function<std::string()>> (&list)[list_size])
|
||||
{
|
||||
for (usz pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
@@ -65,12 +70,16 @@ namespace fmt
|
||||
const usz comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (src.substr(pos, comp_length) == list[i].first)
|
||||
{
|
||||
src = (pos ? src.substr(0, pos) + list[i].second() : list[i].second()) + src.substr(pos + comp_length);
|
||||
pos += list[i].second().length() - 1;
|
||||
src.erase(pos, comp_length);
|
||||
auto replacement = list[i].second();
|
||||
src.insert(pos, replacement);
|
||||
pos += replacement.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-18
@@ -1231,20 +1231,16 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
|
||||
bool handled = rsx::g_access_violation_handler(addr, is_writing);
|
||||
|
||||
if (cpu && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
g_tls_fault_rsx++;
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
const u8* const code = reinterpret_cast<u8*>(RIP(context));
|
||||
@@ -1389,11 +1385,6 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
|
||||
if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable))
|
||||
{
|
||||
if (cpu && cpu->test_stopped())
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1580,7 +1571,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
}
|
||||
}
|
||||
|
||||
Emu.Pause();
|
||||
Emu.Pause(true);
|
||||
|
||||
if (!g_tls_access_violation_recovered)
|
||||
{
|
||||
@@ -1591,7 +1582,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
|
||||
// Do not log any further access violations in this case.
|
||||
if (!g_tls_access_violation_recovered)
|
||||
{
|
||||
vm_log.fatal("Access violation %s location 0x%x (%s) [type=u%u]", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory", d_size * 8);
|
||||
vm_log.fatal("Access violation %s location 0x%x (%s) [type=u%u]", is_writing ? "writing" : (cpu && cpu->id_type() == 1 && cpu->get_pc() == addr ? "executing" : "reading"), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory", d_size * 8);
|
||||
}
|
||||
|
||||
while (Emu.IsPaused())
|
||||
@@ -1835,6 +1826,14 @@ const bool s_exception_handler_set = []() -> bool
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (::sigaction(SIGBUS, &sa, NULL) == -1)
|
||||
{
|
||||
std::fprintf(stderr, "sigaction(SIGBUS) failed (%d).\n", errno);
|
||||
std::abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
sa.sa_handler = sigpipe_signaling_handler;
|
||||
if (::sigaction(SIGPIPE, &sa, NULL) == -1)
|
||||
{
|
||||
@@ -2191,7 +2190,7 @@ thread_base::native_entry thread_base::finalize(u64 _self) noexcept
|
||||
|
||||
thread_base::native_entry thread_base::make_trampoline(u64(*entry)(thread_base* _base))
|
||||
{
|
||||
return build_function_asm<native_entry>([&](asmjit::X86Assembler& c, auto& args)
|
||||
return build_function_asm<native_entry>("thread_base_trampoline", [&](asmjit::x86::Assembler& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
@@ -2204,7 +2203,7 @@ thread_base::native_entry thread_base::make_trampoline(u64(*entry)(thread_base*
|
||||
|
||||
// Call finalize, return if zero
|
||||
c.mov(args[0], x86::rax);
|
||||
c.call(imm_ptr<native_entry(*)(u64)>(finalize));
|
||||
c.call(imm_ptr(static_cast<native_entry(*)(u64)>(&finalize)));
|
||||
c.test(x86::rax, x86::rax);
|
||||
c.jz(_ret);
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ private:
|
||||
static void set_name(std::string);
|
||||
|
||||
// Make entry point
|
||||
static native_entry make_trampoline(u64(*)(thread_base*));
|
||||
static native_entry make_trampoline(u64(*entry)(thread_base* _base));
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
|
||||
+260
-61
@@ -9,6 +9,8 @@
|
||||
#include "util/endian.hpp"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
LOG_CHANNEL(patch_log, "PAT");
|
||||
|
||||
template <>
|
||||
@@ -38,6 +40,10 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
case patch_type::invalid: return "invalid";
|
||||
case patch_type::alloc: return "alloc";
|
||||
case patch_type::code_alloc: return "calloc";
|
||||
case patch_type::jump: return "jump";
|
||||
case patch_type::jump_link: return "jumpl";
|
||||
case patch_type::jump_func: return "jumpf";
|
||||
case patch_type::load: return "load";
|
||||
case patch_type::byte: return "byte";
|
||||
case patch_type::le16: return "le16";
|
||||
@@ -61,12 +67,6 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
|
||||
patch_engine::patch_engine()
|
||||
{
|
||||
const std::string patches_path = get_patches_path();
|
||||
|
||||
if (!fs::create_path(patches_path))
|
||||
{
|
||||
patch_log.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
std::string patch_engine::get_patch_config_path()
|
||||
@@ -174,6 +174,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
continue;
|
||||
}
|
||||
|
||||
if (main_key.empty())
|
||||
{
|
||||
append_log_message(log_messages, "Error: Skipping empty key");
|
||||
patch_log.error("Skipping empty key (file: %s)", path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip Anchors
|
||||
if (main_key == patch_key::anchors)
|
||||
{
|
||||
@@ -182,8 +190,8 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
|
||||
// Find or create an entry matching the key/hash in our map
|
||||
auto& container = patches_map[main_key];
|
||||
container.hash = main_key;
|
||||
container.version = version;
|
||||
container.hash = main_key;
|
||||
container.version = version;
|
||||
|
||||
// Go through each patch
|
||||
for (auto patches_entry : pair.second)
|
||||
@@ -221,10 +229,18 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
{
|
||||
const std::string& title = game_node.first.Scalar();
|
||||
|
||||
if (title.empty())
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Empty game title (key: %s, file: %s)", main_key, path));
|
||||
patch_log.error("Empty game title (key: %s, file: %s)", main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (const auto yml_type = game_node.second.Type(); yml_type != YAML::NodeType::Map)
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Skipping %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
|
||||
patch_log.error("Skipping %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
|
||||
append_log_message(log_messages, fmt::format("Error: Skipping game %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
|
||||
patch_log.error("Skipping game %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
@@ -235,7 +251,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
{
|
||||
const std::string& serial = serial_node.first.Scalar();
|
||||
|
||||
if (serial == patch_key::all)
|
||||
if (serial.empty())
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Using empty serial (title: %s, patch: %s, key: %s)", title, description, main_key));
|
||||
patch_log.error("Using empty serial (title: %s, patch: %s, key: %s, file: %s)", title, description, main_key, path);
|
||||
is_valid = false;
|
||||
continue;
|
||||
}
|
||||
else if (serial == patch_key::all)
|
||||
{
|
||||
if (!title_is_all_key)
|
||||
{
|
||||
@@ -361,11 +384,11 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
patch_type patch_engine::get_patch_type(const std::string& text)
|
||||
{
|
||||
u64 type_val = 0;
|
||||
|
||||
if (!node || !node.IsScalar() || !cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, node.Scalar()))
|
||||
if (!cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, text))
|
||||
{
|
||||
return patch_type::invalid;
|
||||
}
|
||||
@@ -373,6 +396,16 @@ patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
return static_cast<patch_type>(type_val);
|
||||
}
|
||||
|
||||
patch_type patch_engine::get_patch_type(YAML::Node node)
|
||||
{
|
||||
if (!node || !node.IsScalar())
|
||||
{
|
||||
return patch_type::invalid;
|
||||
}
|
||||
|
||||
return get_patch_type(node.Scalar());
|
||||
}
|
||||
|
||||
bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifier, const YAML::Node& root, std::stringstream* log_messages)
|
||||
{
|
||||
if (!node || !node.IsSequence())
|
||||
@@ -434,6 +467,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
|
||||
switch (p_data.type)
|
||||
{
|
||||
case patch_type::utf8:
|
||||
case patch_type::jump_func:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -516,10 +550,21 @@ void patch_engine::append_title_patches(const std::string& title_id)
|
||||
}
|
||||
|
||||
void ppu_register_range(u32 addr, u32 size);
|
||||
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link = false, bool with_toc = false, std::string module_name = {});
|
||||
u32 ppu_generate_id(std::string_view name);
|
||||
|
||||
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
|
||||
void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
|
||||
{
|
||||
std::basic_string<u32> applied;
|
||||
if (ptr && ptr->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(0, true, &ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns old 'applied' size
|
||||
static usz apply_modification(std::basic_string<u32>& applied, const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
|
||||
{
|
||||
const usz old_applied_size = applied.size();
|
||||
|
||||
for (const auto& p : patch.data_list)
|
||||
{
|
||||
@@ -531,26 +576,24 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
|
||||
|
||||
// Allocate map if needed, if allocated flags will indicate that bit 62 is set (unique identifier)
|
||||
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | vm::preallocated | vm::bf0_0x2 | (1ull << 62));
|
||||
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | (1ull << 62));
|
||||
|
||||
u64 flags = 0;
|
||||
u64 flags = vm::alloc_unwritable;
|
||||
|
||||
switch (p.offset % patch_engine::mem_protection::mask)
|
||||
{
|
||||
case patch_engine::mem_protection::wx: flags |= vm::page_writable + vm::page_readable + vm::page_executable; break;
|
||||
case patch_engine::mem_protection::ro: flags |= vm::page_readable; break;
|
||||
case patch_engine::mem_protection::rx: flags |= vm::page_writable + vm::page_executable; break;
|
||||
case patch_engine::mem_protection::rw: flags |= vm::page_writable + vm::page_readable; break;
|
||||
case patch_engine::mem_protection::wx: flags = vm::alloc_executable; break;
|
||||
case patch_engine::mem_protection::ro: break;
|
||||
case patch_engine::mem_protection::rx: flags |= vm::alloc_executable; break;
|
||||
case patch_engine::mem_protection::rw: flags &= ~vm::alloc_unwritable; break;
|
||||
default: ensure(false);
|
||||
}
|
||||
|
||||
if (alloc_map)
|
||||
{
|
||||
if (alloc_map->falloc(alloc_at, alloc_size))
|
||||
if ((p.alloc_addr = alloc_map->falloc(alloc_at, alloc_size, nullptr, flags)))
|
||||
{
|
||||
vm::page_protect(alloc_at, alloc_size, 0, flags, flags ^ (vm::page_writable + vm::page_readable + vm::page_executable));
|
||||
|
||||
if (flags & vm::page_executable)
|
||||
if (flags & vm::alloc_executable)
|
||||
{
|
||||
ppu_register_range(alloc_at, alloc_size);
|
||||
}
|
||||
@@ -560,45 +603,41 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
}
|
||||
|
||||
// Revert if allocated map before failure
|
||||
if (alloc_map->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(vm::any, alloc_map->addr);
|
||||
}
|
||||
unmap_vm_area(alloc_map);
|
||||
}
|
||||
}
|
||||
|
||||
// Revert in case of failure
|
||||
for (u32 index : applied)
|
||||
std::for_each(applied.begin() + old_applied_size, applied.end(), [&](u32 index)
|
||||
{
|
||||
const u32 addr = patch.data_list[index].offset & -4096;
|
||||
const u32 addr = std::exchange(patch.data_list[index].alloc_addr, 0);
|
||||
|
||||
// Try different alignments until works
|
||||
if (!vm::dealloc(addr))
|
||||
{
|
||||
if (!vm::dealloc(addr & -0x10000))
|
||||
{
|
||||
vm::dealloc(addr & -0x100000);
|
||||
}
|
||||
}
|
||||
vm::dealloc(addr);
|
||||
|
||||
if (auto alloc_map = vm::get(vm::any, addr); alloc_map->flags & (1ull << 62))
|
||||
{
|
||||
vm::unmap(vm::any, alloc_map->addr);
|
||||
}
|
||||
}
|
||||
auto alloc_map = vm::get(vm::any, addr);
|
||||
unmap_vm_area(alloc_map);
|
||||
});
|
||||
|
||||
applied.clear();
|
||||
return applied;
|
||||
applied.resize(old_applied_size);
|
||||
return old_applied_size;
|
||||
}
|
||||
|
||||
// Fixup values from before
|
||||
std::fill(applied.begin(), applied.end(), u32{umax});
|
||||
std::fill(applied.begin() + old_applied_size, applied.end(), u32{umax});
|
||||
|
||||
u32 relocate_instructions_at = 0;
|
||||
|
||||
for (const auto& p : patch.data_list)
|
||||
{
|
||||
u32 offset = p.offset;
|
||||
|
||||
if (offset < min_addr || offset - min_addr >= filesz)
|
||||
if (relocate_instructions_at && vm::read32(relocate_instructions_at) != 0x6000'0000u)
|
||||
{
|
||||
// No longer points a NOP to be filled, meaning we ran out of instructions
|
||||
relocate_instructions_at = 0;
|
||||
}
|
||||
|
||||
if (!relocate_instructions_at && (offset < min_addr || offset - min_addr >= filesz))
|
||||
{
|
||||
// This patch is out of range for this segment
|
||||
continue;
|
||||
@@ -608,6 +647,13 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
|
||||
auto ptr = dst + offset;
|
||||
|
||||
if (relocate_instructions_at)
|
||||
{
|
||||
offset = relocate_instructions_at;
|
||||
ptr = vm::get_super_ptr<u8>(relocate_instructions_at);
|
||||
relocate_instructions_at += 4; // Advance to the next instruction on dynamic memory
|
||||
}
|
||||
|
||||
u32 resval = umax;
|
||||
|
||||
switch (p.type)
|
||||
@@ -623,6 +669,135 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
// Applied before
|
||||
continue;
|
||||
}
|
||||
case patch_type::code_alloc:
|
||||
{
|
||||
relocate_instructions_at = 0;
|
||||
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
if (out_branch < 0x10000 || out_branch >= 0x4000'0000 || !vm::check_addr<4>(out_branch, vm::page_executable))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value + 1) * 4, 0x10000);
|
||||
|
||||
// Always executable
|
||||
u64 flags = vm::alloc_executable | vm::alloc_unwritable;
|
||||
|
||||
switch (p.offset % patch_engine::mem_protection::mask)
|
||||
{
|
||||
case patch_engine::mem_protection::rw:
|
||||
case patch_engine::mem_protection::wx:
|
||||
{
|
||||
flags &= ~vm::alloc_unwritable;
|
||||
break;
|
||||
}
|
||||
case patch_engine::mem_protection::ro:
|
||||
case patch_engine::mem_protection::rx:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default: ensure(false);
|
||||
}
|
||||
|
||||
const auto alloc_map = ensure(vm::get(vm::any, out_branch));
|
||||
|
||||
// Range allowed for absolute branches to operate at
|
||||
// It takes into account that we need to put a branch for return at the end of memory space
|
||||
const u32 addr = p.alloc_addr = alloc_map->alloc(alloc_size, nullptr, 0x10000, flags);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
patch_log.error("Failed to allocate 0x%x bytes for code (entry=0x%x)", alloc_size, addr, out_branch);
|
||||
continue;
|
||||
}
|
||||
|
||||
patch_log.success("Allocated 0x%x for code at 0x%x (entry=0x%x)", alloc_size, addr, out_branch);
|
||||
|
||||
// NOP filled
|
||||
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
|
||||
|
||||
// Register code
|
||||
ppu_register_range(addr, alloc_size);
|
||||
|
||||
// Write branch to code
|
||||
ppu_form_branch_to_code(out_branch, addr);
|
||||
resval = out_branch & -4;
|
||||
|
||||
// Write address of the allocated memory to the code entry
|
||||
*vm::get_super_ptr<u32>(resval) = addr;
|
||||
|
||||
// Write branch to return to code
|
||||
ppu_form_branch_to_code(addr + static_cast<u32>(p.value.long_value) * 4, resval + 4);
|
||||
relocate_instructions_at = addr;
|
||||
break;
|
||||
}
|
||||
case patch_type::jump:
|
||||
case patch_type::jump_link:
|
||||
{
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
const u32 dest = static_cast<u32>(p.value.long_value);
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
if (!ppu_form_branch_to_code(out_branch, dest, p.type == patch_type::jump_link))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
resval = out_branch & -4;
|
||||
break;
|
||||
}
|
||||
case patch_type::jump_func:
|
||||
{
|
||||
const std::string& str = p.original_value;
|
||||
|
||||
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
|
||||
const usz sep_pos = str.find_first_of(':');
|
||||
|
||||
// Must contain only a single ':' or none
|
||||
// If ':' is found: Left string is the module name, right string is the function name
|
||||
// If ':' is not found: The entire string is a direct address of the function's descriptor in hexadecimal
|
||||
if (str.size() <= 2 || !sep_pos || sep_pos == str.size() - 1 || sep_pos != str.find_last_of(":"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string_view func_name{std::string_view(str).substr(sep_pos + 1)};
|
||||
u32 id = 0;
|
||||
|
||||
if (func_name.starts_with("0x"sv))
|
||||
{
|
||||
// Raw hexadeciaml-formatted FNID (real function name cannot contain a digit at the start, derived from C/CPP which were used in PS3 development)
|
||||
const auto result = std::from_chars(func_name.data() + 2, func_name.data() + func_name.size() - 2, id, 16);
|
||||
|
||||
if (result.ec != std::errc() || str.data() + sep_pos != result.ptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sep_pos == umax)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate FNID using function name
|
||||
id = ppu_generate_id(func_name);
|
||||
}
|
||||
|
||||
// Allow only if points to a PPU executable instruction
|
||||
// FNID/OPD-address is placed at target
|
||||
if (!ppu_form_branch_to_code(out_branch, id, true, true, std::string{str.data(), sep_pos != umax ? sep_pos : 0}))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
resval = out_branch & -4;
|
||||
break;
|
||||
}
|
||||
case patch_type::byte:
|
||||
{
|
||||
*ptr = static_cast<u8>(p.value.long_value);
|
||||
@@ -721,7 +896,7 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
|
||||
applied.push_back(resval);
|
||||
}
|
||||
|
||||
return applied;
|
||||
return old_applied_size;
|
||||
}
|
||||
|
||||
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
|
||||
@@ -812,11 +987,12 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
// Apply modifications sequentially
|
||||
auto apply_func = [&](const patch_info& patch)
|
||||
{
|
||||
auto applied = apply_modification(patch, dst, filesz, min_addr);
|
||||
const usz old_size = apply_modification(applied_total, patch, dst, filesz, min_addr);
|
||||
|
||||
applied_total += applied;
|
||||
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied.size());
|
||||
if (applied_total.size() != old_size)
|
||||
{
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied_total.size() - old_size);
|
||||
}
|
||||
};
|
||||
|
||||
// Sort specific patches after global patches
|
||||
@@ -858,18 +1034,36 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
return applied_total;
|
||||
}
|
||||
|
||||
void patch_engine::unload(const std::string& name)
|
||||
{
|
||||
if (m_map.find(name) == m_map.cend())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& container = m_map.at(name);
|
||||
|
||||
for (const auto& [description, patch] : container.patch_info_map)
|
||||
{
|
||||
for (auto& entry : patch.data_list)
|
||||
{
|
||||
// Deallocate used memory
|
||||
if (u32 addr = std::exchange(entry.alloc_addr, 0))
|
||||
{
|
||||
vm::dealloc(addr);
|
||||
|
||||
auto alloc_map = vm::get(vm::any, addr);
|
||||
unmap_vm_area(alloc_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void patch_engine::save_config(const patch_map& patches_map)
|
||||
{
|
||||
const std::string path = get_patch_config_path();
|
||||
patch_log.notice("Saving patch config file %s", path);
|
||||
|
||||
fs::file file(path, fs::rewrite);
|
||||
if (!file)
|
||||
{
|
||||
patch_log.fatal("Failed to open patch config file %s (%s)", path, fs::g_tls_error);
|
||||
return;
|
||||
}
|
||||
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
|
||||
@@ -933,7 +1127,12 @@ void patch_engine::save_config(const patch_map& patches_map)
|
||||
|
||||
out << YAML::EndMap;
|
||||
|
||||
file.write(out.c_str(), out.size());
|
||||
fs::pending_file file(path);
|
||||
|
||||
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
|
||||
{
|
||||
patch_log.error("Failed to create patch config file %s (%s)", path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
static void append_patches(patch_engine::patch_map& existing_patches, const patch_engine::patch_map& new_patches, usz& count, usz& total, std::stringstream* log_messages)
|
||||
|
||||
@@ -27,6 +27,10 @@ enum class patch_type
|
||||
invalid,
|
||||
load,
|
||||
alloc, // Allocate memory at address (zeroized executable memory)
|
||||
code_alloc,// Allocate memory somewhere, saves branch to memory at specfied address (filled with PPU NOP and branch for returning)
|
||||
jump, // Install special 32-bit jump instruction (PPU only atm)
|
||||
jump_link, // jump + set link (PPU only atm)
|
||||
jump_func, // jump to exported function (PPU only, forever)
|
||||
byte,
|
||||
le16,
|
||||
le32,
|
||||
@@ -56,6 +60,7 @@ public:
|
||||
u64 long_value;
|
||||
f64 double_value;
|
||||
} value{0};
|
||||
mutable u32 alloc_addr = 0; // Used to save optional allocation address (if occured)
|
||||
};
|
||||
|
||||
using patch_app_versions = std::unordered_map<std::string /*app_version*/, bool /*enabled*/>;
|
||||
@@ -118,6 +123,9 @@ public:
|
||||
// Read and add a patch node to the patch info
|
||||
static bool read_patch_node(patch_info& info, YAML::Node node, const YAML::Node& root, std::stringstream* log_messages = nullptr);
|
||||
|
||||
// Get the patch type from a string
|
||||
static patch_type get_patch_type(const std::string& text);
|
||||
|
||||
// Get the patch type of a patch node
|
||||
static patch_type get_patch_type(YAML::Node node);
|
||||
|
||||
@@ -148,6 +156,9 @@ public:
|
||||
// Apply patch (returns the number of entries applied)
|
||||
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = -1, u32 min_addr = 0);
|
||||
|
||||
// Deallocate memory used by patches
|
||||
void unload(const std::string& name);
|
||||
|
||||
private:
|
||||
// Database
|
||||
patch_map m_map{};
|
||||
|
||||
+10
-4
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T>
|
||||
struct size2_base
|
||||
@@ -670,17 +671,22 @@ struct area_base
|
||||
{
|
||||
return{ x1 / size.width, y1 / size.height, x2 / size.width, y2 / size.height };
|
||||
}
|
||||
constexpr area_base operator / (const T& value) const
|
||||
|
||||
template <typename U> requires (std::is_arithmetic_v<U>)
|
||||
constexpr area_base operator / (const U& value) const
|
||||
{
|
||||
return{ x1 / value, y1 / value, x2 / value, y2 / value };
|
||||
return area_base{static_cast<T>(x1 / value), static_cast<T>(y1 / value), static_cast<T>(x2 / value), static_cast<T>(y2 / value)};
|
||||
}
|
||||
|
||||
constexpr area_base operator * (const size2_base<T>& size) const
|
||||
{
|
||||
return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height };
|
||||
}
|
||||
constexpr area_base operator * (const f32& value) const
|
||||
|
||||
template <typename U> requires (std::is_arithmetic_v<U>)
|
||||
constexpr area_base operator * (const U& value) const
|
||||
{
|
||||
return{ static_cast<T>(x1 * value), static_cast<T>(y1 * value), static_cast<T>(x2 * value), static_cast<T>(y2 * value) };
|
||||
return area_base{static_cast<T>(x1 * value), static_cast<T>(y1 * value), static_cast<T>(x2 * value), static_cast<T>(y2 * value)};
|
||||
}
|
||||
|
||||
template<typename NT>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "Utilities/simple_ringbuf.h"
|
||||
|
||||
simple_ringbuf::simple_ringbuf(u32 size)
|
||||
{
|
||||
set_buf_size(size);
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_free_size()
|
||||
{
|
||||
const u64 _rw_ptr = rw_ptr;
|
||||
const u32 rd = static_cast<u32>(_rw_ptr);
|
||||
const u32 wr = static_cast<u32>(_rw_ptr >> 32);
|
||||
|
||||
return wr >= rd ? buf_size - 1 - (wr - rd) : rd - wr - 1U;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::get_used_size()
|
||||
{
|
||||
return buf_size - 1 - get_free_size();
|
||||
}
|
||||
|
||||
void simple_ringbuf::set_buf_size(u32 size)
|
||||
{
|
||||
ensure(size);
|
||||
|
||||
this->buf_size = size + 1;
|
||||
buf = std::make_unique<u8[]>(this->buf_size);
|
||||
flush();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void simple_ringbuf::flush()
|
||||
{
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
{
|
||||
val = static_cast<u32>(val >> 32) | (val & 0xFFFFFFFF'00000000);
|
||||
});
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::push(const void *data, u32 size)
|
||||
{
|
||||
ensure(data != nullptr && initialized.observe());
|
||||
|
||||
const u32 old = static_cast<u32>(rw_ptr.load() >> 32);
|
||||
const u32 to_push = std::min(size, get_free_size());
|
||||
auto b_data = static_cast<const u8*>(data);
|
||||
|
||||
if (!to_push) return 0;
|
||||
|
||||
if (old + to_push > buf_size)
|
||||
{
|
||||
const auto first_write_sz = buf_size - old;
|
||||
memcpy(&buf[old], b_data, first_write_sz);
|
||||
memcpy(&buf[0], b_data + first_write_sz, to_push - first_write_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&buf[old], b_data, to_push);
|
||||
}
|
||||
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
{
|
||||
val = static_cast<u64>((old + to_push) % buf_size) << 32 | static_cast<u32>(val);
|
||||
});
|
||||
|
||||
return to_push;
|
||||
}
|
||||
|
||||
u32 simple_ringbuf::pop(void *data, u32 size)
|
||||
{
|
||||
ensure(data != nullptr && initialized.observe());
|
||||
|
||||
const u32 old = static_cast<u32>(rw_ptr.load());
|
||||
const u32 to_pop = std::min(size, get_used_size());
|
||||
u8 *b_data = static_cast<u8*>(data);
|
||||
|
||||
if (!to_pop) return 0;
|
||||
|
||||
if (old + to_pop > buf_size)
|
||||
{
|
||||
const auto first_read_sz = buf_size - old;
|
||||
memcpy(b_data, &buf[old], first_read_sz);
|
||||
memcpy(b_data + first_read_sz, &buf[0], to_pop - first_read_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_data, &buf[old], to_pop);
|
||||
}
|
||||
|
||||
rw_ptr.atomic_op([&](u64 &val)
|
||||
{
|
||||
val = (old + to_pop) % buf_size | (val & 0xFFFFFFFF'00000000);
|
||||
});
|
||||
|
||||
return to_pop;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
// Single reader/writer simple ringbuffer.
|
||||
// Counters are 32-bit.
|
||||
class simple_ringbuf
|
||||
{
|
||||
private:
|
||||
|
||||
atomic_t<u64> rw_ptr = 0;
|
||||
u32 buf_size = 0;
|
||||
std::unique_ptr<u8[]> buf{};
|
||||
atomic_t<bool> initialized = false;
|
||||
|
||||
public:
|
||||
|
||||
simple_ringbuf() {};
|
||||
simple_ringbuf(u32 size);
|
||||
|
||||
u32 get_free_size();
|
||||
u32 get_used_size();
|
||||
|
||||
// Thread unsafe functions.
|
||||
void set_buf_size(u32 size);
|
||||
void flush(); // Could be safely called from reader.
|
||||
|
||||
u32 push(const void *data, u32 size);
|
||||
u32 pop(void *data, u32 size);
|
||||
};
|
||||
+2
-2
@@ -35,13 +35,13 @@ jobs:
|
||||
displayName: ccache
|
||||
|
||||
- bash: |
|
||||
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.1
|
||||
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.3
|
||||
docker run \
|
||||
-v $(pwd):/rpcs3 \
|
||||
--env-file .ci/docker.env \
|
||||
-v $CCACHE_DIR:/root/.ccache \
|
||||
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
rpcs3/rpcs3-ci-bionic:1.1 \
|
||||
rpcs3/rpcs3-ci-bionic:1.3 \
|
||||
/rpcs3/.ci/build-linux.sh
|
||||
displayName: Docker setup and build
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,8 +2,7 @@
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:throwingNew- /constexpr:steps16777216 /D _CRT_SECURE_NO_DEPRECATE=1 /D _CRT_NON_CONFORMING_SWPRINTFS=1 /D _SCL_SECURE_NO_WARNINGS=1")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0 /MT")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
|
||||
|
||||
#TODO: Some of these could be cleaned up
|
||||
@@ -82,7 +81,9 @@ else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
add_compile_options(-stdlib=libc++)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
add_compile_options(-stdlib=libc++)
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-image_base,0x10000 -Wl,-pagezero_size,0x10000")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-no_pie")
|
||||
elseif(WIN32)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
@@ -61,6 +61,7 @@
|
||||
LLVMBitWriter.lib;
|
||||
LLVMCoroutines.lib;
|
||||
LLVMObjCARCOpts.lib;
|
||||
LLVMIntelJITEvents.lib;
|
||||
</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets" />
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
@@ -62,6 +62,7 @@
|
||||
LLVMBitWriter.lib;
|
||||
LLVMCoroutines.lib;
|
||||
LLVMObjCARCOpts.lib;
|
||||
LLVMIntelJITEvents.lib;
|
||||
</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
+1
-1
Submodule llvm updated: 5836324d64...1c0ca194dc
@@ -39,9 +39,9 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT ../llvm</CmakeReleaseCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT ../llvm</CmakeDebugCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>echo Cleaning..
|
||||
for /F "delims= eol=|" %%f in ('
|
||||
dir /b ^| findstr /V "[^.]*\build[^.]*\.vcxproj"') do (
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT ../llvm</CmakeReleaseCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Release" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm</CmakeReleaseCLI>
|
||||
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT ../llvm</CmakeDebugCLI>
|
||||
cmake -G Ninja -DCMAKE_CXX_COMPILER="clang-cl.exe" -DCMAKE_C_COMPILER="clang-cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_DEFAULT_TARGET_TRIPLE:STRING=x86_64-pc-windows-msvc -DLLVM_HOST_TRIPLE:STRING=x86_64-pc-windows-msvc -DCMAKE_INSTALL_PREFIX="./Debug" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=ON -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=$(WindowsTargetPlatformVersion) -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -DLLVM_USE_INTEL_JITEVENTS=ON ../llvm</CmakeDebugCLI>
|
||||
<CmakeCleanCLI>echo Cleaning..
|
||||
for /F "delims= eol=|" %%f in ('
|
||||
dir /b ^| findstr /V "[^.]*\build[^.]*\.vcxproj"') do (
|
||||
|
||||
@@ -17,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rpcs3.emu", "rpcs3.emu", "{
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XAudio", "rpcs3\XAudio.vcxproj", "{78CB2F39-B809-4A06-8329-8C0A19119D3D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenAL", "rpcs3\OpenAL.vcxproj", "{30A05C4D-F5FD-421C-A864-17A64BDEAA75}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pnglibconf", "3rdparty\libpng\pnglibconf.vcxproj", "{EB33566E-DA7F-4D28-9077-88C0B7C77E35}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "3rdparty\libpng\libpng.vcxproj", "{D6973076-9317-4EF2-A0B8-B7A18AC0713E}"
|
||||
@@ -47,10 +45,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxpro
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038} = {C4A10229-4712-4BD2-B63E-50D93C67A038}
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D} = {78CB2F39-B809-4A06-8329-8C0A19119D3D}
|
||||
{3384223A-6D97-4799-9862-359F85312892} = {3384223A-6D97-4799-9862-359F85312892}
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75} = {30A05C4D-F5FD-421C-A864-17A64BDEAA75}
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF} = {60F89955-91C6-3A36-8000-13C592FEC2DF}
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D} = {3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {D6973076-9317-4EF2-A0B8-B7A18AC0713E}
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2} = {9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A} = {FDA7B080-03B0-48C8-B24F-88118981422A}
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB} = {DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122} = {FDC361C5-7734-493B-8CFB-037308B35122}
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C} = {8F85B6CC-250F-4ACA-A617-E820A74E3E3C}
|
||||
@@ -66,7 +65,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "3rdp
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7zlib", "3rdparty\7z\7zlib.vcxproj", "{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rdparty\curl\libcurl.vcxproj", "{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
@@ -79,6 +78,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rdParty", "3rdParty", "{6C
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "llvm_build_clang_cl", "llvm_build\llvm_build_clang_cl.vcxproj", "{A37E4273-85DB-4217-B775-CE971B87D9DF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Cubeb", "rpcs3\Cubeb.vcxproj", "{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcubeb", "3rdparty\cubeb\libcubeb.vcxproj", "{FDA7B080-03B0-48C8-B24F-88118981422A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -103,10 +106,6 @@ Global
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug|x64.Build.0 = Debug|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Release|x64.ActiveCfg = Release|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Release|x64.Build.0 = Release|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug|x64.Build.0 = Debug|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Release|x64.ActiveCfg = Release|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Release|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug|x64.ActiveCfg = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Release|x64.ActiveCfg = Release|x64
|
||||
@@ -161,6 +160,14 @@ Global
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Release|x64.ActiveCfg = Release|x64
|
||||
{A37E4273-85DB-4217-B775-CE971B87D9DF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A37E4273-85DB-4217-B775-CE971B87D9DF}.Release|x64.ActiveCfg = Release|x64
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}.Debug|x64.Build.0 = Debug|x64
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}.Release|x64.ActiveCfg = Release|x64
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}.Release|x64.Build.0 = Release|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Debug|x64.Build.0 = Debug|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Release|x64.ActiveCfg = Release|x64
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -171,7 +178,6 @@ Global
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672} = {B0AC29FD-7B01-4B5E-9C8D-0A081E4C5668}
|
||||
{3384223A-6D97-4799-9862-359F85312892} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
@@ -186,6 +192,8 @@ Global
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1} = {B0AC29FD-7B01-4B5E-9C8D-0A081E4C5668}
|
||||
{A37E4273-85DB-4217-B775-CE971B87D9DF} = {B0AC29FD-7B01-4B5E-9C8D-0A081E4C5668}
|
||||
{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2} = {10FBF193-D532-4CCF-B875-4C7091A7F6C2}
|
||||
{FDA7B080-03B0-48C8-B24F-88118981422A} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
|
||||
|
||||
+53
-10
@@ -3,6 +3,9 @@ if(USE_PRECOMPILED_HEADERS AND NOT COMMAND target_precompile_headers)
|
||||
include(cotire)
|
||||
endif()
|
||||
|
||||
# Define GNU standard installation directories
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Generate git-version.h at build time.
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake)
|
||||
|
||||
@@ -23,6 +26,7 @@ elseif(NOT MSVC AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3")
|
||||
# Optionally enable X11 for window management
|
||||
find_package(X11)
|
||||
if(X11_FOUND)
|
||||
@@ -151,29 +155,68 @@ elseif(UNIX)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git)
|
||||
elseif(WIN32)
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# this is a single-config generator workaround
|
||||
# multi-config generator will fail.
|
||||
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# offical Qt binaries are built with -MD(d) only as stated in offical wiki
|
||||
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
|
||||
# If we build our libs with /MT(d), we might encounter some issues.
|
||||
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
|
||||
|
||||
# Qt installed from Qt installer has following hierarchy:
|
||||
# bin/ for release and debug dlls and windeployqt tools
|
||||
# lib/cmake/Qt5/ for Qt5_Dir
|
||||
# Qt installed from vcpkg has following hierarchy:
|
||||
# bin/ for release dlls
|
||||
# debug/bin/ for debug dlls
|
||||
# tools/qt5/bin/ for tools including windeployqt
|
||||
# tools/qt5/debug/bin/ for tools with debug build including windeployqt
|
||||
# share/cmake/Qt5/ for Qt5_Dir
|
||||
|
||||
# If Qt5 is installed from official Qt installer
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../bin/")
|
||||
|
||||
# If Qt5 is installed from vcpkg
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/debug/bin/")
|
||||
else()
|
||||
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/bin/")
|
||||
endif()
|
||||
|
||||
find_program(_WINDEPLOYQT
|
||||
NAMES windeployqt
|
||||
PATHS ${_QT5_TOOLS_PATHS}
|
||||
REQUIRED)
|
||||
message("Use windeployqt in ${_WINDEPLOYQT}")
|
||||
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
|
||||
COMMAND "${Qt5_DIR}/../../../bin/windeployqt" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-svg --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" --release "$<TARGET_FILE:rpcs3>")
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
|
||||
# If Qt5 is installed from vcpkg, add binary path to PATH
|
||||
# otherwise windeployqt tool won't be able to locate necessary dlls
|
||||
COMMAND set PATH=${Qt5_DIR}/../../../$<$<CONFIG:Debug>:debug/>bin/$<SEMICOLON>%PATH%
|
||||
COMMAND "${_WINDEPLOYQT}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" $<$<CONFIG:Debug>:--debug> $<$<CONFIG:Release>:--release> "$<TARGET_FILE:rpcs3>")
|
||||
endif()
|
||||
|
||||
# Unix installation
|
||||
if(UNIX AND NOT APPLE)
|
||||
# Install the binary
|
||||
install(TARGETS rpcs3 RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
install(TARGETS rpcs3 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
# Install the application icon and menu item
|
||||
install(FILES rpcs3.svg
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps)
|
||||
install(FILES rpcs3.png
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps)
|
||||
install(FILES rpcs3.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||
install(FILES rpcs3.metainfo.xml
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo)
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo)
|
||||
# Install other files
|
||||
install(DIRECTORY ../bin/Icons
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ../bin/GuiConfigs
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
install(DIRECTORY ../bin/git
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3)
|
||||
endif()
|
||||
|
||||
@@ -33,6 +33,37 @@ struct SELF_KEY
|
||||
SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct);
|
||||
};
|
||||
|
||||
constexpr u32 PASSPHRASE_KEY_LEN = 16;
|
||||
constexpr u32 PASSPHRASE_OUT_LEN = 4096;
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_KEY_1[PASSPHRASE_KEY_LEN] = {
|
||||
0xD4, 0x13, 0xB8, 0x96, 0x63, 0xE1, 0xFE, 0x9F, 0x75, 0x14, 0x3D, 0x3B, 0xB4, 0x56, 0x52, 0x74 // D413B89663E1FE9F75143D3BB4565274
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_KEY_2[PASSPHRASE_KEY_LEN] = {
|
||||
0xFA, 0x72, 0xCE, 0xEF, 0x59, 0xB4, 0xD2, 0x98, 0x9F, 0x11, 0x19, 0x13, 0x28, 0x7F, 0x51, 0xC7 // FA72CEEF59B4D2989F111913287F51C7
|
||||
};
|
||||
|
||||
constexpr u8 SC_KEY_FOR_MASTER_1[PASSPHRASE_KEY_LEN] = {
|
||||
0xDA, 0xA4, 0xB9, 0xF2, 0xBC, 0x70, 0xB2, 0x80, 0xA7, 0xB3, 0x40, 0xFA, 0x0D, 0x04, 0xBA, 0x14 // DAA4B9F2BC70B280A7B340FA0D04BA14
|
||||
};
|
||||
|
||||
constexpr u8 SC_KEY_FOR_MASTER_2[PASSPHRASE_KEY_LEN] = {
|
||||
0x29, 0xC1, 0x94, 0xFF, 0xEC, 0x1F, 0xD1, 0x4D, 0x4A, 0xAE, 0x00, 0x6C, 0x32, 0xB3, 0x59, 0x90 // 29C194FFEC1FD14D4AAE006C32B35990
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_1[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x00 // 73636B65795F7365726965736B657900
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_2[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x32 // 73636B65795F7365726965736B657932
|
||||
};
|
||||
|
||||
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_3[PASSPHRASE_KEY_LEN] = {
|
||||
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x66, 0x6F, 0x72, 0x5F, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72 // 73636B65795F666F725F6D6173746572
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_IDU[0x10] = {
|
||||
0x5d, 0xb9, 0x11, 0xe6, 0xb7, 0xe5, 0x0a, 0x7d, 0x32, 0x15, 0x38, 0xfd, 0x7c, 0x66, 0xf1, 0x7b
|
||||
};
|
||||
@@ -180,6 +211,104 @@ constexpr u8 PUP_KEY[0x40] = {
|
||||
0x30, 0xCE, 0x83, 0x66
|
||||
};
|
||||
|
||||
// name; location; notes
|
||||
constexpr s64 PAID_01 = 0x0003CD28CB47D3C1L; // spu_token_processor.self; CoreOS; Only for 2E - 083.007
|
||||
constexpr s64 PAID_02 = 0x1010000001000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 2E - 080.006
|
||||
constexpr s64 PAID_03 = 0x1010000001000003L; // retail games and their updates
|
||||
constexpr s64 PAID_04 = 0x1010000002000003L;
|
||||
constexpr s64 PAID_05 = 0x1020000401000001L; // ps2emu; /dev_flash/ps2emu; CEX DEX DECR ?
|
||||
constexpr s64 PAID_06 = 0x1050000003000001L; // lv2_kernel.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_07 = 0x1070000001000002L; // onicore_child.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_08 = 0x1070000002000002L; // mcore.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_09 = 0x1070000003000002L; // mgvideo.self; /dev_flash/vsh/module; same for CEX DEX DECR
|
||||
constexpr s64 PAID_10 = 0x1070000004000002L; // swagner / swreset; /dev_flash/vsh/module; DTCP-IP DRM modules
|
||||
constexpr s64 PAID_11 = 0x107000000E000001L; // vtrm_server.fself; lv1
|
||||
constexpr s64 PAID_12 = 0x107000000F000001L; // update_manager_server.fself; lv1
|
||||
constexpr s64 PAID_13 = 0x1070000010000001L; // sc_manager_server.fself; lv1
|
||||
constexpr s64 PAID_14 = 0x1070000011000001L; // secure_rtc_server.fself; lv1
|
||||
constexpr s64 PAID_15 = 0x1070000012000001L; // spm_server.fself; lv1
|
||||
constexpr s64 PAID_16 = 0x1070000013000001L; // sb_manager_server.fself; lv1
|
||||
constexpr s64 PAID_17 = 0x1070000014000001L; // framework.fself; lv1
|
||||
constexpr s64 PAID_18 = 0x1070000015000001L; // lv2_loader.fself; lv1
|
||||
constexpr s64 PAID_19 = 0x1070000016000001L; // profile_loader.fself; lv1
|
||||
constexpr s64 PAID_20 = 0x1070000017000001L; // ss_init.fself; lv1
|
||||
constexpr s64 PAID_21 = 0x1070000018000001L; // individual_info_mgr_server.fself; lv1
|
||||
constexpr s64 PAID_22 = 0x1070000019000001L; // app_info_manager_server.fself; lv1
|
||||
constexpr s64 PAID_23 = 0x107000001A000001L; // ss_sc_init_pu.fself; JIG lv1 proc
|
||||
constexpr s64 PAID_24 = 0x107000001C000001L; // updater_frontend.fself; lv1
|
||||
constexpr s64 PAID_25 = 0x107000001D000001L; // sysmgr_ss.fself; lv1
|
||||
constexpr s64 PAID_26 = 0x107000001F000001L; // sb_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_27 = 0x1070000020000001L; // sc_iso.self / sc_iso_factory.self; CoreOS / [2.43 JIG PUP]; same for CEX DEX DECR
|
||||
constexpr s64 PAID_28 = 0x1070000021000001L; // spp_verifier.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_29 = 0x1070000022000001L; // spu_pkg_rvk_verifier.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_30 = 0x1070000023000001L; // spu_token_processor.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_31 = 0x1070000024000001L; // sv_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_32 = 0x1070000025000001L; // aim_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_33 = 0x1070000026000001L; // ss_sc_init.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_34 = 0x1070000027000001L; // dispatcher.fself; lv1;
|
||||
constexpr s64 PAID_35 = 0x1070000028000001L; // factory_data_mngr_server.fself; JIG lv1 proc
|
||||
constexpr s64 PAID_36 = 0x1070000029000001L; // fdm_spu_module.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_37 = 0x107000002A000001L;
|
||||
constexpr s64 PAID_38 = 0x1070000031000001L;
|
||||
constexpr s64 PAID_39 = 0x1070000032000001L; // ss_server1.fself; lv1
|
||||
constexpr s64 PAID_40 = 0x1070000033000001L; // ss_server2.fself; lv1
|
||||
constexpr s64 PAID_41 = 0x1070000034000001L; // ss_server3.fself; lv1
|
||||
constexpr s64 PAID_42 = 0x1070000037000001L; // mc_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_43 = 0x1070000039000001L; // bdp_bdmv.self; /dev_flash/bdplayer
|
||||
constexpr s64 PAID_44 = 0x107000003A000001L; // bdj.self; /dev_flash/bdplayer
|
||||
constexpr s64 PAID_45 = 0x1070000040000001L; // sys/external modules; /dev_flash/sys/external; same for CEX DEX DECR (incl. liblv2dbg_for_cex.sprx + liblv2dbg_for_dex.sprx)
|
||||
constexpr s64 PAID_46 = 0x1070000041000001L; // ps1emu; /dev_flash/ps1emu; CEX DEX DECR ?
|
||||
constexpr s64 PAID_47 = 0x1070000043000001L; // me_iso_spu_module.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_48 = 0x1070000044000001L; // (related to usb dongle)
|
||||
constexpr s64 PAID_49 = 0x1070000045000001L; // USB Dongle Authenticator; ss_server1.fself; same for CEX DEX DECR
|
||||
constexpr s64 PAID_50 = 0x1070000046000001L; // spu_mode_auth.self; [2.43 JIG PUP]
|
||||
constexpr s64 PAID_51 = 0x1070000047000001L; // otheros.self; otheros.self
|
||||
constexpr s64 PAID_52 = 0x1070000048000001L; // ftpd; cell_root/target/images; DECR
|
||||
constexpr s64 PAID_53 = 0x107000004C000001L; // spu_utoken_processor.self; CoreOS (since FW 2.40)
|
||||
constexpr s64 PAID_54 = 0x107000004E000001L; // (syscall 878)
|
||||
constexpr s64 PAID_55 = 0x107000004F000001L;
|
||||
constexpr s64 PAID_56 = 0x1070000050000001L;
|
||||
constexpr s64 PAID_57 = 0x1070000051000001L;
|
||||
constexpr s64 PAID_58 = 0x1070000052000001L; // sys/internal CEX + vsh/module modules CEX; /dev_flash/sys/internal + /dev_flash/vsh/module; Differs between CEX (this authid) and DECR
|
||||
constexpr s64 PAID_59 = 0x1070000054000001L; // (syscall 21)
|
||||
constexpr s64 PAID_60 = 0x1070000055000001L; // manu_info_spu_module.self; CoreOS (since FW 3.50)
|
||||
constexpr s64 PAID_61 = 0x1070000058000001L; // me_iso_for_ps2emu.self; CoreOS (since FW 3.70)
|
||||
constexpr s64 PAID_62 = 0x1070000059000001L; // sv_iso_for_ps2emu.self; CoreOS (since FW 3.70)
|
||||
constexpr s64 PAID_63 = 0x1070000300000001L; // Lv2diag.self; BD-remarry toolkit
|
||||
constexpr s64 PAID_64 = 0x10700003FC000001L; // emer_init.self; CoreOS (since FW 2.00)
|
||||
constexpr s64 PAID_65 = 0x10700003FD000001L; // ps3swu; PUP root; same for CEX DEX DECR
|
||||
constexpr s64 PAID_66 = 0x10700003FD000001L; // PS3ToolUpdater; cell_root/target/images; Only DECR
|
||||
constexpr s64 PAID_67 = 0x10700003FD000001L; // manufacturing_updater_for_reset.self; BD-remarry toolkit
|
||||
constexpr s64 PAID_68 = 0x10700003FE000001L; // sys_agent.self DECR; /dev_flash/sys/internal; DECR
|
||||
constexpr s64 PAID_69 = 0x10700003FF000001L; // db_backup, mkfs, mkfs_085, mount_hdd, registry_backup, set_monitor, most sys/internal modules DECR + most vsh/module modules DECR; /dev_flash/sys/internal + /dev_flash/vsh/module + cell_root/target/images; Differs between DECR (this authid) and CEX
|
||||
constexpr s64 PAID_70 = 0x1070000400000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 081.003 - 083.007
|
||||
constexpr s64 PAID_71 = 0x1070000409000001L; // psp_emulator.self; /dev_flash/pspemu/psp_emulator.self; CEX DEX DECR ?
|
||||
constexpr s64 PAID_72 = 0x107000040A000001L; // psp_translator.self; /dev_flash/pspemu/psp_translator.self; CEX DEX DECR ?
|
||||
constexpr s64 PAID_73 = 0x107000040B000001L; // emulator_api.sprx and other .sprx; /dev_flash/pspemu/release/; CEX DEX DECR ?
|
||||
constexpr s64 PAID_74 = 0x107000040C000001L; // emulator_drm.sprx; /dev_flash/pspemu/release/emulator_drm.sprx; CEX DEX DECR ?
|
||||
constexpr s64 PAID_75 = 0x107000040C000001L; // libchnnlsv.sprx; /dev_flash/sys/internal/; CEX DEX DECR ?
|
||||
constexpr s64 PAID_76 = 0x107000040D000001L; // ?psp related?; ?/dev_flash/pspemu/release/?; CEX DEX DECR ?
|
||||
constexpr s64 PAID_77 = 0x1070000500000001L; // cellftp; cell_root/target/images/; DECR
|
||||
constexpr s64 PAID_78 = 0x1070000501000001L; // hdd_copy.self; CoreOS (since FW 3.10)
|
||||
constexpr s64 PAID_79 = 0x10700005FC000001L; // sys_audio; /dev_flash/sys/internal; CEX
|
||||
constexpr s64 PAID_80 = 0x10700005FD000001L; // sys_init_osd; /dev_flash/sys/internal; CEX
|
||||
constexpr s64 PAID_81 = 0x10700005FF000001L; // vsh.self; /dev_flash/vsh/; CEX
|
||||
constexpr s64 PAID_82 = 0x1070001002000001L; // PvrRecSvr.sprx; BCJB95006\USRDIR\v320; CEX
|
||||
constexpr s64 PAID_83 = 0x1070200056000001L; // cachemgr.self; WebMAF apps/USRDIR
|
||||
constexpr s64 PAID_84 = 0x1070200057000001L; // EBOOT.BIN.self + .sprx files; WebMAF apps/USRDIR/prx/ps3; Demen_prx.ppu.sprx + WebMAF sprx files
|
||||
constexpr s64 PAID_85 = 0x1FF0000001000001L; // lv0; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_86 = 0x1FF0000002000001L; // lv1.self; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_87 = 0x1FF0000008000001L; // lv1ldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_88 = 0x1FF0000009000001L; // lv2ldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_89 = 0x1FF000000A000001L; // isoldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_90 = 0x1FF000000B000001L; // rvkldr; CoreOS; same for CEX DEX DECR
|
||||
constexpr s64 PAID_91 = 0x1FF000000C000001L; // appldr; CoreOS; same for CEX DEX DECR
|
||||
|
||||
constexpr s64 LAID_1 = 0x1070000001000001L; // (= HV processes / SCE_CELLOS_PME); flash and vflash
|
||||
constexpr s64 LAID_2 = 0x1070000002000001L; // (= GameOS / PS3_LPAR); flash and vflash
|
||||
constexpr s64 LAID_3 = 0x1020000003000001L; // (= PS2_LPAR / PS2_GX_LPAR / PS2_SW_LPAR / PS2_NE_LPAR); (used in ps3vflashc region inside vflash in NOR consoles, and ps3db region)... dev_flash and dev_hdd0 regions
|
||||
constexpr s64 LAID_4 = 0x1080000004000001L; // (= LINUX_LPAR); (used in ps3vflashf region inside vflash in NOR consoles)... otheros bootloader region
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr{};
|
||||
|
||||
+75
-91
@@ -6,6 +6,7 @@
|
||||
#include "ec.h"
|
||||
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include <cmath>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
@@ -548,6 +549,11 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
|
||||
bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
{
|
||||
if ((npd->license & 0x3) != 0x3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char dev[0x60] = { 0 };
|
||||
|
||||
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
|
||||
@@ -565,12 +571,6 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
u128 klic;
|
||||
std::memcpy(&klic, klicensee, sizeof(klic));
|
||||
|
||||
if (!klic)
|
||||
{
|
||||
// Allow empty dev hash.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generate klicensee xor key.
|
||||
u128 key = klic ^ std::bit_cast<u128>(NP_OMAC_KEY_2);
|
||||
|
||||
@@ -578,13 +578,19 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
return cmac_hash_compare(reinterpret_cast<uchar*>(&key), 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
}
|
||||
|
||||
bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, bool verbose)
|
||||
bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, EDAT_HEADER* edat, bool verbose)
|
||||
{
|
||||
if (!file_name)
|
||||
{
|
||||
fmt::throw_exception("Empty filename");
|
||||
}
|
||||
|
||||
// Ignore header validation in DEBUG data.
|
||||
if (edat->flags & EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const usz file_name_length = std::strlen(file_name);
|
||||
const usz buf_len = 0x30 + file_name_length;
|
||||
|
||||
@@ -649,7 +655,7 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
|
||||
EDAT.file_size = swap64(*reinterpret_cast<u64*>(&edat_header[8]));
|
||||
}
|
||||
|
||||
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
|
||||
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose)
|
||||
{
|
||||
// Setup NPD and EDAT/SDAT structs.
|
||||
NPD_HEADER NPD;
|
||||
@@ -658,8 +664,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(input, NPD, EDAT);
|
||||
|
||||
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
|
||||
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||
if (NPD.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return true;
|
||||
@@ -671,6 +676,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
edat_log.notice("NPD version: %d", NPD.version);
|
||||
edat_log.notice("NPD license: %d", NPD.license);
|
||||
edat_log.notice("NPD type: %d", NPD.type);
|
||||
edat_log.notice("NPD content_id: %s", NPD.content_id);
|
||||
}
|
||||
|
||||
// Set decryption key.
|
||||
@@ -703,39 +709,36 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
// Perform header validation (EDAT only).
|
||||
char real_file_name[CRYPTO_MAX_PATH];
|
||||
extract_file_name(input_file_name, real_file_name);
|
||||
if (!validate_npd_hashes(real_file_name, devklic, &NPD, verbose))
|
||||
if (!validate_npd_hashes(real_file_name, devklic, &NPD, &EDAT, verbose))
|
||||
{
|
||||
// Ignore header validation in DEBUG data.
|
||||
if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return true;
|
||||
}
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Select EDAT key.
|
||||
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||
memcpy(&key, devklic, 0x10);
|
||||
else if ((NPD.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
||||
{
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
std::memcpy(&key, devklic, 0x10);
|
||||
}
|
||||
else // Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
|
||||
{
|
||||
const std::string rap_path = rpcs3::utils::get_rap_file_path(NPD.content_id);
|
||||
|
||||
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(key))
|
||||
{
|
||||
key = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (!key)
|
||||
{
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (local activation)");
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", NPD.license);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ((NPD.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||
{
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (!key)
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.error("A valid RAP file is needed for this EDAT file! (network activation)");
|
||||
return true;
|
||||
edat_log.notice("RIFKEY: %s", std::bit_cast<be_t<u128>>(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -745,8 +748,6 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
|
||||
std::memcpy(&data, devklic, sizeof(data));
|
||||
edat_log.notice("DEVKLIC: %s", data);
|
||||
std::memcpy(&data, rifkey, sizeof(data));
|
||||
edat_log.notice("RIF KEY: %s", data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,7 +785,7 @@ u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
|
||||
return rifkey;
|
||||
}
|
||||
|
||||
bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID)
|
||||
bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID, u32* license)
|
||||
{
|
||||
// Setup NPD and EDAT/SDAT structs.
|
||||
NPD_HEADER NPD;
|
||||
@@ -793,8 +794,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&input, NPD, EDAT);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&NPD.magic, npd_magic, 4))
|
||||
if (NPD.magic != "NPD\0"_u32)
|
||||
{
|
||||
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
|
||||
return false;
|
||||
@@ -809,28 +809,32 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
|
||||
// Perform header validation (EDAT only).
|
||||
char real_file_name[CRYPTO_MAX_PATH];
|
||||
extract_file_name(input_file_name.c_str(), real_file_name);
|
||||
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, false))
|
||||
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, &EDAT, false))
|
||||
{
|
||||
// Ignore header validation in DEBUG data.
|
||||
if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
|
||||
{
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return false;
|
||||
}
|
||||
edat_log.error("NPD hash validation failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
*contentID = std::string(reinterpret_cast<const char*>(NPD.content_id));
|
||||
std::string_view sv{NPD.content_id, std::size(NPD.content_id)};
|
||||
sv = sv.substr(0, sv.find_first_of('\0'));
|
||||
|
||||
if (contentID) *contentID = std::string(sv);
|
||||
if (license) *license = (NPD.license & 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Decrypts full file
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose)
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose)
|
||||
{
|
||||
if (!input)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Prepare the files.
|
||||
input.seek(0);
|
||||
|
||||
// Set keys (RIF and DEVKLIC).
|
||||
u128 rifKey{};
|
||||
// Set DEVKLIC
|
||||
u128 devklic{};
|
||||
|
||||
// Select the EDAT key mode.
|
||||
@@ -875,17 +879,9 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Read the RAP file, if provided.
|
||||
if (!rap_file_name.empty())
|
||||
{
|
||||
const fs::file rap(rap_file_name);
|
||||
|
||||
rifKey = GetEdatRifKeyFromRapFile(rap);
|
||||
}
|
||||
|
||||
// Delete the bad output file if any errors arise.
|
||||
fs::file output = fs::make_stream<std::vector<u8>>();
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), reinterpret_cast<uchar*>(&rifKey), verbose))
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), verbose))
|
||||
{
|
||||
output.release();
|
||||
return fs::file{};
|
||||
@@ -901,8 +897,7 @@ bool EDATADecrypter::ReadHeader()
|
||||
// Read in the NPD and EDAT/SDAT headers.
|
||||
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
|
||||
|
||||
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
|
||||
if (memcmp(&npdHeader.magic, npd_magic, 4))
|
||||
if (npdHeader.magic != "NPD\0"_u32)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -916,33 +911,13 @@ bool EDATADecrypter::ReadHeader()
|
||||
else
|
||||
{
|
||||
// verify key
|
||||
if (!validate_dev_klic(reinterpret_cast<uchar*>(&dev_key), &npdHeader))
|
||||
if (!validate_dev_klic(reinterpret_cast<uchar*>(&dec_key), &npdHeader))
|
||||
{
|
||||
edat_log.error("Failed validating klic");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Select EDAT key.
|
||||
if ((npdHeader.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
|
||||
dec_key = std::move(dev_key);
|
||||
else if ((npdHeader.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
|
||||
{
|
||||
dec_key = std::move(rif_key);
|
||||
|
||||
if (!dec_key)
|
||||
{
|
||||
edat_log.warning("Empty Dec key for local activation!");
|
||||
}
|
||||
}
|
||||
else if ((npdHeader.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||
{
|
||||
dec_key = std::move(rif_key);
|
||||
|
||||
if (!dec_key)
|
||||
{
|
||||
edat_log.warning("Empty Dec key for network activation!");
|
||||
}
|
||||
}
|
||||
// Use provided dec_key
|
||||
}
|
||||
|
||||
edata_file.seek(0);
|
||||
@@ -958,29 +933,37 @@ bool EDATADecrypter::ReadHeader()
|
||||
file_size = edatHeader.file_size;
|
||||
total_blocks = utils::aligned_div(edatHeader.file_size, edatHeader.block_size);
|
||||
|
||||
// Try decrypting the first block instead
|
||||
u8 data_sample[1];
|
||||
|
||||
if (file_size && !ReadData(0, data_sample, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
{
|
||||
if (pos > edatHeader.file_size)
|
||||
return 0;
|
||||
size = std::min<u64>(size, pos > edatHeader.file_size ? 0 : edatHeader.file_size - pos);
|
||||
|
||||
// now we need to offset things to account for the actual 'range' requested
|
||||
const u64 startOffset = pos % edatHeader.block_size;
|
||||
|
||||
const u32 num_blocks = static_cast<u32>(utils::aligned_div(startOffset + size, edatHeader.block_size));
|
||||
const u64 bufSize = num_blocks*edatHeader.block_size;
|
||||
if (data_buf_size < (bufSize))
|
||||
if (!size)
|
||||
{
|
||||
data_buf.reset(new u8[bufSize]);
|
||||
data_buf_size = bufSize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find and decrypt block range covering pos + size
|
||||
const u32 starting_block = static_cast<u32>(pos / edatHeader.block_size);
|
||||
const u32 ending_block = std::min(starting_block + num_blocks, total_blocks);
|
||||
// Now we need to offset things to account for the actual 'range' requested
|
||||
const u64 startOffset = pos % edatHeader.block_size;
|
||||
|
||||
const u64 num_blocks = utils::aligned_div(startOffset + size, edatHeader.block_size);
|
||||
data_buf.resize(num_blocks * edatHeader.block_size);
|
||||
|
||||
// Find and decrypt block range covering pos + size
|
||||
const u32 starting_block = ::narrow<u32>(pos / edatHeader.block_size);
|
||||
const u32 ending_block = ::narrow<u32>(std::min<u64>(starting_block + num_blocks, total_blocks));
|
||||
u64 writeOffset = 0;
|
||||
|
||||
for (u32 i = starting_block; i < ending_block; ++i)
|
||||
{
|
||||
edata_file.seek(0);
|
||||
@@ -990,6 +973,7 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
edat_log.error("Error Decrypting data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeOffset += res;
|
||||
}
|
||||
|
||||
|
||||
+25
-25
@@ -16,9 +16,21 @@ constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
|
||||
|
||||
struct loaded_npdrm_keys
|
||||
{
|
||||
atomic_t<u128> devKlic{};
|
||||
atomic_t<u128> rifKey{};
|
||||
atomic_t<u128> dec_keys[16]{};
|
||||
atomic_t<u64> dec_keys_pos = 0;
|
||||
atomic_t<u32> npdrm_fds{0};
|
||||
|
||||
void install_decryption_key(u128 key)
|
||||
{
|
||||
dec_keys_pos.atomic_op([&](u64& pos) { dec_keys[pos++ % std::size(dec_keys)] = key; });
|
||||
}
|
||||
|
||||
// TODO: Check if correct for ELF files usage
|
||||
u128 last_key() const
|
||||
{
|
||||
const usz pos = dec_keys_pos;
|
||||
return pos ? dec_keys[(pos - 1) % std::size(dec_keys)].load() : u128{};
|
||||
}
|
||||
};
|
||||
|
||||
struct NPD_HEADER
|
||||
@@ -27,7 +39,7 @@ struct NPD_HEADER
|
||||
s32 version;
|
||||
s32 license;
|
||||
s32 type;
|
||||
u8 content_id[0x30];
|
||||
char content_id[0x30];
|
||||
u8 digest[0x10];
|
||||
u8 title_hash[0x10];
|
||||
u8 dev_hash[0x10];
|
||||
@@ -43,16 +55,16 @@ struct EDAT_HEADER
|
||||
};
|
||||
|
||||
// Decrypts full file, or null/empty file
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose);
|
||||
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID);
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID = nullptr, u32* license = nullptr);
|
||||
|
||||
u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
|
||||
struct EDATADecrypter final : fs::file_base
|
||||
{
|
||||
// file stream
|
||||
const fs::file edata_file;
|
||||
fs::file edata_file;
|
||||
u64 file_size{0};
|
||||
u32 total_blocks{0};
|
||||
u64 pos{0};
|
||||
@@ -61,38 +73,26 @@ struct EDATADecrypter final : fs::file_base
|
||||
EDAT_HEADER edatHeader{};
|
||||
|
||||
// Internal data buffers.
|
||||
std::unique_ptr<u8[]> data_buf{};
|
||||
u64 data_buf_size{0};
|
||||
std::vector<u8> data_buf{};
|
||||
|
||||
u128 dec_key{};
|
||||
|
||||
// edat usage
|
||||
u128 rif_key{};
|
||||
u128 dev_key{};
|
||||
public:
|
||||
// SdataByFd usage
|
||||
EDATADecrypter(fs::file&& input)
|
||||
: edata_file(std::move(input)) {}
|
||||
// Edat usage
|
||||
EDATADecrypter(fs::file&& input, const u128& dev_key, const u128& rif_key)
|
||||
EDATADecrypter(fs::file&& input, u128 dec_key = {})
|
||||
: edata_file(std::move(input))
|
||||
, rif_key(rif_key)
|
||||
, dev_key(dev_key) {}
|
||||
, dec_key(dec_key)
|
||||
{
|
||||
}
|
||||
|
||||
~EDATADecrypter() override {}
|
||||
// false if invalid
|
||||
bool ReadHeader();
|
||||
u64 ReadData(u64 pos, u8* data, u64 size);
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t stats;
|
||||
stats.is_directory = false;
|
||||
stats.is_writable = false;
|
||||
fs::stat_t stats = edata_file.stat();
|
||||
stats.is_writable = false; // TODO
|
||||
stats.size = file_size;
|
||||
stats.atime = -1;
|
||||
stats.ctime = -1;
|
||||
stats.mtime = -1;
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
+60
-64
@@ -15,14 +15,12 @@ LOG_CHANNEL(pkg_log, "PKG");
|
||||
package_reader::package_reader(const std::string& path)
|
||||
: m_path(path)
|
||||
{
|
||||
if (!fs::is_file(path))
|
||||
if (!m_file.open(path))
|
||||
{
|
||||
pkg_log.error("PKG file not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_filelist.emplace_back(fs::file{path});
|
||||
|
||||
m_is_valid = read_header();
|
||||
|
||||
if (!m_is_valid)
|
||||
@@ -51,7 +49,7 @@ package_reader::~package_reader()
|
||||
|
||||
bool package_reader::read_header()
|
||||
{
|
||||
if (m_path.empty() || m_filelist.empty())
|
||||
if (m_path.empty() || !m_file)
|
||||
{
|
||||
pkg_log.error("Reading PKG header: no file to read!");
|
||||
return false;
|
||||
@@ -75,7 +73,7 @@ bool package_reader::read_header()
|
||||
pkg_log.notice("Header: data_size = 0x%x = %d", m_header.data_size, m_header.data_size);
|
||||
pkg_log.notice("Header: title_id = %s", m_header.title_id);
|
||||
pkg_log.notice("Header: qa_digest = 0x%x 0x%x", m_header.qa_digest[0], m_header.qa_digest[1]);
|
||||
//pkg_log.notice("Header: klicensee = 0x%x = %d", header.klicensee, header.klicensee);
|
||||
pkg_log.notice("Header: klicensee = %s", m_header.klicensee.value());
|
||||
|
||||
// Get extended PKG information for PSP or PSVita
|
||||
if (m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA)
|
||||
@@ -131,7 +129,7 @@ bool package_reader::read_header()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_header.pkg_size > m_filelist[0].size())
|
||||
if (m_header.pkg_size > m_file.size())
|
||||
{
|
||||
// Check if multi-files pkg
|
||||
if (!m_path.ends_with("_00.pkg"))
|
||||
@@ -140,11 +138,15 @@ bool package_reader::read_header()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs::file> filelist;
|
||||
filelist.emplace_back(std::move(m_file));
|
||||
|
||||
const std::string name_wo_number = m_path.substr(0, m_path.size() - 7);
|
||||
u64 cursize = m_filelist[0].size();
|
||||
u64 cursize = filelist[0].size();
|
||||
|
||||
while (cursize < m_header.pkg_size)
|
||||
{
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, m_filelist.size());
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
|
||||
|
||||
fs::file archive_file(archive_filename);
|
||||
if (!archive_file)
|
||||
@@ -153,9 +155,20 @@ bool package_reader::read_header()
|
||||
return false;
|
||||
}
|
||||
|
||||
cursize += archive_file.size();
|
||||
m_filelist.emplace_back(std::move(archive_file));
|
||||
const usz add_size = archive_file.size();
|
||||
|
||||
if (!add_size)
|
||||
{
|
||||
pkg_log.error("%s is empty, cannot read PKG", archive_filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
cursize += add_size;
|
||||
filelist.emplace_back(std::move(archive_file));
|
||||
}
|
||||
|
||||
// Gather files
|
||||
m_file = fs::make_gather(std::move(filelist));
|
||||
}
|
||||
|
||||
if (m_header.data_size + m_header.data_offset > m_header.pkg_size)
|
||||
@@ -510,10 +523,10 @@ bool package_reader::read_param_sfo()
|
||||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
|
||||
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
const std::string_view name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
if (name != "PARAM.SFO")
|
||||
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -542,6 +555,12 @@ bool package_reader::read_param_sfo()
|
||||
|
||||
m_psf = psf::load_object(tmp);
|
||||
|
||||
if (m_psf.empty())
|
||||
{
|
||||
// Invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -667,6 +686,8 @@ package_error package_reader::check_target_app_version() const
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
|
||||
|
||||
bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
{
|
||||
if (!m_is_valid)
|
||||
@@ -704,7 +725,9 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
break;
|
||||
}
|
||||
|
||||
dir += m_install_dir + '/';
|
||||
// TODO: Verify whether other content types require appending title ID
|
||||
if (m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
|
||||
dir += m_install_dir + '/';
|
||||
|
||||
// If false, an existing directory is being overwritten: cannot cancel the operation
|
||||
const bool was_null = !fs::is_dir(dir);
|
||||
@@ -742,9 +765,10 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
const std::string path = dir + vfs::escape(name);
|
||||
|
||||
pkg_log.notice("Entry 0x%08x: %s", entry.type, name);
|
||||
const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS);
|
||||
(log_error ? pkg_log.error : pkg_log.notice)("Entry 0x%08x: %s (pad=0x%x)", entry.type, name, entry.pad);
|
||||
|
||||
switch (entry.type & 0xff)
|
||||
switch (const u8 entry_type = entry.type & 0xff)
|
||||
{
|
||||
case PKG_FILE_ENTRY_NPDRM:
|
||||
case PKG_FILE_ENTRY_NPDRMEDAT:
|
||||
@@ -770,7 +794,14 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs::file out{ path, fs::rewrite })
|
||||
const bool is_buffered = entry_type == PKG_FILE_ENTRY_SDAT;
|
||||
|
||||
if (entry_type == PKG_FILE_ENTRY_NPDRMEDAT)
|
||||
{
|
||||
pkg_log.todo("NPDRM EDAT!");
|
||||
}
|
||||
|
||||
if (fs::file out = is_buffered ? fs::make_stream<std::vector<u8>>() : fs::file{ path, fs::rewrite })
|
||||
{
|
||||
bool extract_success = true;
|
||||
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
|
||||
@@ -806,6 +837,17 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_buffered)
|
||||
{
|
||||
out = DecryptEDAT(out, name, 1, reinterpret_cast<u8*>(&m_header.klicensee), true);
|
||||
if (!out || !fs::write_file(path, fs::rewrite, static_cast<fs::container_stream<std::vector<u8>>*>(out.release().get())->obj))
|
||||
{
|
||||
num_failures++;
|
||||
pkg_log.error("Failed to create file %s", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (extract_success)
|
||||
{
|
||||
if (did_overwrite)
|
||||
@@ -874,58 +916,12 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
|
||||
void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode)
|
||||
{
|
||||
if (damode == fs::seek_set)
|
||||
m_cur_offset = new_offset;
|
||||
else if (damode == fs::seek_cur)
|
||||
m_cur_offset += new_offset;
|
||||
|
||||
u64 _offset = 0;
|
||||
for (usz i = 0; i < m_filelist.size(); i++)
|
||||
{
|
||||
if (m_cur_offset < (_offset + m_filelist[i].size()))
|
||||
{
|
||||
m_cur_file = i;
|
||||
m_cur_file_offset = m_cur_offset - _offset;
|
||||
m_filelist[i].seek(m_cur_file_offset);
|
||||
break;
|
||||
}
|
||||
_offset += m_filelist[i].size();
|
||||
}
|
||||
if (m_file) m_file.seek(new_offset, damode);
|
||||
};
|
||||
|
||||
u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
|
||||
{
|
||||
ensure(m_filelist.size() > m_cur_file && m_filelist[m_cur_file]);
|
||||
|
||||
const u64 num_bytes_left = m_filelist[m_cur_file].size() - m_cur_file_offset;
|
||||
|
||||
// check if it continues in another file
|
||||
if (num_bytes > num_bytes_left)
|
||||
{
|
||||
m_filelist[m_cur_file].read(data_ptr, num_bytes_left);
|
||||
|
||||
if ((m_cur_file + 1) < m_filelist.size())
|
||||
{
|
||||
++m_cur_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cur_offset += num_bytes_left;
|
||||
m_cur_file_offset = m_filelist[m_cur_file].size();
|
||||
return num_bytes_left;
|
||||
}
|
||||
const u64 num_read = m_filelist[m_cur_file].read(static_cast<u8*>(data_ptr) + num_bytes_left, num_bytes - num_bytes_left);
|
||||
m_cur_offset += (num_read + num_bytes_left);
|
||||
m_cur_file_offset = num_read;
|
||||
return (num_read + num_bytes_left);
|
||||
}
|
||||
|
||||
const u64 num_read = m_filelist[m_cur_file].read(data_ptr, num_bytes);
|
||||
|
||||
m_cur_offset += num_read;
|
||||
m_cur_file_offset += num_read;
|
||||
|
||||
return num_read;
|
||||
return m_file ? m_file.read(data_ptr, num_bytes) : 0;
|
||||
};
|
||||
|
||||
u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
|
||||
@@ -35,6 +35,8 @@ enum : u32
|
||||
|
||||
PKG_FILE_ENTRY_OVERWRITE = 0x80000000,
|
||||
PKG_FILE_ENTRY_PSP = 0x10000000,
|
||||
|
||||
PKG_FILE_ENTRY_KNOWN_BITS = 0xff | PKG_FILE_ENTRY_PSP | PKG_FILE_ENTRY_OVERWRITE,
|
||||
};
|
||||
|
||||
enum : u32
|
||||
@@ -325,10 +327,7 @@ private:
|
||||
|
||||
std::string m_path{};
|
||||
std::string m_install_dir{};
|
||||
std::vector<fs::file> m_filelist{};
|
||||
usz m_cur_file = 0;
|
||||
u64 m_cur_offset = 0;
|
||||
u64 m_cur_file_offset = 0;
|
||||
fs::file m_file{};
|
||||
std::unique_ptr<u128[]> m_buf{};
|
||||
std::array<uchar, 16> m_dec_key{};
|
||||
|
||||
|
||||
+22
-1
@@ -1343,6 +1343,27 @@ static bool IsSelfElf32(const fs::file& f)
|
||||
return (elf_class[4] == 1);
|
||||
}
|
||||
|
||||
static bool IsDebugSelf(const fs::file& f)
|
||||
{
|
||||
if (f.size() < 0x18)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the key version.
|
||||
f.seek(0x08);
|
||||
|
||||
const u16 key_version = f.read<le_t<u16>>();
|
||||
|
||||
// Check for DEBUG version.
|
||||
if (key_version == 0x80 || key_version == 0xc0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CheckDebugSelf(fs::file& s)
|
||||
{
|
||||
if (s.size() < 0x18)
|
||||
@@ -1442,7 +1463,7 @@ bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
|
||||
|
||||
self.seek(0);
|
||||
|
||||
if (self.size() >= 4 && self.read<u32>() == "SCE\0"_u32)
|
||||
if (self.size() >= 4 && self.read<u32>() == "SCE\0"_u32 && !IsDebugSelf(self))
|
||||
{
|
||||
// Check the ELF file class (32 or 64 bit).
|
||||
const bool isElf32 = IsSelfElf32(self);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "utils.h"
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include "key_vault.h"
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
@@ -142,3 +143,80 @@ void mbedtls_zeroize(void *v, size_t n)
|
||||
static void *(*const volatile unop_memset)(void *, int, size_t) = &memset;
|
||||
(void)unop_memset(v, 0, n);
|
||||
}
|
||||
|
||||
|
||||
// SC passphrase crypto
|
||||
|
||||
void sc_form_key(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* key)
|
||||
{
|
||||
for (u32 i = 0; i < PASSPHRASE_KEY_LEN; i++)
|
||||
{
|
||||
key[i] = static_cast<u8>(sc_key[i] ^ laid_paid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> sc_combine_laid_paid(s64 laid, s64 paid)
|
||||
{
|
||||
const std::string paid_laid = fmt::format("%016llx%016llx", laid, paid);
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> out{};
|
||||
hex_to_bytes(out.data(), paid_laid.c_str(), PASSPHRASE_KEY_LEN * 2);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_get_laid_paid_from_type(int type)
|
||||
{
|
||||
// No idea what this type stands for
|
||||
switch (type)
|
||||
{
|
||||
case 0: return sc_combine_laid_paid(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
|
||||
case 1: return sc_combine_laid_paid(LAID_2, 0x1070000000000001L);
|
||||
case 2: return sc_combine_laid_paid(LAID_2, 0x0000000000000000L);
|
||||
case 3: return sc_combine_laid_paid(LAID_2, PAID_69);
|
||||
default:
|
||||
fmt::throw_exception("vtrm_get_laid_paid_from_type: Wrong type specified (type=%d)", type);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_portability_laid_paid()
|
||||
{
|
||||
// 107000002A000001
|
||||
return sc_combine_laid_paid(0x0000000000000000L, 0x0000000000000000L);
|
||||
}
|
||||
|
||||
int sc_decrypt(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
aes_context ctx;
|
||||
u8 key[PASSPHRASE_KEY_LEN];
|
||||
sc_form_key(sc_key, laid_paid, key);
|
||||
aes_setkey_dec(&ctx, key, 128);
|
||||
return aes_crypt_cbc(&ctx, AES_DECRYPT, PASSPHRASE_OUT_LEN, iv, input, output);
|
||||
}
|
||||
|
||||
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(SC_ISO_SERIES_KEY_2, vtrm_get_laid_paid_from_type(type), iv, input, output);
|
||||
}
|
||||
|
||||
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(SC_ISO_SERIES_INTERNAL_KEY_3, sc_combine_laid_paid(laid, paid), iv, input, output);
|
||||
}
|
||||
|
||||
const u8* vtrm_portability_type_mapper(int type)
|
||||
{
|
||||
// No idea what this type stands for
|
||||
switch (type)
|
||||
{
|
||||
//case 0: return key_for_type_1;
|
||||
case 1: return SC_ISO_SERIES_KEY_2;
|
||||
case 2: return SC_ISO_SERIES_KEY_1;
|
||||
case 3: return SC_KEY_FOR_MASTER_2;
|
||||
default:
|
||||
fmt::throw_exception("vtrm_portability_type_mapper: Wrong type specified (type=%d)", type);
|
||||
}
|
||||
}
|
||||
|
||||
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output)
|
||||
{
|
||||
return sc_decrypt(vtrm_portability_type_mapper(type), vtrm_portability_laid_paid(), iv, input, output);
|
||||
}
|
||||
|
||||
@@ -53,3 +53,9 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
|
||||
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len);
|
||||
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash);
|
||||
void mbedtls_zeroize(void *v, size_t n);
|
||||
|
||||
// SC passphrase crypto
|
||||
|
||||
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output);
|
||||
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output);
|
||||
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
@@ -11,9 +11,8 @@
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{30A05C4D-F5FD-421C-A864-17A64BDEAA75}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>OpenAL</RootNamespace>
|
||||
<ProjectGuid>{9610627D-20FE-4B07-8CE3-9FF68A5F1EC2}</ProjectGuid>
|
||||
<RootNamespace>Cubeb</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
@@ -37,36 +36,28 @@
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_debug.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_llvm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_release.props" />
|
||||
<Import Project="$(SolutionDir)\buildfiles\msvc\rpcs3_llvm.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>.\3rdparty\OpenAL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\cubeb\cubeb\include\;..\3rdparty\cubeb\extra\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|x64'">MaxSpeed</Optimization>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Audio\AL\OpenALBackend.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\AL\OpenALBackend.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="emucore.vcxproj">
|
||||
<Project>{c4a10229-4712-4bd2-b63e-50d93c67a038}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,19 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<UniqueIdentifier>{F573EFFC-2BB8-43DF-AEFA-4E9EA31A817F}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\AL\OpenALBackend.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Audio\AL\OpenALBackend.cpp">
|
||||
<ClCompile Include="Emu\Audio\Cubeb\CubebBackend.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Audio\Cubeb\CubebBackend.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,212 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "OpenALBackend.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "OpenAL32.lib")
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(OpenAL);
|
||||
|
||||
#define checkForAlError(sit) do { ALenum g_last_al_error = alGetError(); if(g_last_al_error != AL_NO_ERROR) OpenAL.error("%s: OpenAL error 0x%04x", sit, g_last_al_error); } while(0)
|
||||
#define checkForAlcError(sit) do { ALCenum g_last_alc_error = alcGetError(m_device); if(g_last_alc_error != ALC_NO_ERROR) { OpenAL.error("%s: OpenALC error 0x%04x", sit, g_last_alc_error); return; }} while(0)
|
||||
|
||||
OpenALBackend::OpenALBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
ALCint attribs[] = {ALC_FREQUENCY, DEFAULT_AUDIO_SAMPLING_RATE, 0, 0};
|
||||
|
||||
ALCdevice* m_device = alcOpenDevice(nullptr);
|
||||
checkForAlcError("alcOpenDevice");
|
||||
|
||||
ALCcontext* m_context = alcCreateContext(m_device, attribs);
|
||||
checkForAlcError("alcCreateContext");
|
||||
|
||||
alcMakeContextCurrent(m_context);
|
||||
checkForAlcError("alcMakeContextCurrent");
|
||||
|
||||
switch (m_channels)
|
||||
{
|
||||
case 2:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32;
|
||||
break;
|
||||
case 6:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_51CHN16 : AL_FORMAT_51CHN32;
|
||||
break;
|
||||
default:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OpenALBackend::~OpenALBackend()
|
||||
{
|
||||
if (alIsSource(m_source))
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
if (ALCcontext* m_context = alcGetCurrentContext())
|
||||
{
|
||||
ALCdevice* m_device = alcGetContextsDevice(m_context);
|
||||
alcMakeContextCurrent(nullptr);
|
||||
alcDestroyContext(m_context);
|
||||
alcCloseDevice(m_device);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenALBackend::Play()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
|
||||
checkForAlError("Play->alGetSourcei(AL_SOURCE_STATE)");
|
||||
|
||||
if (state != AL_PLAYING)
|
||||
{
|
||||
alSourcePlay(m_source);
|
||||
checkForAlError("Play->alSourcePlay");
|
||||
}
|
||||
}
|
||||
|
||||
void OpenALBackend::Pause()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
alSourcePause(m_source);
|
||||
checkForAlError("Pause->alSourcePause");
|
||||
}
|
||||
|
||||
bool OpenALBackend::IsPlaying()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
ALint state;
|
||||
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
|
||||
checkForAlError("IsPlaying->alGetSourcei(AL_SOURCE_STATE)");
|
||||
|
||||
return state == AL_PLAYING;
|
||||
}
|
||||
|
||||
void OpenALBackend::Open(u32 num_buffers)
|
||||
{
|
||||
AUDIT(!alIsSource(m_source));
|
||||
|
||||
// Initialize Source
|
||||
alGenSources(1, &m_source);
|
||||
checkForAlError("Open->alGenSources");
|
||||
|
||||
alSourcei(m_source, AL_LOOPING, AL_FALSE);
|
||||
checkForAlError("Open->alSourcei");
|
||||
|
||||
// Initialize Buffers
|
||||
alGenBuffers(num_buffers, m_buffers);
|
||||
checkForAlError("Open->alGenBuffers");
|
||||
|
||||
m_num_buffers = num_buffers;
|
||||
m_num_unqueued = num_buffers;
|
||||
}
|
||||
|
||||
void OpenALBackend::Close()
|
||||
{
|
||||
if (alIsSource(m_source))
|
||||
{
|
||||
// Stop & Kill Source
|
||||
Pause();
|
||||
alDeleteSources(1, &m_source);
|
||||
|
||||
// Free Buffers
|
||||
alDeleteBuffers(m_num_buffers, m_buffers);
|
||||
checkForAlError("alDeleteBuffers");
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenALBackend::AddData(const void* src, u32 num_samples)
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
// Unqueue processed buffers, if any
|
||||
unqueue_processed();
|
||||
|
||||
// Fail if there are no free buffers remaining
|
||||
if (m_num_unqueued == 0)
|
||||
{
|
||||
OpenAL.warning("No unqueued buffers remaining");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy data to the next available buffer
|
||||
alBufferData(m_buffers[m_next_buffer], m_format, src, num_samples * m_sample_size, m_sampling_rate);
|
||||
checkForAlError("AddData->alBufferData");
|
||||
|
||||
// Enqueue buffer
|
||||
alSourceQueueBuffers(m_source, 1, &m_buffers[m_next_buffer]);
|
||||
checkForAlError("AddData->alSourceQueueBuffers");
|
||||
|
||||
m_num_unqueued--;
|
||||
m_next_buffer = (m_next_buffer + 1) % m_num_buffers;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenALBackend::Flush()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
// Stop source first
|
||||
alSourceStop(m_source);
|
||||
checkForAlError("Flush->alSourceStop");
|
||||
|
||||
// Unqueue processed buffers (should now be all of them)
|
||||
unqueue_processed();
|
||||
}
|
||||
|
||||
void OpenALBackend::unqueue_processed()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
// Get number of buffers
|
||||
ALint num_processed;
|
||||
alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &num_processed);
|
||||
checkForAlError("Flush->alGetSourcei(AL_BUFFERS_PROCESSED)");
|
||||
|
||||
if (num_processed > 0)
|
||||
{
|
||||
// Unqueue all buffers
|
||||
ALuint x[MAX_AUDIO_BUFFERS];
|
||||
alSourceUnqueueBuffers(m_source, num_processed, x);
|
||||
checkForAlError("Flush->alSourceUnqueueBuffers");
|
||||
|
||||
m_num_unqueued += num_processed;
|
||||
}
|
||||
}
|
||||
|
||||
u64 OpenALBackend::GetNumEnqueuedSamples()
|
||||
{
|
||||
AUDIT(alIsSource(m_source));
|
||||
|
||||
// Get number of buffers queued
|
||||
ALint num_queued;
|
||||
alGetSourcei(m_source, AL_BUFFERS_QUEUED, &num_queued);
|
||||
checkForAlError("GetNumEnqueuedSamples->alGetSourcei(AL_BUFFERS_QUEUED)");
|
||||
AUDIT(static_cast<u32>(num_queued) <= m_num_buffers - m_num_unqueued);
|
||||
|
||||
// Get sample position
|
||||
ALint sample_pos;
|
||||
alGetSourcei(m_source, AL_SAMPLE_OFFSET, &sample_pos);
|
||||
checkForAlError("GetNumEnqueuedSamples->alGetSourcei(AL_SAMPLE_OFFSET)");
|
||||
|
||||
// Return
|
||||
return (num_queued * AUDIO_BUFFER_SAMPLES) + (sample_pos % AUDIO_BUFFER_SAMPLES);
|
||||
}
|
||||
|
||||
f32 OpenALBackend::SetFrequencyRatio(f32 new_ratio)
|
||||
{
|
||||
new_ratio = std::clamp(new_ratio, 0.5f, 2.0f);
|
||||
|
||||
alSourcef(m_source, AL_PITCH, new_ratio);
|
||||
checkForAlError("SetFrequencyRatio->alSourcei(AL_PITCH)");
|
||||
|
||||
return new_ratio;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "3rdparty/OpenAL/include/alext.h"
|
||||
|
||||
class OpenALBackend : public AudioBackend
|
||||
{
|
||||
private:
|
||||
ALint m_format{};
|
||||
ALuint m_source{};
|
||||
ALuint m_buffers[MAX_AUDIO_BUFFERS]{};
|
||||
ALsizei m_num_buffers{};
|
||||
|
||||
u32 m_next_buffer = 0;
|
||||
u32 m_num_unqueued = 0;
|
||||
|
||||
void unqueue_processed();
|
||||
|
||||
public:
|
||||
OpenALBackend();
|
||||
~OpenALBackend() override;
|
||||
|
||||
const char* GetName() const override { return "OpenAL"; }
|
||||
|
||||
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
void Open(u32 num_buffers) override;
|
||||
void Close() override;
|
||||
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
bool AddData(const void* src, u32 num_samples) override;
|
||||
void Flush() override;
|
||||
|
||||
u64 GetNumEnqueuedSamples() override;
|
||||
f32 SetFrequencyRatio(f32 new_ratio) override;
|
||||
};
|
||||
@@ -1,166 +0,0 @@
|
||||
#ifndef HAVE_ALSA
|
||||
#error "ALSA support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
#include "ALSABackend.h"
|
||||
|
||||
LOG_CHANNEL(ALSA);
|
||||
|
||||
static void error(int err, const char* reason)
|
||||
{
|
||||
ALSA.error("ALSA: %s failed: %s\n", reason, snd_strerror(err));
|
||||
}
|
||||
|
||||
static bool check(int err, const char* reason)
|
||||
{
|
||||
if (err < 0)
|
||||
{
|
||||
error(err, reason);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ALSABackend::ALSABackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
}
|
||||
|
||||
ALSABackend::~ALSABackend()
|
||||
{
|
||||
if (tls_sw_params || tls_hw_params || tls_handle)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
void ALSABackend::Open(u32 num_buffers)
|
||||
{
|
||||
if (!check(snd_pcm_open(&tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_malloc(&tls_hw_params), "snd_pcm_hw_params_malloc"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_any(tls_handle, tls_hw_params), "snd_pcm_hw_params_any"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_access(tls_handle, tls_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_format(tls_handle, tls_hw_params, m_convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format"))
|
||||
return;
|
||||
|
||||
uint rate = m_sampling_rate;
|
||||
if (!check(snd_pcm_hw_params_set_rate_near(tls_handle, tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_channels(tls_handle, tls_hw_params, m_channels), "snd_pcm_hw_params_set_channels"))
|
||||
return;
|
||||
|
||||
//uint period = 5333;
|
||||
//if (!check(snd_pcm_hw_params_set_period_time_near(tls_handle, tls_hw_params, &period, nullptr), "snd_pcm_hw_params_set_period_time_near"))
|
||||
// return;
|
||||
|
||||
//if (!check(snd_pcm_hw_params_set_periods(tls_handle, tls_hw_params, 4, 0), "snd_pcm_hw_params_set_periods"))
|
||||
// return;
|
||||
|
||||
snd_pcm_uframes_t bufsize_frames = num_buffers * AUDIO_BUFFER_SAMPLES;
|
||||
snd_pcm_uframes_t period_frames = AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_buffer_size_near(tls_handle, tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_set_buffer_size_near"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_period_size_near(tls_handle, tls_hw_params, &period_frames, 0), "snd_pcm_hw_params_set_period_size"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params(tls_handle, tls_hw_params), "snd_pcm_hw_params"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_get_buffer_size(tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_get_buffer_size"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_get_period_size(tls_hw_params, &period_frames, nullptr), "snd_pcm_hw_params_get_period_size"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_malloc(&tls_sw_params), "snd_pcm_sw_params_malloc"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_current(tls_handle, tls_sw_params), "snd_pcm_sw_params_current"))
|
||||
return;
|
||||
|
||||
period_frames *= m_start_threshold;
|
||||
|
||||
if (!check(snd_pcm_sw_params_set_start_threshold(tls_handle, tls_sw_params, period_frames + 1), "snd_pcm_sw_params_set_start_threshold"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_set_stop_threshold(tls_handle, tls_sw_params, bufsize_frames), "snd_pcm_sw_params_set_stop_threshold"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params(tls_handle, tls_sw_params), "snd_pcm_sw_params"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_prepare(tls_handle), "snd_pcm_prepare"))
|
||||
return;
|
||||
|
||||
ALSA.notice("bufsize_frames=%u, period_frames=%u", bufsize_frames, period_frames);
|
||||
}
|
||||
|
||||
void ALSABackend::Close()
|
||||
{
|
||||
if (tls_sw_params)
|
||||
{
|
||||
snd_pcm_sw_params_free(tls_sw_params);
|
||||
tls_sw_params = nullptr;
|
||||
}
|
||||
|
||||
if (tls_hw_params)
|
||||
{
|
||||
snd_pcm_hw_params_free(tls_hw_params);
|
||||
tls_hw_params = nullptr;
|
||||
}
|
||||
|
||||
if (tls_handle)
|
||||
{
|
||||
snd_pcm_close(tls_handle);
|
||||
tls_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ALSABackend::AddData(const void* src, u32 num_samples)
|
||||
{
|
||||
u32 num_frames = num_samples / m_channels;
|
||||
|
||||
int res = snd_pcm_writei(tls_handle, src, num_frames);
|
||||
|
||||
if (res == -EAGAIN)
|
||||
{
|
||||
ALSA.warning("EAGAIN");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
res = snd_pcm_recover(tls_handle, res, 0);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ALSA.warning("Failed to recover (%d)", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res + 0u != num_frames)
|
||||
{
|
||||
ALSA.warning("Error (%d)", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef HAVE_ALSA
|
||||
#error "ALSA support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
class ALSABackend : public AudioBackend
|
||||
{
|
||||
snd_pcm_t* tls_handle{};
|
||||
snd_pcm_hw_params_t* tls_hw_params{};
|
||||
snd_pcm_sw_params_t* tls_sw_params{};
|
||||
|
||||
public:
|
||||
ALSABackend();
|
||||
virtual ~ALSABackend() override;
|
||||
|
||||
ALSABackend(const ALSABackend&) = delete;
|
||||
ALSABackend& operator=(const ALSABackend&) = delete;
|
||||
|
||||
virtual const char* GetName() const override { return "ALSA"; }
|
||||
|
||||
static const u32 capabilities = 0;
|
||||
virtual u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
virtual void Open(u32) override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual bool AddData(const void* src, u32 num_samples) override;
|
||||
};
|
||||
@@ -2,67 +2,29 @@
|
||||
#include "AudioBackend.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
AudioBackend::AudioBackend()
|
||||
{
|
||||
m_convert_to_u16 = static_cast<bool>(g_cfg.audio.convert_to_u16);
|
||||
m_sample_size = m_convert_to_u16 ? sizeof(u16) : sizeof(float);
|
||||
m_start_threshold = g_cfg.audio.start_threshold;
|
||||
|
||||
const u32 sampling_period_multiplier_u32 = g_cfg.audio.sampling_period_multiplier;
|
||||
|
||||
if (sampling_period_multiplier_u32 == 100)
|
||||
{
|
||||
m_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const f32 sampling_period_multiplier = sampling_period_multiplier_u32 / 100.0f;
|
||||
const f32 sampling_rate_multiplier = 1.0f / sampling_period_multiplier;
|
||||
m_sampling_rate = static_cast<u32>(f32{ DEFAULT_AUDIO_SAMPLING_RATE } *sampling_rate_multiplier);
|
||||
}
|
||||
|
||||
const audio_downmix downmix = g_cfg.audio.audio_channel_downmix.get();
|
||||
|
||||
switch (downmix)
|
||||
{
|
||||
case audio_downmix::no_downmix:
|
||||
m_channels = 8;
|
||||
break;
|
||||
case audio_downmix::downmix_to_stereo:
|
||||
m_channels = 2;
|
||||
break;
|
||||
case audio_downmix::downmix_to_5_1:
|
||||
m_channels = 6;
|
||||
break;
|
||||
case audio_downmix::use_application_settings:
|
||||
m_channels = 2; // TODO
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Unknown audio channel mode %s (%d)", downmix, static_cast<int>(downmix));
|
||||
}
|
||||
}
|
||||
AudioBackend::AudioBackend() {}
|
||||
|
||||
/*
|
||||
* Helper methods
|
||||
*/
|
||||
u32 AudioBackend::get_sampling_rate() const
|
||||
{
|
||||
return m_sampling_rate;
|
||||
return static_cast<std::underlying_type_t<decltype(m_sampling_rate)>>(m_sampling_rate);
|
||||
}
|
||||
|
||||
u32 AudioBackend::get_sample_size() const
|
||||
{
|
||||
return m_sample_size;
|
||||
return static_cast<std::underlying_type_t<decltype(m_sample_size)>>(m_sample_size);
|
||||
}
|
||||
|
||||
u32 AudioBackend::get_channels() const
|
||||
{
|
||||
return m_channels;
|
||||
return static_cast<std::underlying_type_t<decltype(m_channels)>>(m_channels);
|
||||
}
|
||||
|
||||
bool AudioBackend::get_convert_to_u16() const
|
||||
bool AudioBackend::get_convert_to_s16() const
|
||||
{
|
||||
return m_convert_to_u16;
|
||||
return m_sample_size == AudioSampleSize::S16;
|
||||
}
|
||||
|
||||
bool AudioBackend::has_capability(u32 cap) const
|
||||
@@ -75,24 +37,6 @@ void AudioBackend::dump_capabilities(std::string& out) const
|
||||
u32 count = 0;
|
||||
const u32 capabilities = GetCapabilities();
|
||||
|
||||
if (capabilities & PLAY_PAUSE_FLUSH)
|
||||
{
|
||||
fmt::append(out, "PLAY_PAUSE_FLUSH");
|
||||
count++;
|
||||
}
|
||||
|
||||
if (capabilities & IS_PLAYING)
|
||||
{
|
||||
fmt::append(out, "%sIS_PLAYING", count > 0 ? " | " : "");
|
||||
count++;
|
||||
}
|
||||
|
||||
if (capabilities & GET_NUM_ENQUEUED_SAMPLES)
|
||||
{
|
||||
fmt::append(out, "%sGET_NUM_ENQUEUED_SAMPLES", count > 0 ? " | " : "");
|
||||
count++;
|
||||
}
|
||||
|
||||
if (capabilities & SET_FREQUENCY_RATIO)
|
||||
{
|
||||
fmt::append(out, "%sSET_FREQUENCY_RATIO", count > 0 ? " | " : "");
|
||||
|
||||
@@ -7,7 +7,33 @@ enum : u32
|
||||
{
|
||||
DEFAULT_AUDIO_SAMPLING_RATE = 48000,
|
||||
MAX_AUDIO_BUFFERS = 64,
|
||||
AUDIO_BUFFER_SAMPLES = 256
|
||||
AUDIO_BUFFER_SAMPLES = 256,
|
||||
AUDIO_MIN_LATENCY = 512,
|
||||
AUDIO_MAX_CHANNELS = 8,
|
||||
};
|
||||
|
||||
enum class AudioFreq : u32
|
||||
{
|
||||
FREQ_32K = 32000,
|
||||
FREQ_44K = 44100,
|
||||
FREQ_48K = 48000,
|
||||
FREQ_88K = 88200,
|
||||
FREQ_96K = 96000,
|
||||
FREQ_176K = 176400,
|
||||
FREQ_192K = 192000,
|
||||
};
|
||||
|
||||
enum class AudioSampleSize : u32
|
||||
{
|
||||
FLOAT = sizeof(float),
|
||||
S16 = sizeof(s16),
|
||||
};
|
||||
|
||||
enum class AudioChannelCnt : u32
|
||||
{
|
||||
STEREO = 2,
|
||||
SURROUND_5_1 = 6,
|
||||
SURROUND_7_1 = 8,
|
||||
};
|
||||
|
||||
class AudioBackend
|
||||
@@ -15,10 +41,7 @@ class AudioBackend
|
||||
public:
|
||||
enum Capabilities : u32
|
||||
{
|
||||
PLAY_PAUSE_FLUSH = 0x1, // Implements Play, Pause, Flush
|
||||
IS_PLAYING = 0x2, // Implements IsPlaying
|
||||
GET_NUM_ENQUEUED_SAMPLES = 0x4, // Implements GetNumEnqueuedSamples
|
||||
SET_FREQUENCY_RATIO = 0x8, // Implements SetFrequencyRatio
|
||||
SET_FREQUENCY_RATIO = 0x1, // Implements SetFrequencyRatio
|
||||
};
|
||||
|
||||
AudioBackend();
|
||||
@@ -28,62 +51,43 @@ public:
|
||||
/*
|
||||
* Pure virtual methods
|
||||
*/
|
||||
virtual const char* GetName() const = 0;
|
||||
virtual std::string_view GetName() const = 0;
|
||||
virtual u32 GetCapabilities() const = 0;
|
||||
|
||||
virtual void Open(u32 num_buffers) = 0;
|
||||
virtual void Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual bool AddData(const void* src, u32 num_samples) = 0;
|
||||
// Sets write callback. It's called when backend requests new data to be sent
|
||||
// Callback should return number of submitted bytes. Calling other backend functions from callback is unsafe
|
||||
virtual void SetWriteCallback(std::function<u32(u32 /* byte_cnt */, void * /* buffer */)> cb) = 0;
|
||||
|
||||
// Returns length of one callback frame in seconds.
|
||||
virtual f64 GetCallbackFrameLen() = 0;
|
||||
|
||||
// Returns true if audio is currently being played, false otherwise
|
||||
virtual bool IsPlaying() = 0;
|
||||
|
||||
// Start playing enqueued data
|
||||
virtual void Play() = 0;
|
||||
|
||||
// Pause playing enqueued data
|
||||
virtual void Pause() = 0;
|
||||
|
||||
/*
|
||||
* This virtual method should be reimplemented if backend can fail to be initialized under non-error conditions
|
||||
* eg. when there is no audio devices attached
|
||||
*/
|
||||
virtual bool Initialized() const { return true; }
|
||||
virtual bool Initialized() { return true; }
|
||||
|
||||
/*
|
||||
* This virtual method should be reimplemented if backend can fail during normal operation
|
||||
*/
|
||||
virtual bool Operational() { return true; }
|
||||
|
||||
/*
|
||||
* Virtual methods - should be implemented depending on backend capabilities
|
||||
*/
|
||||
|
||||
// Start playing enqueued data
|
||||
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
|
||||
virtual void Play()
|
||||
{
|
||||
fmt::throw_exception("Play() not implemented");
|
||||
}
|
||||
|
||||
// Pause playing enqueued data
|
||||
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
|
||||
virtual void Pause()
|
||||
{
|
||||
fmt::throw_exception("Pause() not implemented");
|
||||
}
|
||||
|
||||
// Pause audio, and unqueue all currently queued buffers
|
||||
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
|
||||
virtual void Flush()
|
||||
{
|
||||
|
||||
fmt::throw_exception("Flush() not implemented");
|
||||
}
|
||||
|
||||
// Returns true if audio is currently being played, false otherwise
|
||||
// Should be implemented if capabilities & IS_PLAYING
|
||||
virtual bool IsPlaying()
|
||||
{
|
||||
fmt::throw_exception("IsPlaying() not implemented");
|
||||
}
|
||||
|
||||
// Returns the number of currently enqueued samples
|
||||
// Should be implemented if capabilities & GET_NUM_ENQUEUED_SAMPLES
|
||||
virtual u64 GetNumEnqueuedSamples()
|
||||
{
|
||||
fmt::throw_exception("GetNumEnqueuedSamples() not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sets a new frequency ratio. Backend is allowed to modify the ratio value, e.g. clamping it to the allowed range
|
||||
// Returns the new frequency ratio set
|
||||
// Should be implemented if capabilities & SET_FREQUENCY_RATIO
|
||||
@@ -93,7 +97,6 @@ public:
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper methods
|
||||
*/
|
||||
@@ -103,16 +106,14 @@ public:
|
||||
|
||||
u32 get_channels() const;
|
||||
|
||||
bool get_convert_to_u16() const;
|
||||
bool get_convert_to_s16() const;
|
||||
|
||||
bool has_capability(u32 cap) const;
|
||||
|
||||
void dump_capabilities(std::string& out) const;
|
||||
|
||||
protected:
|
||||
bool m_convert_to_u16 = false;
|
||||
u32 m_sample_size = sizeof(float);
|
||||
u32 m_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
||||
u32 m_channels = 0;
|
||||
u32 m_start_threshold = 1;
|
||||
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "Utilities/date_time.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
AudioDumper::AudioDumper(u16 ch)
|
||||
: m_header(ch)
|
||||
AudioDumper::AudioDumper(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
: m_header(ch, sample_rate, sample_size)
|
||||
{
|
||||
if (GetCh())
|
||||
{
|
||||
|
||||
@@ -30,19 +30,19 @@ struct WAVHeader
|
||||
u32 SampleRate; // 48000
|
||||
u32 ByteRate; // SampleRate * NumChannels * BitsPerSample/8
|
||||
u16 BlockAlign; // NumChannels * BitsPerSample/8
|
||||
u16 BitsPerSample; // sizeof(float) * 8
|
||||
u16 BitsPerSample; // SampleSize * 8
|
||||
|
||||
FMTHeader() = default;
|
||||
|
||||
FMTHeader(u16 ch)
|
||||
FMTHeader(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
: ID("fmt "_u32)
|
||||
, Size(16)
|
||||
, AudioFormat(3)
|
||||
, AudioFormat(sample_size == sizeof(float) ? 3 : 1)
|
||||
, NumChannels(ch)
|
||||
, SampleRate(48000)
|
||||
, ByteRate(SampleRate * ch * sizeof(float))
|
||||
, BlockAlign(ch * sizeof(float))
|
||||
, BitsPerSample(sizeof(float) * 8)
|
||||
, SampleRate(sample_rate)
|
||||
, ByteRate(SampleRate * ch * sample_size)
|
||||
, BlockAlign(ch * sample_size)
|
||||
, BitsPerSample(sample_size * 8)
|
||||
{
|
||||
}
|
||||
} FMT;
|
||||
@@ -52,9 +52,9 @@ struct WAVHeader
|
||||
|
||||
WAVHeader() = default;
|
||||
|
||||
WAVHeader(u16 ch)
|
||||
WAVHeader(u16 ch, u32 sample_rate, u32 sample_size)
|
||||
: RIFF(sizeof(RIFFHeader) + sizeof(FMTHeader))
|
||||
, FMT(ch)
|
||||
, FMT(ch, sample_rate, sample_size)
|
||||
, ID("data"_u32)
|
||||
, Size(0)
|
||||
{
|
||||
@@ -67,7 +67,7 @@ class AudioDumper
|
||||
fs::file m_output{};
|
||||
|
||||
public:
|
||||
AudioDumper(u16 ch);
|
||||
AudioDumper(u16 ch, u32 sample_rate, u32 sample_size);
|
||||
~AudioDumper();
|
||||
|
||||
void WriteData(const void* buffer, u32 size);
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "Emu/Audio/Cubeb/CubebBackend.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include "util/logs.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#include <system_error>
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(Cubeb);
|
||||
|
||||
CubebBackend::CubebBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_com_init_success = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr))
|
||||
{
|
||||
Cubeb.error("cubeb_init() failed: 0x%08x", err);
|
||||
m_ctx = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Cubeb.notice("Using backend %s", cubeb_get_backend_id(m_ctx));
|
||||
}
|
||||
|
||||
CubebBackend::~CubebBackend()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (m_ctx)
|
||||
{
|
||||
cubeb_destroy(m_ctx);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_com_init_success)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CubebBackend::Initialized()
|
||||
{
|
||||
return m_ctx != nullptr;
|
||||
}
|
||||
|
||||
bool CubebBackend::Operational()
|
||||
{
|
||||
return m_ctx != nullptr && m_stream != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
void CubebBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
if (m_ctx == nullptr) return;
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
|
||||
cubeb_stream_params stream_param{};
|
||||
stream_param.format = get_convert_to_s16() ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE;
|
||||
stream_param.rate = get_sampling_rate();
|
||||
stream_param.channels = get_channels();
|
||||
stream_param.layout = [&]()
|
||||
{
|
||||
switch (ch_cnt)
|
||||
{
|
||||
case AudioChannelCnt::STEREO: return CUBEB_LAYOUT_STEREO;
|
||||
case AudioChannelCnt::SURROUND_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
||||
case AudioChannelCnt::SURROUND_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
||||
default:
|
||||
ensure(false);
|
||||
return CUBEB_LAYOUT_UNDEFINED;
|
||||
}
|
||||
}();
|
||||
|
||||
u32 min_latency{};
|
||||
if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency))
|
||||
{
|
||||
Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, nullptr, &stream_param, std::max<u32>(AUDIO_MIN_LATENCY, min_latency), data_cb, state_cb, this))
|
||||
{
|
||||
m_stream = nullptr;
|
||||
Cubeb.error("cubeb_stream_init() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
if (m_stream == nullptr)
|
||||
{
|
||||
Cubeb.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
CloseUnlocked();
|
||||
return;
|
||||
}
|
||||
|
||||
if (int err = cubeb_stream_set_volume(m_stream, 1.0))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_set_volume() failed: 0x%08x", err);
|
||||
}
|
||||
}
|
||||
|
||||
void CubebBackend::CloseUnlocked()
|
||||
{
|
||||
if (m_stream == nullptr) return;
|
||||
|
||||
if (int err = cubeb_stream_stop(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_stop() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(m_stream);
|
||||
|
||||
m_playing = false;
|
||||
m_stream = nullptr;
|
||||
memset(m_last_sample, 0, sizeof(m_last_sample));
|
||||
}
|
||||
|
||||
void CubebBackend::Close()
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
void CubebBackend::Play()
|
||||
{
|
||||
if (m_playing) return;
|
||||
|
||||
if (int err = cubeb_stream_start(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_start() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = true;
|
||||
}
|
||||
|
||||
void CubebBackend::Pause()
|
||||
{
|
||||
if (int err = cubeb_stream_stop(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_stop() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
bool CubebBackend::IsPlaying()
|
||||
{
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
void CubebBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 CubebBackend::GetCallbackFrameLen()
|
||||
{
|
||||
cubeb_stream_params stream_param{};
|
||||
stream_param.format = get_convert_to_s16() ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE;
|
||||
stream_param.rate = get_sampling_rate();
|
||||
stream_param.channels = get_channels();
|
||||
|
||||
u32 min_latency{};
|
||||
if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency))
|
||||
{
|
||||
Cubeb.error("cubeb_get_min_latency() failed: 0x%08x", err);
|
||||
}
|
||||
|
||||
return static_cast<f64>(std::max<u32>(AUDIO_MIN_LATENCY, min_latency)) / get_sampling_rate();
|
||||
}
|
||||
|
||||
long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock);
|
||||
|
||||
if (nframes && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
|
||||
{
|
||||
const u32 sample_size = cubeb->get_sample_size() * cubeb->get_channels();
|
||||
const u32 bytes_req = nframes * cubeb->get_sample_size() * cubeb->get_channels();
|
||||
u32 written = std::min(cubeb->m_write_callback(bytes_req, output_buffer), bytes_req);
|
||||
written -= written % sample_size;
|
||||
|
||||
if (written >= sample_size)
|
||||
{
|
||||
memcpy(cubeb->m_last_sample, static_cast<u8*>(output_buffer) + written - sample_size, sample_size);
|
||||
}
|
||||
|
||||
for (u32 i = written; i < bytes_req; i += sample_size)
|
||||
{
|
||||
memcpy(static_cast<u8*>(output_buffer) + i, cubeb->m_last_sample, sample_size);
|
||||
}
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
void CubebBackend::state_cb(cubeb_stream* /* stream */, void* user_ptr, cubeb_state state)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
|
||||
if (state == CUBEB_STATE_ERROR)
|
||||
{
|
||||
Cubeb.error("Stream entered error state");
|
||||
cubeb->m_reset_req = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "Utilities/mutex.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#include "cubeb/cubeb.h"
|
||||
|
||||
class CubebBackend final : public AudioBackend
|
||||
{
|
||||
public:
|
||||
CubebBackend();
|
||||
~CubebBackend() override;
|
||||
|
||||
CubebBackend(const CubebBackend&) = delete;
|
||||
CubebBackend& operator=(const CubebBackend&) = delete;
|
||||
|
||||
std::string_view GetName() const override { return "Cubeb"sv; }
|
||||
|
||||
static const u32 capabilities = 0;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
void Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
private:
|
||||
cubeb* m_ctx = nullptr;
|
||||
cubeb_stream* m_stream = nullptr;
|
||||
#ifdef _WIN32
|
||||
bool m_com_init_success = false;
|
||||
#endif
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
u8 m_last_sample[sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)]{};
|
||||
|
||||
bool m_playing = false;
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
// Cubeb callbacks
|
||||
static long data_cb(cubeb_stream* stream, void* user_ptr, void const* input_buffer, void* output_buffer, long nframes);
|
||||
static void state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state);
|
||||
|
||||
void CloseUnlocked();
|
||||
};
|
||||
@@ -12,28 +12,41 @@ LOG_CHANNEL(FAudio_, "FAudio");
|
||||
FAudioBackend::FAudioBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
u32 res = FAudioCreate(&m_instance, 0, FAUDIO_DEFAULT_PROCESSOR);
|
||||
FAudio *instance;
|
||||
|
||||
u32 res = FAudioCreate(&instance, 0, FAUDIO_DEFAULT_PROCESSOR);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioCreate() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioCreate() failed(0x%08x)", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, m_channels, 48000, 0, 0, nullptr);
|
||||
res = FAudio_CreateMasteringVoice(instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, 0, nullptr);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
|
||||
FAudio_StopEngine(instance);
|
||||
return;
|
||||
}
|
||||
|
||||
OnProcessingPassStart = nullptr;
|
||||
OnProcessingPassEnd = nullptr;
|
||||
OnCriticalError = OnCriticalError_func;
|
||||
|
||||
res = FAudio_RegisterForCallbacks(instance, this);
|
||||
if (res)
|
||||
{
|
||||
// Some error recovery functionality will be lost, but otherwise backend is operational
|
||||
FAudio_.error("FAudio_RegisterForCallbacks() failed(0x%08x)", res);
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
m_instance = instance;
|
||||
}
|
||||
|
||||
FAudioBackend::~FAudioBackend()
|
||||
{
|
||||
if (m_source_voice != nullptr)
|
||||
{
|
||||
FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
FAudioVoice_DestroyVoice(m_source_voice);
|
||||
}
|
||||
Close();
|
||||
|
||||
if (m_master_voice != nullptr)
|
||||
{
|
||||
@@ -49,127 +62,222 @@ FAudioBackend::~FAudioBackend()
|
||||
|
||||
void FAudioBackend::Play()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
FAudio_.error("Play() called uninitialized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_playing) return;
|
||||
|
||||
const u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioSourceVoice_Start() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioSourceVoice_Start() failed(0x%08x)", res);
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = true;
|
||||
}
|
||||
|
||||
void FAudioBackend::Pause()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
if (m_source_voice)
|
||||
{
|
||||
u32 res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_Stop() failed(0x%08x)", res);
|
||||
}
|
||||
|
||||
if (res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice))
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_FlushSourceBuffers() failed(0x%08x)", res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FAudio_.error("Pause() called uninitialized");
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void FAudioBackend::CloseUnlocked()
|
||||
{
|
||||
if (m_source_voice == nullptr) return;
|
||||
|
||||
const u32 res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioSourceVoice_Stop() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioSourceVoice_Stop() failed(0x%08x)", res);
|
||||
}
|
||||
}
|
||||
|
||||
void FAudioBackend::Flush()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
const u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioSourceVoice_FlushSourceBuffers() failed(0x%08x)", res);
|
||||
}
|
||||
}
|
||||
|
||||
bool FAudioBackend::IsPlaying()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
FAudioVoiceState state;
|
||||
FAudioSourceVoice_GetState(m_source_voice, &state, FAUDIO_VOICE_NOSAMPLESPLAYED);
|
||||
|
||||
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
||||
FAudioVoice_DestroyVoice(m_source_voice);
|
||||
m_playing = false;
|
||||
m_source_voice = nullptr;
|
||||
m_data_buf = nullptr;
|
||||
m_data_buf_len = 0;
|
||||
memset(m_last_sample, 0, sizeof(m_last_sample));
|
||||
}
|
||||
|
||||
void FAudioBackend::Close()
|
||||
{
|
||||
Pause();
|
||||
Flush();
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
void FAudioBackend::Open(u32 /* num_buffers */)
|
||||
bool FAudioBackend::Initialized()
|
||||
{
|
||||
return m_instance != nullptr;
|
||||
}
|
||||
|
||||
bool FAudioBackend::Operational()
|
||||
{
|
||||
return m_instance != nullptr && m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
void FAudioBackend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
if (m_instance == nullptr) return;
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
|
||||
FAudioWaveFormatEx waveformatex;
|
||||
waveformatex.wFormatTag = m_convert_to_u16 ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = m_channels;
|
||||
waveformatex.nSamplesPerSec = m_sampling_rate;
|
||||
waveformatex.nAvgBytesPerSec = static_cast<u32>(m_sampling_rate * m_channels * m_sample_size);
|
||||
waveformatex.nBlockAlign = m_channels * m_sample_size;
|
||||
waveformatex.wBitsPerSample = m_sample_size * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = get_channels();
|
||||
waveformatex.nSamplesPerSec = get_sampling_rate();
|
||||
waveformatex.nAvgBytesPerSec = static_cast<u32>(get_sampling_rate() * get_channels() * get_sample_size());
|
||||
waveformatex.nBlockAlign = get_channels() * get_sample_size();
|
||||
waveformatex.wBitsPerSample = get_sample_size() * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
|
||||
const u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, nullptr, nullptr, nullptr);
|
||||
OnVoiceProcessingPassStart = OnVoiceProcessingPassStart_func;
|
||||
OnVoiceProcessingPassEnd = nullptr;
|
||||
OnStreamEnd = nullptr;
|
||||
OnBufferStart = nullptr;
|
||||
OnBufferEnd = nullptr;
|
||||
OnLoopEnd = nullptr;
|
||||
OnVoiceError = nullptr;
|
||||
|
||||
const u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudio_CreateSourceVoice() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudio_CreateSourceVoice() failed(0x%08x)", res);
|
||||
}
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
FAudio_.fatal("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
return;
|
||||
}
|
||||
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW);
|
||||
|
||||
m_data_buf_len = get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(FAUDIO_DEFAULT_FREQ_RATIO) / 1000;
|
||||
m_data_buf = std::make_unique<u8[]>(m_data_buf_len);
|
||||
}
|
||||
|
||||
bool FAudioBackend::AddData(const void* src, u32 num_samples)
|
||||
bool FAudioBackend::IsPlaying()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
FAudioVoiceState state;
|
||||
FAudioSourceVoice_GetState(m_source_voice, &state, FAUDIO_VOICE_NOSAMPLESPLAYED);
|
||||
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
FAudio_.warning("Too many buffers enqueued (%d)", state.BuffersQueued);
|
||||
return false;
|
||||
}
|
||||
|
||||
FAudioBuffer buffer;
|
||||
buffer.AudioBytes = num_samples * m_sample_size;
|
||||
buffer.Flags = 0;
|
||||
buffer.LoopBegin = FAUDIO_NO_LOOP_REGION;
|
||||
buffer.LoopCount = 0;
|
||||
buffer.LoopLength = 0;
|
||||
buffer.pAudioData = static_cast<const u8*>(src);
|
||||
buffer.pContext = nullptr;
|
||||
buffer.PlayBegin = 0;
|
||||
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
const u32 res = FAudioSourceVoice_SubmitSourceBuffer(m_source_voice, &buffer, nullptr);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioSourceVoice_SubmitSourceBuffer() failed(0x%08x)", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 FAudioBackend::GetNumEnqueuedSamples()
|
||||
{
|
||||
FAudioVoiceState state;
|
||||
FAudioSourceVoice_GetState(m_source_voice, &state, 0);
|
||||
|
||||
// all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining
|
||||
return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES);
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
f32 FAudioBackend::SetFrequencyRatio(f32 new_ratio)
|
||||
{
|
||||
new_ratio = std::clamp(new_ratio, FAUDIO_MIN_FREQ_RATIO, FAUDIO_DEFAULT_FREQ_RATIO);
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
FAudio_.error("SetFrequencyRatio() called uninitialized");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
const u32 res = FAudioSourceVoice_SetFrequencyRatio(m_source_voice, new_ratio, FAUDIO_COMMIT_NOW);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.fatal("FAudioSourceVoice_SetFrequencyRatio() failed(0x%08x)", res);
|
||||
FAudio_.error("FAudioSourceVoice_SetFrequencyRatio() failed(0x%08x)", res);
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return new_ratio;
|
||||
}
|
||||
|
||||
void FAudioBackend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 FAudioBackend::GetCallbackFrameLen()
|
||||
{
|
||||
constexpr f64 _10ms = 0.01;
|
||||
|
||||
if (m_instance == nullptr)
|
||||
{
|
||||
FAudio_.error("GetCallbackFrameLen() called uninitialized");
|
||||
return _10ms;
|
||||
}
|
||||
|
||||
f64 min_latency{};
|
||||
|
||||
u32 samples_per_q = 0, freq = 0;
|
||||
FAudio_GetProcessingQuantum(m_instance, &samples_per_q, &freq);
|
||||
|
||||
if (freq)
|
||||
{
|
||||
min_latency = static_cast<f64>(samples_per_q) / freq;
|
||||
}
|
||||
|
||||
return std::max<f64>(min_latency, _10ms);
|
||||
}
|
||||
|
||||
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 && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= faudio->m_data_buf_len, "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.get()), BytesRequired);
|
||||
written -= written % sample_size;
|
||||
|
||||
if (written >= sample_size)
|
||||
{
|
||||
memcpy(faudio->m_last_sample, faudio->m_data_buf.get() + written - sample_size, sample_size);
|
||||
}
|
||||
|
||||
for (u32 i = written; i < BytesRequired; i += sample_size)
|
||||
{
|
||||
memcpy(faudio->m_data_buf.get() + i, faudio->m_last_sample, sample_size);
|
||||
}
|
||||
|
||||
FAudioBuffer buffer{};
|
||||
buffer.AudioBytes = BytesRequired;
|
||||
buffer.LoopBegin = FAUDIO_NO_LOOP_REGION;
|
||||
buffer.pAudioData = static_cast<const u8*>(faudio->m_data_buf.get());
|
||||
|
||||
const u32 res = FAudioSourceVoice_SubmitSourceBuffer(faudio->m_source_voice, &buffer, nullptr);
|
||||
if (res)
|
||||
{
|
||||
FAudio_.error("FAudioSourceVoice_SubmitSourceBuffer() failed(0x%08x)", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FAudioBackend::OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error)
|
||||
{
|
||||
FAudio_.error("OnCriticalError() failed(0x%08x)", Error);
|
||||
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
faudio->m_reset_req = true;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,14 @@
|
||||
#error "FAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#include "FAudio.h"
|
||||
|
||||
class FAudioBackend : public AudioBackend
|
||||
class FAudioBackend : public AudioBackend, public FAudioVoiceCallback, public FAudioEngineCallback
|
||||
{
|
||||
private:
|
||||
FAudio* m_instance{};
|
||||
FAudioMasteringVoice* m_master_voice{};
|
||||
FAudioSourceVoice* m_source_voice{};
|
||||
|
||||
public:
|
||||
FAudioBackend();
|
||||
~FAudioBackend() override;
|
||||
@@ -21,27 +19,47 @@ public:
|
||||
FAudioBackend(const FAudioBackend&) = delete;
|
||||
FAudioBackend& operator=(const FAudioBackend&) = delete;
|
||||
|
||||
const char* GetName() const override
|
||||
{
|
||||
return "FAudio";
|
||||
}
|
||||
std::string_view GetName() const override { return "FAudio"sv; }
|
||||
|
||||
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override
|
||||
{
|
||||
return capabilities;
|
||||
}
|
||||
static const u32 capabilities = SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
void Open(u32 /* num_buffers */) override;
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
void Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
bool AddData(const void* src, u32 num_samples) override;
|
||||
void Flush() override;
|
||||
|
||||
u64 GetNumEnqueuedSamples() override;
|
||||
f32 SetFrequencyRatio(f32 new_ratio) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 INTERNAL_BUF_SIZE_MS = 25;
|
||||
|
||||
FAudio* m_instance{};
|
||||
FAudioMasteringVoice* m_master_voice{};
|
||||
FAudioSourceVoice* m_source_voice{};
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
std::unique_ptr<u8[]> m_data_buf{};
|
||||
u64 m_data_buf_len = 0;
|
||||
u8 m_last_sample[sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)]{};
|
||||
|
||||
bool m_playing = false;
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
// FAudio voice callbacks
|
||||
static void OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, u32 BytesRequired);
|
||||
|
||||
// FAudio engine callbacks
|
||||
static void OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error);
|
||||
|
||||
void CloseUnlocked();
|
||||
};
|
||||
|
||||
@@ -6,19 +6,23 @@ class NullAudioBackend : public AudioBackend
|
||||
{
|
||||
public:
|
||||
NullAudioBackend() {}
|
||||
virtual ~NullAudioBackend() {}
|
||||
~NullAudioBackend() {}
|
||||
|
||||
virtual const char* GetName() const override { return "Null"; }
|
||||
std::string_view GetName() const override { return "Null"sv; }
|
||||
|
||||
static const u32 capabilities = PLAY_PAUSE_FLUSH;
|
||||
virtual u32 GetCapabilities() const override { return capabilities; }
|
||||
static const u32 capabilities = 0;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
virtual void Open(u32) override {}
|
||||
virtual void Close() override {}
|
||||
void Open(AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override { m_playing = false; }
|
||||
void Close() override { m_playing = false; }
|
||||
|
||||
virtual void Play() override {}
|
||||
virtual void Pause() override {}
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> /* cb */) override {};
|
||||
f64 GetCallbackFrameLen() override { return 0.01; };
|
||||
|
||||
virtual bool AddData(const void*, u32) override { return true; }
|
||||
virtual void Flush() override {}
|
||||
void Play() override { m_playing = true; }
|
||||
void Pause() override { m_playing = false; }
|
||||
bool IsPlaying() override { return m_playing; }
|
||||
|
||||
private:
|
||||
bool m_playing = false;
|
||||
};
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
#ifndef HAVE_PULSE
|
||||
#error "PulseAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include "Emu/System.h"
|
||||
#include "PulseBackend.h"
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/error.h>
|
||||
|
||||
PulseBackend::PulseBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
}
|
||||
|
||||
PulseBackend::~PulseBackend()
|
||||
{
|
||||
this->Close();
|
||||
}
|
||||
|
||||
void PulseBackend::Close()
|
||||
{
|
||||
if (this->connection)
|
||||
{
|
||||
pa_simple_free(this->connection);
|
||||
this->connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PulseBackend::Open(u32 /* num_buffers */)
|
||||
{
|
||||
pa_sample_spec ss;
|
||||
ss.format = (m_sample_size == 2) ? PA_SAMPLE_S16LE : PA_SAMPLE_FLOAT32LE;
|
||||
ss.rate = m_sampling_rate;
|
||||
|
||||
pa_channel_map channel_map;
|
||||
|
||||
if (m_channels == 2)
|
||||
{
|
||||
channel_map.channels = 2;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
}
|
||||
else if (m_channels == 6)
|
||||
{
|
||||
channel_map.channels = 6;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_LFE;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
channel_map.channels = 8;
|
||||
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
|
||||
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
|
||||
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
|
||||
channel_map.map[3] = PA_CHANNEL_POSITION_LFE;
|
||||
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
|
||||
channel_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
|
||||
channel_map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
|
||||
channel_map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
|
||||
}
|
||||
ss.channels = channel_map.channels;
|
||||
|
||||
int err;
|
||||
this->connection = pa_simple_new(NULL, "RPCS3", PA_STREAM_PLAYBACK, NULL, "Game", &ss, &channel_map, NULL, &err);
|
||||
if (!this->connection)
|
||||
{
|
||||
fprintf(stderr, "PulseAudio: Failed to initialize audio: %s\n", pa_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
bool PulseBackend::AddData(const void* src, u32 num_samples)
|
||||
{
|
||||
AUDIT(this->connection);
|
||||
|
||||
int err;
|
||||
if (pa_simple_write(this->connection, src, num_samples * m_sample_size, &err) < 0)
|
||||
{
|
||||
fprintf(stderr, "PulseAudio: Failed to write audio stream: %s\n", pa_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef HAVE_PULSE
|
||||
#error "PulseAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
class PulseBackend : public AudioBackend
|
||||
{
|
||||
public:
|
||||
PulseBackend();
|
||||
virtual ~PulseBackend() override;
|
||||
|
||||
PulseBackend(const PulseBackend&) = delete;
|
||||
PulseBackend& operator=(const PulseBackend&) = delete;
|
||||
|
||||
virtual const char* GetName() const override { return "Pulse"; }
|
||||
|
||||
static const u32 capabilities = 0;
|
||||
virtual u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
virtual void Open(u32) override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual bool AddData(const void* src, u32 num_samples) override;
|
||||
|
||||
private:
|
||||
pa_simple* connection{};
|
||||
};
|
||||
@@ -22,24 +22,24 @@ XAudio2Backend::XAudio2Backend()
|
||||
// In order to prevent errors on CreateMasteringVoice, apparently we need CoInitializeEx according to:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/xaudio2fx/nf-xaudio2fx-xaudio2createvolumemeter
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE)
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
XAudio.error("CoInitializeEx() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
m_com_init_success = true;
|
||||
}
|
||||
|
||||
hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_USE_DEFAULT_PROCESSOR);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("XAudio2Create() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
hr = instance->CreateMasteringVoice(&m_master_voice, m_channels, 48000);
|
||||
|
||||
hr = instance->RegisterForCallbacks(this);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
// Some error recovery functionality will be lost, but otherwise backend is operational
|
||||
XAudio.error("RegisterForCallbacks() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
@@ -48,149 +48,262 @@ XAudio2Backend::XAudio2Backend()
|
||||
|
||||
XAudio2Backend::~XAudio2Backend()
|
||||
{
|
||||
if (m_source_voice != nullptr)
|
||||
{
|
||||
m_source_voice->Stop();
|
||||
m_source_voice->DestroyVoice();
|
||||
}
|
||||
|
||||
if (m_master_voice != nullptr)
|
||||
{
|
||||
m_master_voice->DestroyVoice();
|
||||
}
|
||||
Close();
|
||||
|
||||
if (m_xaudio2_instance != nullptr)
|
||||
{
|
||||
m_xaudio2_instance->StopEngine();
|
||||
|
||||
// TODO: Enabling this might crash afterwards in ComPtr::InternalRelease.
|
||||
// Maybe it's both trying to do the same thing?
|
||||
//m_xaudio2_instance->Release();
|
||||
}
|
||||
|
||||
if (m_com_init_success)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Backend::Initialized()
|
||||
{
|
||||
return m_xaudio2_instance != nullptr;
|
||||
}
|
||||
|
||||
bool XAudio2Backend::Operational()
|
||||
{
|
||||
if (m_dev_listener.output_device_changed())
|
||||
{
|
||||
m_reset_req = true;
|
||||
}
|
||||
|
||||
return m_xaudio2_instance != nullptr && m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
void XAudio2Backend::Play()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
XAudio.error("Play() called uninitialized");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_playing) return;
|
||||
|
||||
const HRESULT hr = m_source_voice->Start();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.fatal("Start() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
XAudio.error("Start() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = true;
|
||||
}
|
||||
|
||||
void XAudio2Backend::CloseUnlocked()
|
||||
{
|
||||
if (m_master_voice != nullptr)
|
||||
{
|
||||
m_master_voice->DestroyVoice();
|
||||
m_master_voice = nullptr;
|
||||
}
|
||||
|
||||
if (m_source_voice != nullptr)
|
||||
{
|
||||
const HRESULT hr = m_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("Stop() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
m_source_voice->DestroyVoice();
|
||||
m_source_voice = nullptr;
|
||||
}
|
||||
|
||||
m_playing = false;
|
||||
m_data_buf = nullptr;
|
||||
m_data_buf_len = 0;
|
||||
memset(m_last_sample, 0, sizeof(m_last_sample));
|
||||
}
|
||||
|
||||
void XAudio2Backend::Close()
|
||||
{
|
||||
Pause();
|
||||
Flush();
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
void XAudio2Backend::Pause()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
const HRESULT hr = m_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
if (m_source_voice)
|
||||
{
|
||||
XAudio.fatal("Stop() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
HRESULT hr = m_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("Stop() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
|
||||
hr = m_source_voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("FlushSourceBuffers() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
XAudio.error("Pause() called uninitialized");
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_playing = false;
|
||||
}
|
||||
|
||||
void XAudio2Backend::Open(u32 /* num_buffers */)
|
||||
void XAudio2Backend::Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
WAVEFORMATEX waveformatex{};
|
||||
waveformatex.wFormatTag = m_convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = m_channels;
|
||||
waveformatex.nSamplesPerSec = m_sampling_rate;
|
||||
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(m_sampling_rate * m_channels * m_sample_size);
|
||||
waveformatex.nBlockAlign = m_channels * m_sample_size;
|
||||
waveformatex.wBitsPerSample = m_sample_size * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
CloseUnlocked();
|
||||
|
||||
const HRESULT hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
if (FAILED(hr))
|
||||
if (m_xaudio2_instance == nullptr)
|
||||
{
|
||||
XAudio.fatal("CreateSourceVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
XAudio.error("Open() called unitiliazed");
|
||||
return;
|
||||
}
|
||||
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
HRESULT hr = m_xaudio2_instance->CreateMasteringVoice(&m_master_voice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
XAudio.error("Failed to open audio backend. Make sure that no other application is running that might block audio access (e.g. Netflix).");
|
||||
m_reset_req = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = ch_cnt;
|
||||
|
||||
WAVEFORMATEX waveformatex{};
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = get_channels();
|
||||
waveformatex.nSamplesPerSec = get_sampling_rate();
|
||||
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(get_sampling_rate() * get_channels() * get_sample_size());
|
||||
waveformatex.nBlockAlign = get_channels() * get_sample_size();
|
||||
waveformatex.wBitsPerSample = get_sample_size() * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
|
||||
hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO, this);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateSourceVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
ensure(m_source_voice != nullptr);
|
||||
m_source_voice->SetVolume(1.0f);
|
||||
|
||||
m_data_buf_len = get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS * static_cast<u32>(XAUDIO2_DEFAULT_FREQ_RATIO) / 1000;
|
||||
m_data_buf = std::make_unique<u8[]>(m_data_buf_len);
|
||||
}
|
||||
|
||||
bool XAudio2Backend::IsPlaying()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
m_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
|
||||
|
||||
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
||||
}
|
||||
|
||||
bool XAudio2Backend::AddData(const void* src, u32 num_samples)
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
m_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
|
||||
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
XAudio.warning("Too many buffers enqueued (%d)", state.BuffersQueued);
|
||||
return false;
|
||||
}
|
||||
|
||||
XAUDIO2_BUFFER buffer{};
|
||||
buffer.AudioBytes = num_samples * m_sample_size;
|
||||
buffer.Flags = 0;
|
||||
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
|
||||
buffer.LoopCount = 0;
|
||||
buffer.LoopLength = 0;
|
||||
buffer.pAudioData = static_cast<const BYTE*>(src);
|
||||
buffer.pContext = nullptr;
|
||||
buffer.PlayBegin = 0;
|
||||
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
const HRESULT hr = m_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.fatal("AddData() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2Backend::Flush()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
const HRESULT hr = m_source_voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.fatal("FlushSourceBuffers() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
}
|
||||
|
||||
u64 XAudio2Backend::GetNumEnqueuedSamples()
|
||||
{
|
||||
AUDIT(m_source_voice != nullptr);
|
||||
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
m_source_voice->GetState(&state);
|
||||
|
||||
// all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining
|
||||
return static_cast<u64>(AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES);
|
||||
return m_playing;
|
||||
}
|
||||
|
||||
f32 XAudio2Backend::SetFrequencyRatio(f32 new_ratio)
|
||||
{
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
XAudio.error("SetFrequencyRatio() called uninitialized");
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
new_ratio = std::clamp(new_ratio, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
|
||||
const HRESULT hr = m_source_voice->SetFrequencyRatio(new_ratio);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.fatal("SetFrequencyRatio() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
XAudio.error("SetFrequencyRatio() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
return new_ratio;
|
||||
}
|
||||
|
||||
void XAudio2Backend::SetWriteCallback(std::function<u32(u32, void *)> cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
m_write_callback = cb;
|
||||
}
|
||||
|
||||
f64 XAudio2Backend::GetCallbackFrameLen()
|
||||
{
|
||||
constexpr f64 _10ms = 0.01;
|
||||
|
||||
if (m_source_voice == nullptr)
|
||||
{
|
||||
XAudio.error("GetCallbackFrameLen() called uninitialized");
|
||||
return _10ms;
|
||||
}
|
||||
|
||||
void *ext;
|
||||
f64 min_latency{};
|
||||
|
||||
HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, &ext);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 samples_per_q = 0, freq = 0;
|
||||
static_cast<IXAudio2Extension *>(ext)->GetProcessingQuantum(&samples_per_q, &freq);
|
||||
|
||||
if (freq)
|
||||
{
|
||||
min_latency = static_cast<f64>(samples_per_q) / freq;
|
||||
}
|
||||
}
|
||||
|
||||
return std::max<f64>(min_latency, _10ms); // 10ms is the minimum for XAudio
|
||||
}
|
||||
|
||||
void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired)
|
||||
{
|
||||
std::unique_lock lock(m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && lock.try_lock() && m_write_callback && m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= m_data_buf_len, "XAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
const u32 sample_size = get_sample_size() * get_channels();
|
||||
u32 written = std::min(m_write_callback(BytesRequired, m_data_buf.get()), BytesRequired);
|
||||
written -= written % sample_size;
|
||||
|
||||
if (written >= sample_size)
|
||||
{
|
||||
memcpy(m_last_sample, m_data_buf.get() + written - sample_size, sample_size);
|
||||
}
|
||||
|
||||
for (u32 i = written; i < BytesRequired; i += sample_size)
|
||||
{
|
||||
memcpy(m_data_buf.get() + i, m_last_sample, sample_size);
|
||||
}
|
||||
|
||||
XAUDIO2_BUFFER buffer{};
|
||||
buffer.AudioBytes = BytesRequired;
|
||||
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
|
||||
buffer.pAudioData = static_cast<const BYTE*>(m_data_buf.get());
|
||||
|
||||
const HRESULT hr = m_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("SubmitSourceBuffer() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Backend::OnCriticalError(HRESULT Error)
|
||||
{
|
||||
XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast<u32>(Error));
|
||||
m_reset_req = true;
|
||||
}
|
||||
|
||||
@@ -4,19 +4,16 @@
|
||||
#error "XAudio2 can only be built on Windows."
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "Emu/Audio/audio_device_listener.h"
|
||||
|
||||
#include <xaudio2redist.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
|
||||
class XAudio2Backend final : public AudioBackend
|
||||
class XAudio2Backend final : public AudioBackend, public IXAudio2VoiceCallback, public IXAudio2EngineCallback
|
||||
{
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<IXAudio2> m_xaudio2_instance{};
|
||||
IXAudio2MasteringVoice* m_master_voice{};
|
||||
IXAudio2SourceVoice* m_source_voice{};
|
||||
|
||||
public:
|
||||
XAudio2Backend();
|
||||
~XAudio2Backend() override;
|
||||
@@ -24,23 +21,58 @@ public:
|
||||
XAudio2Backend(const XAudio2Backend&) = delete;
|
||||
XAudio2Backend& operator=(const XAudio2Backend&) = delete;
|
||||
|
||||
const char* GetName() const override { return "XAudio2"; }
|
||||
std::string_view GetName() const override { return "XAudio2"sv; }
|
||||
|
||||
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
|
||||
static const u32 capabilities = SET_FREQUENCY_RATIO;
|
||||
u32 GetCapabilities() const override { return capabilities; }
|
||||
|
||||
bool Initialized() const override { return m_xaudio2_instance != nullptr; }
|
||||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
void Open(u32 /* num_buffers */) override;
|
||||
void Open(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
void Close() override;
|
||||
|
||||
void SetWriteCallback(std::function<u32(u32, void *)> cb) override;
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
||||
void Play() override;
|
||||
void Pause() override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
bool AddData(const void* src, u32 num_samples) override;
|
||||
void Flush() override;
|
||||
|
||||
u64 GetNumEnqueuedSamples() override;
|
||||
f32 SetFrequencyRatio(f32 new_ratio) override;
|
||||
|
||||
private:
|
||||
static constexpr u32 INTERNAL_BUF_SIZE_MS = 25;
|
||||
|
||||
Microsoft::WRL::ComPtr<IXAudio2> m_xaudio2_instance{};
|
||||
IXAudio2MasteringVoice* m_master_voice{};
|
||||
IXAudio2SourceVoice* m_source_voice{};
|
||||
bool m_com_init_success = false;
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
std::unique_ptr<u8[]> m_data_buf{};
|
||||
u64 m_data_buf_len = 0;
|
||||
u8 m_last_sample[sizeof(float) * static_cast<u32>(AudioChannelCnt::SURROUND_7_1)]{};
|
||||
|
||||
bool m_playing = false;
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
audio_device_listener m_dev_listener{};
|
||||
|
||||
// XAudio voice callbacks
|
||||
void OnVoiceProcessingPassStart(UINT32 BytesRequired) override;
|
||||
void OnVoiceProcessingPassEnd() override {}
|
||||
void OnStreamEnd() override {}
|
||||
void OnBufferStart(void* /* pBufferContext */) override {}
|
||||
void OnBufferEnd(void* /* pBufferContext*/) override {}
|
||||
void OnLoopEnd(void* /* pBufferContext */) override {}
|
||||
void OnVoiceError(void* /* pBufferContext */, HRESULT /* Error */) override {}
|
||||
|
||||
// XAudio engine callbacks
|
||||
void OnProcessingPassStart() override {};
|
||||
void OnProcessingPassEnd() override {};
|
||||
void OnCriticalError(HRESULT Error) override;
|
||||
|
||||
void CloseUnlocked();
|
||||
};
|
||||
|
||||
@@ -10,15 +10,26 @@ LOG_CHANNEL(IO);
|
||||
audio_device_listener::audio_device_listener()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_com_init_success = true;
|
||||
}
|
||||
|
||||
// Try to register a listener for device changes
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_device_enumerator));
|
||||
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_device_enumerator));
|
||||
if (hr != S_OK)
|
||||
{
|
||||
IO.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
}
|
||||
else if (m_device_enumerator)
|
||||
{
|
||||
m_device_enumerator->RegisterEndpointNotificationCallback(&m_listener);
|
||||
hr = m_device_enumerator->RegisterEndpointNotificationCallback(this);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
IO.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
m_device_enumerator->Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -32,11 +43,27 @@ audio_device_listener::~audio_device_listener()
|
||||
#ifdef _WIN32
|
||||
if (m_device_enumerator != nullptr)
|
||||
{
|
||||
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
m_device_enumerator->Release();
|
||||
}
|
||||
|
||||
if (m_com_init_success)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool audio_device_listener::input_device_changed()
|
||||
{
|
||||
return m_input_device_changed.test_and_reset();
|
||||
}
|
||||
|
||||
bool audio_device_listener::output_device_changed()
|
||||
{
|
||||
return m_output_device_changed.test_and_reset();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
template <>
|
||||
void fmt_class_string<ERole>::format(std::string& out, u64 arg)
|
||||
@@ -70,7 +97,7 @@ void fmt_class_string<EDataFlow>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
HRESULT audio_device_listener::listener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
|
||||
HRESULT audio_device_listener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id)
|
||||
{
|
||||
IO.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id);
|
||||
|
||||
@@ -80,8 +107,9 @@ HRESULT audio_device_listener::listener::OnDefaultDeviceChanged(EDataFlow flow,
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Only listen for console and communication device changes.
|
||||
if ((role != eConsole && role != eCommunications) || (flow != eRender && flow != eCapture))
|
||||
// Listen only for one device role, otherwise we're going to receive more than one
|
||||
// notification for flow type
|
||||
if (role != eConsole)
|
||||
{
|
||||
IO.notice("OnDefaultDeviceChanged(): we don't care about this device");
|
||||
return S_OK;
|
||||
@@ -90,15 +118,27 @@ HRESULT audio_device_listener::listener::OnDefaultDeviceChanged(EDataFlow flow,
|
||||
const std::wstring tmp(new_default_device_id);
|
||||
const std::string new_device_id = wchar_to_utf8(tmp.c_str());
|
||||
|
||||
if (device_id != new_device_id)
|
||||
if (flow == eRender || flow == eAll)
|
||||
{
|
||||
device_id = new_device_id;
|
||||
|
||||
IO.warning("Default device changed: new device = '%s'", device_id);
|
||||
|
||||
if (auto& g_audio = g_fxo->get<cell_audio>(); g_fxo->is_init<cell_audio>())
|
||||
if (output_device_id != new_device_id)
|
||||
{
|
||||
g_audio.m_update_configuration = true;
|
||||
output_device_id = new_device_id;
|
||||
|
||||
IO.warning("Default output device changed: new device = '%s'", output_device_id);
|
||||
|
||||
m_output_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (flow == eCapture || flow == eAll)
|
||||
{
|
||||
if (input_device_id != new_device_id)
|
||||
{
|
||||
input_device_id = new_device_id;
|
||||
|
||||
IO.warning("Default input device changed: new device = '%s'", input_device_id);
|
||||
|
||||
m_input_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,28 +4,39 @@
|
||||
#include <MMDeviceAPI.h>
|
||||
#endif
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
class audio_device_listener : public IMMNotificationClient
|
||||
#else
|
||||
class audio_device_listener
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
audio_device_listener();
|
||||
~audio_device_listener();
|
||||
|
||||
bool input_device_changed();
|
||||
bool output_device_changed();
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
struct listener : public IMMNotificationClient
|
||||
{
|
||||
std::string device_id;
|
||||
std::string input_device_id;
|
||||
std::string output_device_id;
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
|
||||
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID iid, void** object) override { return S_OK; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR device_id) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR device_id) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
|
||||
} m_listener;
|
||||
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
|
||||
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
|
||||
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
|
||||
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
|
||||
|
||||
IMMDeviceEnumerator* m_device_enumerator = nullptr;
|
||||
bool m_com_init_success = false;
|
||||
#endif
|
||||
|
||||
atomic_t<bool> m_input_device_changed = false;
|
||||
atomic_t<bool> m_output_device_changed = false;
|
||||
};
|
||||
|
||||
+16
-15
@@ -1,4 +1,4 @@
|
||||
add_library(rpcs3_emu
|
||||
add_library(rpcs3_emu
|
||||
cache_utils.cpp
|
||||
IdManager.cpp
|
||||
localized_string.cpp
|
||||
@@ -7,6 +7,7 @@
|
||||
system_config_types.cpp
|
||||
system_progress.cpp
|
||||
system_utils.cpp
|
||||
vfs_config.cpp
|
||||
VFS.cpp
|
||||
GDB.cpp
|
||||
title.cpp
|
||||
@@ -53,6 +54,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../../Utilities/mutex.cpp
|
||||
../../Utilities/rXml.cpp
|
||||
../../Utilities/sema.cpp
|
||||
../../Utilities/simple_ringbuf.cpp
|
||||
../../Utilities/StrFmt.cpp
|
||||
../../Utilities/Thread.cpp
|
||||
../../Utilities/version.cpp
|
||||
@@ -109,22 +111,9 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Audio/audio_device_listener.cpp
|
||||
Audio/AudioDumper.cpp
|
||||
Audio/AudioBackend.cpp
|
||||
Audio/AL/OpenALBackend.cpp
|
||||
Audio/Cubeb/CubebBackend.cpp
|
||||
)
|
||||
|
||||
if(USE_ALSA)
|
||||
find_package(ALSA)
|
||||
if(ALSA_FOUND)
|
||||
target_sources(rpcs3_emu PRIVATE Audio/ALSA/ALSABackend.cpp)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::alsa)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(USE_PULSE AND PULSE_FOUND)
|
||||
target_sources(rpcs3_emu PRIVATE Audio/Pulse/PulseBackend.cpp)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::pulse)
|
||||
endif()
|
||||
|
||||
if(USE_FAUDIO)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND AND NOT SDL2_VERSION VERSION_LESS 2.0.9)
|
||||
@@ -146,6 +135,9 @@ target_link_libraries(rpcs3_emu
|
||||
PUBLIC
|
||||
3rdparty::openal)
|
||||
|
||||
target_link_libraries(rpcs3_emu
|
||||
PUBLIC
|
||||
3rdparty::cubeb)
|
||||
|
||||
# Cell
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
@@ -358,6 +350,7 @@ target_link_libraries(rpcs3_emu
|
||||
|
||||
# Io
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Io/camera_config.cpp
|
||||
Io/interception.cpp
|
||||
Io/KeyboardHandler.cpp
|
||||
Io/pad_config.cpp
|
||||
@@ -368,13 +361,19 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Io/GHLtar.cpp
|
||||
Io/Buzz.cpp
|
||||
Io/Turntable.cpp
|
||||
Io/usio.cpp
|
||||
)
|
||||
|
||||
# Np
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
NP/fb_helpers.cpp
|
||||
NP/np_cache.cpp
|
||||
NP/np_contexts.cpp
|
||||
NP/np_dnshook.cpp
|
||||
NP/np_handler.cpp
|
||||
NP/np_helpers.cpp
|
||||
NP/np_notifications.cpp
|
||||
NP/np_requests.cpp
|
||||
NP/signaling_handler.cpp
|
||||
NP/np_structs_extra.cpp
|
||||
NP/rpcn_client.cpp
|
||||
@@ -394,6 +393,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/GSRender.cpp
|
||||
RSX/RSXFIFO.cpp
|
||||
RSX/rsx_methods.cpp
|
||||
RSX/rsx_vertex_data.cpp
|
||||
RSX/RSXOffload.cpp
|
||||
RSX/RSXTexture.cpp
|
||||
RSX/RSXThread.cpp
|
||||
@@ -457,6 +457,7 @@ if(TARGET 3rdparty_vulkan)
|
||||
RSX/VK/vkutils/chip_class.cpp
|
||||
RSX/VK/vkutils/commands.cpp
|
||||
RSX/VK/vkutils/data_heap.cpp
|
||||
RSX/VK/vkutils/descriptors.cpp
|
||||
RSX/VK/vkutils/image.cpp
|
||||
RSX/VK/vkutils/image_helpers.cpp
|
||||
RSX/VK/vkutils/scratch.cpp
|
||||
|
||||
+26
-24
@@ -17,12 +17,13 @@ class cpu_thread;
|
||||
class CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
const cpu_disasm_mode m_mode{};
|
||||
const std::add_pointer_t<const u8> m_offset{};
|
||||
cpu_disasm_mode m_mode{};
|
||||
const u8* m_offset{};
|
||||
const u32 m_start_pc;
|
||||
const std::add_pointer_t<const cpu_thread> m_cpu{};
|
||||
u32 m_op = 0;
|
||||
|
||||
void Write(const std::string& value)
|
||||
void format_by_mode()
|
||||
{
|
||||
switch (m_mode)
|
||||
{
|
||||
@@ -32,28 +33,27 @@ protected:
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
static_cast<u8>(m_op >> 0), last_opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
case cpu_disasm_mode::interpreter:
|
||||
{
|
||||
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
|
||||
last_opcode.insert(0, fmt::format("[%08x] %02x %02x %02x %02x: ", dump_pc,
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
static_cast<u8>(m_op >> 0)));
|
||||
break;
|
||||
}
|
||||
|
||||
case cpu_disasm_mode::compiler_elf:
|
||||
{
|
||||
last_opcode = value + '\n';
|
||||
last_opcode += '\n';
|
||||
break;
|
||||
}
|
||||
case cpu_disasm_mode::normal:
|
||||
{
|
||||
last_opcode = value;
|
||||
break;
|
||||
}
|
||||
default: fmt::throw_exception("Unreachable");
|
||||
@@ -64,26 +64,27 @@ public:
|
||||
std::string last_opcode{};
|
||||
u32 dump_pc{};
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
|
||||
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
|
||||
cpu_disasm_mode change_mode(cpu_disasm_mode mode)
|
||||
{
|
||||
return T{mode, dis.m_offset, dis.m_cpu};
|
||||
return std::exchange(m_mode, mode);
|
||||
}
|
||||
|
||||
const u8* change_ptr(const u8* ptr)
|
||||
{
|
||||
return std::exchange(m_offset, ptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0, const cpu_thread* cpu = nullptr)
|
||||
: m_mode(mode)
|
||||
, m_offset(offset)
|
||||
, m_offset(offset - start_pc)
|
||||
, m_start_pc(start_pc)
|
||||
, m_cpu(cpu)
|
||||
{
|
||||
}
|
||||
|
||||
CPUDisAsm(const CPUDisAsm&) = delete;
|
||||
|
||||
CPUDisAsm& operator=(const CPUDisAsm&) = delete;
|
||||
|
||||
virtual ~CPUDisAsm() = default;
|
||||
|
||||
virtual u32 DisAsmBranchTarget(s32 /*imm*/);
|
||||
|
||||
// TODO: Add builtin fmt helpper for best performance
|
||||
@@ -109,16 +110,17 @@ protected:
|
||||
return fmt::format("%s%s", v < 0 ? "-" : "", av);
|
||||
}
|
||||
|
||||
std::string FixOp(std::string op) const
|
||||
// Signify the formatting function the minimum required amount of characters to print for an instruction
|
||||
// Padding with spaces
|
||||
int PadOp(std::string_view op = {}, int min_spaces = 0) const
|
||||
{
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.resize(std::max<usz>(op.length(), 10), ' ');
|
||||
}
|
||||
|
||||
return op;
|
||||
return m_mode == cpu_disasm_mode::normal ? (static_cast<int>(op.size()) + min_spaces) : 10;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CPUDisAsm() = default;
|
||||
|
||||
virtual u32 disasm(u32 pc) = 0;
|
||||
virtual std::pair<const void*, usz> get_memory_span() const = 0;
|
||||
virtual std::unique_ptr<CPUDisAsm> copy_type_erased() const = 0;
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::ret: return "ret";
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::pending: return "pend";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
@@ -411,11 +412,6 @@ void cpu_thread::operator()()
|
||||
}
|
||||
if (id_type() == 2)
|
||||
{
|
||||
if (g_cfg.core.lower_spu_priority)
|
||||
{
|
||||
thread_ctrl::set_native_priority(-1);
|
||||
}
|
||||
|
||||
// force input/output denormals to zero for SPU threads (FTZ/DAZ)
|
||||
_mm_setcsr( _mm_getcsr() | 0x8040 );
|
||||
|
||||
@@ -469,7 +465,7 @@ void cpu_thread::operator()()
|
||||
cpu_thread* _cpu = get_current_cpu_thread();
|
||||
|
||||
// Wait flag isn't set asynchronously so this should be thread-safe
|
||||
if (progress == 0 && cpu_flag::wait - _cpu->state)
|
||||
if (progress == 0 && _cpu->state.none_of(cpu_flag::wait + cpu_flag::temp))
|
||||
{
|
||||
// Operation just started and syscall is imminent
|
||||
_cpu->state += cpu_flag::wait + cpu_flag::temp;
|
||||
@@ -739,6 +735,12 @@ bool cpu_thread::check_state() noexcept
|
||||
cpu_counter::add(this);
|
||||
}
|
||||
|
||||
if ((state0 & (cpu_flag::pending + cpu_flag::temp)) == cpu_flag::pending)
|
||||
{
|
||||
// Execute pending work
|
||||
cpu_work();
|
||||
}
|
||||
|
||||
if (retval)
|
||||
{
|
||||
cpu_on_stop();
|
||||
|
||||
@@ -21,6 +21,7 @@ enum class cpu_flag : u32
|
||||
ret, // Callback return requested
|
||||
signal, // Thread received a signal (HLE)
|
||||
memory, // Thread must unlock memory mutex
|
||||
pending, // Thread has postponed work
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_pause, // Thread paused
|
||||
@@ -169,6 +170,9 @@ public:
|
||||
// Callback for cpu_flag::suspend
|
||||
virtual void cpu_sleep() {}
|
||||
|
||||
// Callback for cpu_flag::pending
|
||||
virtual void cpu_work() {}
|
||||
|
||||
// Callback for cpu_flag::ret
|
||||
virtual void cpu_return() {}
|
||||
|
||||
|
||||
+129
-18
@@ -7,11 +7,60 @@
|
||||
|
||||
llvm::LLVMContext g_llvm_ctx;
|
||||
|
||||
llvm::Value* peek_through_bitcasts(llvm::Value* arg)
|
||||
{
|
||||
llvm::CastInst* i;
|
||||
|
||||
while ((i = llvm::dyn_cast_or_null<llvm::CastInst>(arg)) && i->getOpcode() == llvm::Instruction::BitCast)
|
||||
{
|
||||
arg = i->getOperand(0);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
|
||||
: m_context(g_llvm_ctx)
|
||||
, m_module(_module)
|
||||
, m_is_be(is_be)
|
||||
{
|
||||
register_intrinsic("x86_pshufb", [&](llvm::CallInst* ci) -> llvm::Value*
|
||||
{
|
||||
const auto data0 = ci->getOperand(0);
|
||||
const auto index = ci->getOperand(1);
|
||||
const auto zeros = llvm::ConstantAggregateZero::get(get_type<u8[16]>());
|
||||
|
||||
if (m_use_ssse3)
|
||||
{
|
||||
return m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_ssse3_pshuf_b_128), {data0, index});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Emulate PSHUFB (TODO)
|
||||
const auto mask = m_ir->CreateAnd(index, 0xf);
|
||||
const auto loop = llvm::BasicBlock::Create(m_context, "", m_ir->GetInsertBlock()->getParent());
|
||||
const auto prev = ci->getParent();
|
||||
const auto next = prev->splitBasicBlock(ci->getNextNode());
|
||||
|
||||
llvm::cast<llvm::BranchInst>(m_ir->GetInsertBlock()->getTerminator())->setOperand(0, loop);
|
||||
|
||||
llvm::Value* result;
|
||||
//m_ir->CreateBr(loop);
|
||||
m_ir->SetInsertPoint(loop);
|
||||
const auto i = m_ir->CreatePHI(get_type<u32>(), 2);
|
||||
const auto v = m_ir->CreatePHI(get_type<u8[16]>(), 2);
|
||||
i->addIncoming(m_ir->getInt32(0), prev);
|
||||
i->addIncoming(m_ir->CreateAdd(i, m_ir->getInt32(1)), loop);
|
||||
v->addIncoming(zeros, prev);
|
||||
result = m_ir->CreateInsertElement(v, m_ir->CreateExtractElement(data0, m_ir->CreateExtractElement(mask, i)), i);
|
||||
v->addIncoming(result, loop);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpULT(i, m_ir->getInt32(16)), loop, next);
|
||||
m_ir->SetInsertPoint(next->getFirstNonPHI());
|
||||
result = m_ir->CreateSelect(m_ir->CreateICmpSLT(index, zeros), zeros, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngine& engine)
|
||||
@@ -36,29 +85,35 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
||||
m_use_ssse3 = false;
|
||||
}
|
||||
|
||||
// Test AVX feature (TODO)
|
||||
if (cpu == "sandybridge" ||
|
||||
cpu == "ivybridge" ||
|
||||
cpu == "bdver1")
|
||||
{
|
||||
m_use_avx = true;
|
||||
}
|
||||
|
||||
// Test FMA feature (TODO)
|
||||
if (cpu == "haswell" ||
|
||||
cpu == "broadwell" ||
|
||||
cpu == "skylake" ||
|
||||
cpu == "alderlake" ||
|
||||
cpu == "bdver2" ||
|
||||
cpu == "bdver3" ||
|
||||
cpu == "bdver4" ||
|
||||
cpu.startswith("znver"))
|
||||
{
|
||||
m_use_fma = true;
|
||||
m_use_avx = true;
|
||||
}
|
||||
|
||||
// Test AVX-512 feature (TODO)
|
||||
if (cpu == "skylake-avx512" ||
|
||||
cpu == "cascadelake" ||
|
||||
cpu == "cannonlake" ||
|
||||
cpu == "cooperlake" ||
|
||||
cpu == "icelake" ||
|
||||
cpu == "icelake-client" ||
|
||||
cpu == "icelake-server" ||
|
||||
cpu == "tigerlake" ||
|
||||
cpu == "rocketlake")
|
||||
cpu == "cooperlake")
|
||||
{
|
||||
m_use_avx = true;
|
||||
m_use_fma = true;
|
||||
m_use_avx512 = true;
|
||||
}
|
||||
@@ -79,6 +134,9 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
|
||||
cpu == "rocketlake" ||
|
||||
cpu == "sapphirerapids")
|
||||
{
|
||||
m_use_avx = true;
|
||||
m_use_fma = true;
|
||||
m_use_avx512 = true;
|
||||
m_use_avx512_icl = true;
|
||||
m_use_vnni = true;
|
||||
}
|
||||
@@ -90,9 +148,9 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
uint s2 = val->getType()->getScalarSizeInBits();
|
||||
|
||||
if (type->isVectorTy())
|
||||
s1 *= llvm::cast<llvm::VectorType>(type)->getNumElements();
|
||||
s1 *= llvm::cast<llvm::FixedVectorType>(type)->getNumElements();
|
||||
if (val->getType()->isVectorTy())
|
||||
s2 *= llvm::cast<llvm::VectorType>(val->getType())->getNumElements();
|
||||
s2 *= llvm::cast<llvm::FixedVectorType>(val->getType())->getNumElements();
|
||||
|
||||
if (s1 != s2)
|
||||
{
|
||||
@@ -108,7 +166,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
|
||||
}
|
||||
|
||||
template <>
|
||||
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 a, u32 b)
|
||||
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 _pos, u32 _line)
|
||||
{
|
||||
v128 result{};
|
||||
|
||||
@@ -131,12 +189,12 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
fmt::throw_exception("[0x%x, %u] Not a vector", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Not a vector", _pos, _line);
|
||||
}
|
||||
|
||||
if (auto v = llvm::cast<llvm::VectorType>(t); v->getScalarSizeInBits() * v->getNumElements() != 128)
|
||||
if (auto v = llvm::cast<llvm::FixedVectorType>(t); v->getScalarSizeInBits() * v->getNumElements() != 128)
|
||||
{
|
||||
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", a, b, v->getScalarSizeInBits(), v->getNumElements());
|
||||
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", _pos, _line, v->getScalarSizeInBits(), v->getNumElements());
|
||||
}
|
||||
|
||||
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
|
||||
@@ -148,13 +206,18 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result;
|
||||
llvm::raw_string_ostream out(result);
|
||||
c->print(out, true);
|
||||
out.flush();
|
||||
|
||||
if (llvm::isa<llvm::ConstantExpr>(c))
|
||||
{
|
||||
// Sorry, if we cannot evaluate it we cannot use it
|
||||
fmt::throw_exception("[0x%x, %u] Constant Expression!", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Constant Expression!\n%s", _pos, _line, result);
|
||||
}
|
||||
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected constant type", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected constant type!\n%s", _pos, _line, result);
|
||||
}
|
||||
|
||||
const auto sct = t->getScalarType();
|
||||
@@ -203,14 +266,14 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", a, b);
|
||||
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", _pos, _line);
|
||||
}
|
||||
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
template <>
|
||||
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
|
||||
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t, u32 _line)
|
||||
{
|
||||
if (const auto ct = llvm::dyn_cast<llvm::IntegerType>(t); ct && ct->getBitWidth() == 128)
|
||||
{
|
||||
@@ -218,7 +281,7 @@ llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
|
||||
}
|
||||
|
||||
ensure(t->isVectorTy());
|
||||
ensure(128 == t->getScalarSizeInBits() * llvm::cast<llvm::VectorType>(t)->getNumElements());
|
||||
ensure(128 == t->getScalarSizeInBits() * llvm::cast<llvm::FixedVectorType>(t)->getNumElements());
|
||||
|
||||
const auto sct = t->getScalarType();
|
||||
|
||||
@@ -247,7 +310,55 @@ llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
|
||||
return llvm::ConstantDataVector::get(m_context, llvm::makeArrayRef(reinterpret_cast<const f64*>(v._bytes), 2));
|
||||
}
|
||||
|
||||
fmt::throw_exception("No supported constant type");
|
||||
fmt::throw_exception("[line %u] No supported constant type", _line);
|
||||
}
|
||||
|
||||
void cpu_translator::replace_intrinsics(llvm::Function& f)
|
||||
{
|
||||
for (auto& bb : f)
|
||||
{
|
||||
for (auto bit = bb.begin(); bit != bb.end();)
|
||||
{
|
||||
if (auto ci = llvm::dyn_cast<llvm::CallInst>(&*bit))
|
||||
{
|
||||
if (auto cf = ci->getCalledFunction())
|
||||
{
|
||||
if (auto it = m_intrinsics.find(std::string_view(cf->getName().data(), cf->getName().size())); it != m_intrinsics.end())
|
||||
{
|
||||
m_ir->SetInsertPoint(ci);
|
||||
ci->replaceAllUsesWith(it->second(ci));
|
||||
bit = ci->eraseFromParent();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
i = *bci->use_begin();
|
||||
}
|
||||
|
||||
if (auto si = llvm::dyn_cast_or_null<llvm::StoreInst>(i))
|
||||
{
|
||||
si->eraseFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user